diff -urN vanilla-2.2.15pre17/Documentation/Configure.help bttv-2.2.15pre17/Documentation/Configure.help --- vanilla-2.2.15pre17/Documentation/Configure.help Mon May 1 23:01:13 2000 +++ bttv-2.2.15pre17/Documentation/Configure.help Mon May 1 23:02:30 2000 @@ -12174,6 +12174,96 @@ say Y here. # +I2C support +CONFIG_I2C + I2C (pronounce: I-square-C) is a slow serial bus protocol used in + many micro controller applications and developed by Philips. SMBus, + or System Management Bus is a subset of the I2C protocol. More + information is contained in the directory Documentation/i2c/, + especially in the file called "summary" there. + + Both I2C and SMBus are supported here. You will need this for + hardware sensors support, and also for Video for Linux support. + Specifically, if you want to use a BT848 based frame grabber/overlay + boards under Linux, say Y here and also to "I2C bit-banging + interfaces", below. + + If you want I2C support, you should say Y here and also to the + specific driver for your bus adapter(s) below. If you say Y to + "/proc file system" below, you will then get a /proc interface which + is documented in Documentation/i2c/proc-interface. + + This I2C support is also available as a module. If you want to + compile it as a module, say M here and read + Documentation/modules.txt. The module will be called i2c-core.o. + +I2C bit-banging interfaces +CONFIG_I2C_ALGOBIT + This allows you to use a range of I2C adapters called bit-banging + adapters. Say Y if you own an I2C adapter belonging to this class + and then say Y to the specific driver for you adapter below. + + This support is also available as a module. If you want to compile + it as a module, say M here and read Documentation/modules.txt. The + module will be called i2c-algo-bit.o. + +Philips style parallel port adapter +CONFIG_I2C_PHILIPSPAR + This supports parallel-port I2C adapters made by Philips. Say Y if + you own such an adapter. + + This driver is also available as a module. If you want to compile + it as a module, say M here and read Documentation/modules.txt. The + module will be called i2c-philips-par.o. + +ELV adapter +CONFIG_I2C_ELV + This supports parallel-port I2C adapters called ELV. Say Y if you + own such an adapter. + + This driver is also available as a module. If you want to compile + it as a module, say M here and read Documentation/modules.txt. The + module will be called i2c-elv.o. + +Velleman K9000 adapter +CONFIG_I2C_VELLEMAN + This supports the Velleman K9000 parallel-port I2C adapter. Say Y if + you own such an adapter. + + This driver is also available as a module. If you want to compile + it as a module, say M here and read Documentation/modules.txt. The + module will be called i2c-velleman.o. + +I2C PCF 8584 interfaces +CONFIG_I2C_ALGOPCF + This allows you to use a range of I2C adapters called PCF adapters. + Say Y if you own an I2C adapter belonging to this class and then say + Y to the specific driver for you adapter below. + + This support is also available as a module. If you want to compile + it as a module, say M here and read Documentation/modules.txt. The + module will be called i2c-algo-pcf.o. + +Elektor ISA card +CONFIG_I2C_ELEKTOR + This supports the PCF8584 ISA bus I2C adapter. Say Y if you own such + an adapter. + + This driver is also available as a module. If you want to compile + it as a module, say M here and read Documentation/modules.txt. The + module will be called i2c-elektor.o. + +I2C device interface +CONFIG_I2C_CHARDEV + Say Y here to use i2c-* device files, usually found in the /dev + directory on your system. They make it possible to have user-space + programs use the I2C bus. Information on how to do this is contained + in the file Documentation/i2c/dev-interface. + + This code is also available as a module. If you want to compile + it as a module, say M here and read Documentation/modules.txt. The + module will be called i2c-dev.o. + # A couple of things I keep forgetting: # capitalize: AppleTalk, Ethernet, DOS, DMA, FAT, FTP, Internet, # Intel, IRQ, Linux, MSDOS, NetWare, NetWinder, NFS, diff -urN vanilla-2.2.15pre17/Documentation/i2c/dev-interface bttv-2.2.15pre17/Documentation/i2c/dev-interface --- vanilla-2.2.15pre17/Documentation/i2c/dev-interface Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/i2c/dev-interface Mon May 1 23:02:30 2000 @@ -0,0 +1,138 @@ +Usually, i2c devices are controlled by a kernel driver. But it is also +possible to access all devices on an adapter from userspace, through +the /dev interface. You need to load module i2c-dev for this. + +Each registered i2c adapter gets a number, counting from 0. You can +examine /proc/bus/i2c to see what number corresponds to which adapter. +I2C device files are character device files with major device number 89 +and a minor device number corresponding to the number assigned as +explained above. They should be called "i2c-%d" (i2c-0, i2c-1, ..., +i2c-10, ...). All 256 minor device numbers are reserved for i2c. + + +C example +========= + +So let's say you want to access an i2c adapter from a C program. The +first thing to do is `#include " and "#include . +Yes, I know, you should never include kernel header files, but until glibc +knows about i2c, there is not much choice. + +Now, you have to decide which adapter you want to access. You should +inspect /proc/bus/i2c to decide this. Adapter numbers are assigned +somewhat dynamically, so you can not even assume /dev/i2c-0 is the +first adapter. + +Next thing, open the device file, as follows: + int file; + int adapter_nr = 2; /* probably dynamically determined */ + char filename[20]; + + sprintf(filename,"/dev/i2c-%d",adapter_nr); + if ((file = open(filename,O_RDWR)) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + exit(1); + } + +When you have opened the device, you must specify with what device +address you want to communicate: + int addr = 0x40; /* The I2C address */ + if (ioctl(file,I2C_SLAVE,addr) < 0) { + /* ERROR HANDLING; you can check errno to see what went wrong */ + exit(1); + } + +Well, you are all set up now. You can now use SMBus commands or plain +I2C to communicate with your device. SMBus commands are preferred if +the device supports them. Both are illustrated below. + __u8 register = 0x10; /* Device register to access */ + __s32 res; + char buf[10]; + /* Using SMBus commands */ + res = i2c_smbus_read_word_data(file,register); + if (res < 0) { + /* ERROR HANDLING: i2c transaction failed */ + } else { + /* res contains the read word */ + } + /* Using I2C Write, equivalent of + i2c_smbus_write_word_data(file,register,0x6543) */ + buf[0] = register; + buf[1] = 0x43; + buf[2] = 0x65; + if ( write(file,buf,3) != 3) { + /* ERROR HANDLING: i2c transaction failed */ + } + /* Using I2C Read, equivalent of i2c_smbus_read_byte(file) */ + if (read(file,buf,1) != 1) { + /* ERROR HANDLING: i2c transaction failed */ + } else { + /* buf[0] contains the read byte */ + } + + +Full interface description +========================== + +The following IOCTLs are defined and fully supported +(see also i2c-dev.h and i2c.h): + +ioctl(file,I2C_SLAVE,long addr) + Change slave address. The address is passed in the 7 lower bits of the + argument (except for 10 bit addresses, passed in the 10 lower bits in this + case). + +ioctl(file,I2C_TENBIT,long select) + Selects ten bit addresses if select not equals 0, selects normal 7 bit + addresses if select equals 0. + +ioctl(file,I2C_FUNCS,unsigned long *funcs) + Gets the adapter functionality and puts it in *funcs. + +ioctl(file,I2C_RDWR,struct i2c_ioctl_rdwr_data *msgset) + + Do combined read/write transaction without stop in between. + The argument is a pointer to a struct i2c_ioctl_rdwr_data { + + struct i2c_msg *msgs; /* ptr to array of simple messages */ + int nmsgs; /* number of messages to exchange */ + } + + The msgs[] themselves contain further pointers into data buffers. + The function will write or read data to or from that buffers depending + on whether the I2C_M_RD flag is set in a particular message or not. + The slave address and whether to use ten bit address mode has to be + set in each message, overriding the values set with the above ioctl's. + + +Other values are NOT supported at this moment, except for I2C_SMBUS, +which you should never directly call; instead, use the access functions +below. + +You can do plain i2c transactions by using read(2) and write(2) calls. +You do not need to pass the address byte; instead, set it through +ioctl I2C_SLAVE before you try to access the device. + +You can do SMBus level transactions (see documentation file smbus-protocol +for details) through the following functions: + __s32 i2c_smbus_write_quick(int file, __u8 value); + __s32 i2c_smbus_read_byte(int file); + __s32 i2c_smbus_write_byte(int file, __u8 value); + __s32 i2c_smbus_read_byte_data(int file, __u8 command); + __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value); + __s32 i2c_smbus_read_word_data(int file, __u8 command); + __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value); + __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value); + __s32 i2c_smbus_read_block_data(int file, __u8 command, __u8 *values); + __s32 i2c_smbus_write_block_data(int file, __u8 command, __u8 length, + __u8 *values); +All these tranactions return -1 on failure; you can read errno to see +what happened. The 'write' transactions return 0 on success; the +'read' transactions return the read value, except for read_block, which +returns the number of values read. The block buffers need not be longer +than 32 bytes. + +The above functions are all macros, that resolve to calls to the +i2c_smbus_access function, that on its turn calls a specific ioctl +with the data in a specific format. Read the source code if you +want to know what happens behind the screens. diff -urN vanilla-2.2.15pre17/Documentation/i2c/i2c-protocol bttv-2.2.15pre17/Documentation/i2c/i2c-protocol --- vanilla-2.2.15pre17/Documentation/i2c/i2c-protocol Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/i2c/i2c-protocol Mon May 1 23:02:30 2000 @@ -0,0 +1,68 @@ +This document describes the i2c protocol. Or will, when it is finished :-) + +Key to symbols +============== + +S (1 bit) : Start bit +P (1 bit) : Stop bit +Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0. +A, NA (1 bit) : Accept and reverse accept bit. +Addr (7 bits): I2C 7 bit address. Note that this can be expanded as usual to + get a 10 bit I2C address. +Comm (8 bits): Command byte, a data byte which often selects a register on + the device. +Data (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh + for 16 bit data. +Count (8 bits): A data byte containing the length of a block operation. + +[..]: Data sent by I2C device, as opposed to data sent by the host adapter. + + +Simple send tranaction +====================== + +This corresponds to i2c_master_send. + + S Addr Wr [A] Data [A] Data [A] ... [A] Data [A] P + + +Simple receive transaction +=========================== + +This corresponds to i2c_master_recv + + S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P + + +Combined tranactions +==================== + +This corresponds to i2c_transfer + +They are just like the above transactions, but instead of a stop bit P +a start bit S is sent and the transaction continues. An example of +a byte read, followed by a byte write: + + S Addr Rd [A] [Data] NA S Addr Wr [A] Data [A] P + + +Modified transactions +===================== + +We have found some I2C devices that needs the following modifications: + + Flag I2C_M_NOSTART: + In a combined transaction, no 'S Addr' is generated at some point. + For example, setting I2C_M_NOSTART on the second partial message + generateds something like: + S Addr Rd [A] [Data] NA Wr [A] Data [A] P + If you set the I2C_M_NOSTART variable for the first partial message, + we do not generate Addr, but we do generate the startbit S. This will + probably confuse all other clients on your bus, so don't try this. + + Flags I2C_M_REV_DIR_ADDR + This toggles the Rd/Wr flag. That is, if you want to do a write, but + need to emit an Rd instead of a Wr, or vice versa, you set this + flag. For example: + S Addr Rd [A] Data [A] Data [A] ... [A] Data [A] P + diff -urN vanilla-2.2.15pre17/Documentation/i2c/proc-interface bttv-2.2.15pre17/Documentation/i2c/proc-interface --- vanilla-2.2.15pre17/Documentation/i2c/proc-interface Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/i2c/proc-interface Mon May 1 23:02:30 2000 @@ -0,0 +1,53 @@ +i2c-core is the core i2c module (surprise!) which offers general routines on +which other modules build. You will find that all i2c-related modules depend +on this module, so it will (need to) be loaded whenever another i2c-related +module is loaded. Seen from the outside, the most interesting is the /proc +interface. Note that there is no corresponding sysctl interface! + +/proc/bus/i2c +============= + +Whenever i2c-core is loaded, you will find a file /proc/bus/i2c, which lists +all currently registered I2C adapters. Each line contains exactly one +I2C adapter. Each line has the following format: "i2c-%d\t%9s\t%-32s't%-32s\n", +which works out to four columns separated by tabs. Note that the file +will be empty, if no adapters are registered at all. + +Adapters are numbered from 0 upwards. The first column contains the number +of the adapter, for example "i2c-4" for adapter 4. The name listed is also +the name of the /proc file which lists all devices attached to it, and +of the /dev file which corresponds to this adapter. + +The second column documents what kind of adapter this is. Some adapters +understand the full I2C protocol, others only a subset called SMBus, +and yet others are some kind of pseudo-adapters that do not understand +i2c at all. Possible values in here are "i2c", "smbus", "i2c/smbus" +and "dummy". Because the SMBus protocol can be fully emulated by i2c +adapters, if you see "i2c" here, SMBus is supported too. There may +be some future adapters which support both specific SMBus commands and +general I2C, and they will display "i2c/smbus". + +The third and fourth column are respectively the algorithm and adapter +name of this adapter. Each adapter is associated with an algorithm, +and several adapters can share the same algorithm. The combination of +algorithm name and adapter name should be unique for an adapter, but +you can't really count on that yet. + + +/proc/bus/i2c-* +=============== + +Each registered adapter gets its own file in /proc/bus/, which lists +the devices registered to the adapter. Each line in such a file contains +one registered device. Each line has the following format: +"%02x\t%-32s\t%-32s\n", which works out to three columns separated by +tabs. Note that this file can be empty, if no devices are found on +the adapter. + +The first column contains the (hexadecimal) address of the client. As +only 7-bit addresses are supported at this moment, two digits are +enough. + +The second and third column are respectively the client name and the +driver name of this client. Each client is associated with a driver, +and several clients can share the same driver. diff -urN vanilla-2.2.15pre17/Documentation/i2c/smbus-protocol bttv-2.2.15pre17/Documentation/i2c/smbus-protocol --- vanilla-2.2.15pre17/Documentation/i2c/smbus-protocol Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/i2c/smbus-protocol Mon May 1 23:02:30 2000 @@ -0,0 +1,127 @@ +Some adapters understand only the SMBus (System Management Bus) protocol, +which is a subset from the I2C protocol. Fortunately, many devices use +only the same subset, which makes it possible to put them on an SMBus. +If you write a driver for some I2C device, please try to use the SMBus +commands if at all possible (if the device uses only that subset of the +I2C protocol). This makes it possible to use the device driver on both +SMBus adapters and I2C adapters (the SMBus command set is automatically +translated to I2C on I2C adapters, but plain I2C commands can not be +handled at all on a pure SMBus adapter). + +Below is a list of SMBus commands. + +Key to symbols +============== + +S (1 bit) : Start bit +P (1 bit) : Stop bit +Rd/Wr (1 bit) : Read/Write bit. Rd equals 1, Wr equals 0. +A, NA (1 bit) : Accept and reverse accept bit. +Addr (7 bits): I2C 7 bit address. Note that this can be expanded as usual to + get a 10 bit I2C address. +Comm (8 bits): Command byte, a data byte which often selects a register on + the device. +Data (8 bits): A plain data byte. Sometimes, I write DataLow, DataHigh + for 16 bit data. +Count (8 bits): A data byte containing the length of a block operation. + +[..]: Data sent by I2C device, as opposed to data sent by the host adapter. + + +SMBus Write Quick +================= + +This sends a single byte to the device, at the place of the Rd/Wr bit. +There is no equivalent Read Quick command. + +A Addr Rd/Wr [A] P + + +SMBus Read Byte +=============== + +This reads a single byte from a device, without specifying a device +register. Some devices are so simple that this interface is enough; for +others, it is a shorthand if you want to read the same register as in +the previous SMBus command. + +S Addr Rd [A] [Data] NA P + + +SMBus Write Byte +================ + +This is the reverse of Read Byte: it sends a single byte to a device. +See Read Byte for more information. + +S Addr Wr [A] Data NA P + + +SMBus Read Byte Data +==================== + +This reads a single byte from a device, from a designated register. +The register is specified through the Comm byte. + +S Addr Wr [A] Comm [A] S Addr Rd [A] [Data] NA P + + +SMBus Read Word Data +==================== + +This command is very like Read Byte Data; again, data is read from a +device, from a designated register that is specified through the Comm +byte. But this time, the data is a complete word (16 bits). + +S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P + + +SMBus Write Byte Data +===================== + +This writes a single byte to a device, to a designated register. The +register is specified through the Comm byte. This is the opposite of +the Read Byte Data command. + +S Addr Wr [A] Comm [A] Data [A] P + + +SMBus Write Word Data +===================== + +This is the opposite operation of the Read Word Data command. 16 bits +of data is read from a device, from a designated register that is +specified through the Comm byte. + +S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] P + + +SMBus Process Call +================== + +This command selects a device register (through the Comm byte), sends +16 bits of data to it, and reads 16 bits of data in return. + +S Addr Wr [A] Comm [A] DataLow [A] DataHigh [A] + S Addr Rd [A] [DataLow] A [DataHigh] NA P + + +SMBus Block Read +================ + +This command reads a block of upto 32 bytes from a device, from a +designated register that is specified through the Comm byte. The amount +of data is specified by the device in the Count byte. + +S Addr Wr [A] Comm [A] + S Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P + + +SMBus Block Write +================= + +The opposite of the Block Read command, this writes upto 32 bytes to +a device, to a designated register that is specified through the +Comm byte. The amount of data is specified in the Count byte. + +S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P diff -urN vanilla-2.2.15pre17/Documentation/i2c/summary bttv-2.2.15pre17/Documentation/i2c/summary --- vanilla-2.2.15pre17/Documentation/i2c/summary Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/i2c/summary Mon May 1 23:02:30 2000 @@ -0,0 +1,63 @@ +This is an explanation of what i2c is, and what is supported. + +I2C and SMBus +============= + +I2C (pronounce: I square C) is a protocol developed by Philips. It is a +slow two-wire protocol (10-100 kHz), but it suffices for many types of +devices. + +SMBus (System Management Bus) is a subset of the I2C protocol. Many +modern mainboards have a System Management Bus. There are a lot of +devices which can be connected to a SMBus; the most notable are modern +memory chips with EEPROM memories and chips for hardware monitoring. + +Because the SMBus is just a special case of the generalized I2C bus, we +can simulate the SMBus protocol on plain I2C busses. The reverse is +regretfully impossible. + + +Terminology +=========== + +When we talk about I2C, we use the following terms: + Bus -> Algorithm + Adapter + Device -> Driver + Client +An Algorithm driver contains general code that can be used for a whole class +of I2C adapters. Each specific adapter driver depends on one algorithm +driver. +A Driver driver (yes, this sounds ridiculous, sorry) contains the general +code to access some type of device. Each detected device gets its own +data in the Client structure. Usually, Driver and Client are more closely +integrated than Algorithm and Adapter. + +For a given configuration, you will need a driver for your I2C bus (usually +a separate Adapter and Algorithm driver), and drivers for your I2C devices +(usually one driver for each device). + + +Included Drivers +================ + +Base modules +------------ + +i2c-core: The basic I2C code, including the /proc interface +i2c-dev: The /dev interface + +Algorithm drivers +----------------- + +i2c-algo-bit: A bit-banging algorithm +i2c-algo-pcf: A PCF 8584 style algorithm + +Adapter drivers +--------------- + +i2c-elektor: Elektor ISA card (uses i2c-algo-pcf) +i2c-elv: ELV parallel port adapter (uses i2c-algo-bit) +i2c-philips-par: Philips style parallel port adapter (uses i2c-algo-bit) +i2c-velleman: Velleman K9000 parallel port adapter (uses i2c-algo-bit) + diff -urN vanilla-2.2.15pre17/Documentation/i2c/ten-bit-addresses bttv-2.2.15pre17/Documentation/i2c/ten-bit-addresses --- vanilla-2.2.15pre17/Documentation/i2c/ten-bit-addresses Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/i2c/ten-bit-addresses Mon May 1 23:02:30 2000 @@ -0,0 +1,22 @@ +The I2C protocol knows about two kinds of device addresses: normal 7 bit +addresses, and an extended set of 10 bit addresses. The sets of addresses +do not intersect: the 7 bit address 0x10 is not the same as the 10 bit +address 0x10 (though a single device could respond to both of them). You +select a 10 bit address by adding an extra byte after the address +byte: + S Addr7 Rd/Wr .... +becomes + S 11110 Addr10 Rd/Wr +S is the start bit, Rd/Wr the read/write bit, and if you count the number +of bits, you will see the there are 8 after the S bit for 7 bit addresses, +and 16 after the S bit for 10 bit addresses. + +WARNING! The current 10 bit address support is EXPERIMENTAL. There are +several places in the code that will cause SEVERE PROBLEMS with 10 bit +addresses, even though there is some basic handling and hooks. Also, +almost no supported adapter handles the 10 bit addresses correctly. + +As soon as a real 10 bit address device is spotted 'in the wild', we +can and will add proper support. Right now, 10 bit address devices +are defined by the I2C protocol, but we have never seen a single device +which supports them. diff -urN vanilla-2.2.15pre17/Documentation/i2c/writing-clients bttv-2.2.15pre17/Documentation/i2c/writing-clients --- vanilla-2.2.15pre17/Documentation/i2c/writing-clients Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/i2c/writing-clients Mon May 1 23:02:30 2000 @@ -0,0 +1,857 @@ +This is a small guide for those who want to write kernel drivers for I2C +or SMBus devices. + +To set up a driver, you need to do several things. Some are optional, and +some things can be done slightly or completely different. Use this as a +guide, not as a rule book! + + +General remarks +=============== + +Try to keep the kernel namespace as clean as possible. The best way to +do this is to use a unique prefix for all global symbols. This is +especially important for exported symbols, but it is a good idea to do +it for non-exported symbols too. We will use the prefix `foo_' in this +tutorial, and `FOO_' for preprocessor variables. + + +The driver structure +==================== + +Usually, you will implement a single driver structure, and instantiate +all clients from it. Remember, a driver structure contains general access +routines, a client structure specific information like the actual I2C +address. + + struct i2c_driver foo_driver + { + /* name */ "Foo version 2.3 and later driver", + /* id */ I2C_DRIVERID_FOO, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &foo_attach_adapter, + /* detach_client */ &foo_detach_client, + /* command */ &foo_command, /* May be NULL */ + /* inc_use */ &foo_inc_use, /* May be NULL */ + /* dec_use */ &foo_dev_use /* May be NULL */ + } + +The name can be choosen freely, and may be upto 40 characters long. Please +use something descriptive here. + +The id should be a unique ID. The range 0xf000 to 0xffff is reserved for +local use, and you can use one of those until you start distributing the +driver. Before you do that, contact the i2c authors to get your own ID(s). + +Don't worry about the flags field; just put I2C_DF_NOTIFY into it. This +means that your driver will be notified when new adapters are found. +This is almost always what you want. + +All other fields are for call-back functions which will be explained +below. + + +Module usage count +================== + +If your driver can also be compiled as a module, there are moments at +which the module can not be removed from memory. For example, when you +are doing a lengthy transaction, or when you create a /proc directory, +and some process has entered that directory (this last case is the +main reason why these call-backs were introduced). + +To increase or decrease the module usage count, you can use the +MOD_{INC,DEC}_USE_COUNT macros. They must be called from the module +which needs to get its usage count changed; that is why each driver +module has to implement its own callback. + + void foo_inc_use (struct i2c_client *client) + { + #ifdef MODULE + MOD_INC_USE_COUNT; + #endif + } + + void foo_dec_use (struct i2c_client *client) + { + #ifdef MODULE + MOD_DEC_USE_COUNT; + #endif + } + +Do not call these call-back functions directly; instead, use one of the +following functions defined in i2c.h: + void i2c_inc_use_client(struct i2c_client *); + void i2c_dec_use_client(struct i2c_client *); + +You should *not* increase the module count just because a device is +detected and a client created. This would make it impossible to remove +an adapter driver! + + +Extra client data +================= + +The client structure has a special `data' field that can point to any +structure at all. You can use this to keep client-specific data. You +do not always need this, but especially for `sensors' drivers, it can +be very useful. + +An example structure is below. + + struct foo_data { + struct semaphore lock; /* For ISA access in `sensors' drivers. */ + int sysctl_id; /* To keep the /proc directory entry for + `sensors' drivers. */ + enum chips type; /* To keep the chips type for `sensors' drivers. */ + + /* Because the i2c bus is slow, it is often useful to cache the read + information of a chip for some time (for example, 1 or 2 seconds). + It depends of course on the device whether this is really worthwhile + or even sensible. */ + struct semaphore update_lock; /* When we are reading lots of information, + another process should not update the + below information */ + char valid; /* != 0 if the following fields are valid. */ + unsigned long last_updated; /* In jiffies */ + /* Add the read information here too */ + }; + + +Accessing the client +==================== + +Let's say we have a valid client structure. At some time, we will need +to gather information from the client, or write new information to the +client. How we will export this information to user-space is less +important at this moment (perhaps we do not need to do this at all for +some obscure clients). But we need generic reading and writing routines. + +I have found it useful to define foo_read and foo_write function for this. +For some cases, it will be easier to call the i2c functions directly, +but many chips have some kind of register-value idea that can easily +be encapsulated. Also, some chips have both ISA and I2C interfaces, and +it useful to abstract from this (only for `sensors' drivers). + +The below functions are simple examples, and should not be copied +literally. + + int foo_read_value(struct i2c_client *client, u8 reg) + { + if (reg < 0x10) /* byte-sized register */ + return i2c_smbus_read_byte_data(client,reg); + else /* word-sized register */ + return i2c_smbus_read_word_data(client,reg); + } + + int foo_write_value(struct i2c_client *client, u8 reg, u16 value) + { + if (reg == 0x10) /* Impossible to write - driver error! */ { + return -1; + else if (reg < 0x10) /* byte-sized register */ + return i2c_smbus_write_byte_data(client,reg,value); + else /* word-sized register */ + return i2c_smbus_write_word_data(client,reg,value); + } + +For sensors code, you may have to cope with ISA registers too. Something +like the below often works. Note the locking! + + int foo_read_value(struct i2c_client *client, u8 reg) + { + int res; + if (i2c_is_isa_client(client)) { + down(&(((struct foo_data *) (client->data)) -> lock)); + outb_p(reg,client->addr + FOO_ADDR_REG_OFFSET); + res = inb_p(client->addr + FOO_DATA_REG_OFFSET); + up(&(((struct foo_data *) (client->data)) -> lock)); + return res; + } else + return i2c_smbus_read_byte_data(client,reg); + } + +Writing is done the same way. + + +Probing and attaching +===================== + +Most i2c devices can be present on several i2c addresses; for some this +is determined in hardware (by soldering some chip pins to Vcc or Ground), +for others this can be changed in software (by writing to specific client +registers). Some devices are usually on a specific address, but not always; +and some are even more tricky. So you will probably need to scan several +i2c addresses for your clients, and do some sort of detection to see +whether it is actually a device supported by your driver. + +To give the user a maximum of possibilities, some default module parameters +are defined to help determine what addresses are scanned. Several macros +are defined in i2c.h to help you support them, as well as a generic +detection algorithm. + +You do not have to use this parameter interface; but don't try to use +function i2c_probe() (or sensors_detect()) if you don't. + +NOTE: If you want to write a `sensors' driver, the interface is slightly + different! See below. + + + +Probing classes (i2c) +--------------------- + +All parameters are given as lists of unsigned 16-bit integers. Lists are +terminated by I2C_CLIENT_END. +The following lists are used internally: + + normal_i2c: filled in by the module writer. + A list of I2C addresses which should normally be examined. + normal_i2c_range: filled in by the module writer. + A list of pairs of I2C addresses, each pair being an inclusive range of + addresses which should normally be examined. + probe: insmod parameter. + A list of pairs. The first value is a bus number (-1 for any I2C bus), + the second is the address. These addresses are also probed, as if they + were in the 'normal' list. + probe_range: insmod parameter. + A list of triples. The first value is a bus number (-1 for any I2C bus), + the second and third are addresses. These form an inclusive range of + addresses that are also probed, as if they were in the 'normal' list. + ignore: insmod parameter. + A list of pairs. The first value is a bus number (-1 for any I2C bus), + the second is the I2C address. These addresses are never probed. + This parameter overrules 'normal' and 'probe', but not the 'force' lists. + ignore_range: insmod parameter. + A list of triples. The first value is a bus number (-1 for any I2C bus), + the second and third are addresses. These form an inclusive range of + I2C addresses that are never probed. + This parameter overrules 'normal' and 'probe', but not the 'force' lists. + force: insmod parameter. + A list of pairs. The first value is a bus number (-1 for any I2C bus), + the second is the I2C address. A device is blindly assumed to be on + the given address, no probing is done. + +Fortunately, as a module writer, you just have to define the `normal' +and/or `normal_range' parameters. The complete declaration could look +like this: + + /* Scan 0x20 to 0x2f, 0x37, and 0x40 to 0x4f */ + static unsigned short normal_i2c[] = { 0x37,I2C_CLIENT_END }; + static unsigned short normal_i2c_range[] = { 0x20, 0x2f, 0x40, 0x4f, + I2C_CLIENT_END }; + + /* Magic definition of all other variables and things */ + I2C_CLIENT_INSMOD; + +Note that you *have* to call the two defined variables `normal_i2c' and +`normal_i2c_range', without any prefix! + + +Probing classes (sensors) +------------------------- + +If you write a `sensors' driver, you use a slightly different interface. +As well as I2C addresses, we have to cope with ISA addresses. Also, we +use a enum of chip types. Don't forget to include `sensors.h'. + +The following lists are used internally. They are all lists of integers. + + normal_i2c: filled in by the module writer. Terminated by SENSORS_I2C_END. + A list of I2C addresses which should normally be examined. + normal_i2c_range: filled in by the module writer. Terminated by + SENSORS_I2C_END + A list of pairs of I2C addresses, each pair being an inclusive range of + addresses which should normally be examined. + normal_isa: filled in by the module writer. Terminated by SENSORS_ISA_END. + A list of ISA addresses which should normally be examined. + normal_isa_range: filled in by the module writer. Terminated by + SENSORS_ISA_END + A list of triples. The first two elements are ISA addresses, being an + range of addresses which should normally be examined. The third is the + modulo parameter: only addresses which are 0 module this value relative + to the first address of the range are actually considered. + probe: insmod parameter. Initialize this list with SENSORS_I2C_END values. + A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second is the address. These + addresses are also probed, as if they were in the 'normal' list. + probe_range: insmod parameter. Initialize this list with SENSORS_I2C_END + values. + A list of triples. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second and third are addresses. + These form an inclusive range of addresses that are also probed, as + if they were in the 'normal' list. + ignore: insmod parameter. Initialize this list with SENSORS_I2C_END values. + A list of pairs. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second is the I2C address. These + addresses are never probed. This parameter overrules 'normal' and + 'probe', but not the 'force' lists. + ignore_range: insmod parameter. Initialize this list with SENSORS_I2C_END + values. + A list of triples. The first value is a bus number (SENSORS_ISA_BUS for + the ISA bus, -1 for any I2C bus), the second and third are addresses. + These form an inclusive range of I2C addresses that are never probed. + This parameter overrules 'normal' and 'probe', but not the 'force' lists. + +Also used is a list of pointers to sensors_force_data structures: + force_data: insmod parameters. A list, ending with an element of which + the force field is NULL. + Each element contains the type of chip and a list of pairs. + The first value is a bus number (SENSORS_ISA_BUS for the ISA bus, + -1 for any I2C bus), the second is the address. + These are automatically translated to insmod variables of the form + force_foo. + +So we have a generic insmod variabled `force', and chip-specific variables +`force_CHIPNAME'. + +Fortunately, as a module writer, you just have to define the `normal' +and/or `normal_range' parameters, and define what chip names are used. +The complete declaration could look like this: + /* Scan i2c addresses 0x20 to 0x2f, 0x37, and 0x40 to 0x4f + static unsigned short normal_i2c[] = {0x37,SENSORS_I2C_END}; + static unsigned short normal_i2c_range[] = {0x20,0x2f,0x40,0x4f, + SENSORS_I2C_END}; + /* Scan ISA address 0x290 */ + static unsigned int normal_isa[] = {0x0290,SENSORS_ISA_END}; + static unsigned int normal_isa_range[] = {SENSORS_ISA_END}; + + /* Define chips foo and bar, as well as all module parameters and things */ + SENSORS_INSMOD_2(foo,bar); + +If you have one chip, you use macro SENSORS_INSMOD_1(chip), if you have 2 +you use macro SENSORS_INSMOD_2(chip1,chip2), etc. If you do not want to +bother with chip types, you can use SENSORS_INSMOD_0. + +A enum is automatically defined as follows: + enum chips { any_chip, chip1, chip2, ... } + + +Attaching to an adapter +----------------------- + +Whenever a new adapter is inserted, or for all adapters if the driver is +being registered, the callback attach_adapter() is called. Now is the +time to determine what devices are present on the adapter, and to register +a client for each of them. + +The attach_adapter callback is really easy: we just call the generic +detection function. This function will scan the bus for us, using the +information as defined in the lists explained above. If a device is +detected at a specific address, another callback is called. + + int foo_attach_adapter(struct i2c_adapter *adapter) + { + return i2c_probe(adapter,&addr_data,&foo_detect_client); + } + +For `sensors' drivers, use the sensors_detect function instead: + + int foo_attach_adapter(struct i2c_adapter *adapter) + { + return sensors_detect(adapter,&addr_data,&foo_detect_client); + } + +Remember, structure `addr_data' is defined by the macros explained above, +so you do not have to define it yourself. + +The i2c_probe or sensors_detect function will call the foo_detect_client +function only for those i2c addresses that actually have a device on +them (unless a `force' parameter was used). In addition, addresses that +are already in use (by some other registered client) are skipped. + + +The detect client function +-------------------------- + +The detect client function is called by i2c_probe or sensors_detect. +The `kind' parameter contains 0 if this call is due to a `force' +parameter, and 0 otherwise (for sensors_detect, it contains 0 if +this call is due to the generic `force' parameter, and the chip type +number if it is due to a specific `force' parameter). + +Below, some things are only needed if this is a `sensors' driver. Those +parts are between /* SENSORS ONLY START */ and /* SENSORS ONLY END */ +markers. + +This function should only return an error (any value != 0) if there is +some reason why no more detection should be done anymore. If the +detection just fails for this address, return 0. + +For now, you can ignore the `flags' parameter. It is there for future use. + + /* Unique ID allocation */ + static int foo_id = 0; + + int foo_detect_client(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) + { + int err = 0; + int i; + struct i2c_client *new_client; + struct foo_data *data; + const char *client_name = ""; /* For non-`sensors' drivers, put the real + name here! */ + + /* Let's see whether this adapter can support what we need. + Please substitute the things you need here! + For `sensors' drivers, add `! is_isa &&' to the if statement */ + if (i2c_check_functionality(adapter,I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE)) + goto ERROR0; + + /* SENSORS ONLY START */ + const char *type_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + + if (is_isa) { + + /* If this client can't be on the ISA bus at all, we can stop now + (call `goto ERROR0'). But for kicks, we will assume it is all + right. */ + + /* Discard immediately if this ISA range is already used */ + if (check_region(address,FOO_EXTENT)) + goto ERROR0; + + /* Probe whether there is anything on this address. + Some example code is below, but you will have to adapt this + for your own driver */ + + if (kind < 0) /* Only if no force parameter was used */ { + /* We may need long timeouts at least for some chips. */ + #define REALLY_SLOW_IO + i = inb_p(address + 1); + if (inb_p(address + 2) != i) + goto ERROR0; + if (inb_p(address + 3) != i) + goto ERROR0; + if (inb_p(address + 7) != i) + goto ERROR0; + #undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f,address+5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i,address+5); + return 0; + } + } + } + + /* SENSORS ONLY END */ + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access several i2c functions safely */ + + /* Note that we reserve some space for foo_data too. If you don't + need it, remove it. We do it here to help to lessen memory + fragmentation. */ + if (! (new_client = kmalloc(sizeof(struct i2c_client)) + + sizeof(struct foo_data), + GFP_KERNEL)) { + err = -ENOMEM; + goto ERROR0; + } + + /* This is tricky, but it will set the data to the right value. */ + client->data = new_client + 1; + data = (struct foo_data *) (client->data); + + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &foo_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. If no `force' parameter is used. */ + + /* First, the generic detection (if any), that is skipped if any force + parameter was used. */ + if (kind < 0) { + /* The below is of course bogus */ + if (foo_read(new_client,FOO_REG_GENERIC) != FOO_GENERIC_VALUE) + goto ERROR1; + } + + /* SENSORS ONLY START */ + + /* Next, specific detection. This is especially important for `sensors' + devices. */ + + /* Determine the chip type. Not needed if a `force_CHIPTYPE' parameter + was used. */ + if (kind <= 0) { + i = foo_read(new_client,FOO_REG_CHIPTYPE); + if (i == FOO_TYPE_1) + kind = chip1; /* As defined in the enum */ + else if (i == FOO_TYPE_2) + kind = chip2; + else { + printk("foo: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n",i2c_adapter_id(adapter),address); + goto ERROR1; + } + } + + /* Now set the type and chip names */ + if (kind == chip1) { + type_name = "chip1"; /* For /proc entry */ + client_name = "CHIP 1"; + } else if (kind == chip2) { + type_name = "chip2"; /* For /proc entry */ + client_name = "CHIP 2"; + } + + /* Reserve the ISA region */ + if (is_isa) + request_region(address,FOO_EXTENT,type_name); + + /* SENSORS ONLY END */ + + /* Fill in the remaining client fields. */ + strcpy(new_client->name,client_name); + + /* SENSORS ONLY BEGIN */ + data->type = kind; + /* SENSORS ONLY END */ + + new_client->id = foo_id++; /* Automatically unique */ + data->valid = 0; /* Only if you use this field */ + init_MUTEX(&data->update_lock); /* Only if you use this field */ + + /* Any other initializations in data must be done here too. */ + + /* Tell the i2c layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* SENSORS ONLY BEGIN */ + /* Register a new directory entry with module sensors. See below for + the `template' structure. */ + if ((i = sensors_register_entry(new_client, type_name, + foo_dir_table_template,THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* SENSORS ONLY END */ + + /* This function can write default values to the client registers, if + needed. */ + foo_init_client(new_client); + return 0; + + /* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR2: + /* SENSORS ONLY START */ + if (is_isa) + release_region(address,FOO_EXTENT); + /* SENSORS ONLY END */ + ERROR1: + kfree(new_client); + ERROR0: + return err; + } + + +Removing the client +=================== + +The detach_client call back function is called when a client should be +removed. It may actually fail, but only when panicking. This code is +much simpler than the attachment code, fortunately! + + int foo_detach_client(struct i2c_client *client) + { + int err,i; + + /* SENSORS ONLY START */ + /* Deregister with the `sensors' module. */ + sensors_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id); + /* SENSORS ONLY END */ + + /* Try to detach the client from i2c space */ + if ((err = i2c_detach_client(client))) { + printk("foo.o: Client deregistration failed, client not detached.\n"); + return err; + } + + /* SENSORS ONLY START */ + if i2c_is_isa_client(client) + release_region(client->addr,LM78_EXTENT); + /* SENSORS ONLY END */ + + kfree(client); /* Frees client data too, if allocated at the same time */ + return 0; + } + + +Initializing the module or kernel +================================= + +When the kernel is booted, or when your foo driver module is inserted, +you have to do some initializing. Fortunately, just attaching (registering) +the driver module is usually enough. + + /* Keep track of how far we got in the initialization process. If several + things have to initialized, and we fail halfway, only those things + have to be cleaned up! */ + static int __initdata foo_initialized = 0; + + int __init foo_init(void) + { + int res; + printk("foo version %s (%s)\n",FOO_VERSION,FOO_DATE); + + if ((res = i2c_add_driver(&foo_driver))) { + printk("foo: Driver registration failed, module not inserted.\n"); + foo_cleanup(); + return res; + } + foo_initialized ++; + return 0; + } + + int __init foo_cleanup(void) + { + int res; + if (foo_initialized == 1) { + if ((res = i2c_del_driver(&foo_driver))) { + printk("foo: Driver registration failed, module not removed.\n"); + return res; + } + foo_initialized --; + } + return 0; + } + + #ifdef MODULE + + /* Substitute your own name and email address */ + MODULE_AUTHOR("Frodo Looijaard " + MODULE_DESCRIPTION("Driver for Barf Inc. Foo I2C devices"); + + int init_module(void) + { + return foo_init(); + } + + int cleanup_module(void) + { + return foo_cleanup(); + } + + #endif /* def MODULE */ + +Note that some functions are marked by `__init', and some data structures +by `__init_data'. If this driver is compiled as part of the kernel (instead +of as a module), those functions and structures can be removed after +kernel booting is completed. + +Command function +================ + +A generic ioctl-like function call back is supported. You will seldomly +need this. You may even set it to NULL. + + /* No commands defined */ + int foo_command(struct i2c_client *client, unsigned int cmd, void *arg) + { + return 0; + } + + +Sending and receiving +===================== + +If you want to communicate with your device, there are several functions +to do this. You can find all of them in i2c.h. + +If you can choose between plain i2c communication and SMBus level +communication, please use the last. All adapters understand SMBus level +commands, but only some of them understand plain i2c! + + +Plain i2c communication +----------------------- + + extern int i2c_master_send(struct i2c_client *,const char* ,int); + extern int i2c_master_recv(struct i2c_client *,char* ,int); + +These routines read and write some bytes from/to a client. The client +contains the i2c address, so you do not have to include it. The second +parameter contains the bytes the read/write, the third the length of the +buffer. Returned is the actual number of bytes read/written. + + extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num); + +This sends a series of messages. Each message can be a read or write, +and they can be mixed in any way. The transactions are combined: no +stop bit is sent between transaction. The i2c_msg structure contains +for each message the client address, the number of bytes of the message +and the message data itself. + +You can read the file `i2c-protocol' for more information about the +actual i2c protocol. + + +SMBus communication +------------------- + + extern s32 i2c_smbus_xfer (struct i2c_adapter * adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data); + + This is the generic SMBus function. All functions below are implemented + in terms of it. Never use this function directly! + + + extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value); + extern s32 i2c_smbus_read_byte(struct i2c_client * client); + extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value); + extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command); + extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, + u8 command, u8 value); + extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command); + extern s32 i2c_smbus_write_word_data(struct i2c_client * client, + u8 command, u16 value); + extern s32 i2c_smbus_process_call(struct i2c_client * client, + u8 command, u16 value); + extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values); + extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, + u8 *values); + +All these tranactions return -1 on failure. The 'write' transactions +return 0 on success; the 'read' transactions return the read value, except +for read_block, which returns the number of values read. The block buffers +need not be longer than 32 bytes. + +You can read the file `smbus-protocol' for more information about the +actual SMBus protocol. + + +General purpose routines +======================== + +Below all general purpose routines are listed, that were not mentioned +before. + + /* This call returns a unique low identifier for each registered adapter, + * or -1 if the adapter was not regisitered. + */ + extern int i2c_adapter_id(struct i2c_adapter *adap); + + +The sensors sysctl/proc interface +================================= + +This section only applies if you write `sensors' drivers. + +Each sensors driver creates a directory in /proc/sys/dev/sensors for each +registered client. The directory is called something like foo-i2c-4-65. +The sensors module helps you to do this as easily as possible. + +The template +------------ + +You will need to define a ctl_table template. This template will automatically +be copied to a newly allocated structure and filled in where necessary when +you call sensors_register_entry. + +First, I will give an example definition. + static ctl_table foo_dir_table_template[] = { + { FOO_SYSCTL_FUNC1, "func1", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real,NULL,&foo_func }, + { FOO_SYSCTL_FUNC2, "func2", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real,NULL,&foo_func }, + { FOO_SYSCTL_DATA, "data", NULL, 0, 0644, NULL, &sensors_proc_real, + &sensors_sysctl_real,NULL,&foo_data }, + { 0 } + }; + +In the above example, three entries are defined. They can either be +accessed through the /proc interface, in the /proc/sys/dev/sensors/* +directories, as files named func1, func2 and data, or alternatively +through the sysctl interface, in the appropriate table, with identifiers +FOO_SYSCTL_FUNC1, FOO_SYSCTL_FUNC2 and FOO_SYSCTL_DATA. + +The third, sixth and ninth parameters should always be NULL, and the +fourth should always be 0. The fifth is the mode of the /proc file; +0644 is safe, as the file will be owned by root:root. + +The seventh and eigth parameters should be &sensors_proc_real and +&sensors_sysctl_real if you want to export lists of reals (scaled +integers). You can also use your own function for them, as usual. +Finally, the last parameter is the call-back to gather the data +(see below) if you use the *_proc_real functions. + + +Gathering the data +------------------ + +The call back functions (foo_func and foo_data in the above example) +can be called in several ways; the operation parameter determines +what should be done: + + * If operation == SENSORS_PROC_REAL_INFO, you must return the + magnitude (scaling) in nrels_mag; + * If operation == SENSORS_PROC_REAL_READ, you must read information + from the chip and return it in results. The number of integers + to display should be put in nrels_mag; + * If operation == SENSORS_PROC_REAL_WRITE, you must write the + supplied information to the chip. nrels_mag will contain the number + of integers, results the integers themselves. + +The *_proc_real functions will display the elements as reals for the +/proc interface. If you set the magnitude to 2, and supply 345 for +SENSORS_PROC_REAL_READ, it would display 3.45; and if the user would +write 45.6 to the /proc file, it would be returned as 4560 for +SENSORS_PROC_REAL_WRITE. A magnitude may even be negative! + +An example function: + + /* FOO_FROM_REG and FOO_TO_REG translate between scaled values and + register values. Note the use of the read cache. */ + void foo_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) + { + struct foo_data *data = client->data; + int nr = ctl_name - FOO_SYSCTL_FUNC1; /* reduce to 0 upwards */ + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + /* Update the readings cache (if necessary) */ + foo_update_client(client); + /* Get the readings from the cache */ + results[0] = FOO_FROM_REG(data->foo_func_base[nr]); + results[1] = FOO_FROM_REG(data->foo_func_more[nr]); + results[2] = FOO_FROM_REG(data->foo_func_readonly[nr]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + /* Update the cache */ + data->foo_base[nr] = FOO_TO_REG(results[0]); + /* Update the chip */ + foo_write_value(client,FOO_REG_FUNC_BASE(nr),data->foo_base[nr]); + } + if (*nrels_mag >= 2) { + /* Update the cache */ + data->foo_more[nr] = FOO_TO_REG(results[1]); + /* Update the chip */ + foo_write_value(client,FOO_REG_FUNC_MORE(nr),data->foo_more[nr]); + } + } + } diff -urN vanilla-2.2.15pre17/Documentation/video4linux/bttv/CARDLIST bttv-2.2.15pre17/Documentation/video4linux/bttv/CARDLIST --- vanilla-2.2.15pre17/Documentation/video4linux/bttv/CARDLIST Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/video4linux/bttv/CARDLIST Mon May 1 23:03:21 2000 @@ -0,0 +1,59 @@ +bttv.o + card=0 - *** UNKNOWN *** + card=1 - MIRO PCTV + card=2 - Hauppauge old + card=3 - STB + card=4 - Intel + card=5 - Diamond DTV2000 + card=6 - AVerMedia TVPhone + card=7 - MATRIX-Vision MV-Delta + card=8 - Fly Video II + card=9 - TurboTV + card=10 - Hauppauge new (bt878) + card=11 - MIRO PCTV pro + card=12 - ADS Technologies Channel Surfer TV + card=13 - AVerMedia TVCapture 98 + card=14 - Aimslab VHX + card=15 - Zoltrix TV-Max + card=16 - Pixelview PlayTV (bt878) + card=17 - Leadtek WinView 601 + card=18 - AVEC Intercapture + card=19 - LifeView FlyKit w/o Tuner + card=20 - CEI Raffles Card + card=21 - Lucky Star Image World ConferenceTV + card=22 - Phoebe Tv Master + FM + card=23 - Modular Technology MM205 PCTV, bt878 + card=24 - Askey/Typhoon/Anubis Magic TView CPH051/061 (bt878) + card=25 - Terratec/Vobis TV-Boostar + card=26 - Newer Hauppauge WinCam (bt878) + card=27 - MAXI TV Video PCI2 + card=28 - Terratec TerraTV+ + card=29 - Imagenation PXC200 + card=30 - FlyVideo 98 + card=31 - iProTV + card=32 - Intel Create and Share PCI + card=33 - Terratec TerraTValue + card=34 - Leadtek WinFast 2000 + card=35 - Chronos Video Shuttle II + card=36 - Typhoon TView TV/FM Tuner + card=37 - PixelView PlayTV pro + card=38 - TView99 CPH063 + card=39 - Pinnacle PCTV Rave + card=40 - STB2 + +tuner.o + type=0 - Temic PAL + type=1 - Philips PAL_I + type=2 - Philips NTSC + type=3 - Philips SECAM + type=4 - NoTuner + type=5 - Philips PAL + type=6 - Temic NTSC + type=7 - Temic PAL_I + type=8 - Temic 4036 FY5 NTSC + type=9 - Alps HSBH1 + type=10 - Alps TSBE1 + type=11 - Alps TSBB5 + type=12 - Alps TSBE5 + type=13 - Alps TSBC5 + type=14 - Temic 4006FH5 diff -urN vanilla-2.2.15pre17/Documentation/video4linux/bttv/INSTALL bttv-2.2.15pre17/Documentation/video4linux/bttv/INSTALL --- vanilla-2.2.15pre17/Documentation/video4linux/bttv/INSTALL Mon May 1 22:59:26 2000 +++ bttv-2.2.15pre17/Documentation/video4linux/bttv/INSTALL Mon May 1 23:03:21 2000 @@ -1,82 +0,0 @@ -- Make sure you have a recent 2.0.x kernel (I recommend AT LEAST 2.0.33!) - or a recent 2.1.x kernel. - Older kernels might lead to problems. - -- Do NOT compile videodev into your kernel! - Use the module supplied with bttv. - -- Edit "driver/Makefile": - - - If you do NOT have a Miro card: - Adjust TUNER to a number between 0 and 7. - - This number has the following meaning: - - 0: Temic PAL tuner - 1: Philips PAL_I tuner - 2: Philips NTSC tuner - 3: Philips SECAM tuner - 4: no tuner - 5: Philips PAL tuner - 6: Temic NTSC tuner - 7: Temic PAL tuner - 8: Temic 4036 FY5 NTSC tuner - - The number corresponds to the number (-1) given at the GPIO port of the - Bt848 on Miro cards. - - - - Adjust CARD to one of the numbers below: - - 0: Auto-Detect - 1: Miro - 2: Hauppauge - 3: STB - 4: Intel - 5: Diamond - 6: AVerMedia - 7: Matrix Vision MV-Delta - 8: Fly Video II - 9: TurboTV - 10: Newer Hauppauge (Bt878) - 11: Miro PCTV Pro - 12: ADS Tech Channel Surfer TV (and maybe TV+FM) - 13: AVerMedia TVCapture 98 - 14: Aimslab VHX - 15: Zoltrix TV-Max - - - You may have to adjust BTTV_MAJOR to a different number depending on your - kernel version. The official number 81 does not work on some setups. - But instead of changing it, better update to a newer kernel. - - - If you have a Bt848a or Bt849 on your board you might have to - uncomment: -DUSE_PLL - -- do a "make" in the main directory. - -If you have Hauppauge card read "README.HAUPPAUGE" before proceeding. - -- type "make ins" - - This creates the bttv devices in /dev and installs the bttv module - - Look in the kernel log file (/var/adm/syslog or /var/log/kernel or something - else depending on your /etc/syslogd.conf or just call "dmesg") - and see what bttv reported (lines starting with "bttv:") - If the installation failed and you send e-mail to me always include those - lines! Dumps of the insmod output alone do not help at all. - -- Start X11 in hi or true color mode - 8 bit color is also supported but really ugly! - (If you have an S3 card you might have to start X11 before installing - the module!) - - If you have Motif or LessTif, "xtvscreen" in the "XTV" directory should - have been compiled with the "make" above. - Otherwise use the statically linked version which should be available - on the web site you got bttv from. - Read the documentation in "XTV" and start xtvscreen. - -- make applications by typing "make" in "apps" - - diff -urN vanilla-2.2.15pre17/Documentation/video4linux/bttv/Insmod-options bttv-2.2.15pre17/Documentation/video4linux/bttv/Insmod-options --- vanilla-2.2.15pre17/Documentation/video4linux/bttv/Insmod-options Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/video4linux/bttv/Insmod-options Mon May 1 23:03:21 2000 @@ -0,0 +1,92 @@ + +bttv.o + the bt848 (grabber chip) driver + + insmod args: + card=n card type, see cardlist for a list. + radio=0/1 card supports radio + pll=0/1/2 pll settings + 0: don't use PLL + 1: 28 MHz crystal installed + 2: 35 MHz crystal installed + triton1=0/1 for Triton1 compatibility + Triton1 is automatically recognized + but this might also help with other chipsets + bigendian=n Set the endianness of the gfx framebuffer. + Default is native endian. + fieldnr=0/1 Count fields. Some TV descrambling software + needs this, for others it only generates + 50 useless IRQs/sec. default is 0 (off). + autoload=0/1 autoload helper modules (tuner, audio). + default is 1 (on). + verbose=0/1/2 verbose level (at insmod time, while looking at + the hardware). default is 1. + debug=0/1 debug messages (for capture). + default is 0 (off). + gbuffers=2-64 number of capture buffers for mmap'ed capture. + default is 2. + gbufsize=n size of capture buffers. default and + maximum value is 0x208000 (~2MB) + + remap, card, radio and pll accept up to four comma-separated arguments + (for multiple boards). + +msp3400.o + The driver for the msp34xx sound processor chips. If you have a + stereo card, you probably want to insmod this one. + + insmod args: + debug=1/2 print some debug info to the syslog, + 2 is more verbose. + simple=1 Use the "short programming" method. Newer + msp34xx versions support this. You need this + for dbx stereo. + once=1 Don't check the TV-stations Audio mode + every few seconds, but only once after + channel switches. + amsound=1 Audio carrier is AM/NICAM at 6.5 Mhz. This + should improve things for french people, the + carrier autoscan seems to work with FM only... + mixer=n allocate mixer device #n. Default is the + first free slot. + +tea6300.o + The driver for the tea6300 fader chip. If you have a stereo + card and the msp3400.o doesn't work, you might want to try this + one. This chip is seen on most STB TV/FM cards (usually from + Gateway OEM sold surplus on auction sites). + + insmod args: + debug=1 print some debug info to the syslog. + +tda8425.o + The driver for the tda8425 fader chip. This driver used to be + part of bttv.c, so if your sound used to work but does not + anymore, try loading this module. + + insmod args: + debug=1 print some debug info to the syslog. + +tda985x.o + The driver for the tda9850/55 audio chips. + + insmod args: + debug=1 print some debug info to the syslog. + chip=9850/9855 set the chip type. + +tuner.o + The tuner driver. You need this unless you want to use only + with a camera or external tuner ... + + insmod args: + debug=1 print some debug info to the syslog + type=n type of the tuner chip. n as follows: + 0: Temic PAL tuner + 1: Philips PAL_I tuner + 2: Philips NTSC tuner + 3: Philips SECAM tuner + 4: no tuner + 5: Philips PAL tuner + 6: Temic NTSC tuner + 7: Temic PAL tuner + diff -urN vanilla-2.2.15pre17/Documentation/video4linux/bttv/MAKEDEV bttv-2.2.15pre17/Documentation/video4linux/bttv/MAKEDEV --- vanilla-2.2.15pre17/Documentation/video4linux/bttv/MAKEDEV Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/video4linux/bttv/MAKEDEV Mon May 1 23:03:21 2000 @@ -0,0 +1,28 @@ +#!/bin/bash + +function makedev () { + + for dev in 0 1 2 3; do + echo "/dev/$1$dev: char 81 $[ $2 + $dev ]" + rm -f /dev/$1$dev + mknod /dev/$1$dev c 81 $[ $2 + $dev ] + chmod 666 /dev/$1$dev + done + + # symlink for default device + rm -f /dev/$1 + ln -s /dev/${1}0 /dev/$1 +} + +# see http://roadrunner.swansea.uk.linux.org/v4lapi.shtml + +echo "*** new device names ***" +makedev video 0 +makedev radio 64 +makedev vtx 192 +makedev vbi 224 + +#echo "*** old device names (for compatibility only) ***" +#makedev bttv 0 +#makedev bttv-fm 64 +#makedev bttv-vbi 224 diff -urN vanilla-2.2.15pre17/Documentation/video4linux/bttv/Modules.conf bttv-2.2.15pre17/Documentation/video4linux/bttv/Modules.conf --- vanilla-2.2.15pre17/Documentation/video4linux/bttv/Modules.conf Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/video4linux/bttv/Modules.conf Mon May 1 23:03:21 2000 @@ -0,0 +1,15 @@ +# i2c +alias char-major-89 i2c-dev +options i2c-core i2c_debug=1 +options i2c-algo-bit bit_test=1 + +# bttv +alias char-major-81 videodev +alias char-major-81-0 bttv +options bttv card=2 radio=1 +options tuner debug=1 + +# make alsa + msp3400 play nicely +options snd-card-ens snd_index=0 +options msp3400 mixer=1 + diff -urN vanilla-2.2.15pre17/Documentation/video4linux/bttv/README bttv-2.2.15pre17/Documentation/video4linux/bttv/README --- vanilla-2.2.15pre17/Documentation/video4linux/bttv/README Sun Aug 23 22:32:25 1998 +++ bttv-2.2.15pre17/Documentation/video4linux/bttv/README Mon May 1 23:03:21 2000 @@ -1,41 +1,104 @@ -bttv - BT848 frame grabber driver -Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) - & Marcus Metzler (mocm@thp.uni-koeln.de) - according to GNU GPL in file COPYING. - - -Bttv is a device driver for frame grabber cards using the Bt848 family -of video decoder chips. -Among those are the Bt848, Bt848A, Bt849, Bt878 and Bt879. -The only major differences between the cards by different manufacturers -are the types of tuners and extra components on the boards. -E.g., some cards by Hauppauge have an additional Videotext decoder -and/or sound decoder chip. -Also type (Composite or S-VHS) and number of inputs differ. -Other Brooktree chips (e.g. the Bt829) or chips by other manufacturers -(Philips, Zoran, ...) are NOT supported by bttv. - -You can use several cards at the same time. -Interrupts can be shared with other Bt848 cards or any other drivers -which allow it. -The (arbitrary) maximum number of cards is 4 but can be adjusted by -changing BTTV_MAX at the beginning of bttv.c if you need more. -(But which board has more than 4 PCI slots plus 1 for the VGA card?) - -Bttv is a standard component of all newer 2.1.x kernels. -This distribution additionally supports 2.0.x kernels and all other -changes and improvements which did not make it into the kernel version -yet. -It also includes versions of videodev.c, i2.c, tuner.c and others -which are the same as in the latest 2.1.x kernel but with 2.0.x support. -A kernel version >2.0.30 is recommended. - -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -Although bttv is now used and tested by many people it still might crash your -computer! Take all precautions to avoid data loss until you are certain -bttv runs on your setup without problems. -!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +Release notes for bttv-0.7.x +============================ -The latest version of bttv can be found at: -http://www.thp.uni-koeln.de/~rjkm/linux/bttv.html +This version is based on Ralphs 0.6.4 release. There are alot of +changes. Bugfixes, merged patches from other people, merged fixes +from the kernel version, port to the new i2c stack, removed support +for 2.0.x, code cleanups, ... + +To compile this bttv version, you'll the new i2c stack. Kernels +newer than 2.3.34 have this already included. If you have a older +kernel, download it from: + http://www2.lm-sensors.nu/~lm78/download.html + +You'll need at least these i2c config options for bttv: +CONFIG_I2C=m +CONFIG_I2C_ALGOBIT=m + +The latest bttv version is available here: + http://me.in-berlin.de/~kraxel/bttv.html + +You'll find Ralphs original (mostly outdated) documentation in the +ralphs-doc subdirectory. + + +Compile bttv +------------ + +If you are compiling the kernel version, just say 'm' if you are asked +for bttv. I /strongly/ suggest to compile bttv as module, because +there are some insmod options for configuring the driver. + +If you downloaded the separate bttv bundle: You need configured kernel +sources to compile the bttv driver. The driver uses some Makefile +magic to compile the modules with your kernel's configuration +(wrt. module-versions, SMP, ...). If you already have compiled the +kernel at least once, you probably don't have do worry about this. If +not, go to /usr/src/linux and run at least "make config". Even +better, compile your own kernel, you'll never become a real hacker +else ;-) + + +Make bttv work with your card +----------------------------- + +Of course you have to load the modules as very first thing. The +separate bttv bundle comes with a script called "update". I use this +one to load a new version while doing driver hacking. You can use it +too, but check the module arguments first. They work for my setup, +and probably do *not* for yours. Another way is to setup your +/etc/modules.conf file and let kmod load the modules. See also: + +Modules.conf: some sample entries for /etc/modules.conf +Insmod-options: list of all insmod options available for bttv and + the helper modules. +MAKEDEV: a script to create the special files for v4l +CARDLIST: List of all supported cards + +Loading just the bttv modules isn't enouth for most cards. The +drivers for the i2c tuner/sound chips must also be loaded. bttv tries +to load them automagically by calling request_module() now, but this +obviously works only with kmod enabled. + +The most important insmod option for bttv is "card=n" to select the +correct card type. If you get video but no sound you've very likely +specified the wrong (or no) card type. A list of supported cards is +in CARDLIST. + +If your card isn't listed in CARDLIST, you should read the Sound-FAQ. + + +Still doesn't work? +------------------- + +I do NOT have a lab with 30+ different grabber boards and a +PAL/NTSC/SECAM test signal generator at home, so I often can't +reproduce your problems. This makes debugging very difficult for me. +If you have some knowledge and spare time, please try to fix this +yourself (patches very welcome of course...) You know: The linux +slogan is "Do it yourself". + +There is a mailing list: video4linux-list@redhat.com. If you have +trouble with some specific TV card, try to ask there instead of +mailing me directly. The chance that someone with the same card +listens there is much higher... + +For problems with sound: There are alot of different systems used +for TV sound all over the world. And there are also different chips +which decode the audio signal. Reports about sound problems ("stereo +does'nt work") are pretty useless unless you include some details +about your hardware and the TV sound scheme used in your country (or +at least the country you are living in). + + +Finally: If you mail some patches for bttv around the world (to +linux-kernel/Alan/Linus/...), please Cc: me. + + +Have fun with bttv, + + Gerd + +-- +Gerd Knorr diff -urN vanilla-2.2.15pre17/Documentation/video4linux/bttv/Sound-FAQ bttv-2.2.15pre17/Documentation/video4linux/bttv/Sound-FAQ --- vanilla-2.2.15pre17/Documentation/video4linux/bttv/Sound-FAQ Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/Documentation/video4linux/bttv/Sound-FAQ Mon May 1 23:03:21 2000 @@ -0,0 +1,96 @@ + +bttv and sound mini howto +========================= + +There are alot of different bt848/849/878/879 based boards available. +Making video work often is not a big deal, because this is handled +completely by the bt8xx chip, which is common on all boards. But +sound is handled in slightly different ways on each board. + +To handle the grabber boards correctly, there is a array tvcards[] in +bttv.c, which holds the informations required for each board. Sound +will work only, if the correct entry is used (for video it often makes +no difference). The bttv driver prints a line to the kernel log, +telling which card type is used. Like this one: + + bttv0: model: BT848(Hauppauge old) + +You should verify this is correct. If it is'nt, you have to pass the +correct board type as insmod argument, "insmod bttv card=2" for +example. The file CARDLIST has a list of valid arguments for card. +If your card is'nt listed there, you might check the source code for +new entries which are not listed yet. If there is'nt one for your +card, you can check if one of the existing entries does work for you +(just trial and error...). + +Some boards have an extra processor for sound to do stereo decoding +and other nice features. The msp34xx chips are used by Hauppauge for +example. If your board has one, you might have to load a helper +module like msp3400.o to make sound work. If there is'nt one for the +chip used on your board: Bad luck. Start writing a new one. Well, +you might want to check the video4linux mailing list archive first... + +Of course you need a correctly installed soundcard unless you have the +speakers connected directly to the grabber board. Hint: check the +mixer settings too... + + +How sound works in detail +========================= + +Still doesn't work? Looks like some driver hacking is required. +Below is a do-it-yourself description for you. + +The bt8xx chips have 32 general purpose pins, and registers to control +these pins. One register is the output enable register +(BT848_GPIO_OUT_EN), it says which pins are actively driven by the +bt848 chip. Another one is the data register (BT848_GPIO_DATA), where +you can get/set the status if these pins. They can be used for input +and output. + +All grabber board vendors use these pins to control an external chip +which does the sound routing. But every board is a little different. +These pins are also used by some companies to drive remote control +receiver chips. + +As mentioned above, there is a array which holds the required +informations for each known board. You basically have to create a new +line for your board. What is in there: + +struct tvcard +{ + char *name; + int inputs; /* number of video inputs */ + int tuner; /* which of them is the tuner */ + int svhs; /* which of them is the svhs input */ + u32 gpiomask; + u32 muxsel[8]; /* video mux */ + u32 audiomux[6]; /* audio mux: Tuner, Radio, external, internal, mute, stereo */ + u32 gpiomask2; /* GPIO MUX mask (this is video) */ +}; + +gpiomask has all bits set which are used to control the audio mux. +This value basically goes to the gpio output enable register. It is +also used to mask bits when switching the audio mux (which is done by +read-modify-write on the gpio data register). + +What you have to do is figure out the correct values for gpiomask and +the audiomux array. If you have Windows and the drivers four your +card installed, you might to check out if you can read these registers +values used by the windows driver. A tool to do this is available +from ftp://telepresence.dmem.strath.ac.uk/pub/bt848/winutil. There is +some #ifdef'ed code in bttv.c (search for "new card") which prints +these values before board initialization, this might help too: boot +win, start tv app, softboot (loadlin) into linux and load bttv with +this enabled. If you hav'nt Windows installed, this is a trial and +error game... + +Good luck, + + Gerd + + +PS: If you have a new working entry, mail it to me. + +-- +Gerd Knorr diff -urN vanilla-2.2.15pre17/MAINTAINERS bttv-2.2.15pre17/MAINTAINERS --- vanilla-2.2.15pre17/MAINTAINERS Mon May 1 23:01:13 2000 +++ bttv-2.2.15pre17/MAINTAINERS Mon May 1 23:02:30 2000 @@ -394,6 +394,15 @@ M: perex@jcu.cz S: Maintained +I2C DRIVERS +P: Simon Vogl +M: simon@tk.uni-linz.ac.at +P: Frodo Looijaard +M: frodol@dds.nl +L: linux-i2c@pelican.tk.uni-linz.ac.at +W: http://www.tk.uni-linz.ac.at/~simon/private/i2c +S: Maintained + IBM MCA SCSI SUBSYSTEM DRIVER P: Michael Lang M: langa2@kph.uni-mainz.de diff -urN vanilla-2.2.15pre17/Makefile bttv-2.2.15pre17/Makefile --- vanilla-2.2.15pre17/Makefile Mon May 1 23:01:13 2000 +++ bttv-2.2.15pre17/Makefile Mon May 1 23:02:30 2000 @@ -206,6 +206,10 @@ DRIVERS := $(DRIVERS) drivers/i2o/i2o.a endif +ifeq ($(CONFIG_I2C),y) +DRIVERS := $(DRIVERS) drivers/i2c/i2c.a +endif + ifeq ($(CONFIG_PHONE),y) DRIVERS := $(DRIVERS) drivers/telephony/telephony.a endif diff -urN vanilla-2.2.15pre17/drivers/Makefile bttv-2.2.15pre17/drivers/Makefile --- vanilla-2.2.15pre17/drivers/Makefile Mon May 1 23:01:14 2000 +++ bttv-2.2.15pre17/drivers/Makefile Mon May 1 23:02:30 2000 @@ -10,7 +10,7 @@ SUB_DIRS := block char net misc sound MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) pci sgi scsi sbus cdrom isdn pnp \ - macintosh video dio zorro fc4 usb telephony i2o + macintosh video dio zorro fc4 usb telephony i2o i2c ifdef CONFIG_DIO SUB_DIRS += dio @@ -137,6 +137,15 @@ ifeq ($(CONFIG_HAMRADIO),y) SUB_DIRS += net/hamradio MOD_SUB_DIRS += net/hamradio +endif + +ifeq ($(CONFIG_I2C),y) +SUB_DIRS += i2c +MOD_SUB_DIRS += i2c +else + ifeq ($(CONFIG_I2C),m) + MOD_SUB_DIRS += i2c + endif endif include $(TOPDIR)/Rules.make diff -urN vanilla-2.2.15pre17/drivers/char/Config.in bttv-2.2.15pre17/drivers/char/Config.in --- vanilla-2.2.15pre17/drivers/char/Config.in Mon May 1 23:01:14 2000 +++ bttv-2.2.15pre17/drivers/char/Config.in Mon May 1 23:02:30 2000 @@ -116,6 +116,8 @@ tristate '/dev/nvram support' CONFIG_NVRAM bool 'Enhanced Real Time Clock Support' CONFIG_RTC +source drivers/i2c/Config.in + if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then bool 'Tadpole ANA H8 Support' CONFIG_H8 fi diff -urN vanilla-2.2.15pre17/drivers/char/Makefile bttv-2.2.15pre17/drivers/char/Makefile --- vanilla-2.2.15pre17/drivers/char/Makefile Mon May 1 23:01:14 2000 +++ bttv-2.2.15pre17/drivers/char/Makefile Mon May 1 23:05:07 2000 @@ -380,21 +380,13 @@ endif ifeq ($(CONFIG_VIDEO_BT848),y) -L_OBJS += bttv.o tuner.o +L_OBJS += tuner.o msp3400.o tda7432.o tda8425.o tda985x.o tda9875.o tea6300.o tea6420.o +LX_OBJS += bttv.o L_I2C=y else ifeq ($(CONFIG_VIDEO_BT848),m) - M_OBJS += bttv.o tuner.o - M_I2C=y - endif -endif - -ifeq ($(CONFIG_VIDEO_MSP3400),y) -L_OBJS += msp3400.o -L_I2C=y -else - ifeq ($(CONFIG_VIDEO_MSP3400),m) - M_OBJS += msp3400.o + M_OBJS += tuner.o msp3400.o tda7432.o tda8425.o tda985x.o tda9875.o tea6300.o tea6420.o + MX_OBJS += bttv.o M_I2C=y endif endif @@ -596,10 +588,10 @@ endif ifeq ($(L_I2C),y) -LX_OBJS += i2c.o +LX_OBJS += i2c-old.o else ifeq ($(M_I2C),y) - MX_OBJS += i2c.o + MX_OBJS += i2c-old.o endif endif diff -urN vanilla-2.2.15pre17/drivers/char/Makefile~ bttv-2.2.15pre17/drivers/char/Makefile~ --- vanilla-2.2.15pre17/drivers/char/Makefile~ Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/char/Makefile~ Mon May 1 23:04:59 2000 @@ -0,0 +1,639 @@ +# +# Makefile for the kernel character device drivers. +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now inherited from the +# parent makes.. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) rio ftape joystick + +# +# This file contains the font map for the default (hardware) font +# +FONTMAPFILE = cp437.uni + +L_TARGET := char.a +M_OBJS := +L_OBJS := tty_io.o n_tty.o tty_ioctl.o mem.o random.o +LX_OBJS := pty.o misc.o + +ifdef CONFIG_VT +L_OBJS += vt.o vc_screen.o consolemap.o consolemap_deftbl.o +LX_OBJS += console.o selection.o +endif + +ifeq ($(CONFIG_SERIAL),y) + ifeq ($(CONFIG_SUN_SERIAL),) + ifeq ($(CONFIG_SGI_SERIAL),) + ifeq ($(CONFIG_DECSTATION),) + ifeq ($(CONFIG_BAGET_MIPS),) + LX_OBJS += serial.o + endif + endif + endif + endif +else + ifeq ($(CONFIG_SERIAL),m) + ifeq ($(CONFIG_SUN_SERIAL),) + ifeq ($(CONFIG_SGI_SERIAL),) + ifeq ($(CONFIG_DECSTATION),) + ifeq ($(CONFIG_BAGET_MIPS),) + MX_OBJS += serial.o + endif + endif + endif + endif + endif +endif + +ifndef CONFIG_DECSTATION +ifndef CONFIG_BAGET_MIPS +ifndef CONFIG_SUN_KEYBOARD +ifdef CONFIG_VT +LX_OBJS += keyboard.o +endif + ifneq ($(ARCH),m68k) + ifneq ($(ARCH),s390) + L_OBJS += pc_keyb.o defkeymap.o + endif + endif +else +ifdef CONFIG_PCI +L_OBJS += defkeymap.o +LX_OBJS += keyboard.o +endif +endif + +ifdef CONFIG_MAGIC_SYSRQ +LX_OBJS += sysrq.o +endif +endif +endif + +ifeq ($(CONFIG_ATARI_DSP56K),y) +L_OBJS += dsp56k.o +S = y +else + ifeq ($(CONFIG_ATARI_DSP56K),m) + M_OBJS += dsp56k.o + SM = y + endif +endif + +ifeq ($(CONFIG_ROCKETPORT),y) +L_OBJS += rocket.o +else + ifeq ($(CONFIG_ROCKETPORT),m) + M_OBJS += rocket.o + endif +endif + +ifeq ($(CONFIG_MOXA_SMARTIO),y) +L_OBJS += mxser.o +else + ifeq ($(CONFIG_MOXA_SMARTIO),m) + M_OBJS += mxser.o + endif +endif + +ifeq ($(CONFIG_MOXA_INTELLIO),y) +L_OBJS += moxa.o +else + ifeq ($(CONFIG_MOXA_INTELLIO),m) + M_OBJS += moxa.o + endif +endif + +ifeq ($(CONFIG_DIGI),y) +L_OBJS += pcxx.o +else + ifeq ($(CONFIG_DIGI),m) + M_OBJS += pcxx.o + endif +endif + +ifeq ($(CONFIG_DIGIEPCA),y) +L_OBJS += epca.o +else + ifeq ($(CONFIG_DIGIEPCA),m) + M_OBJS += epca.o + endif +endif + +ifeq ($(CONFIG_CYCLADES),y) +L_OBJS += cyclades.o +else + ifeq ($(CONFIG_CYCLADES),m) + M_OBJS += cyclades.o + endif +endif + +ifeq ($(CONFIG_STALLION),y) +L_OBJS += stallion.o +else + ifeq ($(CONFIG_STALLION),m) + M_OBJS += stallion.o + endif +endif + +ifeq ($(CONFIG_ISTALLION),y) +L_OBJS += istallion.o +else + ifeq ($(CONFIG_ISTALLION),m) + M_OBJS += istallion.o + endif +endif + +ifeq ($(CONFIG_COMPUTONE),y) +L_OBJS += ip2.o ip2main.o +else + ifeq ($(CONFIG_COMPUTONE),m) + M_OBJS += ip2.o ip2main.o + endif +endif + +ifeq ($(CONFIG_RISCOM8),y) +L_OBJS += riscom8.o +else + ifeq ($(CONFIG_RISCOM8),m) + M_OBJS += riscom8.o + endif +endif + +ifeq ($(CONFIG_ISI),y) +L_OBJS += isicom.o +else + ifeq ($(CONFIG_ISI),m) + M_OBJS += isicom.o + endif +endif + +ifeq ($(CONFIG_ESPSERIAL),y) +L_OBJS += esp.o +else + ifeq ($(CONFIG_ESPSERIAL),m) + M_OBJS += esp.o + endif +endif + +ifeq ($(CONFIG_SYNCLINK),m) + M_OBJS += synclink.o +endif + +ifeq ($(CONFIG_N_HDLC),m) + M_OBJS += n_hdlc.o +endif + +ifeq ($(CONFIG_SPECIALIX),y) +L_OBJS += specialix.o +else + ifeq ($(CONFIG_SPECIALIX),m) + M_OBJS += specialix.o + endif +endif + +ifeq ($(CONFIG_SX),y) +L_OBJS += sx.o generic_serial.o +else + ifeq ($(CONFIG_SX),m) + M_OBJS += sx.o generic_serial.o + endif +endif + +ifeq ($(CONFIG_RIO),y) +L_OBJS += rio/rio.o generic_serial.o +SUB_DIRS += rio +MOD_SUB_DIRS += rio +else + ifeq ($(CONFIG_RIO),m) + M_OBJS += generic_serial.o + MOD_SUB_DIRS += rio + endif +endif + +ifeq ($(CONFIG_ATIXL_BUSMOUSE),y) +L_OBJS += atixlmouse.o +else + ifeq ($(CONFIG_ATIXL_BUSMOUSE),m) + M_OBJS += atixlmouse.o + endif +endif + +ifeq ($(CONFIG_BUSMOUSE),y) +L_OBJS += busmouse.o +else + ifeq ($(CONFIG_BUSMOUSE),m) + M_OBJS += busmouse.o + endif +endif + +ifeq ($(CONFIG_PRINTER),y) +L_OBJS += lp.o +else + ifeq ($(CONFIG_PRINTER),m) + M_OBJS += lp.o + endif +endif + +ifeq ($(CONFIG_JOYSTICK),y) +L_OBJS += joystick/js.o +SUB_DIRS += joystick +MOD_SUB_DIRS += joystick +else + ifeq ($(CONFIG_JOYSTICK),m) + MOD_SUB_DIRS += joystick + endif +endif + +ifeq ($(CONFIG_DTLK),y) +L_OBJS += dtlk.o +else + ifeq ($(CONFIG_DTLK),m) + M_OBJS += dtlk.o + endif +endif + +ifeq ($(CONFIG_MS_BUSMOUSE),y) +L_OBJS += msbusmouse.o +else + ifeq ($(CONFIG_MS_BUSMOUSE),m) + M_OBJS += msbusmouse.o + endif +endif + +ifeq ($(CONFIG_82C710_MOUSE),y) +L_OBJS += qpmouse.o +else + ifeq ($(CONFIG_82C710_MOUSE),m) + M_OBJS += qpmouse.o + endif +endif + +ifeq ($(CONFIG_SOFT_WATCHDOG),y) +L_OBJS += softdog.o +else + ifeq ($(CONFIG_SOFT_WATCHDOG),m) + M_OBJS += softdog.o + endif +endif + +ifeq ($(CONFIG_PCWATCHDOG),y) +L_OBJS += pcwd.o +else + ifeq ($(CONFIG_PCWATCHDOG),m) + M_OBJS += pcwd.o + endif +endif + +ifeq ($(CONFIG_ACQUIRE_WDT),y) +L_OBJS += acquirewdt.o +else + ifeq ($(CONFIG_ACQUIRE_WDT),m) + M_OBJS += acquirewdt.o + endif +endif + +ifeq ($(CONFIG_MIXCOMWD),y) +L_OBJS += mixcomwd.o +else + ifeq ($(CONFIG_MIXCOMWD),m) + M_OBJS += mixcomwd.o + endif +endif + +ifeq ($(CONFIG_AMIGAMOUSE),y) +L_OBJS += amigamouse.o +else + ifeq ($(CONFIG_AMIGAMOUSE),m) + M_OBJS += amigamouse.o + endif +endif + +ifeq ($(CONFIG_ATARIMOUSE),y) +L_OBJS += atarimouse.o +else + ifeq ($(CONFIG_ATARIMOUSE),m) + M_OBJS += atarimouse.o + endif +endif + +ifeq ($(CONFIG_ADBMOUSE),y) +L_OBJS += adbmouse.o +else + ifeq ($(CONFIG_ADBMOUSE),m) + M_OBJS += adbmouse.o + endif +endif + +ifeq ($(CONFIG_PC110_PAD),y) +L_OBJS += pc110pad.o +else + ifeq ($(CONFIG_PC110_PAD),m) + M_OBJS += pc110pad.o + endif +endif + +ifeq ($(CONFIG_WDT),y) +L_OBJS += wdt.o +else + ifeq ($(CONFIG_WDT),m) + M_OBJS += wdt.o + endif +endif + +ifeq ($(CONFIG_RTC),y) +L_OBJS += rtc.o +endif + +ifeq ($(CONFIG_NVRAM),y) + ifeq ($(CONFIG_PPC),) + L_OBJS += nvram.o + endif +else + ifeq ($(CONFIG_NVRAM),m) + ifeq ($(CONFIG_PPC),) + M_OBJS += nvram.o + endif + endif +endif + +ifeq ($(CONFIG_VIDEO_DEV),y) +LX_OBJS += videodev.o +else + ifeq ($(CONFIG_VIDEO_DEV),m) + MX_OBJS += videodev.o + endif +endif + +ifeq ($(CONFIG_BUS_I2C),y) + L_I2C=y +else + ifeq ($(CONFIG_BUS_I2C),m) + M_I2C=y + endif +endif + +ifeq ($(CONFIG_VIDEO_BT848),y) +L_OBJS += tuner.o msp3400.c tda7432.c tda8425.c tda985x.c tda9875.c tea6300.c tea6420.c +LX_OBJS += bttv.o +L_I2C=y +else + ifeq ($(CONFIG_VIDEO_BT848),m) + M_OBJS += tuner.o msp3400.c tda7432.c tda8425.c tda985x.c tda9875.c tea6300.c tea6420.c + MX_OBJS += bttv.o + M_I2C=y + endif +endif + +ifeq ($(CONFIG_VIDEO_MSP3400),y) +L_OBJS += msp3400.o +L_I2C=y +else + ifeq ($(CONFIG_VIDEO_MSP3400),m) + M_OBJS += msp3400.o + M_I2C=y + endif +endif + +ifeq ($(CONFIG_VIDEO_SAA5249),y) +L_OBJS += saa5249.o +L_I2C=y +else + ifeq ($(CONFIG_VIDEO_SAA5249),m) + M_OBJS += saa5249.o + M_I2C=y + endif +endif + +ifeq ($(CONFIG_VIDEO_BWQCAM),y) +L_OBJS += bw-qcam.o +else + ifeq ($(CONFIG_VIDEO_BWQCAM),m) + M_OBJS += bw-qcam.o + endif +endif + +ifeq ($(CONFIG_VIDEO_CQCAM),y) +L_OBJS += c-qcam.o +else + ifeq ($(CONFIG_VIDEO_CQCAM),m) + M_OBJS += c-qcam.o + endif +endif + +ifeq ($(CONFIG_VIDEO_ZORAN),y) +L_OBJS += buz.o +L_I2C=y +else + ifeq ($(CONFIG_VIDEO_ZORAN),m) + M_OBJS += buz.o + M_I2C=y + endif +endif + +ifeq ($(CONFIG_VIDEO_LML33),y) +L_OBJS += bt856.o bt819.o +else + ifeq ($(CONFIG_VIDEO_LML33),m) + M_OBJS += bt856.o bt819.o + endif +endif + +ifeq ($(CONFIG_VIDEO_BUZ),y) +L_OBJS += saa7111.o saa7185.o +else + ifeq ($(CONFIG_VIDEO_BUZ),m) + M_OBJS += saa7111.o saa7185.o + endif +endif + +ifeq ($(CONFIG_VIDEO_PMS),y) +L_OBJS += pms.o +else + ifeq ($(CONFIG_VIDEO_PMS),m) + M_OBJS += pms.o + endif +endif + +ifeq ($(CONFIG_VIDEO_PLANB),y) +L_OBJS += planb.o +else + ifeq ($(CONFIG_VIDEO_PLANB),m) + M_OBJS += planb.o + endif +endif + +ifeq ($(CONFIG_VIDEO_VINO),y) +L_OBJS += vino.o +else + ifeq ($(CONFIG_VIDEO_VINO),m) + M_OBJS += vino.o + endif +endif + +ifeq ($(CONFIG_VIDEO_CPIA),y) +LX_OBJS += cpia.o +else + ifeq ($(CONFIG_VIDEO_CPIA),m) + MX_OBJS += cpia.o + endif +endif + +ifeq ($(CONFIG_VIDEO_CPIA_PP),y) +L_OBJS += cpia_pp.o +else + ifeq ($(CONFIG_VIDEO_CPIA_PP),m) + M_OBJS += cpia_pp.o + endif +endif + +ifeq ($(CONFIG_RADIO_AZTECH),y) +L_OBJS += radio-aztech.o +else + ifeq ($(CONFIG_RADIO_AZTECH),m) + M_OBJS += radio-aztech.o + endif +endif + +ifeq ($(CONFIG_RADIO_SF16FMI),y) +L_OBJS += radio-sf16fmi.o +else + ifeq ($(CONFIG_RADIO_SF16FMI),m) + M_OBJS += radio-sf16fmi.o + endif +endif + +ifeq ($(CONFIG_RADIO_RTRACK),y) +L_OBJS += radio-aimslab.o +else + ifeq ($(CONFIG_RADIO_RTRACK),m) + M_OBJS += radio-aimslab.o + endif +endif + +ifeq ($(CONFIG_RADIO_RTRACK2),y) +L_OBJS += radio-rtrack2.o +else + ifeq ($(CONFIG_RADIO_RTRACK2),m) + M_OBJS += radio-rtrack2.o + endif +endif + +ifeq ($(CONFIG_RADIO_TYPHOON),y) +L_OBJS += radio-typhoon.o +else + ifeq ($(CONFIG_RADIO_TYPHOON),m) + M_OBJS += radio-typhoon.o + endif +endif + +ifeq ($(CONFIG_RADIO_ZOLTRIX),y) +L_OBJS += radio-zoltrix.o +else + ifeq ($(CONFIG_RADIO_ZOLTRIX),m) + M_OBJS += radio-zoltrix.o + endif +endif + +ifeq ($(CONFIG_RADIO_CADET),y) +L_OBJS += radio-cadet.o +else + ifeq ($(CONFIG_RADIO_CADET),m) + M_OBJS += radio-cadet.o + endif +endif + +ifeq ($(CONFIG_RADIO_MIROPCM20),y) +L_OBJS += radio-miropcm20.o +else + ifeq ($(CONFIG_RADIO_MIROPCM20),m) + M_OBJS += radio-miropcm20.o + endif +endif + +ifeq ($(CONFIG_RADIO_GEMTEK),y) +L_OBJS += radio-gemtek.o +else + ifeq ($(CONFIG_RADIO_GEMTEK),m) + M_OBJS += radio-gemtek.o + endif +endif + +ifeq ($(CONFIG_RADIO_TRUST),y) +L_OBJS += radio-trust.o +else + ifeq ($(CONFIG_RADIO_TRUST),m) + M_OBJS += radio-trust.o + endif +endif + +ifeq ($(CONFIG_QIC02_TAPE),y) +L_OBJS += tpqic02.o +else + ifeq ($(CONFIG_QIC02_TAPE),m) + M_OBJS += tpqic02.o + endif +endif + +ifeq ($(CONFIG_FTAPE),y) +L_OBJS += ftape/ftape.o +SUB_DIRS += ftape +ifneq ($(CONFIG_ZFTAPE),n) +MOD_SUB_DIRS += ftape +endif +else + ifeq ($(CONFIG_FTAPE),m) + MOD_SUB_DIRS += ftape + endif +endif + +ifdef CONFIG_H8 +LX_OBJS += h8.o +endif + +ifeq ($(L_I2C),y) +LX_OBJS += i2c-old.o +else + ifeq ($(M_I2C),y) + MX_OBJS += i2c-old.o + endif +endif + + +ifeq ($(CONFIG_HFMODEM),y) +ALL_SUB_DIRS += hfmodem +SUB_DIRS += hfmodem +L_OBJS += hfmodem/hfmodem.o +else + ifeq ($(CONFIG_HFMODEM),m) + ALL_SUB_DIRS += hfmodem + MOD_SUB_DIRS += hfmodem + endif + +endif + +ifeq ($(CONFIG_DZ),y) + L_OBJS += dz.o +endif + +include $(TOPDIR)/Rules.make + +fastdep: + +conmakehash: conmakehash.c + $(HOSTCC) $(HOSTCFLAGS) -o conmakehash conmakehash.c + +consolemap_deftbl.c: $(FONTMAPFILE) conmakehash + ./conmakehash $(FONTMAPFILE) > consolemap_deftbl.c + +consolemap_deftbl.o: consolemap_deftbl.c $(TOPDIR)/include/linux/types.h + +defkeymap.c: defkeymap.map + loadkeys --mktable defkeymap.map > defkeymap.c + diff -urN vanilla-2.2.15pre17/drivers/char/audiochip.h bttv-2.2.15pre17/drivers/char/audiochip.h --- vanilla-2.2.15pre17/drivers/char/audiochip.h Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/char/audiochip.h Mon May 1 23:03:21 2000 @@ -0,0 +1,60 @@ +#ifndef AUDIOCHIP_H +#define AUDIOCHIP_H + +/* ---------------------------------------------------------------------- */ + +#define MIN(a,b) (((a)>(b))?(b):(a)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +/* v4l device was opened in Radio mode */ +#define AUDC_SET_RADIO _IO('m',2) +/* select from TV,radio,extern,MUTE */ +#define AUDC_SET_INPUT _IOW('m',17,int) + +/* all the stuff below is obsolete and just here for reference. I'll + * remove it once the driver is tested and works fine. + * + * Instead creating alot of tiny API's for all kinds of different + * chips, we'll just pass throuth the v4l ioctl structs (v4l2 not + * yet...). It is a bit less flexible, but most/all used i2c chips + * make sense in v4l context only. So I think that's acceptable... + */ + +#if 0 + +/* TODO (if it is ever [to be] accessible in the V4L[2] spec): + * maybe fade? (back/front) + * notes: + * NEWCHANNEL and SWITCH_MUTE are here because the MSP3400 has a special + * routine to go through when it tunes in to a new channel before turning + * back on the sound. + * Either SET_RADIO, NEWCHANNEL, and SWITCH_MUTE or SET_INPUT need to be + * implemented (MSP3400 uses SET_RADIO to select inputs, and SWITCH_MUTE for + * channel-change mute -- TEA6300 et al use SET_AUDIO to select input [TV, + * radio, external, or MUTE]). If both methods are implemented, you get a + * cookie for doing such a good job! :) + */ + +#define AUDC_SET_TVNORM _IOW('m',1,int) /* TV mode + PAL/SECAM/NTSC */ +#define AUDC_NEWCHANNEL _IO('m',3) /* indicate new chan - off mute */ + +#define AUDC_GET_VOLUME_LEFT _IOR('m',4,__u16) +#define AUDC_GET_VOLUME_RIGHT _IOR('m',5,__u16) +#define AUDC_SET_VOLUME_LEFT _IOW('m',6,__u16) +#define AUDC_SET_VOLUME_RIGHT _IOW('m',7,__u16) + +#define AUDC_GET_STEREO _IOR('m',8,__u16) +#define AUDC_SET_STEREO _IOW('m',9,__u16) + +#define AUDC_GET_DC _IOR('m',10,__u16)/* ??? */ + +#define AUDC_GET_BASS _IOR('m',11,__u16) +#define AUDC_SET_BASS _IOW('m',12,__u16) +#define AUDC_GET_TREBLE _IOR('m',13,__u16) +#define AUDC_SET_TREBLE _IOW('m',14,__u16) + +#define AUDC_GET_UNIT _IOR('m',15,int) /* ??? - unimplemented in MSP3400 */ +#define AUDC_SWITCH_MUTE _IO('m',16) /* turn on mute */ +#endif + +#endif /* AUDIOCHIP_H */ diff -urN vanilla-2.2.15pre17/drivers/char/bttv.c bttv-2.2.15pre17/drivers/char/bttv.c --- vanilla-2.2.15pre17/drivers/char/bttv.c Mon May 1 23:01:14 2000 +++ bttv-2.2.15pre17/drivers/char/bttv.c Mon May 1 23:03:21 2000 @@ -1,9 +1,9 @@ - -/* +/* bttv - Bt848 frame grabber driver Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) & Marcus Metzler (mocm@thp.uni-koeln.de) + (c) 1999,2000 Gerd Knorr 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 @@ -20,8 +20,8 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include +#include #include #include #include @@ -29,9 +29,7 @@ #include #include #include -#if LINUX_VERSION_CODE >= 0x020100 #include -#endif #include #include #include @@ -43,80 +41,70 @@ #include #include #include - -#if LINUX_VERSION_CODE >= 0x020100 -#include +#include #include -#else -#include -#define mdelay(x) udelay((x)*1000) -#define signal_pending(current) (current->signal & ~current->blocked) -#define sigfillset(set) - -static inline int time_before(unsigned long a, unsigned long b) -{ - return((long)((a) - (b)) < 0L); -} - -static inline unsigned long -copy_to_user(void *to, const void *from, unsigned long n) -{ - memcpy_tofs(to,from,n); - return 0; -} - -static inline unsigned long -copy_from_user(void *to, const void *from, unsigned long n) -{ - memcpy_fromfs(to,from,n); - return 0; -} -#define ioremap vremap -#define iounmap vfree -#endif -#include -#include #include "bttv.h" #include "tuner.h" -#define DEBUG(x) /* Debug driver */ +#define DEBUG(x) /* Debug driver */ #define IDEBUG(x) /* Debug interrupt handler */ +#define MIN(a,b) (((a)>(b))?(b):(a)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + + +/* Anybody who uses more than four? */ +#define BTTV_MAX 4 +static void bt848_set_risc_jmps(struct bttv *btv, int state); + +static int bttv_num; /* number of Bt848s in use */ +static struct bttv bttvs[BTTV_MAX]; -#if LINUX_VERSION_CODE >= 0x020117 -MODULE_PARM(vidmem,"i"); + +/* insmod args */ MODULE_PARM(triton1,"i"); MODULE_PARM(remap,"1-4i"); MODULE_PARM(radio,"1-4i"); MODULE_PARM(card,"1-4i"); MODULE_PARM(pll,"1-4i"); -#endif +MODULE_PARM(bigendian,"i"); +MODULE_PARM(fieldnr,"i"); +MODULE_PARM(verbose,"i"); +MODULE_PARM(debug,"i"); +MODULE_PARM(autoload,"i"); +MODULE_PARM(gbuffers,"i"); +MODULE_PARM(gbufsize,"i"); + +EXPORT_SYMBOL(bttv_get_id); +EXPORT_SYMBOL(bttv_gpio_enable); +EXPORT_SYMBOL(bttv_read_gpio); +EXPORT_SYMBOL(bttv_write_gpio); +EXPORT_SYMBOL(bttv_get_gpio_queue); -/* Anybody who uses more than four? */ -#define BTTV_MAX 4 +MODULE_DESCRIPTION("bttv - v4l driver module for bt848/878 based cards"); +MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); -static int find_vga(void); -static void bt848_set_risc_jmps(struct bttv *btv); - -static unsigned int vidmem=0; /* manually set video mem address */ -static int triton1=0; -#ifndef USE_PLL -/* 0=no pll, 1=28MHz, 2=34MHz */ -#define USE_PLL 0 -#endif -#ifndef CARD_DEFAULT -/* card type (see bttv.h) 0=autodetect */ -#define CARD_DEFAULT 0 +#if defined(__sparc__) || defined(__powerpc__) +static unsigned int bigendian=1; +#else +static unsigned int bigendian=0; #endif - -static unsigned long remap[BTTV_MAX]; /* remap Bt848 */ +static int triton1=0; +static unsigned long remap[BTTV_MAX]; static unsigned int radio[BTTV_MAX]; -static unsigned int card[BTTV_MAX] = { CARD_DEFAULT, CARD_DEFAULT, - CARD_DEFAULT, CARD_DEFAULT }; -static unsigned int pll[BTTV_MAX] = { USE_PLL, USE_PLL, USE_PLL, USE_PLL }; +static unsigned int card[BTTV_MAX] = { 0, 0, 0, 0 }; +static unsigned int pll[BTTV_MAX] = { -1, -1, -1, -1}; +static unsigned int fieldnr = 0; +static unsigned int verbose = 1; +static unsigned int debug = 0; +static unsigned int gbuffers = 2; +static unsigned int gbufsize = BTTV_MAX_FBUF; +#ifdef MODULE +static unsigned int autoload = 1; +#else +static unsigned int autoload = 0; +#endif -static int bttv_num; /* number of Bt848s in use */ -static struct bttv bttvs[BTTV_MAX]; #define I2C_TIMING (0x7<<4) #define I2C_DELAY 10 @@ -125,8 +113,86 @@ { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); } #define I2C_GET() (btread(BT848_I2C)&1) -#define EEPROM_WRITE_DELAY 20000 #define BURSTOFFSET 76 +#define BTTV_ERRORS 5 + + +/* ----------------------------------------------------------------------- */ +/* Exported functions - for other modules which want to access the */ +/* gpio ports (IR for example) */ +/* see bttv.h for comments */ + +int bttv_get_id(unsigned int card) +{ + if (card >= bttv_num) { + return -1; + } + return bttvs[card].type; +} + +int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data) +{ + struct bttv *btv; + + if (card >= bttv_num) { + return -EINVAL; + } + + btv = &bttvs[card]; + btaor(data, ~mask, BT848_GPIO_OUT_EN); + return 0; +} + +int bttv_read_gpio(unsigned int card, unsigned long *data) +{ + struct bttv *btv; + + if (card >= bttv_num) { + return -EINVAL; + } + + btv = &bttvs[card]; + + if(btv->shutdown) { + return -ENODEV; + } + +/* prior setting BT848_GPIO_REG_INP is (probably) not needed + because we set direct input on init */ + *data = btread(BT848_GPIO_DATA); + return 0; +} + +int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data) +{ + struct bttv *btv; + + if (card >= bttv_num) { + return -EINVAL; + } + + btv = &bttvs[card]; + +/* prior setting BT848_GPIO_REG_INP is (probably) not needed + because direct input is set on init */ + btaor(data & mask, ~mask, BT848_GPIO_DATA); + return 0; +} + +WAIT_QUEUE* bttv_get_gpio_queue(unsigned int card) +{ + struct bttv *btv; + + if (card >= bttv_num) { + return NULL; + } + + btv = &bttvs[card]; + if (bttvs[card].shutdown) { + return NULL; + } + return &btv->gpioq; +} /*******************************/ /* Memory management functions */ @@ -204,11 +270,11 @@ return ret; } -static void * rvmalloc(unsigned long size) +static void * rvmalloc(signed long size) { void * mem; unsigned long adr, page; - + mem=vmalloc(size); if (mem) { @@ -225,7 +291,7 @@ return mem; } -static void rvfree(void * mem, unsigned long size) +static void rvfree(void * mem, signed long size) { unsigned long adr, page; @@ -255,7 +321,7 @@ static int fbuffer_alloc(struct bttv *btv) { if(!btv->fbuffer) - btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF); + btv->fbuffer=(unsigned char *) rvmalloc(gbuffers*gbufsize); else printk(KERN_ERR "bttv%d: Double alloc of fbuffer!\n", btv->nr); @@ -268,194 +334,220 @@ /* ----------------------------------------------------------------------- */ /* I2C functions */ -/* software I2C functions */ - -static void i2c_setlines(struct i2c_bus *bus,int ctrl,int data) +static void bttv_bit_setscl(void *data, int state) { - struct bttv *btv = (struct bttv*)bus->data; - btwrite((ctrl<<1)|data, BT848_I2C); - btread(BT848_I2C); /* flush buffers */ - udelay(I2C_DELAY); + struct bttv *btv = (struct bttv*)data; + + if (state) + btv->i2c_state |= 0x02; + else + btv->i2c_state &= ~0x02; + btwrite(btv->i2c_state, BT848_I2C); + btread(BT848_I2C); } -static int i2c_getdataline(struct i2c_bus *bus) +static void bttv_bit_setsda(void *data, int state) { - struct bttv *btv = (struct bttv*)bus->data; - return btread(BT848_I2C)&1; + struct bttv *btv = (struct bttv*)data; + + if (state) + btv->i2c_state |= 0x01; + else + btv->i2c_state &= ~0x01; + btwrite(btv->i2c_state, BT848_I2C); + btread(BT848_I2C); } -/* hardware I2C functions */ +static int bttv_bit_getscl(void *data) +{ + struct bttv *btv = (struct bttv*)data; + int state; + + state = btread(BT848_I2C) & 0x02 ? 1 : 0; + return state; +} -/* read I2C */ -static int I2CRead(struct i2c_bus *bus, unsigned char addr) +static int bttv_bit_getsda(void *data) { - u32 i; - u32 stat; - struct bttv *btv = (struct bttv*)bus->data; - - /* clear status bit ; BT848_INT_RACK is ro */ - btwrite(BT848_INT_I2CDONE, BT848_INT_STAT); - - btwrite(((addr & 0xff) << 24) | btv->i2c_command, BT848_I2C); - - /* - * Timeout for I2CRead is 1 second (this should be enough, really!) - */ - for (i=1000; i; i--) - { - stat=btread(BT848_INT_STAT); - if (stat & BT848_INT_I2CDONE) - break; - mdelay(1); - } - - if (!i) - { - printk(KERN_DEBUG "bttv%d: I2CRead timeout\n", - btv->nr); - return -1; - } - if (!(stat & BT848_INT_RACK)) - return -2; - - i=(btread(BT848_I2C)>>8)&0xff; - return i; + struct bttv *btv = (struct bttv*)data; + int state; + + state = btread(BT848_I2C) & 0x01; + return state; } -/* set both to write both bytes, reset it to write only b1 */ +static void bttv_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} -static int I2CWrite(struct i2c_bus *bus, unsigned char addr, unsigned char b1, - unsigned char b2, int both) +static void bttv_dec_use(struct i2c_adapter *adap) { - u32 i; - u32 data; - u32 stat; - struct bttv *btv = (struct bttv*)bus->data; - - /* clear status bit; BT848_INT_RACK is ro */ - btwrite(BT848_INT_I2CDONE, BT848_INT_STAT); - - data=((addr & 0xff) << 24) | ((b1 & 0xff) << 16) | btv->i2c_command; - if (both) - { - data|=((b2 & 0xff) << 8); - data|=BT848_I2C_W3B; - } - - btwrite(data, BT848_I2C); + MOD_DEC_USE_COUNT; +} - for (i=0x1000; i; i--) - { - stat=btread(BT848_INT_STAT); - if (stat & BT848_INT_I2CDONE) - break; - mdelay(1); - } - - if (!i) - { - printk(KERN_DEBUG "bttv%d: I2CWrite timeout\n", - btv->nr); - return -1; +static void call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg) +{ + int i; + + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (NULL == btv->i2c_clients[i]) + continue; + if (NULL == btv->i2c_clients[i]->driver->command) + continue; + btv->i2c_clients[i]->driver->command( + btv->i2c_clients[i],cmd,arg); } - if (!(stat & BT848_INT_RACK)) - return -2; - - return 0; } -/* read EEPROM */ -static void readee(struct i2c_bus *bus, unsigned char *eedata) +static int attach_inform(struct i2c_client *client) { - int i, k; - - if (I2CWrite(bus, 0xa0, 0, -1, 0)<0) - { - printk(KERN_WARNING "bttv: readee error\n"); - return; - } - - for (i=0; i<256; i++) - { - k=I2CRead(bus, 0xa1); - if (k<0) - { - printk(KERN_WARNING "bttv: readee error\n"); + struct bttv *btv = (struct bttv*)client->adapter->data; + int i; + + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (btv->i2c_clients[i] == NULL || + btv->i2c_clients[i]->driver->id == client->driver->id) { + btv->i2c_clients[i] = client; break; } - eedata[i]=k; } + if (btv->tuner_type != -1) + call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); + if (verbose) + printk("bttv%d: i2c attach [%s]\n",btv->nr,client->name); + return 0; } -/* write EEPROM */ -static void writeee(struct i2c_bus *bus, unsigned char *eedata) +static int detach_inform(struct i2c_client *client) { + struct bttv *btv = (struct bttv*)client->adapter->data; int i; - - for (i=0; i<256; i++) - { - if (I2CWrite(bus, 0xa0, i, eedata[i], 1)<0) - { - printk(KERN_WARNING "bttv: writeee error (%d)\n", i); + + if (verbose) + printk("bttv%d: i2c detach [%s]\n",btv->nr,client->name); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (NULL != btv->i2c_clients[i] && + btv->i2c_clients[i]->driver->id == client->driver->id) { + btv->i2c_clients[i] = NULL; break; } - udelay(EEPROM_WRITE_DELAY); } + return 0; } -static void attach_inform(struct i2c_bus *bus, int id) +static struct i2c_algo_bit_data i2c_algo_template = { + NULL, + bttv_bit_setsda, + bttv_bit_setscl, + bttv_bit_getsda, + bttv_bit_getscl, + 10, 10, 100, +}; + +static struct i2c_adapter i2c_adap_template = { + "bt848", + I2C_HW_B_BT848, + NULL, + NULL, + bttv_inc_use, + bttv_dec_use, + attach_inform, + detach_inform, + NULL, +}; + +static struct i2c_client i2c_client_template = { + "bttv internal", + -1, + 0, + 0, + NULL, + NULL +}; + +static int init_bttv_i2c(struct bttv *btv) { - struct bttv *btv = (struct bttv*)bus->data; - - switch (id) - { - case I2C_DRIVERID_MSP3400: - btv->have_msp3400 = 1; - break; - case I2C_DRIVERID_TUNER: - btv->have_tuner = 1; - if (btv->tuner_type != -1) - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_TUNER, - TUNER_SET_TYPE,&btv->tuner_type); - break; - } + /* i2c bit_adapter */ + memcpy(&btv->i2c_adap, &i2c_adap_template, sizeof(struct i2c_adapter)); + memcpy(&btv->i2c_algo, &i2c_algo_template, sizeof(struct i2c_algo_bit_data)); + memcpy(&btv->i2c_client, &i2c_client_template, sizeof(struct i2c_client)); + + sprintf(btv->i2c_adap.name+strlen(btv->i2c_adap.name), + " #%d", btv->nr); + btv->i2c_algo.data = btv; + btv->i2c_adap.data = btv; + btv->i2c_adap.algo_data = &btv->i2c_algo; + btv->i2c_client.adapter = &btv->i2c_adap; + + bttv_bit_setscl(btv,1); + bttv_bit_setsda(btv,1); + + btv->i2c_ok = i2c_bit_add_bus(&btv->i2c_adap); + return btv->i2c_ok; } -static void detach_inform(struct i2c_bus *bus, int id) +/* read I2C */ +static int I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) { - struct bttv *btv = (struct bttv*)bus->data; + unsigned char buffer = 0; - switch (id) - { - case I2C_DRIVERID_MSP3400: - btv->have_msp3400 = 0; - break; - case I2C_DRIVERID_TUNER: - btv->have_tuner = 0; - break; + if (0 != btv->i2c_ok) + return -1; + if (verbose && NULL != probe_for) + printk(KERN_INFO "bttv%d: i2c: checking for %s @ 0x%02x... ", + btv->nr,probe_for,addr); + btv->i2c_client.addr = addr >> 1; + if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) { + if (NULL != probe_for) { + if (verbose) + printk("not found\n"); + } else + printk(KERN_WARNING "bttv%d: i2c read 0x%x: error\n", + btv->nr,addr); + return -1; } + if (verbose && NULL != probe_for) + printk("found\n"); + return buffer; } -static struct i2c_bus bttv_i2c_bus_template = +/* write I2C */ +static int I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, + unsigned char b2, int both) { - "bt848", - I2C_BUSID_BT848, - NULL, + unsigned char buffer[2]; + int bytes = both ? 2 : 1; + + if (0 != btv->i2c_ok) + return -1; + btv->i2c_client.addr = addr >> 1; + buffer[0] = b1; + buffer[1] = b2; + if (bytes != i2c_master_send(&btv->i2c_client, buffer, bytes)) + return -1; + return 0; +} + +/* read EEPROM */ +static void readee(struct bttv *btv, unsigned char *eedata, int addr) +{ + int i; + + if (I2CWrite(btv, addr, 0, -1, 0)<0) { + printk(KERN_WARNING "bttv: readee error\n"); + return; + } + btv->i2c_client.addr = addr >> 1; + for (i=0; i<256; i+=16) { + if (16 != i2c_master_recv(&btv->i2c_client,eedata+i,16)) { + printk(KERN_WARNING "bttv: readee error\n"); + break; + } + } +} -#if LINUX_VERSION_CODE >= 0x020100 - SPIN_LOCK_UNLOCKED, -#endif - attach_inform, - detach_inform, - - i2c_setlines, - i2c_getdataline, - I2CRead, - I2CWrite, -}; - /* ----------------------------------------------------------------------- */ /* some hauppauge specific stuff */ @@ -471,7 +563,7 @@ { TUNER_ABSENT, "External" }, { TUNER_ABSENT, "Unspecified" }, { TUNER_ABSENT, "Philips FI1216" }, - { TUNER_ABSENT, "Philips FI1216MF" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, { TUNER_PHILIPS_NTSC, "Philips FI1236" }, { TUNER_ABSENT, "Philips FI1246" }, { TUNER_ABSENT, "Philips FI1256" }, @@ -491,97 +583,387 @@ { TUNER_PHILIPS_PAL, "Philips FM1216" }, { TUNER_ABSENT, "Philips FM1216MF" }, { TUNER_PHILIPS_NTSC, "Philips FM1236" }, + { TUNER_PHILIPS_PAL_I, "Philips FM1246" }, + { TUNER_ABSENT, "Philips FM1256" }, + { TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5" }, + { TUNER_ABSENT, "Samsung TCPN9082D" }, + { TUNER_ABSENT, "Samsung TCPM9092P" }, + { TUNER_TEMIC_PAL, "Temic 4006FH5" }, + { TUNER_ABSENT, "Samsung TCPN9085D" }, + { TUNER_ABSENT, "Samsung TCPB9085P" }, + { TUNER_ABSENT, "Samsung TCPL9091P" }, + { TUNER_ABSENT, "Temic 4039FR5" }, + { TUNER_ABSENT, "Philips FQ1216 ME" }, + { TUNER_TEMIC_PAL_I, "Temic 4066FY5" }, + { TUNER_ABSENT, "Philips TD1536" }, + { TUNER_ABSENT, "Philips TD1536D" }, + { TUNER_ABSENT, "Philips FMR1236" }, + { TUNER_ABSENT, "Philips FI1256MP" }, + { TUNER_ABSENT, "Samsung TCPQ9091P" }, + { TUNER_ABSENT, "Temic 4006FN5" }, + { TUNER_ABSENT, "Temic 4009FR5" }, + { TUNER_ABSENT, "Temic 4046FM5" }, }; static void -hauppauge_eeprom(struct i2c_bus *bus) +hauppauge_eeprom(struct bttv *btv) { - struct bttv *btv = (struct bttv*)bus->data; - - readee(bus, eeprom_data); if (eeprom_data[9] < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) { btv->tuner_type = hauppauge_tuner[eeprom_data[9]].id; - printk("bttv%d: Hauppauge eeprom: tuner=%s (%d)\n",btv->nr, - hauppauge_tuner[eeprom_data[9]].name,btv->tuner_type); + if (verbose) + printk("bttv%d: Hauppauge eeprom: tuner=%s (%d)\n",btv->nr, + hauppauge_tuner[eeprom_data[9]].name,btv->tuner_type); } } static void -hauppauge_msp_reset(struct bttv *btv) +hauppauge_boot_msp34xx(struct bttv *btv) { - /* Reset the MSP on some Hauppauge cards */ + int i; + + /* reset/enable the MSP on some Hauppauge cards */ /* Thanks to Kyösti Mälkki (kmalkki@cc.hut.fi)! */ - /* Can this hurt cards without one? What about Miros with MSP? */ btaor(32, ~32, BT848_GPIO_OUT_EN); btaor(0, ~32, BT848_GPIO_DATA); udelay(2500); btaor(32, ~32, BT848_GPIO_DATA); - /* btaor(0, ~32, BT848_GPIO_OUT_EN); */ + + if (verbose) + printk("bttv%d: Hauppauge msp34xx: reset line init\n",btv->nr); + + /* look if the msp3400 driver is already registered */ + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (btv->i2c_clients[i] != NULL && + btv->i2c_clients[i]->driver->id == I2C_DRIVERID_MSP3400) { + return; + } + } + + /* if not: look for the chip ... */ + if (I2CRead(btv, I2C_MSP3400, "MSP34xx")) { + /* ... if found re-register to trigger a i2c bus rescan, */ + /* this time with the msp34xx chip activated */ + i2c_bit_del_bus(&btv->i2c_adap); + i2c_bit_add_bus(&btv->i2c_adap); + } +} + + +/* Imagenation L-Model PXC200 Framegrabber */ +/* This is basically the same procedure as + * used by Alessandro Rubini in his pxc200 + * driver, but using BTTV functions */ +static void init_PXC200(struct bttv *btv) +{ + static const int vals[] = { 0x08, 0x09, 0x0a, 0x0b, 0x0d, 0x0d, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, + 0x00 }; + int i,tmp; + + /* Initialise GPIO-connevted stuff */ + btwrite(1<<13,BT848_GPIO_OUT_EN); /* Reset pin only */ + btwrite(0,BT848_GPIO_DATA); + udelay(3); + btwrite(1<<13,BT848_GPIO_DATA); + /* GPIO inputs are pulled up, so no need to drive + * reset pin any longer */ + btwrite(0,BT848_GPIO_OUT_EN); + + /* we could/should try and reset/control the AD pots? but + right now we simply turned off the crushing. Without + this the AGC drifts drifts + remember the EN is reverse logic --> + setting BT848_ADC_AGC_EN disable the AGC + tboult@eecs.lehigh.edu + */ + btwrite(BT848_ADC_RESERVED|BT848_ADC_AGC_EN, BT848_ADC); + + /* Initialise MAX517 DAC */ + printk(KERN_INFO "Setting DAC reference voltage level ...\n"); + I2CWrite(btv,0x5E,0,0x80,1); + + /* Initialise 12C508 PIC */ + /* The I2CWrite and I2CRead commmands are actually to the + * same chips - but the R/W bit is included in the address + * argument so the numbers are different */ + + printk(KERN_INFO "Initialising 12C508 PIC chip ...\n"); + + for (i = 0; i < sizeof(vals)/sizeof(int); i++) { + tmp=I2CWrite(btv,0x1E,vals[i],0,1); + printk(KERN_INFO "I2C Write(0x08) = %i\nI2C Read () = %x\n\n", + tmp,I2CRead(btv,0x1F,NULL)); + } + printk(KERN_INFO "PXC200 Initialised.\n"); } /* ----------------------------------------------------------------------- */ +static struct CARD { + unsigned id; + int cardnr; + char *name; +} cards[] = { + { 0x00011002, BTTV_HAUPPAUGE878, "ATI TV Wonder" }, + { 0x00011461, BTTV_AVERMEDIA98, "AVerMedia TVPhone98" }, + { 0x00031461, BTTV_AVERMEDIA98, "AVerMedia TVPhone98" }, + { 0x10b42636, BTTV_HAUPPAUGE878, "STB ???" }, + { 0x1118153b, BTTV_TERRATVALUE, "Terratec TV Value" }, + { 0x1200bd11, BTTV_PINNACLERAVE, "Pinnacle PCTV Rave" }, + { 0x13eb0070, BTTV_HAUPPAUGE878, "Hauppauge WinTV" }, + { 0x14610002, BTTV_AVERMEDIA98, "Avermedia TVCapture 98" }, + { 0x18501851, BTTV_CHRONOS_VS2, "Chronos Video Shuttle II" }, + { 0x18521852, BTTV_TYPHOON_TVIEW, "Typhoon TView TV/FM Tuner" }, + { 0x263610b4, BTTV_STB2, "STB TV PCI FM, P/N 6000704" }, + { 0x3000144f, BTTV_MAGICTVIEW063, "TView 99 (CPH063)" }, + { 0x300014ff, BTTV_MAGICTVIEW061, "TView 99 (CPH061)" }, + { 0x3002144f, BTTV_MAGICTVIEW061, "Askey Magic TView" }, + { 0x300214ff, BTTV_PHOEBE_TVMAS, "Phoebe TV Master" }, + { 0x402010fc, 0 /* no tvcards entry yet */, "I-O Data Co. GV-BCV3/PCI" }, + { 0x6606217d, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, + { 0, -1, NULL } +}; struct tvcard { + char *name; int video_inputs; int audio_inputs; int tuner; int svhs; u32 gpiomask; u32 muxsel[8]; - u32 audiomux[6]; /* Tuner, Radio, internal, external, mute, stereo */ - u32 gpiomask2; /* GPIO MUX mask */ + u32 audiomux[6]; /* Tuner, Radio, external, internal, mute, stereo */ + u32 gpiomask2; /* GPIO MUX mask */ + + /* look for these i2c audio chips */ + int msp34xx:1; + int tda8425:1; + int tda9840:1; + int tda985x:1; + int tea63xx:1; + int tea64xx:1; + int tda7432:1; + int tda9875:1; + + /* other settings */ + int pll; + int tuner_type; }; static struct tvcard tvcards[] = { - /* default */ - { 3, 1, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0}}, - /* MIRO */ - { 4, 1, 0, 2,15, { 2, 3, 1, 1}, { 2, 0, 0, 0,10}}, - /* Hauppauge */ - { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, - /* STB */ - { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1}}, - /* Intel??? */ - { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, - /* Diamond DTV2000 */ - { 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 0, 1, 0, 1, 3}}, - /* AVerMedia TVPhone */ - { 3, 1, 0, 3,15, { 2, 3, 1, 1}, {12, 0,11,11, 0}}, - /* Matrix Vision MV-Delta */ - { 5, 1, -1, 3, 0, { 2, 3, 1, 0, 0}}, - /* Fly Video II */ - { 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1}, - {0, 0xc00, 0x800, 0x400, 0xc00, 0}}, - /* TurboTV */ - { 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0}}, - /* Newer Hauppauge (bt878) */ - { 3, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4}}, - /* MIRO PCTV pro */ - { 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10}}, - /* ADS Technologies Channel Surfer TV (and maybe TV+FM) */ - { 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0}, 0}, - /* AVerMedia TVCapture 98 */ - { 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0}, 0}, - /* Aimslab VHX */ - { 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4}}, - /* Zoltrix TV-Max */ - { 3, 1, 0, 2, 0x00000f, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0x8}}, - /* Pixelview PlayTV (bt878) */ - { 3, 4, 0, 2, 0x01e000, { 2, 0, 1, 1}, {0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 }}, - /* "Leadtek WinView 601", */ - { 3, 1, 0, 2, 0x8300f8, { 2, 3, 1, 1,0}, {0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007}}, - /* AVEC Intercapture */ - { 3, 1, 9, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0}}, - /* LifeView FlyKit w/o Tuner */ - { 3, 1, -1, -1, 0x8dff00, { 2, 3, 1, 1}} + /* 0x00 */ + { " *** UNKNOWN *** ", + 3, 1, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "MIRO PCTV", + 4, 1, 0, 2,15, { 2, 3, 1, 1}, { 2, 0, 0, 0,10},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "Hauppauge old", + 4, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,0,1,0,0,0,1, PLL_NONE, -1 }, + { "STB", + 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1},0, + 0,1,1,1,1,0,0,1, PLL_NONE, -1 }, + + { "Intel", + 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "Diamond DTV2000", + 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 0, 1, 0, 1, 3},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "AVerMedia TVPhone", + 3, 1, 0, 3,15, { 2, 3, 1, 1}, {12, 4,11,11, 0},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "MATRIX-Vision MV-Delta", + 5, 1, -1, 3, 0, { 2, 3, 1, 0, 0},{0 }, 0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + + /* 0x08 */ + { "Fly Video II", + 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1}, + { 0, 0xc00, 0x800, 0x400, 0xc00, 0},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "TurboTV", + 3, 1, 0, 2, 3, { 2, 3, 1, 1}, { 1, 1, 2, 3, 0},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "Hauppauge new (bt878)", + 4, 1, 0, 2, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,0,1,0,0,0,1, PLL_28, -1 }, + { "MIRO PCTV pro", + 3, 1, 0, 2, 65551, { 2, 3, 1, 1}, {1,65537, 0, 0,10},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + + { "ADS Technologies Channel Surfer TV", + 3, 1, 2, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "AVerMedia TVCapture 98", + 3, 4, 0, 2, 15, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0},0, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, + { "Aimslab VHX", + 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "Zoltrix TV-Max", + 3, 1, 0, 2,15, { 2, 3, 1, 1}, {0 , 0, 1 , 0, 10},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + + /* 0x10 */ + { "Pixelview PlayTV (bt878)", + 3, 1, 0, 2, 0x01fe00, { 2, 0, 1, 1}, + { 0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 },0, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, + { "Leadtek WinView 601", + 3, 1, 0, 2, 0x8300f8, { 2, 3, 1, 1,0}, + { 0x4fa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007,0xcfa007},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "AVEC Intercapture", + 3, 2, 0, 2, 0, {2, 3, 1, 1}, {1, 0, 0, 0, 0},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "LifeView FlyKit w/o Tuner", + 3, 1, -1, -1, 0x8dff00, { 2, 3, 1, 1}, { 0 },0, + 0,0,0,0,0,0,0,1, PLL_NONE, -1 }, + + { "CEI Raffles Card", + 3, 3, 0, 2, 0, {2, 3, 1, 1}, {0, 0, 0, 0 ,0},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "Lucky Star Image World ConferenceTV", + 3, 1, 0, 2, 0x00fffe07, { 2, 3, 1, 1}, { 131072, 1, 1638400, 3, 4},0, + 1,1,1,1,0,0,0,1, PLL_28, TUNER_PHILIPS_PAL_I }, + { "Phoebe Tv Master + FM", + 3, 1, 0, 2, 0xc00, { 2, 3, 1, 1},{0, 1, 0x800, 0x400, 0xc00, 0},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "Modular Technology MM205 PCTV, bt878", + 2, 1, 0, -1, 7, { 2, 3 }, { 0, 0, 0, 0, 0 },0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + + /* 0x18 */ + { "Askey/Typhoon/Anubis Magic TView CPH051/061 (bt878)", + 3, 1, 0, 2, 0xe00, { 2, 3, 1, 1}, {0x400, 0x400, 0x400, 0x400, 0},0, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, + { "Terratec/Vobis TV-Boostar", + 3, 1, 0, 2, 16777215 , { 2, 3, 1, 1}, { 131072, 1, 1638400, 3,4},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "Newer Hauppauge WinCam (bt878)", + 4, 1, 0, 3, 7, { 2, 0, 1, 1}, { 0, 1, 2, 3, 4},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "MAXI TV Video PCI2", + 3, 1, 0, 2, 0xffff, { 2, 3, 1, 1}, { 0, 1, 2, 3, 0xc00},0, + 1,1,1,1,0,0,0,1, PLL_NONE, TUNER_PHILIPS_SECAM }, + + { "Terratec TerraTV+", + 3, 1, 0, 2, 0x70000, { 2, 3, 1, 1}, + { 0x20000, 0x30000, 0x00000, 0x10000, 0x40000},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "Imagenation PXC200", + 5, 1, -1, 4, 0, { 2, 3, 1, 0, 0}, { 0 }, 0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "FlyVideo 98", + 3, 1, 0, 2, 0x8dff00, {2, 3, 1, 1}, + { 0, 0x8dff00, 0x8df700, 0x8de700, 0x8dff00, 0 },0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "iProTV", + 3, 1, 0, 2, 1, { 2, 3, 1, 1}, { 1, 0, 0, 0, 0 },0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + + /* 0x20 */ + { "Intel Create and Share PCI", + 4, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 4, 4, 4, 4},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "Terratec TerraTValue", + 3, 1, 0, 2, 0xffff00, { 2, 3, 1, 1}, + { 0x500, 0, 0x300, 0x900, 0x900},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + { "Leadtek WinFast 2000", + 3, 1, 0, 2, 0xfff000, { 2, 3, 1, 1,0}, + { 0x621000,0x620100,0x621100,0x620000,0xE210000,0x620000},0, + 1,1,1,1,1,0,0,1, PLL_28, -1 }, + { "Chronos Video Shuttle II", + 3, 3, 0, 2, 0x1800, { 2, 3, 1, 1}, { 0, 0, 0x1000, 0x1000, 0x0800},0, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, + + { "Typhoon TView TV/FM Tuner", + 3, 3, 0, 2, 0x1800, { 2, 3, 1, 1}, { 0, 0x800, 0, 0, 0x1800, 0 },0, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, + { "PixelView PlayTV pro", + 3, 1, 0, 2, 0xff, { 2, 3, 1, 1 }, + { 0x21, 0x20, 0x24, 0x2c, 0x29, 0x29 }, 0, + 0,0,0,0,0,0,0,1, PLL_28, -1 }, + { "TView99 CPH063", + 3, 1, 0, 2, 0x551e00, { 2, 3, 1, 1}, + { 0x551400, 0x551200, 0, 0, 0, 0x551200 }, 0, + 1,1,1,1,0,0,0,1, PLL_28, -1 }, + { "Pinnacle PCTV Rave", + 3, 1, 0, 2, 0x03000F, { 2, 3, 1, 1}, { 2, 0, 0, 0, 1},0, + 1,1,1,1,0,0,0,1, PLL_NONE, -1 }, + + /* 0x28 */ + { "STB2", + 3, 1, 0, 2, 7, { 2, 3, 1, 1}, { 4, 0, 2, 3, 1},0, + 0,1,1,1,0,1,1,1, PLL_NONE, -1 }, }; -#define TVCARDS (sizeof(tvcards)/sizeof(tvcard)) +#define TVCARDS (sizeof(tvcards)/sizeof(struct tvcard)) + +static void +dump_eeprom(struct bttv *btv,int addr) +{ + int i; + + if (verbose < 2) + return; + /* for debugging: dump eeprom to syslog */ + printk(KERN_DEBUG "bttv%d: dump eeprom @ 0x%02x\n",btv->nr,addr); + for (i = 0; i < 256;) { + printk(KERN_DEBUG " %02x:",i); + do { + printk(" %02x",eeprom_data[i++]); + } while (i % 16); + printk("\n"); + } +} + +static int +idcard_eeprom(struct bttv *btv) +{ + unsigned id; + int i,n; + + id = (eeprom_data[252] << 24) | + (eeprom_data[253] << 16) | + (eeprom_data[254] << 8) | + (eeprom_data[255]); + if (id == 0 || id == 0xffffffff) + return -1; + + /* look for the card */ + for (n = -1, i = 0; cards[i].id != 0; i++) + if (cards[i].id == id) + n = i; + + if (n != -1) { + /* found it */ + printk(KERN_INFO "bttv%d: id: %s (0x%08x)\n", + btv->nr,cards[n].name,id); + if (verbose) + printk(KERN_INFO "bttv%d: => card=%d (%s)\n", + btv->nr,cards[n].cardnr, + tvcards[cards[n].cardnr].name); + return cards[n].cardnr; + } else { + /* 404 */ + printk(KERN_INFO "bttv%d: id: unknown (0x%08x)\n", + btv->nr, id); + printk(KERN_INFO "please mail id, board name and " + "the correct card= insmod option to " + "kraxel@goldbach.in-berlin.de\n"); + return -1; + } +} + +/* ----------------------------------------------------------------------- */ -static void audio(struct bttv *btv, int mode) +static void audio(struct bttv *btv, int mode, int no_irq_context) { btaor(tvcards[btv->type].gpiomask, ~tvcards[btv->type].gpiomask, BT848_GPIO_OUT_EN); @@ -607,17 +989,14 @@ break; } /* if audio mute or not in H-lock, turn audio off */ - if ((btv->audio&AUDIO_MUTE) -#if 0 - || - (!btv->radio && !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)) -#endif - ) + if ((btv->audio&AUDIO_MUTE)) mode=AUDIO_OFF; - if ((mode == 0) && (btv->radio)) - mode = 1; + if ((mode == AUDIO_TUNER) && (btv->radio)) + mode = AUDIO_RADIO; btaor(tvcards[btv->type].audiomux[mode], ~tvcards[btv->type].gpiomask, BT848_GPIO_DATA); + if (no_irq_context) + call_i2c_clients(btv,AUDC_SET_INPUT,&(mode)); } @@ -630,21 +1009,6 @@ } -static void bt848_cap(struct bttv *btv, uint state) -{ - if (state) - { - btv->cap|=3; - bt848_set_risc_jmps(btv); - } - else - { - btv->cap&=~3; - bt848_set_risc_jmps(btv); - } -} - - /* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC*/ /* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C @@ -705,9 +1069,10 @@ /* printk("bttv%d: PLL: no change required\n",btv->nr); */ return 1; } - - printk("bttv%d: PLL: %d => %d ... ",btv->nr, - btv->pll.pll_ifreq, btv->pll.pll_ofreq); + + if (verbose) + printk("bttv%d: PLL: %d => %d ... ",btv->nr, + btv->pll.pll_ifreq, btv->pll.pll_ofreq); set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq); @@ -719,7 +1084,7 @@ } while(time_before(jiffies,tv)); - for (i=0; i<10; i++) + for (i=0; i<100; i++) { if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK)) btwrite(0,BT848_DSTATUS); @@ -727,13 +1092,15 @@ { btwrite(0x08,BT848_TGCTRL); btv->pll.pll_current = btv->pll.pll_ofreq; - printk("ok\n"); + if (verbose) + printk("ok\n"); return 1; } mdelay(10); } btv->pll.pll_current = 0; - printk("oops\n"); + if (verbose) + printk("oops\n"); return -1; } @@ -760,20 +1127,12 @@ } btaor((tvcards[btv->type].muxsel[input&7]&3)<<5, ~(3<<5), BT848_IFORM); audio(btv, (input!=tvcards[btv->type].tuner) ? - AUDIO_EXTERN : AUDIO_TUNER); + AUDIO_EXTERN : AUDIO_TUNER, 1); btaor(tvcards[btv->type].muxsel[input]>>4, ~tvcards[btv->type].gpiomask2, BT848_GPIO_DATA); } -/* - * Set the registers for the size we have specified. Don't bother - * trying to understand this without the BT848 manual in front of - * you [AC]. - * - * PS: The manual is free for download in .pdf format from - * www.brooktree.com - nicely done those folks. - */ - + struct tvnorm { u32 Fsc; @@ -790,29 +1149,14 @@ /* PAL-BDGHI */ /* max. active video is actually 922, but 924 is divisible by 4 and 3! */ /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */ -#ifdef VIDEODAT - { 35468950, - 924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), - 1135, 186, 924, 0x20, 255}, -#else { 35468950, 924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), 1135, 186, 924, 0x20, 255}, -#endif -/* - { 35468950, - 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), - 944, 186, 922, 0x20, 255}, -*/ + /* NTSC */ { 28636363, 768, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0), 910, 128, 910, 0x1a, 144}, -/* - { 28636363, - 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0), - 780, 122, 754, 0x1a, 144}, -*/ #if 0 /* SECAM EAST */ { 35468950, @@ -853,10 +1197,9 @@ unsigned int *po=(unsigned int *) btv->vbi_odd; unsigned int *pe=(unsigned int *) btv->vbi_even; - DEBUG(printk(KERN_DEBUG "vbiodd: 0x%lx\n",(long)btv->vbi_odd)); - DEBUG(printk(KERN_DEBUG "vbievn: 0x%lx\n",(long)btv->vbi_even)); - DEBUG(printk(KERN_DEBUG "po: 0x%lx\n",(long)po)); - DEBUG(printk(KERN_DEBUG "pe: 0x%lx\n",(long)pe)); + if (debug) + printk("bttv%d: vbi1: po=%08lx pe=%08lx\n", + btv->nr,virt_to_bus(po), virt_to_bus(pe)); *(po++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(po++)=0; for (i=0; i<16; i++) @@ -875,15 +1218,17 @@ } *(pe++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16)); *(pe++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+10)); - DEBUG(printk(KERN_DEBUG "po: 0x%lx\n",(long)po)); - DEBUG(printk(KERN_DEBUG "pe: 0x%lx\n",(long)pe)); + + if (debug) + printk("bttv%d: vbi2: po=%08lx pe=%08lx\n", + btv->nr,virt_to_bus(po), virt_to_bus(pe)); } -int fmtbppx2[16] = { +static int fmtbppx2[16] = { 8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0 }; -int palette2fmt[] = { +static int palette2fmt[] = { 0, BT848_COLOR_FMT_Y8, BT848_COLOR_FMT_RGB8, @@ -911,8 +1256,10 @@ unsigned long bpl=1024; /* bytes per line */ unsigned long vadr=(unsigned long) vbuf; - *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(ro++)=0; - *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(re++)=0; + *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(ro++)=cpu_to_le32(0); + *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(re++)=cpu_to_le32(0); /* In PAL 650 blocks of 256 DWORDs are sampled, but only if VDELAY is 2 and without separate VBI grabbing. @@ -923,7 +1270,7 @@ *(ro++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL); *(ro++)=cpu_to_le32(kvirt_to_bus(vadr)); *(re++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL); - *(re++)=cpu_to_le32(kvirt_to_bus(vadr+BTTV_MAX_FBUF/2)); + *(re++)=cpu_to_le32(kvirt_to_bus(vadr+gbufsize/2)); vadr+=bpl; } @@ -935,7 +1282,6 @@ return 0; } - static int make_prisctab(struct bttv *btv, unsigned int *ro, unsigned int *re, unsigned int *vbuf, unsigned short width, @@ -950,6 +1296,9 @@ unsigned long vadr=(unsigned long) vbuf; int shift, csize; + if (debug) + printk("bttv%d: prisc1: ro=%08lx re=%08lx\n", + btv->nr,virt_to_bus(ro), virt_to_bus(re)); switch(fmt) { @@ -982,10 +1331,12 @@ } cbadr=vadr+(width*height); cradr=cbadr+csize; - inter = (height>btv->win.cropheight/2) ? 1 : 0; + inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0; - *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); *(ro++)=0; - *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); *(re++)=0; + *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); + *(ro++)=0; + *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); + *(re++)=0; for (line=0; line < (height<<(1^inter)); line++) { @@ -998,7 +1349,7 @@ if (inter) rp= (line&1) ? &re : &ro; else - rp= (line>=height) ? &re : &ro; + rp= (line>=height) ? &ro : &re; if(line&lmask) @@ -1027,7 +1378,7 @@ vadr+=bl; if((rcmd&(15<<28))==BT848_RISC_WRITE123) { - *((*rp)++)=cpu_to_le32(kvirt_to_bus(cbadr)); + *((*rp)++)=(kvirt_to_bus(cbadr)); cbadr+=blcb; *((*rp)++)=cpu_to_le32(kvirt_to_bus(cradr)); cradr+=blcr; @@ -1042,6 +1393,10 @@ *(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16)); *(re++)=cpu_to_le32(btv->bus_vbi_odd); + if (debug) + printk("bttv%d: prisc2: ro=%08lx re=%08lx\n", + btv->nr,virt_to_bus(ro), virt_to_bus(re)); + return 0; } @@ -1062,26 +1417,31 @@ return make_rawrisctab(btv, ro, re, vbuf); if (palette>=VIDEO_PALETTE_PLANAR) return make_prisctab(btv, ro, re, vbuf, width, height, palette); - - - inter = (height>btv->win.cropheight/2) ? 1 : 0; + + if (debug) + printk("bttv%d: vrisc1: ro=%08lx re=%08lx\n", + btv->nr,virt_to_bus(ro), virt_to_bus(re)); + + inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0; bpl=width*fmtbppx2[palette2fmt[palette]&0xf]/2; - *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(ro++)=0; - *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(re++)=0; + *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(ro++)=cpu_to_le32(0); + *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(re++)=cpu_to_le32(0); for (line=0; line < (height<<(1^inter)); line++) { if (inter) rp= (line&1) ? &re : &ro; else - rp= (line>=height) ? &re : &ro; + rp= (line>=height) ? &ro : &re; bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr); if (bpl<=bl) { *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL| - BT848_RISC_EOL|bpl); + BT848_RISC_EOL|bpl); *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); vadr+=bpl; } @@ -1109,6 +1469,10 @@ *(ro++)=cpu_to_le32(btv->bus_vbi_even); *(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16)); *(re++)=cpu_to_le32(btv->bus_vbi_odd); + + if (debug) + printk("bttv%d: vrisc2: ro=%08lx re=%08lx\n", + btv->nr,virt_to_bus(ro), virt_to_bus(re)); return 0; } @@ -1173,23 +1537,32 @@ static void make_clip_tab(struct bttv *btv, struct video_clip *cr, int ncr) { - int i, line, x, y, bpl, width, height, inter; + int i, line, x, y, bpl, width, height, inter, maxw; unsigned int bpp, dx, sx, **rp, *ro, *re, flags, len; unsigned long adr; - unsigned char *clipmap, cbit, lastbit, outofmem; + unsigned char *clipmap, *clipline, cbit, lastbit, outofmem; - inter=(btv->win.interlace&1)^1; - bpp=btv->win.bpp; - if (bpp==15) /* handle 15bpp as 16bpp in calculations */ - bpp++; + /* take care: bpp != btv->win.bpp is allowed here */ + bpp = fmtbppx2[btv->win.color_fmt&0xf]/2; bpl=btv->win.bpl; - ro=btv->risc_odd; - re=btv->risc_even; - if((width=btv->win.width)>1023) + adr=btv->win.vidadr + btv->win.x * btv->win.bpp + btv->win.y * bpl; + inter=(btv->win.interlace&1)^1; + width=btv->win.width; + height=btv->win.height; + if (debug) + printk("bttv%d: clip1: pal=%d size=%dx%d, bpl=%d bpp=%d\n", + btv->nr,btv->picture.palette,width,height,bpl,bpp); + if(width > 1023) width = 1023; /* sanity check */ - if((height=btv->win.height)>625) + if(height > 625) height = 625; /* sanity check */ - adr=btv->win.vidadr+btv->win.x*bpp+btv->win.y*bpl; + ro=btv->risc_scr_odd; + re=btv->risc_scr_even; + + if (debug) + printk("bttv%d: clip: ro=%08lx re=%08lx\n", + btv->nr,virt_to_bus(ro), virt_to_bus(re)); + if ((clipmap=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) { /* can't clip, don't generate any risc code */ *(ro++)=cpu_to_le32(BT848_RISC_JUMP); @@ -1208,29 +1581,50 @@ /* clip against viewing window AND screen so we do not have to rely on the user program */ - clip_draw_rectangle(clipmap,(btv->win.x+width>btv->win.swidth) ? - (btv->win.swidth-btv->win.x) : width, 0, 1024, 768); + maxw = (bpl - btv->win.x * btv->win.bpp) / bpp; + clip_draw_rectangle(clipmap, (width > maxw) ? maxw : width, + 0, 1024, 768); clip_draw_rectangle(clipmap,0,(btv->win.y+height>btv->win.sheight) ? - (btv->win.sheight-btv->win.y) : height,1024,768); + (btv->win.sheight-btv->win.y) : height,1024,768); if (btv->win.x<0) clip_draw_rectangle(clipmap, 0, 0, -(btv->win.x), 768); if (btv->win.y<0) clip_draw_rectangle(clipmap, 0, 0, 1024, -(btv->win.y)); - *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(ro++)=0; - *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(re++)=0; + *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(ro++)=cpu_to_le32(0); + *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(re++)=cpu_to_le32(0); /* translate bitmap to risc code */ for (line=outofmem=0; line < (height<>inter; rp= (line&1) ? &re : &ro; - lastbit=(clipmap[y<<7]&1); - for(x=dx=1,sx=0; x<=width && !outofmem; x++) { - cbit = (clipmap[(y<<7)+(x>>3)] & (1<<(x&7))); - if (x < width && !lastbit == !cbit) + clipline = clipmap + (y<<7); /* running pointers ... */ + lastbit = *clipline & 1; + for(x=dx=0,sx=0; x<=width && !outofmem;) { + if (0 == (x&7)) { + /* check bytes not bits if we can ... */ + if (lastbit) { + while (0xff==*clipline && xrisc_odd > RISCMEM_LEN/2 - 16) + if (ro - btv->risc_scr_odd>RISCMEM_LEN/2 - 16) outofmem++; - if (re - btv->risc_even > RISCMEM_LEN/2 - 16) + if (re - btv->risc_scr_even>RISCMEM_LEN/2 - 16) outofmem++; } + x++; + if (0 == (x&7)) + clipline++; } if ((!inter)||(line&1)) adr+=bpl; } + vfree(clipmap); /* outofmem flag relies on the following code to discard extra data */ *(ro++)=cpu_to_le32(BT848_RISC_JUMP); *(ro++)=cpu_to_le32(btv->bus_vbi_even); *(re++)=cpu_to_le32(BT848_RISC_JUMP); *(re++)=cpu_to_le32(btv->bus_vbi_odd); -} -/* set geometry for even/odd frames - just if you are wondering: - handling of even and odd frames will be separated, e.g. for grabbing - the even ones as RGB into videomem and the others as YUV in main memory for - compressing and sending to the video conferencing partner. + if (debug) + printk("bttv%d: clip2: pal=%d size=%dx%d, bpl=%d bpp=%d\n", + btv->nr,btv->picture.palette,width,height,bpl,bpp); +} -*/ -static inline void bt848_set_eogeo(struct bttv *btv, int odd, u8 vtc, - u16 hscale, u16 vscale, - u16 hactive, u16 vactive, - u16 hdelay, u16 vdelay, - u8 crop) +/* + * Set the registers for the size we have specified. Don't bother + * trying to understand this without the BT848 manual in front of + * you [AC]. + * + * PS: The manual is free for download in .pdf format from + * www.brooktree.com - nicely done those folks. + */ + +static inline void bt848_set_eogeo(struct bttv *btv, struct tvnorm *tvn, + int odd, int width, int height) { + u16 vscale, hscale; + u32 xsf, sr; + u16 hdelay; + u8 crop, vtc; + int inter = (height>tvn->sheight/2) ? 0 : 1; int off = odd ? 0x80 : 0x00; - + + xsf = (width*tvn->scaledtwidth)/tvn->swidth; + hscale = ((tvn->totalwidth*4096UL)/xsf-4096); + hdelay = tvn->hdelayx1; + hdelay = (hdelay*width)/tvn->swidth; + hdelay &= 0x3fe; + sr=((tvn->sheight>>inter)*512)/height-512; + vscale=(0x10000UL-sr)&0x1fff; + crop=((width>>8)&0x03)|((hdelay>>6)&0x0c)| + ((tvn->sheight>>4)&0x30)|((tvn->vdelay>>2)&0xc0); + vscale |= inter ? (BT848_VSCALE_INT<<8) : 0; + +#if 0 + /* Some people say interpolation looks bad ... */ + vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0); + if (width < 767) + btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off); + else + btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off); +#else + vtc = 0; + btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off); +#endif + btwrite(vtc, BT848_E_VTC+off); btwrite(hscale>>8, BT848_E_HSCALE_HI+off); btwrite(hscale&0xff, BT848_E_HSCALE_LO+off); btaor((vscale>>8), 0xe0, BT848_E_VSCALE_HI+off); btwrite(vscale&0xff, BT848_E_VSCALE_LO+off); - btwrite(hactive&0xff, BT848_E_HACTIVE_LO+off); + btwrite(width&0xff, BT848_E_HACTIVE_LO+off); btwrite(hdelay&0xff, BT848_E_HDELAY_LO+off); - btwrite(vactive&0xff, BT848_E_VACTIVE_LO+off); - btwrite(vdelay&0xff, BT848_E_VDELAY_LO+off); + btwrite(tvn->sheight&0xff, BT848_E_VACTIVE_LO+off); + btwrite(tvn->vdelay&0xff, BT848_E_VDELAY_LO+off); btwrite(crop, BT848_E_CROP+off); } -static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, u16 fmt, int pll) +static void bt848_set_geo(struct bttv *btv, + int no_irq_context) { - u16 vscale, hscale; - u32 xsf, sr; - u16 hdelay, vdelay; - u16 hactive, vactive; - u16 inter; - u8 crop, vtc; + u16 ewidth, eheight, owidth, oheight; + u16 format, bswap; struct tvnorm *tvn; unsigned long flags; - if (!width || !height) - return; - save_flags(flags); cli(); tvn=&tvnorms[btv->win.norm]; - btv->win.cropheight=tvn->sheight; - btv->win.cropwidth=tvn->swidth; - -/* - if (btv->win.cropwidth>tvn->cropwidth) - btv->win.cropwidth=tvn->cropwidth; - if (btv->win.cropheight>tvn->cropheight) - btv->win.cropheight=tvn->cropheight; - - if (width>btv->win.cropwidth) - width=btv->win.cropwidth; - if (height>btv->win.cropheight) - height=btv->win.cropheight; -*/ btwrite(tvn->adelay, BT848_ADELAY); btwrite(tvn->bdelay, BT848_BDELAY); btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM); @@ -1328,61 +1738,55 @@ btwrite(1, BT848_VBI_PACK_DEL); btv->pll.pll_ofreq = tvn->Fsc; - if(pll) - set_pll(btv); - - btwrite(fmt, BT848_COLOR_FMT); -#ifdef __sparc__ - if(fmt == BT848_COLOR_FMT_RGB32 || - fmt == BT848_COLOR_FMT_RGB24) { - btwrite((BT848_COLOR_CTL_GAMMA | - BT848_COLOR_CTL_WSWAP_ODD | - BT848_COLOR_CTL_WSWAP_EVEN | - BT848_COLOR_CTL_BSWAP_ODD | - BT848_COLOR_CTL_BSWAP_EVEN), - BT848_COLOR_CTL); - } else if(fmt == BT848_COLOR_FMT_RGB16 || - fmt == BT848_COLOR_FMT_RGB15) { - btwrite((BT848_COLOR_CTL_GAMMA | - BT848_COLOR_CTL_BSWAP_ODD | - BT848_COLOR_CTL_BSWAP_EVEN), - BT848_COLOR_CTL); - } -#endif - hactive=width; + if (no_irq_context) + set_pll(btv); - vtc=0; - /* Some people say interpolation looks bad ... */ - /* vtc = (hactive < 193) ? 2 : ((hactive < 385) ? 1 : 0); */ - - btv->win.interlace = (height>btv->win.cropheight/2) ? 1 : 0; - inter=(btv->win.interlace&1)^1; - vdelay=btv->win.cropy+tvn->vdelay; + btv->win.interlace = (btv->win.height>tvn->sheight/2) ? 1 : 0; - xsf = (hactive*tvn->scaledtwidth)/btv->win.cropwidth; - hscale = ((tvn->totalwidth*4096UL)/xsf-4096); + if (0 == btv->risc_cap_odd && + 0 == btv->risc_cap_even) { + /* overlay only */ + owidth = btv->win.width; + oheight = btv->win.height; + ewidth = btv->win.width; + eheight = btv->win.height; + format = btv->win.color_fmt; + bswap = btv->fb_color_ctl; + } else if (-1 != btv->gq_grab && + 0 == btv->risc_cap_odd && + !btv->win.interlace && + btv->scr_on) { + /* odd field -> overlay, even field -> capture */ + owidth = btv->win.width; + oheight = btv->win.height; + ewidth = btv->gbuf[btv->gq_grab].width; + eheight = btv->gbuf[btv->gq_grab].height; + format = (btv->win.color_fmt & 0xf0) | + (btv->gbuf[btv->gq_grab].fmt & 0x0f); + bswap = btv->fb_color_ctl & 0x0a; + } else { + /* capture only */ + owidth = btv->gbuf[btv->gq_grab].width; + oheight = btv->gbuf[btv->gq_grab].height; + ewidth = btv->gbuf[btv->gq_grab].width; + eheight = btv->gbuf[btv->gq_grab].height; + format = btv->gbuf[btv->gq_grab].fmt; + bswap = 0; + } + + /* program odd + even fields */ + bt848_set_eogeo(btv, tvn, 1, owidth, oheight); + bt848_set_eogeo(btv, tvn, 0, ewidth, eheight); - hdelay=tvn->hdelayx1+btv->win.cropx; - hdelay=(hdelay*hactive)/btv->win.cropwidth; - hdelay&=0x3fe; + btwrite(format, BT848_COLOR_FMT); + btwrite(bswap | BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL); - sr=((btv->win.cropheight>>inter)*512)/height-512; - vscale=(0x10000UL-sr)&0x1fff; - vactive=btv->win.cropheight; - crop=((hactive>>8)&0x03)|((hdelay>>6)&0x0c)| - ((vactive>>4)&0x30)|((vdelay>>2)&0xc0); - vscale|= btv->win.interlace ? (BT848_VSCALE_INT<<8) : 0; - - bt848_set_eogeo(btv, 0, vtc, hscale, vscale, hactive, vactive, - hdelay, vdelay, crop); - bt848_set_eogeo(btv, 1, vtc, hscale, vscale, hactive, vactive, - hdelay, vdelay, crop); restore_flags(flags); } -int bpp2fmt[4] = { - BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16, +static int bpp2fmt[4] = { + BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16, BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32 }; @@ -1390,9 +1794,31 @@ { unsigned short format; - btv->win.color_fmt = format = - (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 : - bpp2fmt[(btv->win.bpp-1)&3]; + if (btv->picture.palette > 0 && btv->picture.palette <= VIDEO_PALETTE_YUV422) { + /* format set by VIDIOCSPICT */ + format = palette2fmt[btv->picture.palette]; + } else { + /* use default for the given color depth */ + format = (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 : + bpp2fmt[(btv->win.bpp-1)&3]; + } + btv->win.color_fmt = format; + if (bigendian && + format == BT848_COLOR_FMT_RGB32) { + btv->fb_color_ctl = + BT848_COLOR_CTL_WSWAP_ODD | + BT848_COLOR_CTL_WSWAP_EVEN | + BT848_COLOR_CTL_BSWAP_ODD | + BT848_COLOR_CTL_BSWAP_EVEN; + } else if (bigendian && + (format == BT848_COLOR_FMT_RGB16 || + format == BT848_COLOR_FMT_RGB15)) { + btv->fb_color_ctl = + BT848_COLOR_CTL_BSWAP_ODD | + BT848_COLOR_CTL_BSWAP_EVEN; + } else { + btv->fb_color_ctl = 0; + } /* RGB8 seems to be a 9x5x5 GRB color cube starting at * color 16. Why the h... can't they even mention this in the @@ -1406,45 +1832,9 @@ else btor(BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); - bt848_set_geo(btv, btv->win.width, btv->win.height, format, 1); + bt848_set_geo(btv,1); } -/* - * Set TSA5522 synthesizer frequency in 1/16 Mhz steps - */ - -static void set_freq(struct bttv *btv, unsigned short freq) -{ - int fixme = freq; /* XXX */ - - /* mute */ - if (btv->have_msp3400) - i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, - MSP_SWITCH_MUTE,0); - - /* switch channel */ - if (btv->have_tuner) { - if (btv->radio) { - i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER, - TUNER_SET_RADIOFREQ,&fixme); - } else { - i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER, - TUNER_SET_TVFREQ,&fixme); - } - } - - if (btv->have_msp3400) { - if (btv->radio) { - i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, - MSP_SET_RADIO,0); - } else { - i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, - MSP_SET_TVNORM,&(btv->win.norm)); - i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400, - MSP_NEWCHANNEL,0); - } - } -} /* * Grab into virtual memory. @@ -1455,91 +1845,68 @@ { unsigned int *ro, *re; unsigned int *vbuf; - unsigned long flags; if(btv->fbuffer==NULL) { if(fbuffer_alloc(btv)) return -ENOBUFS; } - if(btv->grabbing >= MAX_GBUFFERS) - return -ENOBUFS; - - /* - * No grabbing past the end of the buffer! - */ - - if(mp->frame>1 || mp->frame <0) + + if(mp->frame >= gbuffers || mp->frame < 0) return -EINVAL; + if(btv->gbuf[mp->frame].stat != GBUFFER_UNUSED) + return -EBUSY; - if(mp->height <0 || mp->width <0) - return -EINVAL; - -/* This doesn´t work like this for NTSC anyway. - So, better check the total image size ... -*/ - - if(mp->height>576 || mp->width>768+BURSTOFFSET || mp->height < 32 || mp->width <32) + if(mp->height < 32 || mp->width < 32) return -EINVAL; - if (mp->format >= PALETTEFMT_MAX) return -EINVAL; + if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2 - > BTTV_MAX_FBUF) + > gbufsize) return -EINVAL; if(-1 == palette2fmt[mp->format]) return -EINVAL; /* - * FIXME: Check the format of the grab here. This is probably - * also less restrictive than the normal overlay grabs. Stuff - * like QCIF has meaning as a capture. - */ - - /* * Ok load up the BT848 */ - vbuf=(unsigned int *)(btv->fbuffer+BTTV_MAX_FBUF*mp->frame); -/* if (!(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)) - return -EAGAIN;*/ - ro=btv->grisc+(((btv->grabcount++)&1) ? 4096 :0); + vbuf=(unsigned int *)(btv->fbuffer+gbufsize*mp->frame); + ro=btv->gbuf[mp->frame].risc; re=ro+2048; make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, mp->format); - /* bt848_set_risc_jmps(btv); */ - - save_flags(flags); - cli(); - btv->frame_stat[mp->frame] = GBUFFER_GRABBING; - if (btv->grabbing) { - btv->gfmt_next=palette2fmt[mp->format]; - btv->gwidth_next=mp->width; - btv->gheight_next=mp->height; - btv->gro_next=virt_to_bus(ro); - btv->gre_next=virt_to_bus(re); - btv->grf_next=mp->frame; - } else { - btv->gfmt=palette2fmt[mp->format]; - btv->gwidth=mp->width; - btv->gheight=mp->height; - btv->gro=virt_to_bus(ro); - btv->gre=virt_to_bus(re); - btv->grf=mp->frame; - } - if (!(btv->grabbing++)) { - if(mp->format>=VIDEO_PALETTE_COMPONENT) { - btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI); - btor(BT848_VSCALE_COMB, BT848_O_VSCALE_HI); - } + + if (debug) + printk("bttv%d: cap vgrab: queue %d (%d:%dx%d)\n", + btv->nr,mp->frame,mp->format,mp->width,mp->height); + cli(); + btv->gbuf[mp->frame].stat = GBUFFER_GRABBING; + btv->gbuf[mp->frame].fmt = palette2fmt[mp->format]; + btv->gbuf[mp->frame].width = mp->width; + btv->gbuf[mp->frame].height = mp->height; + btv->gbuf[mp->frame].ro = virt_to_bus(ro); + btv->gbuf[mp->frame].re = virt_to_bus(re); + +#if 1 + if (mp->height <= tvnorms[btv->win.norm].sheight/2 && + mp->format != VIDEO_PALETTE_RAW) + btv->gbuf[mp->frame].ro = 0; +#endif + + if (btv->gq_in == btv->gq_out) { btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); } - restore_flags(flags); + btv->gqueue[btv->gq_in++] = mp->frame; + btv->gq_in = btv->gq_in % MAX_GBUFFERS; + + sti(); btor(3, BT848_CAP_CTL); btor(3, BT848_GPIO_DMA_CTL); - /* interruptible_sleep_on(&btv->capq); */ return 0; } + static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock) { return -EINVAL; @@ -1549,6 +1916,7 @@ { struct bttv *btv= (struct bttv *)v; int q,todo; + /* BROKEN: RETURNS VBI WHEN IT SHOULD RETURN GRABBED VIDEO FRAME */ todo=count; while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) @@ -1588,6 +1956,34 @@ return count; } +static inline void burst(int on) +{ + tvnorms[0].scaledtwidth = 1135 - (on?BURSTOFFSET-2:0); + tvnorms[0].hdelayx1 = 186 - (on?BURSTOFFSET :0); + tvnorms[2].scaledtwidth = 1135 - (on?BURSTOFFSET-2:0); + tvnorms[2].hdelayx1 = 186 - (on?BURSTOFFSET :0); +} + +static void bt848_restart(struct bttv *btv) +{ + if (verbose) + printk("bttv%d: resetting chip\n",btv->nr); + btwrite(0xfffffUL, BT848_INT_STAT); + btand(~15, BT848_GPIO_DMA_CTL); + btwrite(0, BT848_SRESET); + btwrite(virt_to_bus(btv->risc_jmp+2), + BT848_RISC_STRT_ADD); + + /* enforce pll reprogramming */ + btv->pll.pll_current = 0; + set_pll(btv); + + btv->errors = 0; + btv->needs_restart = 0; + bt848_set_geo(btv,0); + bt848_set_risc_jmps(btv,-1); +} + /* * Open a bttv card. Right now the flags stuff is just playing */ @@ -1595,39 +1991,48 @@ static int bttv_open(struct video_device *dev, int flags) { struct bttv *btv = (struct bttv *)dev; - int users, i; + int i,ret; - if (btv->user) - return -EBUSY; - audio(btv, AUDIO_UNMUTE); - for (i=users=0; ifbuffer=NULL; - if (!btv->fbuffer) - btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF); - if (!btv->fbuffer) - return -EINVAL; - btv->grabbing = 0; - btv->grab = 0; - btv->lastgrab = 0; - for (i = 0; i < MAX_GBUFFERS; i++) - btv->frame_stat[i] = GBUFFER_UNUSED; + ret = -EBUSY; + down(&btv->lock); + if (btv->user) + goto out_unlock; + + btv->fbuffer=(unsigned char *) rvmalloc(gbuffers*gbufsize); + ret = -ENOMEM; + if (!btv->fbuffer) + goto out_unlock; + btv->gq_in = 0; + btv->gq_out = 0; + btv->gq_grab = -1; + for (i = 0; i < gbuffers; i++) + btv->gbuf[i].stat = GBUFFER_UNUSED; + + if (btv->needs_restart) + bt848_restart(btv); + burst(0); + set_pll(btv); btv->user++; + up(&btv->lock); MOD_INC_USE_COUNT; - return 0; + return 0; + + out_unlock: + up(&btv->lock); + return ret; } static void bttv_close(struct video_device *dev) { struct bttv *btv=(struct bttv *)dev; - + + down(&btv->lock); btv->user--; - audio(btv, AUDIO_INTERN); - btv->cap&=~3; - bt848_set_risc_jmps(btv); + btv->scr_on = 0; + btv->risc_cap_odd = 0; + btv->risc_cap_even = 0; + bt848_set_risc_jmps(btv,-1); /* * A word of warning. At this point the chip @@ -1643,16 +2048,18 @@ * be sure its safe to free the buffer. We wait 5-6 fields * which is more than sufficient to be sure. */ - + current->state = TASK_UNINTERRUPTIBLE; schedule_timeout(HZ/10); /* Wait 1/10th of a second */ /* * We have allowed it to drain. */ + if(btv->fbuffer) - rvfree((void *) btv->fbuffer, 2*BTTV_MAX_FBUF); + rvfree((void *) btv->fbuffer, gbuffers*gbufsize); btv->fbuffer=0; + up(&btv->lock); MOD_DEC_USE_COUNT; } @@ -1708,589 +2115,546 @@ static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { - unsigned char eedata[256]; struct bttv *btv=(struct bttv *)dev; - int i; - - switch (cmd) - { - case VIDIOCGCAP: - { - struct video_capability b; - strcpy(b.name,btv->video_dev.name); - b.type = VID_TYPE_CAPTURE| - VID_TYPE_TELETEXT| - VID_TYPE_OVERLAY| - VID_TYPE_CLIPPING| - VID_TYPE_FRAMERAM| - VID_TYPE_SCALES| - ((tvcards[btv->type].tuner != -1) - ? VID_TYPE_TUNER : 0); - b.channels = tvcards[btv->type].video_inputs; - b.audios = tvcards[btv->type].audio_inputs; - b.maxwidth = tvnorms[btv->win.norm].swidth; - b.maxheight = tvnorms[btv->win.norm].sheight; - b.minwidth = 32; - b.minheight = 32; - if(copy_to_user(arg,&b,sizeof(b))) - return -EFAULT; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel v; - if(copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - v.flags=VIDEO_VC_AUDIO; - v.tuners=0; - v.type=VIDEO_TYPE_CAMERA; - v.norm = btv->win.norm; - if (v.channel>=tvcards[btv->type].video_inputs) - return -EINVAL; - if(v.channel==tvcards[btv->type].tuner) - { - strcpy(v.name,"Television"); - v.flags|=VIDEO_VC_TUNER; - v.type=VIDEO_TYPE_TV; - v.tuners=1; - } - else if(v.channel==tvcards[btv->type].svhs) - strcpy(v.name,"S-Video"); - else - sprintf(v.name,"Composite%d",v.channel); + int i,ret = 0; - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; - } - /* - * Each channel has 1 tuner - */ - case VIDIOCSCHAN: + if (debug) printk("bttv%d: ioctl 0x%x\n",btv->nr,cmd); + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name,btv->video_dev.name); + b.type = VID_TYPE_CAPTURE| + ((tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) | + VID_TYPE_OVERLAY| + VID_TYPE_CLIPPING| + VID_TYPE_FRAMERAM| + VID_TYPE_SCALES; + b.channels = tvcards[btv->type].video_inputs; + b.audios = tvcards[btv->type].audio_inputs; + b.maxwidth = tvnorms[btv->win.norm].swidth; + b.maxheight = tvnorms[btv->win.norm].sheight; + b.minwidth = 32; + b.minheight = 32; + if(copy_to_user(arg,&b,sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + v.flags=VIDEO_VC_AUDIO; + v.tuners=0; + v.type=VIDEO_TYPE_CAMERA; + v.norm = btv->win.norm; + if (v.channel>=tvcards[btv->type].video_inputs) + return -EINVAL; + if(v.channel==tvcards[btv->type].tuner) { - struct video_channel v; - if(copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - - if (v.channel>tvcards[btv->type].video_inputs) - return -EINVAL; - bt848_muxsel(btv, v.channel); - if(v.norm!=VIDEO_MODE_PAL&&v.norm!=VIDEO_MODE_NTSC - &&v.norm!=VIDEO_MODE_SECAM) - return -EOPNOTSUPP; + strcpy(v.name,"Television"); + v.flags|=VIDEO_VC_TUNER; + v.type=VIDEO_TYPE_TV; + v.tuners=1; + } + else if(v.channel==tvcards[btv->type].svhs) + strcpy(v.name,"S-Video"); + else + sprintf(v.name,"Composite%d",v.channel); + + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + /* + * Each channel has 1 tuner + */ + case VIDIOCSCHAN: + { + struct video_channel v; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + + if (v.channel>tvcards[btv->type].video_inputs) + return -EINVAL; + if (v.norm > (sizeof(tvnorms)/sizeof(*tvnorms))) + return -EOPNOTSUPP; + + call_i2c_clients(btv,cmd,&v); + down(&btv->lock); + bt848_muxsel(btv, v.channel); + btv->channel=v.channel; + if (btv->win.norm != v.norm) { btv->win.norm = v.norm; - make_vbitab(btv); + make_vbitab(btv); bt848_set_winsize(btv); - btv->channel=v.channel; - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner v; - if(copy_from_user(&v,arg,sizeof(v))!=0) - return -EFAULT; - if(v.tuner||btv->channel) /* Only tuner 0 */ - return -EINVAL; - strcpy(v.name, "Television"); - v.rangelow=0; - v.rangehigh=0xFFFFFFFF; - v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; - if (btv->audio_chip == TDA9840) { - v.flags |= VIDEO_AUDIO_VOLUME; - v.mode = VIDEO_SOUND_MONO|VIDEO_SOUND_STEREO; - v.mode |= VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2; - } - if (btv->audio_chip == TDA9850) { - unsigned char ALR1; - ALR1 = I2CRead(&(btv->i2c), I2C_TDA9850|1); - if (ALR1 & 32) - v.flags |= VIDEO_TUNER_STEREO_ON; - } - v.mode = btv->win.norm; - v.signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0; - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; } - /* We have but one tuner */ - case VIDIOCSTUNER: - { - struct video_tuner v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - /* Only one channel has a tuner */ - if(v.tuner!=tvcards[btv->type].tuner) - return -EINVAL; + up(&btv->lock); + return 0; + } + case VIDIOCGTUNER: + { + struct video_tuner v; + if(copy_from_user(&v,arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner||btv->channel) /* Only tuner 0 */ + return -EINVAL; + strcpy(v.name, "Television"); + v.rangelow=0; + v.rangehigh=0xFFFFFFFF; + v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; + v.mode = btv->win.norm; + v.signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0; + call_i2c_clients(btv,cmd,&v); + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + /* We have but one tuner */ + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + /* Only one channel has a tuner */ + if(v.tuner!=tvcards[btv->type].tuner) + return -EINVAL; - if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC - &&v.mode!=VIDEO_MODE_SECAM) - return -EOPNOTSUPP; - btv->win.norm = v.mode; - bt848_set_winsize(btv); - return 0; - } - case VIDIOCGPICT: - { - struct video_picture p=btv->picture; - if(btv->win.depth==8) - p.palette=VIDEO_PALETTE_HI240; - if(btv->win.depth==15) - p.palette=VIDEO_PALETTE_RGB555; - if(btv->win.depth==16) - p.palette=VIDEO_PALETTE_RGB565; - if(btv->win.depth==24) - p.palette=VIDEO_PALETTE_RGB24; - if(btv->win.depth==32) - p.palette=VIDEO_PALETTE_RGB32; - - if(copy_to_user(arg, &p, sizeof(p))) - return -EFAULT; - return 0; + if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC + &&v.mode!=VIDEO_MODE_SECAM) + return -EOPNOTSUPP; + call_i2c_clients(btv,cmd,&v); + if (btv->win.norm != v.mode) { + btv->win.norm = v.mode; + down(&btv->lock); + set_pll(btv); + make_vbitab(btv); + bt848_set_winsize(btv); + up(&btv->lock); } - case VIDIOCSPICT: - { - struct video_picture p; - int format; - if(copy_from_user(&p, arg,sizeof(p))) - return -EFAULT; - /* We want -128 to 127 we get 0-65535 */ - bt848_bright(btv, (p.brightness>>8)-128); - /* 0-511 for the colour */ - bt848_sat_u(btv, p.colour>>7); - bt848_sat_v(btv, ((p.colour>>7)*201L)/237); - /* -128 to 127 */ - bt848_hue(btv, (p.hue>>8)-128); - /* 0-511 */ - bt848_contrast(btv, p.contrast>>7); - btv->picture = p; - - /* set palette if bpp matches */ - if (p.palette < sizeof(palette2fmt)/sizeof(int)) { - format = palette2fmt[p.palette]; - if (fmtbppx2[format&0x0f]/2 == btv->win.bpp) - btv->win.color_fmt = format; - } - return 0; - } - case VIDIOCSWIN: - { - struct video_window vw; - struct video_clip *vcp = NULL; - int on; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p=btv->picture; + if(copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + if(copy_from_user(&p, arg,sizeof(p))) + return -EFAULT; + if (p.palette > PALETTEFMT_MAX) + return -EINVAL; + down(&btv->lock); + /* We want -128 to 127 we get 0-65535 */ + bt848_bright(btv, (p.brightness>>8)-128); + /* 0-511 for the colour */ + bt848_sat_u(btv, p.colour>>7); + bt848_sat_v(btv, ((p.colour>>7)*201L)/237); + /* -128 to 127 */ + bt848_hue(btv, (p.hue>>8)-128); + /* 0-511 */ + bt848_contrast(btv, p.contrast>>7); + btv->picture = p; + up(&btv->lock); + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + struct video_clip *vcp = NULL; - if(copy_from_user(&vw,arg,sizeof(vw))) - return -EFAULT; - - if(vw.flags || vw.width < 16 || vw.height < 16) - { - bt848_cap(btv,0); - return -EINVAL; - } - if (btv->win.bpp < 4) - { /* 32-bit align start and adjust width */ - int i = vw.x; - vw.x = (vw.x + 3) & ~3; - i = vw.x - i; - vw.width -= i; - } - btv->win.x=vw.x; - btv->win.y=vw.y; - btv->win.width=vw.width; - btv->win.height=vw.height; + if(copy_from_user(&vw,arg,sizeof(vw))) + return -EFAULT; - if(btv->win.height>btv->win.cropheight/2) - btv->win.interlace=1; - else - btv->win.interlace=0; + if(vw.flags || vw.width < 16 || vw.height < 16) + { + down(&btv->lock); + btv->scr_on = 0; + bt848_set_risc_jmps(btv,-1); + up(&btv->lock); + return -EINVAL; + } + if (btv->win.bpp < 4) + { /* adjust and align writes */ + vw.x = (vw.x + 3) & ~3; + vw.width &= ~3; + } + down(&btv->lock); + if (btv->needs_restart) + bt848_restart(btv); + btv->win.x=vw.x; + btv->win.y=vw.y; + btv->win.width=vw.width; + btv->win.height=vw.height; - on=(btv->cap&3); - - bt848_cap(btv,0); - bt848_set_winsize(btv); + bt848_set_risc_jmps(btv,0); - /* - * Do any clips. - */ - if(vw.clipcount<0) { - if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) - return -ENOMEM; - if(copy_from_user(vcp, vw.clips, - VIDEO_CLIPMAP_SIZE)) { - vfree(vcp); - return -EFAULT; - } - } else if (vw.clipcount) { - if((vcp=vmalloc(sizeof(struct video_clip)* - (vw.clipcount))) == NULL) - return -ENOMEM; - if(copy_from_user(vcp,vw.clips, - sizeof(struct video_clip)* - vw.clipcount)) { - vfree(vcp); - return -EFAULT; - } - } - make_clip_tab(btv, vcp, vw.clipcount); - if (vw.clipcount != 0) + bt848_set_winsize(btv); + up(&btv->lock); + + /* + * Do any clips. + */ + if(vw.clipcount<0) { + if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) + return -ENOMEM; + if(copy_from_user(vcp, vw.clips, + VIDEO_CLIPMAP_SIZE)) { vfree(vcp); - if(on && btv->win.vidadr!=0) - bt848_cap(btv,1); - return 0; - } - case VIDIOCGWIN: - { - struct video_window vw; - /* Oh for a COBOL move corresponding .. */ - vw.x=btv->win.x; - vw.y=btv->win.y; - vw.width=btv->win.width; - vw.height=btv->win.height; - vw.chromakey=0; - vw.flags=0; - if(btv->win.interlace) - vw.flags|=VIDEO_WINDOW_INTERLACE; - if(copy_to_user(arg,&vw,sizeof(vw))) - return -EFAULT; - return 0; - } - case VIDIOCCAPTURE: - { - int v; - if(copy_from_user(&v, arg,sizeof(v))) return -EFAULT; - if(v==0) - { - bt848_cap(btv,0); } - else - { - if(btv->win.vidadr==0 || btv->win.width==0 - || btv->win.height==0) - return -EINVAL; - bt848_cap(btv,1); + } else if (vw.clipcount) { + if((vcp=vmalloc(sizeof(struct video_clip)* + (vw.clipcount))) == NULL) + return -ENOMEM; + if(copy_from_user(vcp,vw.clips, + sizeof(struct video_clip)* + vw.clipcount)) { + vfree(vcp); + return -EFAULT; } - return 0; } - case VIDIOCGFBUF: - { - struct video_buffer v; - v.base=(void *)btv->win.vidadr; - v.height=btv->win.sheight; - v.width=btv->win.swidth; - v.depth=btv->win.depth; - v.bytesperline=btv->win.bpl; - if(copy_to_user(arg, &v,sizeof(v))) - return -EFAULT; - return 0; + down(&btv->lock); + make_clip_tab(btv, vcp, vw.clipcount); + if (vw.clipcount != 0) + vfree(vcp); + bt848_set_risc_jmps(btv,-1); + up(&btv->lock); + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + /* Oh for a COBOL move corresponding .. */ + vw.x=btv->win.x; + vw.y=btv->win.y; + vw.width=btv->win.width; + vw.height=btv->win.height; + vw.chromakey=0; + vw.flags=0; + if(btv->win.interlace) + vw.flags|=VIDEO_WINDOW_INTERLACE; + if(copy_to_user(arg,&vw,sizeof(vw))) + return -EFAULT; + return 0; + } + case VIDIOCCAPTURE: + { + int v; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + if(btv->win.vidadr == 0) + return -EINVAL; + if (btv->win.width==0 || btv->win.height==0) + return -EINVAL; + down(&btv->lock); + if (v == 1 && btv->win.vidadr != 0) + btv->scr_on = 1; + if (v == 0) + btv->scr_on = 0; + bt848_set_risc_jmps(btv,-1); + up(&btv->lock); + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer v; + v.base=(void *)btv->win.vidadr; + v.height=btv->win.sheight; + v.width=btv->win.swidth; + v.depth=btv->win.depth; + v.bytesperline=btv->win.bpl; + if(copy_to_user(arg, &v,sizeof(v))) + return -EFAULT; + return 0; - } - case VIDIOCSFBUF: - { - struct video_buffer v; -#if LINUX_VERSION_CODE >= 0x020100 - if(!capable(CAP_SYS_ADMIN) - || !capable(CAP_SYS_RAWIO)) -#else - if(!suser()) -#endif - return -EPERM; - if(copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - if(v.depth!=8 && v.depth!=15 && v.depth!=16 && - v.depth!=24 && v.depth!=32 && v.width > 16 && - v.height > 16 && v.bytesperline > 16) - return -EINVAL; - if (v.base) - btv->win.vidadr=(unsigned long)v.base; - btv->win.sheight=v.height; - btv->win.swidth=v.width; - btv->win.bpp=((v.depth+7)&0x38)/8; - btv->win.depth=v.depth; - btv->win.bpl=v.bytesperline; - - DEBUG(printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", - v.base, v.width,v.height, btv->win.bpp, btv->win.bpl)); - bt848_set_winsize(btv); - return 0; - } - case VIDIOCKEY: - { - /* Will be handled higher up .. */ - return 0; - } - case VIDIOCGFREQ: - { - unsigned long v=btv->win.freq; - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCSFREQ: - { - unsigned long v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - btv->win.freq=v; - set_freq(btv, btv->win.freq); - return 0; - } - - case VIDIOCGAUDIO: - { - struct video_audio v; - v=btv->audio_dev; - v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE); - v.flags|=VIDEO_AUDIO_MUTABLE; - strcpy(v.name,"TV"); - if (btv->audio_chip == TDA9850) { - unsigned char ALR1; - ALR1 = I2CRead(&(btv->i2c), I2C_TDA9850|1); - v.mode = VIDEO_SOUND_MONO; - v.mode |= (ALR1 & 32) ? VIDEO_SOUND_STEREO:0; - v.mode |= (ALR1 & 64) ? VIDEO_SOUND_LANG1:0; - } - if (btv->have_msp3400) - { - v.flags|=VIDEO_AUDIO_VOLUME | - VIDEO_AUDIO_BASS | - VIDEO_AUDIO_TREBLE; - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_GET_VOLUME,&(v.volume)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_GET_BASS,&(v.bass)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_GET_TREBLE,&(v.treble)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_GET_STEREO,&(v.mode)); - } - else v.mode = VIDEO_SOUND_MONO; - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCSAUDIO: - { - struct video_audio v; - if(copy_from_user(&v,arg, sizeof(v))) - return -EFAULT; - if(v.flags&VIDEO_AUDIO_MUTE) - audio(btv, AUDIO_MUTE); - /* One audio source per tuner */ - /* if(v.audio!=0) */ - /* ADSTech TV card has more than one */ - if(v.audio<0 || v.audio >= tvcards[btv->type].audio_inputs) - return -EINVAL; - bt848_muxsel(btv,v.audio); - if(!(v.flags&VIDEO_AUDIO_MUTE)) - audio(btv, AUDIO_UNMUTE); - if (btv->audio_chip == TDA9850) { - unsigned char con3 = 0; - if (v.mode & VIDEO_SOUND_LANG1) - con3 = 0x80; /* sap */ - if (v.mode & VIDEO_SOUND_STEREO) - con3 = 0x40; /* stereo */ - I2CWrite(&(btv->i2c), I2C_TDA9850, - TDA9850_CON3, con3, 1); - } - - /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */ - if (btv->type == BTTV_WINVIEW_601) { - int bits_out, loops, vol, data; - - /* 32 levels logarithmic */ - vol = 32 - ((v.volume>>11)); - /* units */ - bits_out = (PT2254_DBS_IN_2>>(vol%5)); - /* tens */ - bits_out |= (PT2254_DBS_IN_10>>(vol/5)); - bits_out |= PT2254_L_CHANEL | PT2254_R_CHANEL; - data = btread(BT848_GPIO_DATA); - data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA| - WINVIEW_PT2254_STROBE); - for (loops = 17; loops >= 0 ; loops--) { - if (bits_out & (1<have_msp3400) - { - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_SET_VOLUME,&(v.volume)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_SET_BASS,&(v.bass)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_SET_TREBLE,&(v.treble)); - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_MSP3400, - MSP_SET_STEREO,&(v.mode)); - } - btv->audio_dev=v; - return 0; + } + case VIDIOCSFBUF: + { + struct video_buffer v; + if(!capable(CAP_SYS_ADMIN) && + !capable(CAP_SYS_RAWIO)) + return -EPERM; + if(copy_from_user(&v, arg,sizeof(v))) + return -EFAULT; + if(v.depth!=8 && v.depth!=15 && v.depth!=16 && + v.depth!=24 && v.depth!=32 && v.width > 16 && + v.height > 16 && v.bytesperline > 16) + return -EINVAL; + down(&btv->lock); + if (v.base) + btv->win.vidadr=(unsigned long)v.base; + btv->win.sheight=v.height; + btv->win.swidth=v.width; + btv->win.bpp=((v.depth+7)&0x38)/8; + btv->win.depth=v.depth; + btv->win.bpl=v.bytesperline; + + /* set sefault color format */ + switch (btv->win.bpp) { + case 8: btv->picture.palette = VIDEO_PALETTE_HI240; break; + case 15: btv->picture.palette = VIDEO_PALETTE_RGB555; break; + case 16: btv->picture.palette = VIDEO_PALETTE_RGB565; break; + case 24: btv->picture.palette = VIDEO_PALETTE_RGB24; break; + case 32: btv->picture.palette = VIDEO_PALETTE_RGB32; break; + } + + if (debug) + printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n", + v.base, v.width,v.height, btv->win.bpp, btv->win.bpl); + bt848_set_winsize(btv); + up(&btv->lock); + return 0; + } + case VIDIOCKEY: + { + /* Will be handled higher up .. */ + return 0; + } + case VIDIOCGFREQ: + { + unsigned long v=btv->win.freq; + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSFREQ: + { + unsigned long v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + btv->win.freq=v; + call_i2c_clients(btv,cmd,&v); + return 0; + } + + case VIDIOCGAUDIO: + { + struct video_audio v; + + v=btv->audio_dev; + v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE); + v.flags|=VIDEO_AUDIO_MUTABLE; + strcpy(v.name,"TV"); + + v.mode = VIDEO_SOUND_MONO; + call_i2c_clients(btv,cmd,&v); + + if (btv->type == BTTV_TERRATV) { + v.mode = VIDEO_SOUND_MONO; + v.mode |= VIDEO_SOUND_STEREO; + v.mode |= VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2; + + } else if (btv->audio_chip == TDA9840) { + /* begin of Horrible Hack */ + v.flags|=VIDEO_AUDIO_VOLUME; + v.mode = VIDEO_SOUND_MONO; + v.mode |= VIDEO_SOUND_STEREO; + v.mode |= VIDEO_SOUND_LANG1|VIDEO_SOUND_LANG2; + v.volume = 32768; /* fixme */ + v.step = 4096; } - case VIDIOCSYNC: - if(copy_from_user((void *)&i,arg,sizeof(int))) - return -EFAULT; -/* if(i>1 || i<0) - return -EINVAL; -*/ - switch (btv->frame_stat[i]) { - case GBUFFER_UNUSED: - return -EINVAL; - case GBUFFER_GRABBING: - while(btv->frame_stat[i]==GBUFFER_GRABBING) { - interruptible_sleep_on(&btv->capq); - if(signal_pending(current)) - return -EINTR; - } - /* fall */ - case GBUFFER_DONE: - btv->frame_stat[i] = GBUFFER_UNUSED; - break; - } - return 0; + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio v; - case BTTV_WRITEE: -#if LINUX_VERSION_CODE >= 0x020100 - if(!capable(CAP_SYS_ADMIN)) -#else - if(!suser()) -#endif - return -EPERM; - if(copy_from_user((void *) eedata, (void *) arg, 256)) - return -EFAULT; - writeee(&(btv->i2c), eedata); - return 0; + if(copy_from_user(&v,arg, sizeof(v))) + return -EFAULT; + down(&btv->lock); + if(v.flags&VIDEO_AUDIO_MUTE) + audio(btv, AUDIO_MUTE, 1); + /* One audio source per tuner -- huh? */ + if(v.audio<0 || v.audio >= tvcards[btv->type].audio_inputs) { + up(&btv->lock); + return -EINVAL; + } + /* bt848_muxsel(btv,v.audio); */ + if(!(v.flags&VIDEO_AUDIO_MUTE)) + audio(btv, AUDIO_UNMUTE, 1); + + up(&btv->lock); + call_i2c_clients(btv,cmd,&v); + down(&btv->lock); + + if (btv->type == BTTV_TERRATV) { + unsigned int con = 0; + btor(0x180000, BT848_GPIO_OUT_EN); + if (v.mode & VIDEO_SOUND_LANG2) + con = 0x080000; + if (v.mode & VIDEO_SOUND_STEREO) + con = 0x180000; + btaor(con, ~0x180000, BT848_GPIO_DATA); + + } else if (btv->type == BTTV_WINVIEW_601) { + /* PT2254A programming Jon Tombs, jon@gte.esi.us.es */ + int bits_out, loops, vol, data; + + /* 32 levels logarithmic */ + vol = 32 - ((v.volume>>11)); + /* units */ + bits_out = (PT2254_DBS_IN_2>>(vol%5)); + /* tens */ + bits_out |= (PT2254_DBS_IN_10>>(vol/5)); + bits_out |= PT2254_L_CHANEL | PT2254_R_CHANEL; + data = btread(BT848_GPIO_DATA); + data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA| + WINVIEW_PT2254_STROBE); + for (loops = 17; loops >= 0 ; loops--) { + if (bits_out & (1<= 0x020100 - if(!capable(CAP_SYS_ADMIN)) -#else - if(!suser()) -#endif - return -EPERM; - readee(&(btv->i2c), eedata); - if(copy_to_user((void *) arg, (void *) eedata, 256)) - return -EFAULT; - break; + btv->audio_dev=v; + up(&btv->lock); + return 0; + } - case BTTV_FIELDNR: - if(copy_to_user((void *) arg, (void *) &btv->last_field, - sizeof(btv->last_field))) - return -EFAULT; - break; + case VIDIOCSYNC: + if(copy_from_user((void *)&i,arg,sizeof(int))) + return -EFAULT; + if (i < 0 || i >= gbuffers) + return -EINVAL; + switch (btv->gbuf[i].stat) { + case GBUFFER_UNUSED: + ret = -EINVAL; + break; + case GBUFFER_GRABBING: + while(btv->gbuf[i].stat==GBUFFER_GRABBING) { + if (debug) + printk("bttv%d: cap sync: sleep on %d\n",btv->nr,i); + interruptible_sleep_on(&btv->capq); + if(signal_pending(current)) + return -EINTR; + } + /* fall throuth */ + case GBUFFER_DONE: + case GBUFFER_ERROR: + ret = (btv->gbuf[i].stat == GBUFFER_ERROR) ? -EIO : 0; + if (debug) + printk("bttv%d: cap sync: buffer %d, retval %d\n",btv->nr,i,ret); + btv->gbuf[i].stat = GBUFFER_UNUSED; + } + if (btv->needs_restart) { + down(&btv->lock); + bt848_restart(btv); + up(&btv->lock); + } + return ret; + + case BTTV_FIELDNR: + if(copy_to_user((void *) arg, (void *) &btv->last_field, + sizeof(btv->last_field))) + return -EFAULT; + break; - case BTTV_PLLSET: { - struct bttv_pll_info p; -#if LINUX_VERSION_CODE >= 0x020100 - if(!capable(CAP_SYS_ADMIN)) -#else - if(!suser()) -#endif - return -EPERM; - if(copy_from_user(&p , (void *) arg, sizeof(btv->pll))) - return -EFAULT; - btv->pll.pll_ifreq = p.pll_ifreq; - btv->pll.pll_ofreq = p.pll_ofreq; - btv->pll.pll_crystal = p.pll_crystal; + case BTTV_PLLSET: { + struct bttv_pll_info p; + if(!capable(CAP_SYS_ADMIN)) + return -EPERM; + if(copy_from_user(&p , (void *) arg, sizeof(btv->pll))) + return -EFAULT; + down(&btv->lock); + btv->pll.pll_ifreq = p.pll_ifreq; + btv->pll.pll_ofreq = p.pll_ofreq; + btv->pll.pll_crystal = p.pll_crystal; + up(&btv->lock); - break; - } - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) - return -EFAULT; - if (vm.frame < 0 || vm.frame >= MAX_GBUFFERS) - return -EIO; - if (btv->frame_stat[vm.frame] == GBUFFER_GRABBING) - return -EBUSY; - return vgrab(btv, &vm); - } + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + int ret; + if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) + return -EFAULT; + down(&btv->lock); + ret = vgrab(btv, &vm); + up(&btv->lock); + return ret; + } - case VIDIOCGMBUF: - { - struct video_mbuf vm; - memset(&vm, 0 , sizeof(vm)); - vm.size=BTTV_MAX_FBUF*2; - vm.frames=2; - vm.offsets[0]=0; - vm.offsets[1]=BTTV_MAX_FBUF; - if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) - return -EFAULT; - return 0; - } + case VIDIOCGMBUF: + { + struct video_mbuf vm; + memset(&vm, 0 , sizeof(vm)); + vm.size=gbufsize*gbuffers; + vm.frames=gbuffers; + for (i = 0; i < gbuffers; i++) + vm.offsets[i]=i*gbufsize; + if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + return -EFAULT; + return 0; + } - case VIDIOCGUNIT: - { - struct video_unit vu; - vu.video=btv->video_dev.minor; - vu.vbi=btv->vbi_dev.minor; - if(btv->radio_dev.minor!=-1) - vu.radio=btv->radio_dev.minor; - else - vu.radio=VIDEO_NO_UNIT; - vu.audio=VIDEO_NO_UNIT; - if(btv->have_msp3400) - { - i2c_control_device(&(btv->i2c), I2C_DRIVERID_MSP3400, - MSP_GET_UNIT, &vu.audio); - } - vu.teletext=VIDEO_NO_UNIT; - if(copy_to_user((void *)arg, (void *)&vu, sizeof(vu))) - return -EFAULT; - return 0; - } + case VIDIOCGUNIT: + { + struct video_unit vu; + vu.video=btv->video_dev.minor; + vu.vbi=btv->vbi_dev.minor; + if(btv->radio_dev.minor!=-1) + vu.radio=btv->radio_dev.minor; + else + vu.radio=VIDEO_NO_UNIT; + vu.audio=VIDEO_NO_UNIT; + vu.teletext=VIDEO_NO_UNIT; + if(copy_to_user((void *)arg, (void *)&vu, sizeof(vu))) + return -EFAULT; + return 0; + } - case BTTV_BURST_ON: - { - tvnorms[0].scaledtwidth=1135-BURSTOFFSET-2; - tvnorms[0].hdelayx1=186-BURSTOFFSET; - return 0; - } + case BTTV_BURST_ON: + { + burst(1); + return 0; + } - case BTTV_BURST_OFF: - { - tvnorms[0].scaledtwidth=1135; - tvnorms[0].hdelayx1=186; - return 0; - } + case BTTV_BURST_OFF: + { + burst(0); + return 0; + } - case BTTV_VERSION: - { - return BTTV_VERSION_CODE; - } + case BTTV_VERSION: + { + return BTTV_VERSION_CODE; + } - case BTTV_PICNR: - { - /* return picture;*/ - return 0; - } + case BTTV_PICNR: + { + /* return picture;*/ + return 0; + } - default: - return -ENOIOCTLCMD; + default: + return -ENOIOCTLCMD; } return 0; } @@ -2308,33 +2672,42 @@ * - remap_page_range is kind of inefficient for page by page remapping. * But e.g. pte_alloc() does not work in modules ... :-( */ - -static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size) + +static int do_bttv_mmap(struct bttv *btv, const char *adr, unsigned long size) { - struct bttv *btv=(struct bttv *)dev; unsigned long start=(unsigned long) adr; - unsigned long page,pos; + unsigned long page,pos; - if (size>2*BTTV_MAX_FBUF) - return -EINVAL; - if (!btv->fbuffer) - { - if(fbuffer_alloc(btv)) - return -EINVAL; - } - pos=(unsigned long) btv->fbuffer; - while (size > 0) - { - page = kvirt_to_pa(pos); - if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - start+=PAGE_SIZE; - pos+=PAGE_SIZE; - size-=PAGE_SIZE; - } - return 0; + if (size>gbuffers*gbufsize) + return -EINVAL; + if (!btv->fbuffer) { + if(fbuffer_alloc(btv)) + return -EINVAL; + } + pos=(unsigned long) btv->fbuffer; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start+=PAGE_SIZE; + pos+=PAGE_SIZE; + size-=PAGE_SIZE; + } + return 0; } +static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + struct bttv *btv=(struct bttv *)dev; + int r; + + down(&btv->lock); + r=do_bttv_mmap(btv, adr, size); + up(&btv->lock); + return r; +} + + static struct video_device bttv_template= { "UNSET", @@ -2344,9 +2717,7 @@ bttv_close, bttv_read, bttv_write, -#if LINUX_VERSION_CODE >= 0x020100 - NULL, /* poll */ -#endif + NULL, bttv_ioctl, bttv_mmap, bttv_init_done, @@ -2365,6 +2736,11 @@ todo=count; while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) { + if (btv->needs_restart) { + down(&btv->lock); + bt848_restart(btv); + up(&btv->lock); + } if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q)) return -EFAULT; todo-=q; @@ -2400,7 +2776,6 @@ return count; } -#if LINUX_VERSION_CODE >= 0x020100 static unsigned int vbi_poll(struct video_device *dev, struct file *file, poll_table *wait) { @@ -2414,15 +2789,18 @@ return mask; } -#endif static int vbi_open(struct video_device *dev, int flags) { struct bttv *btv=(struct bttv *)(dev-2); + down(&btv->lock); + if (btv->needs_restart) + bt848_restart(btv); btv->vbip=VBIBUF_SIZE; - btv->cap|=0x0c; - bt848_set_risc_jmps(btv); + btv->vbi_on = 1; + bt848_set_risc_jmps(btv,-1); + up(&btv->lock); MOD_INC_USE_COUNT; return 0; @@ -2431,17 +2809,45 @@ static void vbi_close(struct video_device *dev) { struct bttv *btv=(struct bttv *)(dev-2); - - btv->cap&=~0x0c; - bt848_set_risc_jmps(btv); + + down(&btv->lock); + btv->vbi_on = 0; + bt848_set_risc_jmps(btv,-1); + up(&btv->lock); MOD_DEC_USE_COUNT; } - static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) { - return -EINVAL; + struct bttv *btv=(struct bttv *)dev; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name,btv->vbi_dev.name); + b.type = ((tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) | + VID_TYPE_TELETEXT; + b.channels = 0; + b.audios = 0; + b.maxwidth = 0; + b.maxheight = 0; + b.minwidth = 0; + b.minheight = 0; + if(copy_to_user(arg,&b,sizeof(b))) + return -EFAULT; + return 0; + } + case VIDIOCGFREQ: + case VIDIOCSFREQ: + return bttv_ioctl((struct video_device *)btv,cmd,arg); + case BTTV_VBISIZE: + /* make alevt happy :-) */ + return VBIBUF_SIZE; + default: + return -EINVAL; + } } static struct video_device vbi_template= @@ -2453,9 +2859,7 @@ vbi_close, vbi_read, bttv_write, -#if LINUX_VERSION_CODE >= 0x020100 vbi_poll, -#endif vbi_ioctl, NULL, /* no mmap yet */ bttv_init_done, @@ -2468,26 +2872,36 @@ static int radio_open(struct video_device *dev, int flags) { struct bttv *btv = (struct bttv *)(dev-1); + unsigned long v; + down(&btv->lock); if (btv->user) - return -EBUSY; + goto busy_unlock; btv->user++; - set_freq(btv,400*16); + btv->radio = 1; + v = 400*16; + call_i2c_clients(btv,VIDIOCSFREQ,&v); + call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type); bt848_muxsel(btv,0); - audio(btv, AUDIO_UNMUTE); - + up(&btv->lock); + MOD_INC_USE_COUNT; return 0; + + busy_unlock: + up(&btv->lock); + return -EBUSY; } static void radio_close(struct video_device *dev) { struct bttv *btv=(struct bttv *)(dev-1); - + + down(&btv->lock); btv->user--; btv->radio = 0; - /*audio(btv, AUDIO_MUTE);*/ + up(&btv->lock); MOD_DEC_USE_COUNT; } @@ -2525,8 +2939,8 @@ if(v.tuner||btv->channel) /* Only tuner 0 */ return -EINVAL; strcpy(v.name, "Radio"); - v.rangelow=(int)(87.5*16); - v.rangehigh=(int)(108*16); + v.rangelow=(int)(76*16); /* jp: 76.0MHz - 89.9MHz */ + v.rangehigh=(int)(108*16); /* eu: 87.5MHz - 108.0MHz */ v.flags= 0; /* XXX */ v.mode = 0; /* XXX */ if(copy_to_user(arg,&v,sizeof(v))) @@ -2565,648 +2979,311 @@ radio_close, radio_read, /* just returns -EINVAL */ bttv_write, /* just returns -EINVAL */ -#if LINUX_VERSION_CODE >= 0x020100 NULL, /* no poll */ -#endif radio_ioctl, NULL, /* no mmap */ bttv_init_done, /* just returns 0 */ NULL, 0, -1 -}; - - -struct vidbases -{ - unsigned short vendor, device; - char *name; - uint badr; -}; - -static struct vidbases vbs[] = { - { PCI_VENDOR_ID_ALLIANCE, PCI_DEVICE_ID_ALLIANCE_AT3D, - "Alliance AT3D", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215CT222, - "ATI MACH64 CT", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX, - "ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_215GT, - "ATI MACH64 GT", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, - "DEC DC21030", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL, - "Matrox Millennium", PCI_BASE_ADDRESS_1}, - { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2, - "Matrox Millennium II", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL_2_AGP, - "Matrox Millennium II AGP", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1}, - { PCI_VENDOR_ID_MATROX, 0x0521, "Matrox G200", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, - "Number Nine Imagine 128", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128_2, - "Number Nine Imagine 128 Series 2", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0}, - { PCI_VENDOR_ID_NVIDIA_SGS, PCI_DEVICE_ID_NVIDIA_SGS_RIVA128, - "Riva128", PCI_BASE_ADDRESS_1}, -}; - - -/* DEC TGA offsets stolen from XFree-3.2 */ - -static uint dec_offsets[4] = { - 0x200000, - 0x804000, - 0, - 0x1004000 -}; - -#define NR_CARDS (sizeof(vbs)/sizeof(struct vidbases)) - -/* Scan for PCI display adapter - if more than one card is present the last one is used for now */ - -#if LINUX_VERSION_CODE >= 0x020100 - -static int find_vga(void) -{ - unsigned short badr; - int found = 0, i, tga_type; - unsigned int vidadr=0; - struct pci_dev *dev; - - - for (dev = pci_devices; dev != NULL; dev = dev->next) - { - if (dev->class != PCI_CLASS_NOT_DEFINED_VGA && - ((dev->class) >> 16 != PCI_BASE_CLASS_DISPLAY)) - { - continue; - } - if (PCI_FUNC(dev->devfn) != 0) - continue; - - badr=0; - printk(KERN_INFO "bttv: PCI display adapter: "); - for (i=0; ivendor == vbs[i].vendor) - { - if (vbs[i].device) - if (vbs[i].device!=dev->device) - continue; - printk("%s.\n", vbs[i].name); - badr=vbs[i].badr; - break; - } - } - if (!badr) - { - printk(KERN_ERR "bttv: Unknown video memory base address.\n"); - continue; - } - pci_read_config_dword(dev, badr, &vidadr); - if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) - { - printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n"); - printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n"); - continue; - } - vidadr &= PCI_BASE_ADDRESS_MEM_MASK; - if (!vidadr) - { - printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!"); - continue; - } - - if (dev->vendor == PCI_VENDOR_ID_DEC && - dev->device == PCI_DEVICE_ID_DEC_TGA) - { - tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f; - if (tga_type != 0 && tga_type != 1 && tga_type != 3) - { - printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type); - found--; - } - vidadr+=dec_offsets[tga_type]; - } - DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr)); - DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", dev->devfn)); - found++; - } - - if (vidmem) - { - vidadr=vidmem<<20; - printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr); - found=1; - } - for (i=0; i> 16; -/* if (class == PCI_CLASS_DISPLAY_VGA) {*/ - if ((class>>8) == PCI_BASE_CLASS_DISPLAY || - /* Number 9 GXE64Pro needs this */ - class == PCI_CLASS_NOT_DEFINED_VGA) - { - badr=0; - printk(KERN_INFO "bttv: PCI display adapter: "); - for (i=0; i> 12) & 0x0f; - if (tga_type != 0 && tga_type != 1 && tga_type != 3) - { - printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type); - found--; - } - vidadr+=dec_offsets[tga_type]; - } - - DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr)); - DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", devfn)); - found++; - } - } - - if (vidmem) - { - if (vidmem < 0x1000) - vidadr=vidmem<<20; - else - vidadr=vidmem; - printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr); - found=1; - } - for (i=0; i= 0x020100 - -static void handle_chipset(void) -{ - struct pci_dev *dev = NULL; - - /* Just in case some nut set this to something dangerous */ - if (triton1) - triton1=BT848_INT_ETBF; - - while ((dev = pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, dev))) - { - /* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */ - printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n"); - } - - /* dev == NULL */ - - while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441, dev))) - { - unsigned char b; - pci_read_config_byte(dev, 0x53, &b); - DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); - DEBUG(printk("bufcon=0x%02x\n",b)); - } - - while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, dev))) - { -/* unsigned char b; - unsigned char bo;*/ - - printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); - triton1=BT848_INT_ETBF; - -#if 0 - /* The ETBF bit SHOULD make all this unnecessary */ - /* 430FX (Triton I) freezes with bus concurrency on -> switch it off */ - - pci_read_config_byte(dev, TRITON_PCON, &b); - bo=b; - DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b)); - if(!(b & TRITON_BUS_CONCURRENCY)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n"); - b |= TRITON_BUS_CONCURRENCY; - } - if(b & TRITON_PEER_CONCURRENCY) - { - printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n"); - b &= ~TRITON_PEER_CONCURRENCY; - } - if(!(b & TRITON_STREAMING)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n"); - b |= TRITON_STREAMING; - } - - if (b!=bo) - { - pci_write_config_byte(dev, TRITON_PCON, b); - printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b); - } -#endif - } -} -#else -static void handle_chipset(void) -{ - int index; - - for (index = 0; index < 8; index++) - { - unsigned char bus, devfn; - unsigned char b; - - /* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */ - - if (!pcibios_find_device(PCI_VENDOR_ID_SI, - PCI_DEVICE_ID_SI_496, - index, &bus, &devfn)) - { - printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n"); - } - - if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82441, - index, &bus, &devfn)) - { - pcibios_read_config_byte(bus, devfn, 0x53, &b); - DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); - DEBUG(printk("bufcon=0x%02x\n",b)); - } - - if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, - index, &bus, &devfn)) - { - printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); - triton1=BT848_INT_ETBF; - -#if 0 - /* The ETBF bit SHOULD make all this unnecessary */ - /* 430FX (Triton I) freezes with bus concurrency on -> switch it off */ - { - unsigned char bo; - - pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b); - bo=b; - DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b)); - - if(!(b & TRITON_BUS_CONCURRENCY)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n"); - b |= TRITON_BUS_CONCURRENCY; - } - - if(b & TRITON_PEER_CONCURRENCY) - { - printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n"); - b &= ~TRITON_PEER_CONCURRENCY; - } - if(!(b & TRITON_STREAMING)) - { - printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n"); - b |= TRITON_STREAMING; - } - - if (b!=bo) - { - pcibios_write_config_byte(bus, devfn, TRITON_PCON, b); - printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b); - } - } -#endif - } - } -} -#endif +}; -static void init_tea6300(struct i2c_bus *bus) -{ - I2CWrite(bus, I2C_TEA6300, TEA6300_VL, 0x35, 1); /* volume left 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6300_VR, 0x35, 1); /* volume right 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6300_BA, 0x07, 1); /* bass 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6300_TR, 0x07, 1); /* treble 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6300_FA, 0x0f, 1); /* fader off */ - I2CWrite(bus, I2C_TEA6300, TEA6300_SW, 0x01, 1); /* mute off input A */ -} -static void init_tea6320(struct i2c_bus *bus) -{ - I2CWrite(bus, I2C_TEA6300, TEA6320_V, 0x28, 1); /* master volume */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FFL, 0x28, 1); /* volume left 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FFR, 0x28, 1); /* volume right 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FRL, 0x28, 1); /* volume rear left 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_FRR, 0x28, 1); /* volume rear right 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_BA, 0x11, 1); /* bass 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_TR, 0x11, 1); /* treble 0dB */ - I2CWrite(bus, I2C_TEA6300, TEA6320_S, TEA6320_S_GMU, 1); /* mute off input A */ -} +#define TRITON_PCON 0x50 +#define TRITON_BUS_CONCURRENCY (1<<0) +#define TRITON_STREAMING (1<<1) +#define TRITON_WRITE_BURST (1<<2) +#define TRITON_PEER_CONCURRENCY (1<<3) + -static void init_tda8425(struct i2c_bus *bus) +static void handle_chipset(void) { - I2CWrite(bus, I2C_TDA8425, TDA8425_VL, 0xFC, 1); /* volume left 0dB */ - I2CWrite(bus, I2C_TDA8425, TDA8425_VR, 0xFC, 1); /* volume right 0dB */ - I2CWrite(bus, I2C_TDA8425, TDA8425_BA, 0xF6, 1); /* bass 0dB */ - I2CWrite(bus, I2C_TDA8425, TDA8425_TR, 0xF6, 1); /* treble 0dB */ - I2CWrite(bus, I2C_TDA8425, TDA8425_S1, 0xCE, 1); /* mute off */ -} + struct pci_dev *dev = NULL; + + /* Just in case some nut set this to something dangerous */ + if (triton1) + triton1=BT848_INT_ETBF; + + while ((dev = pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, dev))) + { + /* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */ + printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n"); + } -static void init_tda9840(struct i2c_bus *bus) -{ - I2CWrite(bus, I2C_TDA9840, TDA9840_SW, 0x2A, 1); /* Sound mode switching */ -} + while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82441, dev))) + { + unsigned char b; + pci_read_config_byte(dev, 0x53, &b); + DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, ")); + DEBUG(printk("bufcon=0x%02x\n",b)); + } -static void init_tda9850(struct i2c_bus *bus) -{ - I2CWrite(bus, I2C_TDA9850, TDA9850_CON1, 0x08, 1); /* noise threshold st */ - I2CWrite(bus, I2C_TDA9850, TDA9850_CON2, 0x08, 1); /* noise threshold sap */ - I2CWrite(bus, I2C_TDA9850, TDA9850_CON3, 0x40, 1); /* stereo mode */ - I2CWrite(bus, I2C_TDA9850, TDA9850_CON4, 0x07, 1); /* 0 dB input gain?*/ - I2CWrite(bus, I2C_TDA9850, TDA9850_ALI1, 0x10, 1); /* wideband alignment? */ - I2CWrite(bus, I2C_TDA9850, TDA9850_ALI2, 0x10, 1); /* spectral alignment? */ - I2CWrite(bus, I2C_TDA9850, TDA9850_ALI3, 0x03, 1); + while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437, dev))) + { + printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); + triton1=BT848_INT_ETBF; + } } +/* can tda9855.c handle this too maybe? */ +static void init_tda9840(struct bttv *btv) +{ + /* Horrible Hack */ + I2CWrite(btv, I2C_TDA9840, TDA9840_SW, 0x2a, 1); /* sound mode switching */ + /* 00 - mute + 10 - mono / averaged stereo + 2a - stereo + 12 - dual A + 1a - dual AB + 16 - dual BA + 1e - dual B + 7a - external */ +} /* Figure out card and tuner type */ static void idcard(int i) { struct bttv *btv = &bttvs[i]; + int type,eeprom = 0; btwrite(0, BT848_GPIO_OUT_EN); DEBUG(printk(KERN_DEBUG "bttv%d: GPIO: 0x%08x\n", i, btread(BT848_GPIO_DATA))); /* Default the card to the user-selected one. */ - btv->type=card[i]; - btv->tuner_type=-1; /* use default tuner type */ + if (card[i] >= 0 && card[i] < TVCARDS) + btv->type=card[i]; - /* If we were asked to auto-detect, then do so! - Right now this will only recognize Miro, Hauppauge or STB - */ - if (btv->type == BTTV_UNKNOWN) - { - if (I2CRead(&(btv->i2c), I2C_HAUPEE)>=0) - { - if(btv->id>849) - btv->type=BTTV_HAUPPAUGE878; - else + /* If we were asked to auto-detect, then do so! */ + if (btv->type == BTTV_UNKNOWN) { + + /* many bt878 cards have a eeprom @ 0xa0 => read ID + and try to identify it */ + if (I2CRead(btv, I2C_HAUPEE, "eeprom") >= 0) { + eeprom = 0xa0; + readee(btv,eeprom_data,0xa0); + dump_eeprom(btv,0xa0); /* DEBUG */ + type = idcard_eeprom(btv); + if (-1 != type) { + btv->type = type; + } else if (btv->id <= 849) { + /* for unknown bt848, assume old Hauppauge */ btv->type=BTTV_HAUPPAUGE; + } - } else if (I2CRead(&(btv->i2c), I2C_STBEE)>=0) { + /* STB cards have a eeprom @ 0xae (old bt848) */ + } else if (I2CRead(btv, I2C_STBEE, "eeprom")>=0) { btv->type=BTTV_STB; - } else { - if (I2CRead(&(btv->i2c), 0x80)>=0) /* check for msp34xx */ + } + +#if 0 + /* check for msp34xx */ + if (I2CRead(btv, 0x80, "msp3400")>=0) btv->type = BTTV_MIROPRO; else - btv->type=BTTV_MIRO; - } + btv->type = BTTV_MIRO; +#endif } + /* print which board we have found */ + sprintf(btv->video_dev.name,"BT%d%s(%.22s)", + btv->id, + (btv->id==848 && btv->revision==0x12) ? "A" : "", + tvcards[btv->type].name); + printk(KERN_INFO "bttv%d: model: %s\n",btv->nr,btv->video_dev.name); + + /* board specific initialisations */ if (btv->type == BTTV_MIRO || btv->type == BTTV_MIROPRO) { /* auto detect tuner for MIRO cards */ btv->tuner_type=((btread(BT848_GPIO_DATA)>>10)-1)&7; } if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) { - hauppauge_msp_reset(btv); - hauppauge_eeprom(&(btv->i2c)); - if (btv->type == BTTV_HAUPPAUGE878) { - /* all bt878 hauppauge boards use this ... */ + /* pick up some config infos from the eeprom */ + if (0xa0 != eeprom) { + eeprom = 0xa0; + readee(btv,eeprom_data,0xa0); + } + hauppauge_eeprom(btv); + hauppauge_boot_msp34xx(btv); + } + if (btv->type == BTTV_PXC200) + init_PXC200(btv); + + + /* pll configuration */ + if (!(btv->id==848 && btv->revision==0x11)) { + /* defaults from card list */ + if (PLL_28 == tvcards[btv->type].pll) { btv->pll.pll_ifreq=28636363; btv->pll.pll_crystal=BT848_IFORM_XT0; } + /* insmod options can override */ + switch (pll[btv->nr]) { + case 0: /* none */ + btv->pll.pll_crystal = 0; + btv->pll.pll_ifreq = 0; + btv->pll.pll_ofreq = 0; + break; + case 1: /* 28 MHz */ + btv->pll.pll_ifreq = 28636363; + btv->pll.pll_ofreq = 0; + btv->pll.pll_crystal=BT848_IFORM_XT0; + break; + case 2: /* 35 MHz */ + btv->pll.pll_ifreq = 35468950; + btv->pll.pll_ofreq = 0; + btv->pll.pll_crystal=BT848_IFORM_XT1; + break; + } } + + + /* tuner configuration */ + if (-1 != tvcards[btv->type].tuner_type) + btv->tuner_type = tvcards[btv->type].tuner_type; + if (btv->tuner_type != -1) + call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); - if (btv->type == BTTV_PIXVIEWPLAYTV) { - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - } + /* try to detect audio/fader chips */ + if (tvcards[btv->type].msp34xx && + I2CRead(btv, I2C_MSP3400, "MSP34xx") >=0) { + if (autoload) + request_module("msp3400"); + } - if(btv->type==BTTV_AVERMEDIA98) - { - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; + if (tvcards[btv->type].tda8425 && + I2CRead(btv, I2C_TDA8425, "TDA8425") >=0) { + if (autoload) + request_module("tda8425"); } - - if (btv->have_tuner && btv->tuner_type != -1) - i2c_control_device(&(btv->i2c), - I2C_DRIVERID_TUNER, - TUNER_SET_TYPE,&btv->tuner_type); - - if (I2CRead(&(btv->i2c), I2C_TDA9840) >=0) - { - btv->audio_chip = TDA9840; - printk(KERN_INFO "bttv%d: audio chip: TDA9840\n", btv->nr); - } - - if (I2CRead(&(btv->i2c), I2C_TDA9850) >=0) - { - btv->audio_chip = TDA9850; - printk(KERN_INFO "bttv%d: audio chip: TDA9850\n",btv->nr); - } + if (tvcards[btv->type].tda9840 && + I2CRead(btv, I2C_TDA9840, "TDA9840") >=0) { + init_tda9840(btv); + btv->audio_chip = TDA9840; + /* move this to a module too? */ + init_tda9840(btv); + } - if (I2CRead(&(btv->i2c), I2C_TDA8425) >=0) - { - btv->audio_chip = TDA8425; - printk(KERN_INFO "bttv%d: audio chip: TDA8425\n",btv->nr); - } - - switch(btv->audio_chip) - { - case TDA9850: - init_tda9850(&(btv->i2c)); - break; - case TDA9840: - init_tda9840(&(btv->i2c)); - break; - case TDA8425: - init_tda8425(&(btv->i2c)); - break; - } - - if (I2CRead(&(btv->i2c), I2C_TEA6300) >=0) - { - if(btv->type==BTTV_AVEC_INTERCAP) - { - printk(KERN_INFO "bttv%d: fader chip: TEA6320\n",btv->nr); - btv->audio_chip = TEA6320; - init_tea6320(&(btv->i2c)); - } else { - printk(KERN_INFO "bttv%d: fader chip: TEA6300\n",btv->nr); - btv->audio_chip = TEA6300; - init_tea6300(&(btv->i2c)); - } - } else - printk(KERN_INFO "bttv%d: NO fader chip: TEA6300\n",btv->nr); - - printk(KERN_INFO "bttv%d: model: ",btv->nr); - - sprintf(btv->video_dev.name,"BT%d",btv->id); - switch (btv->type) - { - case BTTV_MIRO: - case BTTV_MIROPRO: - strcat(btv->video_dev.name, - (btv->type == BTTV_MIRO) ? "(Miro)" : "(Miro pro)"); - break; - case BTTV_HAUPPAUGE: - strcat(btv->video_dev.name,"(Hauppauge old)"); - break; - case BTTV_HAUPPAUGE878: - strcat(btv->video_dev.name,"(Hauppauge new)"); - break; - case BTTV_STB: - strcat(btv->video_dev.name,"(STB)"); - break; - case BTTV_INTEL: - strcat(btv->video_dev.name,"(Intel)"); - break; - case BTTV_DIAMOND: - strcat(btv->video_dev.name,"(Diamond)"); - break; - case BTTV_AVERMEDIA: - strcat(btv->video_dev.name,"(AVerMedia)"); - break; - case BTTV_MATRIX_VISION: - strcat(btv->video_dev.name,"(MATRIX-Vision)"); - break; - case BTTV_AVERMEDIA98: - strcat(btv->video_dev.name,"(AVerMedia TVCapture 98)"); - break; - case BTTV_VHX: - strcpy(btv->video_dev.name,"BT848(Aimslab-VHX)"); - break; - case BTTV_WINVIEW_601: - strcpy(btv->video_dev.name,"BT848(Leadtek WinView 601)"); - break; - case BTTV_AVEC_INTERCAP: - strcpy(btv->video_dev.name,"(AVEC Intercapture)"); - break; + if (tvcards[btv->type].tda985x && + I2CRead(btv, I2C_TDA9850, "TDA985x") >=0) { + if (autoload) + request_module("tda985x"); + } + + if (tvcards[btv->type].tda9875 && + I2CRead(btv, I2C_TDA9875, "TDA9875") >=0) { + if (autoload) + request_module("tda9875"); + } + + if (tvcards[btv->type].tda7432 && + I2CRead(btv, I2C_TDA7432, "TDA7432") >=0) { + if (autoload) + request_module("tda7432"); + } + + if (tvcards[btv->type].tea63xx) { + if (autoload) + request_module("tea6300"); + } + + if (tvcards[btv->type].tea64xx) { + if (autoload) + request_module("tea6420"); + } + + if (tvcards[btv->type].tuner != -1) { + if (autoload) + request_module("tuner"); } - printk("%s\n",btv->video_dev.name); - audio(btv, AUDIO_INTERN); -} + audio(btv, AUDIO_MUTE, 1); +} -static void bt848_set_risc_jmps(struct bttv *btv) +static void bt848_set_risc_jmps(struct bttv *btv, int flags) { - int flags=btv->cap; + if (-1 == flags) { + /* defaults */ + flags = 0; + if (btv->scr_on) + flags |= 0x03; + if (btv->vbi_on) + flags |= 0x0c; + } + + if (debug) printk("bttv%d: set_risc_jmp %08lx:", + btv->nr,virt_to_bus(btv->risc_jmp)); /* Sync to start of odd field */ - btv->risc_jmp[0]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRE); - btv->risc_jmp[1]=0; + btv->risc_jmp[0]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC + |BT848_FIFO_STATUS_VRE); + btv->risc_jmp[1]=cpu_to_le32(0); /* Jump to odd vbi sub */ - btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0x5<<20)); - if (flags&8) + btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0xd<<20)); + if (flags&8) { + if (debug) printk(" ev=%08lx",virt_to_bus(btv->vbi_odd)); btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->vbi_odd)); - else + } else { + if (debug) printk(" -----------"); btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->risc_jmp+4)); + } /* Jump to odd sub */ - btv->risc_jmp[4]=cpu_to_le32(BT848_RISC_JUMP|(0x6<<20)); - if (flags&2) - btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_odd)); - else + btv->risc_jmp[4]=cpu_to_le32(BT848_RISC_JUMP|(0xe<<20)); + if (0 != btv->risc_cap_odd) { + if (debug) printk(" e%d=%08x",btv->gq_grab,btv->risc_cap_odd); + flags |= 3; + btv->risc_jmp[5]=cpu_to_le32(btv->risc_cap_odd); + } else if (flags&2) { + if (debug) printk(" eo=%08lx",virt_to_bus(btv->risc_scr_odd)); + btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_scr_odd)); + } else { + if (debug) printk(" -----------"); btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_jmp+6)); + } /* Sync to start of even field */ - btv->risc_jmp[6]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRO); - btv->risc_jmp[7]=0; + btv->risc_jmp[6]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC + |BT848_FIFO_STATUS_VRO); + btv->risc_jmp[7]=cpu_to_le32(0); /* Jump to even vbi sub */ btv->risc_jmp[8]=cpu_to_le32(BT848_RISC_JUMP); - if (flags&4) + if (flags&4) { + if (debug) printk(" ov=%08lx",virt_to_bus(btv->vbi_even)); btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->vbi_even)); - else + } else { + if (debug) printk(" -----------"); btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->risc_jmp+10)); + } /* Jump to even sub */ btv->risc_jmp[10]=cpu_to_le32(BT848_RISC_JUMP|(8<<20)); - if (flags&1) - btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_even)); - else + if (0 != btv->risc_cap_even) { + if (debug) printk(" o%d=%08x",btv->gq_grab,btv->risc_cap_even); + flags |= 3; + btv->risc_jmp[11]=cpu_to_le32(btv->risc_cap_even); + } else if (flags&1) { + if (debug) printk(" oo=%08lx",virt_to_bus(btv->risc_scr_even)); + btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_scr_even)); + } else { + if (debug) printk(" -----------"); btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_jmp+12)); + } btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP); btv->risc_jmp[13]=cpu_to_le32(virt_to_bus(btv->risc_jmp)); - /* enable capturing */ + /* enable cpaturing and DMA */ + if (debug) printk(" flags=0x%x dma=%s\n", + flags,(flags&0x0f) ? "on" : "off"); btaor(flags, ~0x0f, BT848_CAP_CTL); if (flags&0x0f) bt848_dma(btv, 3); @@ -3214,16 +3291,62 @@ bt848_dma(btv, 0); } +static int +init_video_dev(struct bttv *btv) +{ + int num = btv - bttvs; + + memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template)); + memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template)); + memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template)); + + idcard(num); + + if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0) + return -1; + if(video_register_device(&btv->vbi_dev,VFL_TYPE_VBI)<0) + { + video_unregister_device(&btv->video_dev); + return -1; + } + if (radio[num]) + { + if(video_register_device(&btv->radio_dev, VFL_TYPE_RADIO)<0) + { + video_unregister_device(&btv->vbi_dev); + video_unregister_device(&btv->video_dev); + return -1; + } + } + return 1; +} + static int init_bt848(int i) { struct bttv *btv = &bttvs[i]; + int j; btv->user=0; + init_MUTEX(&btv->lock); + + /* dump current state of the gpio registers before changing them, + * might help to make a new card work */ + if (verbose >= 2) + printk("bttv%d: gpio: out_enable=0x%x, data=0x%x, in=0x%x\n", + i, + btread(BT848_GPIO_OUT_EN), + btread(BT848_GPIO_DATA), + btread(BT848_GPIO_REG_INP)); /* reset the bt848 */ btwrite(0, BT848_SRESET); DEBUG(printk(KERN_DEBUG "bttv%d: bt848_mem: 0x%lx\n",i,(unsigned long) btv->bt848_mem)); + /* not registered yet */ + btv->video_dev.minor = -1; + btv->radio_dev.minor = -1; + btv->vbi_dev.minor = -1; + /* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */ btv->win.norm=0; /* change this to 1 for NTSC, 2 for SECAM */ btv->win.interlace=1; @@ -3231,44 +3354,34 @@ btv->win.y=0; btv->win.width=768; /* 640 */ btv->win.height=576; /* 480 */ - btv->win.cropwidth=768; /* 640 */ - btv->win.cropheight=576; /* 480 */ - btv->win.cropx=0; - btv->win.cropy=0; btv->win.bpp=2; btv->win.depth=16; btv->win.color_fmt=BT848_COLOR_FMT_RGB16; btv->win.bpl=1024*btv->win.bpp; btv->win.swidth=1024; btv->win.sheight=768; - btv->cap=0; - - btv->gmode=0; - btv->risc_odd=0; - btv->risc_even=0; + btv->win.vidadr=0; + btv->vbi_on=0; + btv->scr_on=0; + + btv->risc_scr_odd=0; + btv->risc_scr_even=0; + btv->risc_cap_odd=0; + btv->risc_cap_even=0; btv->risc_jmp=0; btv->vbibuf=0; - btv->grisc=0; - btv->grabbing=0; - btv->grabcount=0; - btv->grab=0; - btv->lastgrab=0; btv->field=btv->last_field=0; - /* cevans - prevents panic if initialization bails due to memory - * alloc failures! - */ - btv->video_dev.minor = -1; - btv->vbi_dev.minor = -1; - btv->radio_dev.minor = -1; + + btv->errors=0; + btv->needs_restart=0; /* i2c */ - memcpy(&(btv->i2c),&bttv_i2c_bus_template,sizeof(struct i2c_bus)); - sprintf(btv->i2c.name,"bt848-%d",i); - btv->i2c.data = btv; + btv->tuner_type=-1; + init_bttv_i2c(btv); - if (!(btv->risc_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) + if (!(btv->risc_scr_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) return -1; - if (!(btv->risc_even=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) + if (!(btv->risc_scr_even=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) return -1; if (!(btv->risc_jmp =(unsigned int *) kmalloc(2048, GFP_KERNEL))) return -1; @@ -3282,9 +3395,13 @@ btv->vbibuf=(unsigned char *) vmalloc(VBIBUF_SIZE); if (!btv->vbibuf) return -1; - if (!(btv->grisc=(unsigned int *) kmalloc(32768, GFP_KERNEL))) + if (!(btv->gbuf = kmalloc(sizeof(struct bttv_gbuf)*gbuffers,GFP_KERNEL))) return -1; - + for (j = 0; j < gbuffers; j++) { + if (!(btv->gbuf[j].risc = kmalloc(16384,GFP_KERNEL))) + return -1; + } + memset(btv->vbibuf, 0, VBIBUF_SIZE); /* We don't want to return random memory to the user */ @@ -3294,14 +3411,21 @@ bt848_set_winsize(btv); /* btwrite(0, BT848_TDEC); */ - btwrite(0x10, BT848_COLOR_CTL); + btwrite(0x10, BT848_COLOR_CTL); btwrite(0x00, BT848_CAP_CTL); - btwrite(0xac, BT848_GPIO_DMA_CTL); + /* set planar and packed mode trigger points and */ + /* set rising edge of inverted GPINTR pin as irq trigger */ + btwrite(BT848_GPIO_DMA_CTL_PKTP_32| + BT848_GPIO_DMA_CTL_PLTP1_16| + BT848_GPIO_DMA_CTL_PLTP23_16| + BT848_GPIO_DMA_CTL_GPINTC| + BT848_GPIO_DMA_CTL_GPINTI, + BT848_GPIO_DMA_CTL); /* select direct input */ btwrite(0x00, BT848_GPIO_REG_INP); - btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_PAL_BDGHI, + btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM); btwrite(0xd8, BT848_CONTRAST_LO); @@ -3330,42 +3454,20 @@ btwrite(btv->triton1| /*BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR| BT848_INT_FDSR|BT848_INT_FTRGT|BT848_INT_FBUS|*/ - BT848_INT_VSYNC| + (fieldnr ? BT848_INT_VSYNC : 0)| + BT848_INT_GPINT| BT848_INT_SCERR| BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES| BT848_INT_FMTCHG|BT848_INT_HLOCK, BT848_INT_MASK); make_vbitab(btv); - bt848_set_risc_jmps(btv); + bt848_set_risc_jmps(btv,-1); /* * Now add the template and register the device unit. */ - - memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template)); - memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template)); - memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template)); - - idcard(i); - - if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0) - return -1; - if(video_register_device(&btv->vbi_dev,VFL_TYPE_VBI)<0) - { - video_unregister_device(&btv->video_dev); - return -1; - } - if (radio[i]) - { - if(video_register_device(&btv->radio_dev, VFL_TYPE_RADIO)<0) - { - video_unregister_device(&btv->vbi_dev); - video_unregister_device(&btv->video_dev); - return -1; - } - } - i2c_register_bus(&btv->i2c); + init_video_dev(btv); return 0; } @@ -3374,7 +3476,7 @@ { u32 stat,astat; u32 dstat; - int count; + int count,i; struct bttv *btv; btv=(struct bttv *)dev_id; @@ -3387,11 +3489,17 @@ if (!astat) return; btwrite(astat,BT848_INT_STAT); - IDEBUG(printk ("bttv%d: astat %08x stat %08x\n", btv->nr, astat, stat)); + IDEBUG(printk ("bttv%d: astat=%08x\n", btv->nr, astat)); + IDEBUG(printk ("bttv%d: stat=%08x\n", btv->nr, stat)); /* get device status bits */ dstat=btread(BT848_DSTATUS); + if (astat&BT848_INT_GPINT) { + IDEBUG(printk ("bttv%d: IRQ_GPINT\n", btv->nr)); + wake_up_interruptible(&btv->gpioq); + } + if (astat&BT848_INT_FMTCHG) { IDEBUG(printk ("bttv%d: IRQ_FMTCHG\n", btv->nr)); @@ -3407,17 +3515,44 @@ IDEBUG(printk ("bttv%d: IRQ_VSYNC\n", btv->nr)); btv->field++; } - if (astat&BT848_INT_SCERR) { - IDEBUG(printk ("bttv%d: IRQ_SCERR\n", btv->nr)); - bt848_dma(btv, 0); - bt848_dma(btv, 3); - wake_up_interruptible(&btv->vbiq); - wake_up_interruptible(&btv->capq); + if (astat&(BT848_INT_SCERR|BT848_INT_OCERR)) { + if (verbose) + printk("bttv%d: irq:%s%s risc_count=%08x\n", + btv->nr, + (astat&BT848_INT_SCERR) ? " SCERR" : "", + (astat&BT848_INT_OCERR) ? " OCERR" : "", + btread(BT848_RISC_COUNT)); + btv->errors++; + if (btv->errors < BTTV_ERRORS) { + btand(~15, BT848_GPIO_DMA_CTL); + btwrite(virt_to_bus(btv->risc_jmp+2), + BT848_RISC_STRT_ADD); + bt848_set_geo(btv,0); + bt848_set_risc_jmps(btv,-1); + } else { + if (verbose) + printk("bttv%d: aiee: error loops\n",btv->nr); + /* cancel all outstanding grab requests */ + btv->gq_in = 0; + btv->gq_out = 0; + btv->gq_grab = -1; + for (i = 0; i < gbuffers; i++) + if (btv->gbuf[i].stat == GBUFFER_GRABBING) + btv->gbuf[i].stat = GBUFFER_ERROR; + /* disable DMA */ + btv->risc_cap_odd = 0; + btv->risc_cap_even = 0; + bt848_set_risc_jmps(btv,0); + btv->needs_restart = 1; + wake_up_interruptible(&btv->vbiq); + wake_up_interruptible(&btv->capq); + } } if (astat&BT848_INT_RISCI) { - IDEBUG(printk ("bttv%d: IRQ_RISCI\n", btv->nr)); + if (debug > 1) + printk("bttv%d: IRQ_RISCI\n",btv->nr); /* captured VBI frame */ if (stat&(1<<28)) @@ -3429,43 +3564,49 @@ } /* captured full frame */ - if (stat&(2<<28)) + if (stat&(2<<28) && btv->gq_grab != -1) { - wake_up_interruptible(&btv->capq); btv->last_field=btv->field; - btv->grab++; - btv->frame_stat[btv->grf] = GBUFFER_DONE; - if ((--btv->grabbing)) + if (debug) + printk("bttv%d: cap irq: done %d\n",btv->nr,btv->gq_grab); + do_gettimeofday(&btv->gbuf[btv->gq_grab].tv); + btv->gbuf[btv->gq_grab].stat = GBUFFER_DONE; + btv->gq_grab = -1; + if (btv->gq_in != btv->gq_out) { - btv->gfmt = btv->gfmt_next; - btv->gwidth = btv->gwidth_next; - btv->gheight = btv->gheight_next; - btv->gro = btv->gro_next; - btv->gre = btv->gre_next; - btv->grf = btv->grf_next; - btv->risc_jmp[5]=cpu_to_le32(btv->gro); - btv->risc_jmp[11]=cpu_to_le32(btv->gre); - bt848_set_geo(btv, btv->gwidth, - btv->gheight, - btv->gfmt, 0); + btv->gq_grab = btv->gqueue[btv->gq_out++]; + btv->gq_out = btv->gq_out % MAX_GBUFFERS; + if (debug) + printk("bttv%d: cap irq: capture %d\n",btv->nr,btv->gq_grab); + btv->risc_cap_odd = btv->gbuf[btv->gq_grab].ro; + btv->risc_cap_even = btv->gbuf[btv->gq_grab].re; + bt848_set_risc_jmps(btv,-1); + bt848_set_geo(btv,0); + btwrite(BT848_COLOR_CTL_GAMMA, + BT848_COLOR_CTL); } else { - bt848_set_risc_jmps(btv); - btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI); - btand(~BT848_VSCALE_COMB, BT848_O_VSCALE_HI); - bt848_set_geo(btv, btv->win.width, - btv->win.height, - btv->win.color_fmt, 0); + btv->risc_cap_odd = 0; + btv->risc_cap_even = 0; + bt848_set_risc_jmps(btv,-1); + bt848_set_geo(btv,0); + btwrite(btv->fb_color_ctl | BT848_COLOR_CTL_GAMMA, + BT848_COLOR_CTL); } wake_up_interruptible(&btv->capq); break; } if (stat&(8<<28)) { - btv->risc_jmp[5]=cpu_to_le32(btv->gro); - btv->risc_jmp[11]=cpu_to_le32(btv->gre); - btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP); - bt848_set_geo(btv, btv->gwidth, btv->gheight, - btv->gfmt, 0); + btv->gq_grab = btv->gqueue[btv->gq_out++]; + btv->gq_out = btv->gq_out % MAX_GBUFFERS; + if (debug) + printk("bttv%d: cap irq: capture %d\n",btv->nr,btv->gq_grab); + btv->risc_cap_odd = btv->gbuf[btv->gq_grab].ro; + btv->risc_cap_even = btv->gbuf[btv->gq_grab].re; + bt848_set_risc_jmps(btv,-1); + bt848_set_geo(btv,0); + btwrite(BT848_COLOR_CTL_GAMMA, + BT848_COLOR_CTL); } } if (astat&BT848_INT_OCERR) @@ -3499,13 +3640,14 @@ if (astat&BT848_INT_HLOCK) { if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio)) - audio(btv, AUDIO_ON); + audio(btv, AUDIO_ON,0); else - audio(btv, AUDIO_OFF); + audio(btv, AUDIO_OFF,0); } if (astat&BT848_INT_I2CDONE) { + IDEBUG(printk ("bttv%d: IRQ_I2CDONE\n", btv->nr)); } count++; @@ -3527,12 +3669,14 @@ * Scan for a Bt848 card, request the irq and map the io memory */ -#if LINUX_VERSION_CODE >= 0x020100 int configure_bt848(struct pci_dev *dev, int bttv_num) { int result; unsigned char command; struct bttv *btv; +#if defined(__powerpc__) + unsigned int cmd; +#endif btv=&bttvs[bttv_num]; btv->dev=dev; @@ -3542,12 +3686,15 @@ btv->risc_jmp=NULL; btv->vbi_odd=NULL; btv->vbi_even=NULL; - btv->vbiq=NULL; - btv->capq=NULL; - btv->capqo=NULL; - btv->capqe=NULL; + init_waitqueue_head(&btv->vbiq); + init_waitqueue_head(&btv->capq); + init_waitqueue_head(&btv->capqo); + init_waitqueue_head(&btv->capqe); btv->vbip=VBIBUF_SIZE; + init_waitqueue_head(&btv->gpioq); + btv->shutdown=0; + btv->id=dev->device; btv->irq=dev->irq; btv->bt848_adr=dev->base_address[0]; @@ -3558,7 +3705,7 @@ if (remap[bttv_num]) { - unsigned int dw = btv->bt848_adr; + unsigned int dw = btv->bt848_adr; if (remap[bttv_num] < 0x1000) remap[bttv_num]<<=20; @@ -3568,8 +3715,8 @@ remap[bttv_num]|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK); pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, remap[bttv_num]); pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &dw); - btv->dev->base_address[0] = btv->bt848_adr = dw; - } + btv->dev->base_address[0] = btv->bt848_adr; + } btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK; pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision); printk(KERN_INFO "bttv%d: Brooktree Bt%d (rev %d) ", @@ -3578,32 +3725,18 @@ printk("irq: %d, ",btv->irq); printk("memory: 0x%lx.\n", btv->bt848_adr); - btv->pll.pll_crystal = 0; - btv->pll.pll_ifreq = 0; - btv->pll.pll_ofreq = 0; - btv->pll.pll_current = 0; - if (!(btv->id==848 && btv->revision==0x11)) { - switch (pll[btv->nr]) { - case 0: - /* off */ - break; - case 1: - /* 28 MHz crystal installed */ - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - break; - case 2: - /* 35 MHz crystal installed */ - btv->pll.pll_ifreq=35468950; - btv->pll.pll_crystal=BT848_IFORM_XT1; - break; - } - } - +#if defined(__powerpc__) + /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */ + /* response on cards with no firmware is not enabled by OF */ + pci_read_config_dword(dev, PCI_COMMAND, &cmd); + cmd = (cmd | PCI_COMMAND_MEMORY ); + pci_write_config_dword(dev, PCI_COMMAND, cmd); +#endif + #ifdef __sparc__ - btv->bt848_mem=(unsigned char *)btv->bt848_adr; + btv->bt848_mem=(unsigned char *)btv->bt848_adr; #else - btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); + btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); #endif /* clear interrupt mask */ @@ -3615,15 +3748,15 @@ { printk(KERN_ERR "bttv%d: Bad irq number or handler\n", bttv_num); - return -EINVAL; + goto fail; } if (result==-EBUSY) { printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq); - return result; + goto fail; } if (result < 0) - return result; + goto fail; pci_set_master(dev); @@ -3639,22 +3772,24 @@ if (!(command&BT878_EN_TBFX)) { printk("bttv: 430FX compatibility could not be enabled\n"); - return -1; + result = -1; + goto fail; } } - return 0; + + fail: + return result; } static int find_bt848(void) { - struct pci_dev *dev = pci_devices; + struct pci_dev *dev; int result=0; bttv_num=0; - while (dev) - { + for (dev = pci_devices; dev; dev = dev->next) { if (dev->vendor == PCI_VENDOR_ID_BROOKTREE) if ((dev->device == PCI_DEVICE_ID_BT848)|| (dev->device == PCI_DEVICE_ID_BT849)|| @@ -3663,189 +3798,26 @@ result=configure_bt848(dev,bttv_num++); if (result) return result; - dev = dev->next; } if(bttv_num) printk(KERN_INFO "bttv: %d Bt8xx card(s) found.\n", bttv_num); return bttv_num; } -#else -static int find_bt848(void) -{ - short pci_index; - unsigned char command, latency; - int result; - unsigned char bus, devfn; - struct bttv *btv; - - bttv_num=0; - - if (!pcibios_present()) - { - DEBUG(printk(KERN_DEBUG "bttv%d: PCI-BIOS not present or not accessable!\n",bttv_num)); - return 0; - } - - for (pci_index = 0; - !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849, - pci_index, &bus, &devfn) - ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, - pci_index, &bus, &devfn) - ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878, - pci_index, &bus, &devfn) - ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879, - pci_index, &bus, &devfn); - ++pci_index) - { - btv=&bttvs[bttv_num]; - btv->nr = bttv_num; - btv->bus=bus; - btv->devfn=devfn; - btv->bt848_mem=NULL; - btv->vbibuf=NULL; - btv->risc_jmp=NULL; - btv->vbi_odd=NULL; - btv->vbi_even=NULL; - btv->vbiq=NULL; - btv->capq=NULL; - btv->capqo=NULL; - btv->capqe=NULL; - - btv->vbip=VBIBUF_SIZE; - - pcibios_read_config_word(btv->bus, btv->devfn, PCI_DEVICE_ID, - &btv->id); - pcibios_read_config_byte(btv->bus, btv->devfn, - PCI_INTERRUPT_LINE, &btv->irq); - pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, - &btv->bt848_adr); - if (btv->id >= 878) - btv->i2c_command = 0x83; - else - btv->i2c_command= - (I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA); - - if (remap[bttv_num]) - { - if (remap[bttv_num] < 0x1000) - remap[bttv_num]<<=20; - remap[bttv_num]&=PCI_BASE_ADDRESS_MEM_MASK; - printk(KERN_INFO "bttv%d: remapping to : 0x%08x.\n", - bttv_num,remap[bttv_num]); - remap[bttv_num]|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK); - pcibios_write_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, - remap[bttv_num]); - pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0, - &btv->bt848_adr); - } - - btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK; - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION, - &btv->revision); - printk(KERN_INFO "bttv%d: Brooktree Bt%d (rev %d) ", - bttv_num,btv->id, btv->revision); - printk("bus: %d, devfn: %d, ", - btv->bus, btv->devfn); - printk("irq: %d, ",btv->irq); - printk("memory: 0x%08x.\n", btv->bt848_adr); - - btv->pll.pll_crystal = 0; - btv->pll.pll_ifreq = 0; - btv->pll.pll_ofreq = 0; - btv->pll.pll_current = 0; - if (!(btv->id==848 && btv->revision==0x11)) { - switch (pll[btv->nr]) { - case 0: - /* off */ - break; - case 1: - /* 28 MHz crystal installed */ - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - break; - case 2: - /* 35 MHz crystal installed */ - btv->pll.pll_ifreq=35468950; - btv->pll.pll_crystal=BT848_IFORM_XT1; - break; - } - } - - btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); - - result = request_irq(btv->irq, bttv_irq, - SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv); - if (result==-EINVAL) - { - printk(KERN_ERR "bttv%d: Bad irq number or handler\n", - bttv_num); - return -EINVAL; - } - if (result==-EBUSY) - { - printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq); - return result; - } - if (result < 0) - return result; - - /* Enable bus-mastering */ - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); - command|=PCI_COMMAND_MASTER; - pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command); - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); - if (!(command&PCI_COMMAND_MASTER)) - { - printk(KERN_ERR "bttv%d: PCI bus-mastering could not be enabled\n",bttv_num); - return -1; - } - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER, - &latency); - if (!latency) - { - latency=32; - pcibios_write_config_byte(btv->bus, btv->devfn, - PCI_LATENCY_TIMER, latency); - } - DEBUG(printk(KERN_DEBUG "bttv%d: latency: %02x\n", - bttv_num, latency)); - - btv->triton1=triton1 ? BT848_INT_ETBF : 0; - if (triton1 && btv->id >= 878) - { - triton1 = 0; - printk("bttv: Enabling 430FX compatibilty for bt878\n"); - pcibios_read_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, &command); - command|=BT878_EN_TBFX; - pcibios_write_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, command); - pcibios_read_config_byte(btv->bus, btv->devfn, BT878_DEVCTRL, &command); - if (!(command&BT878_EN_TBFX)) - { - printk("bttv: 430FX compatibility could not be enabled\n"); - return -1; - } - } - - bttv_num++; - } - if(bttv_num) - printk(KERN_INFO "bttv: %d Bt8xx card(s) found.\n", bttv_num); - return bttv_num; -} -#endif static void release_bttv(void) { u8 command; - int i; + int i,j; struct bttv *btv; for (i=0;ii2c_adap); + /* turn off all capturing, DMA and IRQs */ btand(~15, BT848_GPIO_DMA_CTL); /* first disable interrupts before unmapping the memory! */ @@ -3853,31 +3825,24 @@ btwrite(0xffffffffUL,BT848_INT_STAT); btwrite(0x0, BT848_GPIO_OUT_EN); - /* unregister i2c_bus */ - i2c_unregister_bus((&btv->i2c)); - /* disable PCI bus-mastering */ -#if LINUX_VERSION_CODE >= 0x020100 pci_read_config_byte(btv->dev, PCI_COMMAND, &command); /* Should this be &=~ ?? */ command&=~PCI_COMMAND_MASTER; pci_write_config_byte(btv->dev, PCI_COMMAND, command); -#else - pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command); - command&=~PCI_COMMAND_MASTER; - pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command); - -#endif /* unmap and free memory */ - if (btv->grisc) - kfree((void *) btv->grisc); + for (j = 0; j < gbuffers; j++) + if (btv->gbuf[j].risc) + kfree(btv->gbuf[j].risc); + if (btv->gbuf) + kfree((void *) btv->gbuf); - if (btv->risc_odd) - kfree((void *) btv->risc_odd); + if (btv->risc_scr_odd) + kfree((void *) btv->risc_scr_odd); - if (btv->risc_even) - kfree((void *) btv->risc_even); + if (btv->risc_scr_even) + kfree((void *) btv->risc_scr_even); DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%p.\n", btv->risc_jmp)); if (btv->risc_jmp) @@ -3899,23 +3864,38 @@ video_unregister_device(&btv->vbi_dev); if (radio[btv->nr] && btv->radio_dev.minor != -1) video_unregister_device(&btv->radio_dev); + + /* wake up any waiting processes + because shutdown flag is set, no new processes (in this queue) + are expected + */ + btv->shutdown=1; + wake_up(&btv->gpioq); } } #ifdef MODULE - -EXPORT_NO_SYMBOLS; - int init_module(void) -{ #else int init_bttv_cards(struct video_init *unused) -{ #endif +{ int i; - + + printk(KERN_INFO "bttv: driver version %d.%d.%d loaded\n", + (BTTV_VERSION_CODE >> 16) & 0xff, + (BTTV_VERSION_CODE >> 8) & 0xff, + BTTV_VERSION_CODE & 0xff); + if (gbuffers < 2 || gbuffers > MAX_GBUFFERS) + gbuffers = 2; + if (gbufsize < 0 || gbufsize > BTTV_MAX_FBUF) + gbufsize = BTTV_MAX_FBUF; + if (verbose) + printk(KERN_INFO "bttv: using %d buffers with %dk (%dk total) for capture\n", + gbuffers,gbufsize/1024,gbuffers*gbufsize/1024); + handle_chipset(); - if (find_bt848()<0) + if (find_bt848()<=0) return -EIO; /* initialize Bt848s */ @@ -3932,7 +3912,6 @@ } - #ifdef MODULE void cleanup_module(void) @@ -3944,14 +3923,6 @@ /* * Local variables: - * c-indent-level: 8 - * c-brace-imaginary-offset: 0 - * c-brace-offset: -8 - * c-argdecl-indent: 8 - * c-label-offset: -8 - * c-continued-statement-offset: 8 - * c-continued-brace-offset: 0 - * indent-tabs-mode: nil - * tab-width: 8 + * c-basic-offset: 8 * End: */ diff -urN vanilla-2.2.15pre17/drivers/char/bttv.h bttv-2.2.15pre17/drivers/char/bttv.h --- vanilla-2.2.15pre17/drivers/char/bttv.h Mon May 1 22:59:34 2000 +++ bttv-2.2.15pre17/drivers/char/bttv.h Mon May 1 23:03:21 2000 @@ -1,4 +1,4 @@ -/* +/* bttv - Bt848 frame grabber driver Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) @@ -21,26 +21,71 @@ #ifndef _BTTV_H_ #define _BTTV_H_ -#define BTTV_VERSION_CODE 0x000523 +#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,27) #include #include - +#include #include -#include "msp3400.h" +#include + +#include "audiochip.h" #include "bt848.h" -#include + +#define DECLARE_MUTEX(foo) struct semaphore foo = MUTEX +#define DECLARE_MUTEX_LOCKED(foo) struct semaphore foo = MUTEX_LOCKED +#define init_MUTEX(foo) *(foo) = MUTEX +#define WAIT_QUEUE struct wait_queue* +#define init_waitqueue_head(wq) *(wq) = NULL; + +/* returns card type, + for possible values see lines below beginning with #define BTTV_UNKNOWN + returns negative value if error ocurred +*/ +extern int bttv_get_id(unsigned int card); + +/* sets GPOE register (BT848_GPIO_OUT_EN) to new value: + data | (current_GPOE_value & ~mask) + returns negative value if error ocurred +*/ +extern int bttv_gpio_enable(unsigned int card, + unsigned long mask, unsigned long data); + +/* fills data with GPDATA register contents + returns negative value if error ocurred +*/ +extern int bttv_read_gpio(unsigned int card, unsigned long *data); + +/* sets GPDATA register to new value: + (data & mask) | (current_GPDATA_value & ~mask) + returns negative value if error ocurred +*/ +extern int bttv_write_gpio(unsigned int card, + unsigned long mask, unsigned long data); + +/* returns pointer to task queue which can be used as parameter to + interruptible_sleep_on + in interrupt handler if BT848_INT_GPINT bit is set - this queue is activated + (wake_up_interruptible) and following call to the function bttv_read_gpio + should return new value of GPDATA, + returns NULL value if error ocurred or queue is not available + WARNING: because there is no buffer for GPIO data, one MUST + process data ASAP +*/ +extern WAIT_QUEUE* bttv_get_gpio_queue(unsigned int card); + #ifndef O_NONCAP #define O_NONCAP O_TRUNC #endif -#define MAX_GBUFFERS 2 +#define MAX_GBUFFERS 64 #define RISCMEM_LEN (32744*2) -#define VBIBUF_SIZE 65536 +#define VBI_MAXLINES 16 +#define VBIBUF_SIZE (2048*VBI_MAXLINES*2) -/* maximum needed buffer size for extended VBI frame mode capturing */ -#define BTTV_MAX_FBUF 0x190000 +#define BTTV_MAX_FBUF 0x208000 +#define I2C_CLIENTS_MAX 8 #ifdef __KERNEL__ @@ -50,8 +95,6 @@ ushort width, height; ushort bpp, bpl; ushort swidth, sheight; - short cropx, cropy; - ushort cropwidth, cropheight; unsigned long vidadr; ushort freq; int norm; @@ -67,54 +110,60 @@ unsigned int pll_current; /* Currently programmed ofreq */ }; -/* Per-open data for handling multiple opens on one device */ -struct device_open -{ - int isopen; - int noncapturing; - struct bttv *dev; +struct bttv_gbuf { + int stat; +#define GBUFFER_UNUSED 0 +#define GBUFFER_GRABBING 1 +#define GBUFFER_DONE 2 +#define GBUFFER_ERROR 3 + struct timeval tv; + + u16 width; + u16 height; + u16 fmt; + + u32 *risc; + unsigned long ro; + unsigned long re; }; -#define MAX_OPENS 3 -struct bttv -{ +struct bttv { struct video_device video_dev; struct video_device radio_dev; struct video_device vbi_dev; struct video_picture picture; /* Current picture params */ struct video_audio audio_dev; /* Current audio params */ + struct semaphore lock; int user; int capuser; - struct device_open open_data[MAX_OPENS]; - - struct i2c_bus i2c; - int have_msp3400; - int have_tuner; + + /* i2c */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + int i2c_state, i2c_ok; + struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; + int tuner_type; int channel; unsigned int nr; unsigned short id; -#if LINUX_VERSION_CODE < 0x020100 - unsigned char bus; /* PCI bus the Bt848 is on */ - unsigned char devfn; -#else struct pci_dev *dev; -#endif - unsigned int irq; /* IRQ used by Bt848 card */ + unsigned int irq; /* IRQ used by Bt848 card */ unsigned char revision; - unsigned long bt848_adr; /* bus address of IO mem returned by PCI BIOS */ + unsigned long bt848_adr; /* bus address of IO mem returned by PCI BIOS */ unsigned char *bt848_mem; /* pointer to mapped IO memory */ unsigned long busriscmem; u32 *riscmem; unsigned char *vbibuf; struct bttv_window win; + int fb_color_ctl; int type; /* card type */ int audio; /* audio mode */ - int audio_chip; - int fader_chip; + int audio_chip; /* set to one of the chips supported by bttv.c */ int radio; u32 *risc_jmp; @@ -122,40 +171,24 @@ u32 *vbi_even; u32 bus_vbi_even; u32 bus_vbi_odd; - struct wait_queue *vbiq; - struct wait_queue *capq; - struct wait_queue *capqo; - struct wait_queue *capqe; + WAIT_QUEUE vbiq; + WAIT_QUEUE capq; + WAIT_QUEUE capqo; + WAIT_QUEUE capqe; int vbip; - u32 *risc_odd; - u32 *risc_even; - int cap; + u32 *risc_scr_odd; + u32 *risc_scr_even; + u32 risc_cap_odd; + u32 risc_cap_even; + int scr_on; + int vbi_on; struct video_clip *cliprecs; - struct gbuffer *ogbuffers; - struct gbuffer *egbuffers; - u16 gwidth, gheight, gfmt; - u16 gwidth_next, gheight_next, gfmt_next; - u32 *grisc; - - unsigned long gro; - unsigned long gre; - unsigned long gro_next; - unsigned long gre_next; - - int grf,grf_next; /* frame numbers in grab queue */ - int frame_stat[MAX_GBUFFERS]; -#define GBUFFER_UNUSED 0 -#define GBUFFER_GRABBING 1 -#define GBUFFER_DONE 2 - + struct bttv_gbuf *gbuf; + int gqueue[MAX_GBUFFERS]; + int gq_in,gq_out,gq_grab; char *fbuffer; - int gmode; - int grabbing; - int lastgrab; - int grab; - int grabcount; struct bttv_pll_info pll; unsigned int Fsc; @@ -163,15 +196,25 @@ unsigned int last_field; /* number of last grabbed field */ int i2c_command; int triton1; + + int errors; + int needs_restart; + + WAIT_QUEUE gpioq; + int shutdown; }; #endif -/*The following should be done in more portable way. It depends on define - of _ALPHA_BTTV in the Makefile.*/ +#if defined(__powerpc__) /* big-endian */ +extern __inline__ void io_st_le32(volatile unsigned *addr, unsigned val) +{ + __asm__ __volatile__ ("stwbrx %1,0,%2" : \ + "=m" (*addr) : "r" (val), "r" (addr)); + __asm__ __volatile__ ("eieio" : : : "memory"); +} -#ifdef _ALPHA_BTTV -#define btwrite(dat,adr) writel((dat),(char *) (btv->bt848_adr+(adr))) -#define btread(adr) readl(btv->bt848_adr+(adr)) +#define btwrite(dat,adr) io_st_le32((unsigned *)(btv->bt848_mem+(adr)),(dat)) +#define btread(adr) ld_le32((unsigned *)(btv->bt848_mem+(adr))) #else #define btwrite(dat,adr) writel((dat), (char *) (btv->bt848_mem+(adr))) #define btread(adr) readl(btv->bt848_mem+(adr)) @@ -191,7 +234,7 @@ #define BTTV_BURST_OFF _IOR('v' , BASE_VIDIOCPRIVATE+5, int) #define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int) #define BTTV_PICNR _IOR('v' , BASE_VIDIOCPRIVATE+7, int) - +#define BTTV_VBISIZE _IOR('v' , BASE_VIDIOCPRIVATE+8, int) #define BTTV_UNKNOWN 0x00 #define BTTV_MIRO 0x01 @@ -212,6 +255,32 @@ #define BTTV_PIXVIEWPLAYTV 0x10 #define BTTV_WINVIEW_601 0x11 #define BTTV_AVEC_INTERCAP 0x12 +#define BTTV_LIFE_FLYKIT 0x13 +#define BTTV_CEI_RAFFLES 0x14 +#define BTTV_CONFERENCETV 0x15 +#define BTTV_PHOEBE_TVMAS 0x16 +#define BTTV_MODTEC_205 0x17 +#define BTTV_MAGICTVIEW061 0x18 +#define BTTV_VOBIS_BOOSTAR 0x19 +#define BTTV_HAUPPAUG_WCAM 0x1a +#define BTTV_MAXI 0x1b +#define BTTV_TERRATV 0x1c +#define BTTV_PXC200 0x1d +#define BTTV_FLYVIDEO_98 0x1e +#define BTTV_IPROTV 0x1f +#define BTTV_INTEL_C_S_PCI 0x20 +#define BTTV_TERRATVALUE 0x21 +#define BTTV_WINFAST2000 0x22 +#define BTTV_CHRONOS_VS2 0x23 +#define BTTV_TYPHOON_TVIEW 0x24 +#define BTTV_PXELVWPLTVPRO 0x25 +#define BTTV_MAGICTVIEW063 0x26 +#define BTTV_PINNACLERAVE 0x27 +#define BTTV_STB2 0x28 + +#define PLL_NONE 0 +#define PLL_28 1 +#define PLL_35 2 #define AUDIO_TUNER 0x00 #define AUDIO_RADIO 0x01 @@ -223,61 +292,27 @@ #define AUDIO_UNMUTE 0x81 #define TDA9850 0x01 -#define TDA8425 0x02 -#define TDA9840 0x03 +#define TDA9840 0x02 +#define TDA8425 0x03 #define TEA6300 0x04 -#define TEA6320 0x05 #define I2C_TSA5522 0xc2 -#define I2C_TDA9840 0x84 -#define I2C_TDA9850 0xb6 +#define I2C_TDA7432 0x8a #define I2C_TDA8425 0x82 +#define I2C_TDA9840 0x84 +#define I2C_TDA9850 0xb6 +#define I2C_TDA9875 0xb0 #define I2C_HAUPEE 0xa0 #define I2C_STBEE 0xae -#define I2C_VHX 0xc0 -#define I2C_TEA6300 0x80 /* same as TEA6320 */ - -#define TDA9840_SW 0x00 -#define TDA9840_LVADJ 0x02 -#define TDA9840_STADJ 0x03 -#define TDA9840_TEST 0x04 - -#define TDA9850_CON1 0x04 -#define TDA9850_CON2 0x05 -#define TDA9850_CON3 0x06 -#define TDA9850_CON4 0x07 -#define TDA9850_ALI1 0x08 -#define TDA9850_ALI2 0x09 -#define TDA9850_ALI3 0x0a - -#define TDA8425_VL 0x00 -#define TDA8425_VR 0x01 -#define TDA8425_BA 0x02 -#define TDA8425_TR 0x03 -#define TDA8425_S1 0x08 - -#define TEA6300_VL 0x00 /* volume control left */ -#define TEA6300_VR 0x01 /* volume control right */ -#define TEA6300_BA 0x02 /* bass control */ -#define TEA6300_TR 0x03 /* treble control */ -#define TEA6300_FA 0x04 /* fader control */ -#define TEA6300_SW 0x05 /* mute and source switch */ - - -#define TEA6320_V 0x00 -#define TEA6320_FFR 0x01 /* volume front right */ -#define TEA6320_FFL 0x02 /* volume front left */ -#define TEA6320_FRR 0x03 /* volume rear right */ -#define TEA6320_FRL 0x04 /* volume rear left */ -#define TEA6320_BA 0x05 /* bass */ -#define TEA6320_TR 0x06 /* treble */ -#define TEA6320_S 0x07 /* switch register */ - /* values for those registers: */ -#define TEA6320_S_SA 0x01 /* stereo A input */ -#define TEA6320_S_SB 0x02 /* stereo B */ -#define TEA6320_S_SC 0x04 /* stereo C */ -#define TEA6320_S_GMU 0x80 /* general mute */ - +#define I2C_VHX 0xc0 +#define I2C_MSP3400 0x80 +#define I2C_TEA6300 0x80 +#define I2C_DPL3518 0x84 + +#define TDA9840_SW 0x00 +#define TDA9840_LVADJ 0x02 +#define TDA9840_STADJ 0x03 +#define TDA9840_TEST 0x04 #define PT2254_L_CHANEL 0x10 #define PT2254_R_CHANEL 0x08 @@ -288,3 +323,9 @@ #define WINVIEW_PT2254_STROBE 0x80 #endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN vanilla-2.2.15pre17/drivers/char/buz.c bttv-2.2.15pre17/drivers/char/buz.c --- vanilla-2.2.15pre17/drivers/char/buz.c Mon May 1 23:01:14 2000 +++ bttv-2.2.15pre17/drivers/char/buz.c Mon May 1 23:02:30 2000 @@ -54,7 +54,7 @@ #include #include -#include +#include #include "buz.h" #include #include diff -urN vanilla-2.2.15pre17/drivers/char/i2c-old.c bttv-2.2.15pre17/drivers/char/i2c-old.c --- vanilla-2.2.15pre17/drivers/char/i2c-old.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/char/i2c-old.c Mon May 1 23:02:30 2000 @@ -0,0 +1,460 @@ +/* + * Generic i2c interface for linux + * + * (c) 1998 Gerd Knorr + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REGPRINT(x) if (verbose) (x) +#define I2C_DEBUG(x) if (i2c_debug) (x) + +static int scan = 0; +static int verbose = 0; +static int i2c_debug = 0; + +#if LINUX_VERSION_CODE >= 0x020117 +MODULE_PARM(scan,"i"); +MODULE_PARM(verbose,"i"); +MODULE_PARM(i2c_debug,"i"); +#endif + +/* ----------------------------------------------------------------------- */ + +static struct i2c_bus *busses[I2C_BUS_MAX]; +static struct i2c_driver *drivers[I2C_DRIVER_MAX]; +static int bus_count = 0, driver_count = 0; + +#ifdef CONFIG_VIDEO_BT848 +extern int i2c_tuner_init(void); +extern int msp3400c_init(void); +#endif +#ifdef CONFIG_VIDEO_BUZ +extern int saa7111_init(void); +extern int saa7185_init(void); +#endif +#ifdef CONFIG_VIDEO_LML33 +extern int bt819_init(void); +extern int bt856_init(void); +#endif + +int i2c_init(void) +{ + printk(KERN_INFO "i2c: initialized%s\n", + scan ? " (i2c bus scan enabled)" : ""); + /* anything to do here ? */ +#ifdef CONFIG_VIDEO_BT848 + i2c_tuner_init(); +#endif +#ifdef CONFIG_VIDEO_MSP3400 + msp3400c_init(); +#endif +#ifdef CONFIG_VIDEO_BUZ + saa7111_init(); + saa7185_init(); +#endif +#ifdef CONFIG_VIDEO_LML33 + bt819_init(); + bt856_init(); +#endif + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static void i2c_attach_device(struct i2c_bus *bus, struct i2c_driver *driver) +{ + struct i2c_device *device; + int i,j,ack=1; + unsigned char addr; + LOCK_FLAGS; + + /* probe for device */ + LOCK_I2C_BUS(bus); + for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) + { + i2c_start(bus); + ack = i2c_sendbyte(bus,addr,0); + i2c_stop(bus); + if (!ack) + break; + } + UNLOCK_I2C_BUS(bus); + if (ack) + return; + + /* got answer */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (NULL == driver->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + return; + + for (j = 0; j < I2C_DEVICE_MAX; j++) + if (NULL == bus->devices[j]) + break; + if (I2C_DEVICE_MAX == j) + return; + + if (NULL == (device = kmalloc(sizeof(struct i2c_device),GFP_KERNEL))) + return; + device->bus = bus; + device->driver = driver; + device->addr = addr; + + /* Attach */ + + if (driver->attach(device)!=0) + { + kfree(device); + return; + } + driver->devices[i] = device; + driver->devcount++; + bus->devices[j] = device; + bus->devcount++; + + if (bus->attach_inform) + bus->attach_inform(bus,driver->id); + REGPRINT(printk("i2c: device attached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,addr,bus->name,driver->name)); +} + +static void i2c_detach_device(struct i2c_device *device) +{ + int i; + + if (device->bus->detach_inform) + device->bus->detach_inform(device->bus,device->driver->id); + device->driver->detach(device); + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (device == device->driver->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + { + printk(KERN_WARNING "i2c: detach_device #1: device not found: %s\n", + device->name); + return; + } + device->driver->devices[i] = NULL; + device->driver->devcount--; + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (device == device->bus->devices[i]) + break; + if (I2C_DEVICE_MAX == i) + { + printk(KERN_WARNING "i2c: detach_device #2: device not found: %s\n", + device->name); + return; + } + device->bus->devices[i] = NULL; + device->bus->devcount--; + + REGPRINT(printk("i2c: device detached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,device->addr,device->bus->name,device->driver->name)); + kfree(device); +} + +/* ----------------------------------------------------------------------- */ + +int i2c_register_bus(struct i2c_bus *bus) +{ + int i,ack; + LOCK_FLAGS; + + memset(bus->devices,0,sizeof(bus->devices)); + bus->devcount = 0; + + for (i = 0; i < I2C_BUS_MAX; i++) + if (NULL == busses[i]) + break; + if (I2C_BUS_MAX == i) + return -ENOMEM; + + busses[i] = bus; + bus_count++; + REGPRINT(printk("i2c: bus registered: %s\n",bus->name)); + + MOD_INC_USE_COUNT; + + if (scan) + { + /* scan whole i2c bus */ + LOCK_I2C_BUS(bus); + for (i = 0; i < 256; i+=2) + { + i2c_start(bus); + ack = i2c_sendbyte(bus,i,0); + i2c_stop(bus); + if (!ack) + { + printk(KERN_INFO "i2c: scanning bus %s: found device at addr=0x%02x\n", + bus->name,i); + } + } + UNLOCK_I2C_BUS(bus); + } + + /* probe available drivers */ + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (drivers[i]) + i2c_attach_device(bus,drivers[i]); + return 0; +} + +int i2c_unregister_bus(struct i2c_bus *bus) +{ + int i; + + /* detach devices */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (bus->devices[i]) + i2c_detach_device(bus->devices[i]); + + for (i = 0; i < I2C_BUS_MAX; i++) + if (bus == busses[i]) + break; + if (I2C_BUS_MAX == i) + { + printk(KERN_WARNING "i2c: unregister_bus #1: bus not found: %s\n", + bus->name); + return -ENODEV; + } + + MOD_DEC_USE_COUNT; + + busses[i] = NULL; + bus_count--; + REGPRINT(printk("i2c: bus unregistered: %s\n",bus->name)); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +int i2c_register_driver(struct i2c_driver *driver) +{ + int i; + + memset(driver->devices,0,sizeof(driver->devices)); + driver->devcount = 0; + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) + return -ENOMEM; + + drivers[i] = driver; + driver_count++; + + MOD_INC_USE_COUNT; + + REGPRINT(printk("i2c: driver registered: %s\n",driver->name)); + + /* Probe available busses */ + for (i = 0; i < I2C_BUS_MAX; i++) + if (busses[i]) + i2c_attach_device(busses[i],driver); + + return 0; +} + +int i2c_unregister_driver(struct i2c_driver *driver) +{ + int i; + + /* detach devices */ + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (driver->devices[i]) + i2c_detach_device(driver->devices[i]); + + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) + { + printk(KERN_WARNING "i2c: unregister_driver: driver not found: %s\n", + driver->name); + return -ENODEV; + } + + MOD_DEC_USE_COUNT; + + drivers[i] = NULL; + driver_count--; + REGPRINT(printk("i2c: driver unregistered: %s\n",driver->name)); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +int i2c_control_device(struct i2c_bus *bus, int id, + unsigned int cmd, void *arg) +{ + int i; + + for (i = 0; i < I2C_DEVICE_MAX; i++) + if (bus->devices[i] && bus->devices[i]->driver->id == id) + break; + if (i == I2C_DEVICE_MAX) + return -ENODEV; + if (NULL == bus->devices[i]->driver->command) + return -ENODEV; + return bus->devices[i]->driver->command(bus->devices[i],cmd,arg); +} + +/* ----------------------------------------------------------------------- */ + +#define I2C_SET(bus,ctrl,data) (bus->i2c_setlines(bus,ctrl,data)) +#define I2C_GET(bus) (bus->i2c_getdataline(bus)) + +void i2c_start(struct i2c_bus *bus) +{ + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + I2C_SET(bus,1,0); + I2C_SET(bus,0,0); + I2C_DEBUG(printk("%s: < ",bus->name)); +} + +void i2c_stop(struct i2c_bus *bus) +{ + I2C_SET(bus,0,0); + I2C_SET(bus,1,0); + I2C_SET(bus,1,1); + I2C_DEBUG(printk(">\n")); +} + +void i2c_one(struct i2c_bus *bus) +{ + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + I2C_SET(bus,0,1); +} + +void i2c_zero(struct i2c_bus *bus) +{ + I2C_SET(bus,0,0); + I2C_SET(bus,1,0); + I2C_SET(bus,0,0); +} + +int i2c_ack(struct i2c_bus *bus) +{ + int ack; + + I2C_SET(bus,0,1); + I2C_SET(bus,1,1); + ack = I2C_GET(bus); + I2C_SET(bus,0,1); + return ack; +} + +int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack) +{ + int i, ack; + + I2C_SET(bus,0,0); + for (i=7; i>=0; i--) + (data&(1<=0; i--) + { + I2C_SET(bus,1,1); + if (I2C_GET(bus)) + data |= (1<i2c_read) + return bus->i2c_read(bus, addr); + + i2c_start(bus); + i2c_sendbyte(bus,addr,0); + ret = i2c_readbyte(bus,1); + i2c_stop(bus); + return ret; +} + +int i2c_write(struct i2c_bus *bus, unsigned char addr, + unsigned char data1, unsigned char data2, int both) +{ + int ack; + + if (bus->i2c_write) + return bus->i2c_write(bus, addr, data1, data2, both); + + i2c_start(bus); + i2c_sendbyte(bus,addr,0); + ack = i2c_sendbyte(bus,data1,0); + if (both) + ack = i2c_sendbyte(bus,data2,0); + i2c_stop(bus); + return ack ? -1 : 0 ; +} + +/* ----------------------------------------------------------------------- */ + +#ifdef MODULE + +#if LINUX_VERSION_CODE >= 0x020100 +EXPORT_SYMBOL(i2c_register_bus); +EXPORT_SYMBOL(i2c_unregister_bus); +EXPORT_SYMBOL(i2c_register_driver); +EXPORT_SYMBOL(i2c_unregister_driver); +EXPORT_SYMBOL(i2c_control_device); +EXPORT_SYMBOL(i2c_start); +EXPORT_SYMBOL(i2c_stop); +EXPORT_SYMBOL(i2c_one); +EXPORT_SYMBOL(i2c_zero); +EXPORT_SYMBOL(i2c_ack); +EXPORT_SYMBOL(i2c_sendbyte); +EXPORT_SYMBOL(i2c_readbyte); +EXPORT_SYMBOL(i2c_read); +EXPORT_SYMBOL(i2c_write); +#endif + +int init_module(void) +{ + return i2c_init(); +} + +void cleanup_module(void) +{ +} +#endif diff -urN vanilla-2.2.15pre17/drivers/char/i2c.c bttv-2.2.15pre17/drivers/char/i2c.c --- vanilla-2.2.15pre17/drivers/char/i2c.c Mon May 1 22:59:57 2000 +++ bttv-2.2.15pre17/drivers/char/i2c.c Mon May 1 23:02:30 2000 @@ -1,460 +0,0 @@ -/* - * Generic i2c interface for linux - * - * (c) 1998 Gerd Knorr - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define REGPRINT(x) if (verbose) (x) -#define I2C_DEBUG(x) if (i2c_debug) (x) - -static int scan = 0; -static int verbose = 0; -static int i2c_debug = 0; - -#if LINUX_VERSION_CODE >= 0x020117 -MODULE_PARM(scan,"i"); -MODULE_PARM(verbose,"i"); -MODULE_PARM(i2c_debug,"i"); -#endif - -/* ----------------------------------------------------------------------- */ - -static struct i2c_bus *busses[I2C_BUS_MAX]; -static struct i2c_driver *drivers[I2C_DRIVER_MAX]; -static int bus_count = 0, driver_count = 0; - -#ifdef CONFIG_VIDEO_BT848 -extern int i2c_tuner_init(void); -extern int msp3400c_init(void); -#endif -#ifdef CONFIG_VIDEO_BUZ -extern int saa7111_init(void); -extern int saa7185_init(void); -#endif -#ifdef CONFIG_VIDEO_LML33 -extern int bt819_init(void); -extern int bt856_init(void); -#endif - -int i2c_init(void) -{ - printk(KERN_INFO "i2c: initialized%s\n", - scan ? " (i2c bus scan enabled)" : ""); - /* anything to do here ? */ -#ifdef CONFIG_VIDEO_BT848 - i2c_tuner_init(); -#endif -#ifdef CONFIG_VIDEO_MSP3400 - msp3400c_init(); -#endif -#ifdef CONFIG_VIDEO_BUZ - saa7111_init(); - saa7185_init(); -#endif -#ifdef CONFIG_VIDEO_LML33 - bt819_init(); - bt856_init(); -#endif - return 0; -} - -/* ----------------------------------------------------------------------- */ - -static void i2c_attach_device(struct i2c_bus *bus, struct i2c_driver *driver) -{ - struct i2c_device *device; - int i,j,ack=1; - unsigned char addr; - LOCK_FLAGS; - - /* probe for device */ - LOCK_I2C_BUS(bus); - for (addr = driver->addr_l; addr <= driver->addr_h; addr += 2) - { - i2c_start(bus); - ack = i2c_sendbyte(bus,addr,0); - i2c_stop(bus); - if (!ack) - break; - } - UNLOCK_I2C_BUS(bus); - if (ack) - return; - - /* got answer */ - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (NULL == driver->devices[i]) - break; - if (I2C_DEVICE_MAX == i) - return; - - for (j = 0; j < I2C_DEVICE_MAX; j++) - if (NULL == bus->devices[j]) - break; - if (I2C_DEVICE_MAX == j) - return; - - if (NULL == (device = kmalloc(sizeof(struct i2c_device),GFP_KERNEL))) - return; - device->bus = bus; - device->driver = driver; - device->addr = addr; - - /* Attach */ - - if (driver->attach(device)!=0) - { - kfree(device); - return; - } - driver->devices[i] = device; - driver->devcount++; - bus->devices[j] = device; - bus->devcount++; - - if (bus->attach_inform) - bus->attach_inform(bus,driver->id); - REGPRINT(printk("i2c: device attached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,addr,bus->name,driver->name)); -} - -static void i2c_detach_device(struct i2c_device *device) -{ - int i; - - if (device->bus->detach_inform) - device->bus->detach_inform(device->bus,device->driver->id); - device->driver->detach(device); - - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (device == device->driver->devices[i]) - break; - if (I2C_DEVICE_MAX == i) - { - printk(KERN_WARNING "i2c: detach_device #1: device not found: %s\n", - device->name); - return; - } - device->driver->devices[i] = NULL; - device->driver->devcount--; - - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (device == device->bus->devices[i]) - break; - if (I2C_DEVICE_MAX == i) - { - printk(KERN_WARNING "i2c: detach_device #2: device not found: %s\n", - device->name); - return; - } - device->bus->devices[i] = NULL; - device->bus->devcount--; - - REGPRINT(printk("i2c: device detached: %s (addr=0x%02x, bus=%s, driver=%s)\n",device->name,device->addr,device->bus->name,device->driver->name)); - kfree(device); -} - -/* ----------------------------------------------------------------------- */ - -int i2c_register_bus(struct i2c_bus *bus) -{ - int i,ack; - LOCK_FLAGS; - - memset(bus->devices,0,sizeof(bus->devices)); - bus->devcount = 0; - - for (i = 0; i < I2C_BUS_MAX; i++) - if (NULL == busses[i]) - break; - if (I2C_BUS_MAX == i) - return -ENOMEM; - - busses[i] = bus; - bus_count++; - REGPRINT(printk("i2c: bus registered: %s\n",bus->name)); - - MOD_INC_USE_COUNT; - - if (scan) - { - /* scan whole i2c bus */ - LOCK_I2C_BUS(bus); - for (i = 0; i < 256; i+=2) - { - i2c_start(bus); - ack = i2c_sendbyte(bus,i,0); - i2c_stop(bus); - if (!ack) - { - printk(KERN_INFO "i2c: scanning bus %s: found device at addr=0x%02x\n", - bus->name,i); - } - } - UNLOCK_I2C_BUS(bus); - } - - /* probe available drivers */ - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (drivers[i]) - i2c_attach_device(bus,drivers[i]); - return 0; -} - -int i2c_unregister_bus(struct i2c_bus *bus) -{ - int i; - - /* detach devices */ - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (bus->devices[i]) - i2c_detach_device(bus->devices[i]); - - for (i = 0; i < I2C_BUS_MAX; i++) - if (bus == busses[i]) - break; - if (I2C_BUS_MAX == i) - { - printk(KERN_WARNING "i2c: unregister_bus #1: bus not found: %s\n", - bus->name); - return -ENODEV; - } - - MOD_DEC_USE_COUNT; - - busses[i] = NULL; - bus_count--; - REGPRINT(printk("i2c: bus unregistered: %s\n",bus->name)); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -int i2c_register_driver(struct i2c_driver *driver) -{ - int i; - - memset(driver->devices,0,sizeof(driver->devices)); - driver->devcount = 0; - - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (NULL == drivers[i]) - break; - if (I2C_DRIVER_MAX == i) - return -ENOMEM; - - drivers[i] = driver; - driver_count++; - - MOD_INC_USE_COUNT; - - REGPRINT(printk("i2c: driver registered: %s\n",driver->name)); - - /* Probe available busses */ - for (i = 0; i < I2C_BUS_MAX; i++) - if (busses[i]) - i2c_attach_device(busses[i],driver); - - return 0; -} - -int i2c_unregister_driver(struct i2c_driver *driver) -{ - int i; - - /* detach devices */ - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (driver->devices[i]) - i2c_detach_device(driver->devices[i]); - - for (i = 0; i < I2C_DRIVER_MAX; i++) - if (driver == drivers[i]) - break; - if (I2C_DRIVER_MAX == i) - { - printk(KERN_WARNING "i2c: unregister_driver: driver not found: %s\n", - driver->name); - return -ENODEV; - } - - MOD_DEC_USE_COUNT; - - drivers[i] = NULL; - driver_count--; - REGPRINT(printk("i2c: driver unregistered: %s\n",driver->name)); - - return 0; -} - -/* ----------------------------------------------------------------------- */ - -int i2c_control_device(struct i2c_bus *bus, int id, - unsigned int cmd, void *arg) -{ - int i; - - for (i = 0; i < I2C_DEVICE_MAX; i++) - if (bus->devices[i] && bus->devices[i]->driver->id == id) - break; - if (i == I2C_DEVICE_MAX) - return -ENODEV; - if (NULL == bus->devices[i]->driver->command) - return -ENODEV; - return bus->devices[i]->driver->command(bus->devices[i],cmd,arg); -} - -/* ----------------------------------------------------------------------- */ - -#define I2C_SET(bus,ctrl,data) (bus->i2c_setlines(bus,ctrl,data)) -#define I2C_GET(bus) (bus->i2c_getdataline(bus)) - -void i2c_start(struct i2c_bus *bus) -{ - I2C_SET(bus,0,1); - I2C_SET(bus,1,1); - I2C_SET(bus,1,0); - I2C_SET(bus,0,0); - I2C_DEBUG(printk("%s: < ",bus->name)); -} - -void i2c_stop(struct i2c_bus *bus) -{ - I2C_SET(bus,0,0); - I2C_SET(bus,1,0); - I2C_SET(bus,1,1); - I2C_DEBUG(printk(">\n")); -} - -void i2c_one(struct i2c_bus *bus) -{ - I2C_SET(bus,0,1); - I2C_SET(bus,1,1); - I2C_SET(bus,0,1); -} - -void i2c_zero(struct i2c_bus *bus) -{ - I2C_SET(bus,0,0); - I2C_SET(bus,1,0); - I2C_SET(bus,0,0); -} - -int i2c_ack(struct i2c_bus *bus) -{ - int ack; - - I2C_SET(bus,0,1); - I2C_SET(bus,1,1); - ack = I2C_GET(bus); - I2C_SET(bus,0,1); - return ack; -} - -int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack) -{ - int i, ack; - - I2C_SET(bus,0,0); - for (i=7; i>=0; i--) - (data&(1<=0; i--) - { - I2C_SET(bus,1,1); - if (I2C_GET(bus)) - data |= (1<i2c_read) - return bus->i2c_read(bus, addr); - - i2c_start(bus); - i2c_sendbyte(bus,addr,0); - ret = i2c_readbyte(bus,1); - i2c_stop(bus); - return ret; -} - -int i2c_write(struct i2c_bus *bus, unsigned char addr, - unsigned char data1, unsigned char data2, int both) -{ - int ack; - - if (bus->i2c_write) - return bus->i2c_write(bus, addr, data1, data2, both); - - i2c_start(bus); - i2c_sendbyte(bus,addr,0); - ack = i2c_sendbyte(bus,data1,0); - if (both) - ack = i2c_sendbyte(bus,data2,0); - i2c_stop(bus); - return ack ? -1 : 0 ; -} - -/* ----------------------------------------------------------------------- */ - -#ifdef MODULE - -#if LINUX_VERSION_CODE >= 0x020100 -EXPORT_SYMBOL(i2c_register_bus); -EXPORT_SYMBOL(i2c_unregister_bus); -EXPORT_SYMBOL(i2c_register_driver); -EXPORT_SYMBOL(i2c_unregister_driver); -EXPORT_SYMBOL(i2c_control_device); -EXPORT_SYMBOL(i2c_start); -EXPORT_SYMBOL(i2c_stop); -EXPORT_SYMBOL(i2c_one); -EXPORT_SYMBOL(i2c_zero); -EXPORT_SYMBOL(i2c_ack); -EXPORT_SYMBOL(i2c_sendbyte); -EXPORT_SYMBOL(i2c_readbyte); -EXPORT_SYMBOL(i2c_read); -EXPORT_SYMBOL(i2c_write); -#endif - -int init_module(void) -{ - return i2c_init(); -} - -void cleanup_module(void) -{ -} -#endif diff -urN vanilla-2.2.15pre17/drivers/char/mem.c bttv-2.2.15pre17/drivers/char/mem.c --- vanilla-2.2.15pre17/drivers/char/mem.c Mon May 1 22:59:57 2000 +++ bttv-2.2.15pre17/drivers/char/mem.c Mon May 1 23:02:30 2000 @@ -15,13 +15,15 @@ #include #include #include -#include #include #include #include #include +#ifdef CONFIG_I2C +extern int i2c_init_all(void); +#endif #ifdef CONFIG_SOUND void soundcore_init(void); #ifdef CONFIG_SOUND_OSS @@ -630,6 +632,9 @@ #ifdef CONFIG_USB_OHCI_HCD ohci_hcd_init(); #endif +#endif +#ifdef CONFIG_I2C + i2c_init_all(); #endif #if defined (CONFIG_FB) fbmem_init(); diff -urN vanilla-2.2.15pre17/drivers/char/msp3400.c bttv-2.2.15pre17/drivers/char/msp3400.c --- vanilla-2.2.15pre17/drivers/char/msp3400.c Mon May 1 22:59:52 2000 +++ bttv-2.2.15pre17/drivers/char/msp3400.c Mon May 1 23:03:21 2000 @@ -6,7 +6,8 @@ * what works and what doesn't: * * AM-Mono - * probably doesn't (untested) + * Support for Hauppauge cards added (decoding handled by tuner) added by + * Frederic Crozat * * FM-Mono * should work. The stereo modes are backward compatible to FM-mono, @@ -19,7 +20,7 @@ * should work, no autodetect (i.e. default is mono, but you can * switch to stereo -- untested) * - * NICAM (B/G, used in UK, Scandinavia and Spain) + * NICAM (B/G, L , used in UK, Scandinavia, Spain and France) * should work, with autodetect. Support for NICAM was added by * Pekka Pietikainen * @@ -33,8 +34,8 @@ * */ +#include #include -#include #include #include #include @@ -42,36 +43,56 @@ #include #include #include -#ifdef __SMP__ +#include +#include +#include + +#ifdef CONFIG_SMP #include #include #endif - /* kernel_thread */ #define __KERNEL_SYSCALLS__ #include -#include -#include - -#include "msp3400.h" +#include "audiochip.h" +#define DECLARE_MUTEX(foo) struct semaphore foo = MUTEX +#define DECLARE_MUTEX_LOCKED(foo) struct semaphore foo = MUTEX_LOCKED +#define WAIT_QUEUE struct wait_queue* +#define init_waitqueue_head(wq) *(wq) = NULL; /* sound mixer stuff */ -#include - -#if LINUX_VERSION_CODE > 0x020140 /* need modular sound driver */ -# if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE) -# define REGISTER_MIXER 1 -# endif +#if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE) +# define REGISTER_MIXER 1 #endif +/* Addresses to scan */ +static unsigned short normal_i2c[] = {I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {0x40,0x44,I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; -static int debug = 0; /* insmod parameter */ +/* insmod parameters */ +static int debug = 0; /* debug output */ +static int once = 0; /* no continous stereo monitoring */ +static int amsound = 0; /* hard-wire AM sound at 6.5 Hz (france), + the autoscan seems work well only with FM... */ +static int simple = -1; /* use short programming (>= msp3410 only) */ +static int dolby = 0; +static int mixer = -1; struct msp3400c { - struct i2c_bus *bus; - + int simple; int nicam; int mode; int norm; @@ -84,36 +105,34 @@ /* thread */ struct task_struct *thread; - struct wait_queue *wq; + WAIT_QUEUE wq; + struct semaphore *notify; int active,restart,rmmod; int watch_stereo; struct timer_list wake_stereo; + + /* mixer */ + int mixer_modcnt; + int mixer_num; }; +#define MSP3400_MAX 4 +static struct i2c_client *msps[MSP3400_MAX]; + #define VIDEO_MODE_RADIO 16 /* norm magic for radio mode */ /* ---------------------------------------------------------------------- */ #define dprintk if (debug) printk -#if LINUX_VERSION_CODE < 0x020100 -/* 2.0.x */ -#define signal_pending(current) (current->signal & ~current->blocked) -#define sigfillset(set) -#define mdelay(x) udelay(1000*x) -#else +MODULE_PARM(once,"i"); MODULE_PARM(debug,"i"); -#endif - -#if LINUX_VERSION_CODE < 0x02017f -void schedule_timeout(int j) -{ - current->timeout = jiffies + j; - schedule(); -} -#endif +MODULE_PARM(simple,"i"); +MODULE_PARM(amsound,"i"); +MODULE_PARM(dolby,"i"); +MODULE_PARM(mixer,"i"); /* ---------------------------------------------------------------------- */ @@ -124,103 +143,78 @@ /* ----------------------------------------------------------------------- */ /* functions for talking to the MSP3400C Sound processor */ -static int msp3400c_reset(struct i2c_bus *bus) +static int msp3400c_reset(struct i2c_client *client) { - int ret = 0; - - mdelay(2); - i2c_start(bus); - i2c_sendbyte(bus, I2C_MSP3400C,2000); - i2c_sendbyte(bus, 0x00,0); - i2c_sendbyte(bus, 0x80,0); - i2c_sendbyte(bus, 0x00,0); - i2c_stop(bus); - mdelay(2); - i2c_start(bus); - if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || - 0 != i2c_sendbyte(bus, 0x00,0) || - 0 != i2c_sendbyte(bus, 0x00,0) || - 0 != i2c_sendbyte(bus, 0x00,0)) { - ret = -1; + static char reset_off[3] = { 0x00, 0x80, 0x00 }; + static char reset_on[3] = { 0x00, 0x00, 0x00 }; + + i2c_master_send(client,reset_off,3); /* XXX ignore errors here */ + if (3 != i2c_master_send(client,reset_on, 3)) { printk(KERN_ERR "msp3400: chip reset failed, penguin on i2c bus?\n"); + return -1; } - i2c_stop(bus); - mdelay(2); - return ret; + return 0; } static int -msp3400c_read(struct i2c_bus *bus, int dev, int addr) +msp3400c_read(struct i2c_client *client, int dev, int addr) { - int err,ret; - short val=0; + int err; + + unsigned char write[3]; + unsigned char read[2]; + struct i2c_msg msgs[2] = { + { client->addr, 0, 3, write }, + { client->addr, I2C_M_RD, 2, read } + }; + write[0] = dev+1; + write[1] = addr >> 8; + write[2] = addr & 0xff; for (err = 0; err < 3;) { - ret = 0; - i2c_start(bus); - if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || - 0 != i2c_sendbyte(bus, dev+1, 0) || - 0 != i2c_sendbyte(bus, addr >> 8, 0) || - 0 != i2c_sendbyte(bus, addr & 0xff, 0)) { - ret = -1; - } else { - i2c_start(bus); - if (0 != i2c_sendbyte(bus, I2C_MSP3400C+1,2000)) { - ret = -1; - } else { - val |= (int)i2c_readbyte(bus,0) << 8; - val |= (int)i2c_readbyte(bus,1); - } - } - i2c_stop(bus); - if (0 == ret) + if (2 == i2c_transfer(client->adapter,msgs,2)) break; - - /* some I/O error */ err++; printk(KERN_WARNING "msp34xx: I/O error #%d (read 0x%02x/0x%02x)\n", err, dev, addr); current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/10); } - if (-1 == ret) { + if (3 == err) { printk(KERN_WARNING "msp34xx: giving up, reseting chip. Sound will go off, sorry folks :-|\n"); - msp3400c_reset(bus); + msp3400c_reset(client); + return -1; } - return val; + return read[0] << 8 | read[1]; } static int -msp3400c_write(struct i2c_bus *bus, int dev, int addr, int val) +msp3400c_write(struct i2c_client *client, int dev, int addr, int val) { - int ret,err; - + int err; + unsigned char buffer[5]; + + buffer[0] = dev; + buffer[1] = addr >> 8; + buffer[2] = addr & 0xff; + buffer[3] = val >> 8; + buffer[4] = val & 0xff; + for (err = 0; err < 3;) { - ret = 0; - i2c_start(bus); - if (0 != i2c_sendbyte(bus, I2C_MSP3400C,2000) || - 0 != i2c_sendbyte(bus, dev, 0) || - 0 != i2c_sendbyte(bus, addr >> 8, 0) || - 0 != i2c_sendbyte(bus, addr & 0xff, 0) || - 0 != i2c_sendbyte(bus, val >> 8, 0) || - 0 != i2c_sendbyte(bus, val & 0xff, 0)) - ret = -1; - i2c_stop(bus); - if (0 == ret) + if (5 == i2c_master_send(client, buffer, 5)) break; - - /* some I/O error */ err++; printk(KERN_WARNING "msp34xx: I/O error #%d (write 0x%02x/0x%02x)\n", err, dev, addr); current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/10); } - if (-1 == ret) { + if (3 == err) { printk(KERN_WARNING "msp34xx: giving up, reseting chip. Sound will go off, sorry folks :-|\n"); - msp3400c_reset(bus); + msp3400c_reset(client); + return -1; } - return ret; + return 0; } /* ------------------------------------------------------------------------ */ @@ -235,6 +229,8 @@ #define MSP_MODE_FM_SAT 4 #define MSP_MODE_FM_NICAM1 5 #define MSP_MODE_FM_NICAM2 6 +#define MSP_MODE_AM_NICAM 7 +#define MSP_MODE_BTSC 8 static struct MSP_INIT_DATA_DEM { int fir1[6]; @@ -271,15 +267,20 @@ MSP_CARRIER(6.5), MSP_CARRIER(6.5), 0x00c6, 0x0480, 0x0000, 0x3000}, - /* NICAM B/G, D/K */ + /* NICAM/FM -- B/G (5.5/5.85), D/K (6.5/5.85) */ { { -2, -8, -10, 10, 50, 86 }, { 3, 18, 27, 48, 66, 72 }, MSP_CARRIER(5.5), MSP_CARRIER(5.5), 0x00d0, 0x0040, 0x0120, 0x3000}, - /* NICAM I */ + /* NICAM/FM -- I (6.0/6.552) */ { { 2, 4, -6, -4, 40, 94 }, { 3, 18, 27, 48, 66, 72 }, MSP_CARRIER(6.0), MSP_CARRIER(6.0), 0x00d0, 0x0040, 0x0120, 0x3000}, + + /* NICAM/AM -- L (6.5/5.85) */ + { { -2, -8, -10, 10, 50, 86 }, { -4, -12, -9, 23, 79, 126 }, + MSP_CARRIER(6.5), MSP_CARRIER(6.5), + 0x00c6, 0x0140, 0x0120, 0x7c03}, }; struct CARRIER_DETECT { @@ -303,7 +304,7 @@ static struct CARRIER_DETECT carrier_detect_65[] = { /* PAL SAT / SECAM */ - { MSP_CARRIER(5.85), "5.85 PAL D/K NICAM" }, + { MSP_CARRIER(5.85), "5.85 PAL D/K + SECAM NICAM" }, { MSP_CARRIER(6.2578125), "6.25 PAL D/K1 FM-stereo" }, { MSP_CARRIER(6.7421875), "6.74 PAL D/K2 FM-stereo" }, { MSP_CARRIER(7.02), "7.02 PAL SAT FM-stereo s/b" }, @@ -315,16 +316,16 @@ /* ------------------------------------------------------------------------ */ -static void msp3400c_setcarrier(struct i2c_bus *bus, int cdo1, int cdo2) +static void msp3400c_setcarrier(struct i2c_client *client, int cdo1, int cdo2) { - msp3400c_write(bus,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff); - msp3400c_write(bus,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12); - msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff); - msp3400c_write(bus,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); - msp3400c_write(bus,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0093, cdo1 & 0xfff); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x009b, cdo1 >> 12); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x00a3, cdo2 & 0xfff); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x00ab, cdo2 >> 12); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ } -static void msp3400c_setvolume(struct i2c_bus *bus, int left, int right) +static void msp3400c_setvolume(struct i2c_client *client, int left, int right) { int vol,val,balance; @@ -334,94 +335,106 @@ if (vol > 0) balance = ((right-left) * 127) / vol; - dprintk("msp3400: setvolume: %d:%d 0x%02x 0x%02x\n", + dprintk("msp34xx: setvolume: %d:%d 0x%02x 0x%02x\n", left,right,val>>8,balance); - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ /* scart - on/off only */ - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0001, balance << 8); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, balance << 8); } -static void msp3400c_setbass(struct i2c_bus *bus, int bass) +static void msp3400c_setbass(struct i2c_client *client, int bass) { int val = ((bass-32768) * 0x60 / 65535) << 8; - dprintk("msp3400: setbass: %d 0x%02x\n",bass, val>>8); - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ + dprintk("msp34xx: setbass: %d 0x%02x\n",bass, val>>8); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0002, val); /* loudspeaker */ } -static void msp3400c_settreble(struct i2c_bus *bus, int treble) +static void msp3400c_settreble(struct i2c_client *client, int treble) { int val = ((treble-32768) * 0x60 / 65535) << 8; - dprintk("msp3400: settreble: %d 0x%02x\n",treble, val>>8); - msp3400c_write(bus,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ + dprintk("msp34xx: settreble: %d 0x%02x\n",treble, val>>8); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0003, val); /* loudspeaker */ } -static void msp3400c_setmode(struct msp3400c *msp, int type) +static void msp3400c_setmode(struct i2c_client *client, int type) { + struct msp3400c *msp = client->data; int i; dprintk("msp3400: setmode: %d\n",type); msp->mode = type; msp->stereo = VIDEO_SOUND_MONO; - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x00bb, /* ad_cv */ msp_init_data[type].ad_cv); for (i = 5; i >= 0; i--) /* fir 1 */ - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0001, + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0001, msp_init_data[type].fir1[i]); - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0040); - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, 0x0000); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0004); /* fir 2 */ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0040); + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, 0x0000); for (i = 5; i >= 0; i--) - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0005, + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0005, msp_init_data[type].fir2[i]); - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0083, /* MODE_REG */ msp_init_data[type].mode_reg); - msp3400c_setcarrier(msp->bus, msp_init_data[type].cdo1, + msp3400c_setcarrier(client, msp_init_data[type].cdo1, msp_init_data[type].cdo2); - msp3400c_write(msp->bus,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ + msp3400c_write(client,I2C_MSP3400C_DEM, 0x0056, 0); /*LOAD_REG_1/2*/ - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008, - msp_init_data[type].dfp_src); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009, - msp_init_data[type].dfp_src); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a, + if (dolby) { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, + 0x0520); /* I2S1 */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, + 0x0620); /* I2S2 */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b, + msp_init_data[type].dfp_src); + } else { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008, + msp_init_data[type].dfp_src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009, + msp_init_data[type].dfp_src); + } + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a, msp_init_data[type].dfp_src); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, msp_init_data[type].dfp_matrix); if (msp->nicam) { - /* msp3410 needs some more initialization */ - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0010, 0x3000); + /* nicam prescale */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0010, 0x5a00); /* was: 0x3000 */ } } -static void msp3400c_setstereo(struct msp3400c *msp, int mode) +/* turn on/off nicam + stereo */ +static void msp3400c_setstereo(struct i2c_client *client, int mode) { + struct msp3400c *msp = client->data; int nicam=0; /* channel source: FM/AM or nicam */ int src=0; - + /* switch demodulator */ switch (msp->mode) { case MSP_MODE_FM_TERRA: dprintk("msp3400: FM setstereo: %d\n",mode); - msp3400c_setcarrier(msp->bus,msp->second,msp->main); + msp3400c_setcarrier(client,msp->second,msp->main); switch (mode) { case VIDEO_SOUND_STEREO: - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3001); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3001); break; case VIDEO_SOUND_MONO: case VIDEO_SOUND_LANG1: case VIDEO_SOUND_LANG2: - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000e, 0x3000); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000e, 0x3000); break; } break; @@ -429,29 +442,33 @@ dprintk("msp3400: SAT setstereo: %d\n",mode); switch (mode) { case VIDEO_SOUND_MONO: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); + msp3400c_setcarrier(client, MSP_CARRIER(6.5), MSP_CARRIER(6.5)); break; case VIDEO_SOUND_STEREO: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); + msp3400c_setcarrier(client, MSP_CARRIER(7.2), MSP_CARRIER(7.02)); break; case VIDEO_SOUND_LANG1: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); break; case VIDEO_SOUND_LANG2: - msp3400c_setcarrier(msp->bus, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); + msp3400c_setcarrier(client, MSP_CARRIER(7.38), MSP_CARRIER(7.02)); break; } break; case MSP_MODE_FM_NICAM1: case MSP_MODE_FM_NICAM2: + case MSP_MODE_AM_NICAM: dprintk("msp3400: NICAM setstereo: %d\n",mode); - msp->stereo = mode; - msp3400c_setcarrier(msp->bus,msp->second,msp->main); + msp3400c_setcarrier(client,msp->second,msp->main); if (msp->nicam_on) nicam=0x0100; break; + case MSP_MODE_BTSC: + dprintk("msp3400: BTSC setstereo: %d\n",mode); + nicam=0x0300; + break; default: - /* can't do stereo - abort here */ + dprintk("msp3400: mono setstereo\n"); return; } @@ -459,11 +476,23 @@ switch (mode) { case VIDEO_SOUND_STEREO: src = 0x0020 | nicam; -#if 0 - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0005,0x4000); +#if 0 + /* spatial effect */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0005,0x4000); #endif break; case VIDEO_SOUND_MONO: + if (msp->mode == MSP_MODE_AM_NICAM) { + dprintk("msp3400: switching to AM mono\n"); + /* AM mono decoding is handled by tuner, not MSP chip */ + /* so let's redirect sound from tuner via SCART */ + /* volume prescale for SCART */ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000d, 0x1900); + /* SCART switching control register*/ + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0013, 0xe900); + src = 0x0200; + break; + } case VIDEO_SOUND_LANG1: src = 0x0000 | nicam; break; @@ -471,9 +500,16 @@ src = 0x0010 | nicam; break; } - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0008,src); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0009,src); - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x000a,src); + if (dolby) { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,0x0520); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,0x0620); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000b,src); + } else { + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0008,src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0009,src); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x000a,src); + } } static void @@ -488,7 +524,10 @@ } if (msp->mode == MSP_MODE_FM_NICAM1 || msp->mode == MSP_MODE_FM_NICAM2) - printk("msp3400: NICAM carrier : %d.%03d MHz\n", + printk("msp3400: NICAM/FM carrier : %d.%03d MHz\n", + msp->second/910000,(msp->second/910)%1000); + if (msp->mode == MSP_MODE_AM_NICAM) + printk("msp3400: NICAM/AM carrier : %d.%03d MHz\n", msp->second/910000,(msp->second/910)%1000); if (msp->mode == MSP_MODE_FM_TERRA && msp->main != msp->second) { @@ -513,8 +552,9 @@ }; static int -autodetect_stereo(struct msp3400c *msp) +autodetect_stereo(struct i2c_client *client) { + struct msp3400c *msp = client->data; int val; int newstereo = msp->stereo; int newnicam = msp->nicam_on; @@ -522,8 +562,10 @@ switch (msp->mode) { case MSP_MODE_FM_TERRA: - val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x18); - dprintk("msp3400: stereo detect register: %d\n",val); + val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x18); + if (val > 32768) + val -= 65536; + dprintk("msp34xx: stereo detect register: %d\n",val); if (val > 4096) { newstereo = VIDEO_SOUND_STEREO | VIDEO_SOUND_MONO; @@ -536,8 +578,9 @@ break; case MSP_MODE_FM_NICAM1: case MSP_MODE_FM_NICAM2: - val = msp3400c_read(msp->bus, I2C_MSP3400C_DEM, 0x23); - dprintk("msp3400: nicam sync=%d, mode=%d\n",val & 1, (val & 0x1e) >> 1); + case MSP_MODE_AM_NICAM: + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x23); + dprintk("msp34xx: nicam sync=%d, mode=%d\n",val & 1, (val & 0x1e) >> 1); if (val & 1) { /* nicam synced */ @@ -563,20 +606,33 @@ } newnicam=1; } else { - newnicam=0; + newnicam = 0; newstereo = VIDEO_SOUND_MONO; } break; + case MSP_MODE_BTSC: + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x200); + dprintk("msp3410: status=0x%x (pri=%s, sec=%s, %s%s%s)\n", + val, + (val & 0x0002) ? "no" : "yes", + (val & 0x0004) ? "no" : "yes", + (val & 0x0040) ? "stereo" : "mono", + (val & 0x0080) ? ", nicam 2nd mono" : "", + (val & 0x0100) ? ", bilingual/SAP" : ""); + newstereo = VIDEO_SOUND_MONO; + if (val & 0x0040) newstereo |= VIDEO_SOUND_STEREO; + if (val & 0x0100) newstereo |= VIDEO_SOUND_LANG1; + break; } if (newstereo != msp->stereo) { update = 1; - dprintk("msp3400: watch: stereo %d => %d\n", + dprintk("msp34xx: watch: stereo %d => %d\n", msp->stereo,newstereo); msp->stereo = newstereo; } if (newnicam != msp->nicam_on) { update = 1; - dprintk("msp3400: watch: nicam %d => %d\n", + dprintk("msp34xx: watch: nicam %d => %d\n", msp->nicam_on,newnicam); msp->nicam_on = newnicam; } @@ -596,36 +652,33 @@ } /* stereo/multilang monitoring */ -static void watch_stereo(struct msp3400c *msp) +static void watch_stereo(struct i2c_client *client) { - LOCK_FLAGS; + struct msp3400c *msp = client->data; - LOCK_I2C_BUS(msp->bus); - if (autodetect_stereo(msp)) { + if (autodetect_stereo(client)) { if (msp->stereo & VIDEO_SOUND_STEREO) - msp3400c_setstereo(msp,VIDEO_SOUND_STEREO); + msp3400c_setstereo(client,VIDEO_SOUND_STEREO); else if (msp->stereo & VIDEO_SOUND_LANG1) - msp3400c_setstereo(msp,VIDEO_SOUND_LANG1); + msp3400c_setstereo(client,VIDEO_SOUND_LANG1); else - msp3400c_setstereo(msp,VIDEO_SOUND_MONO); - } - UNLOCK_I2C_BUS(msp->bus); - if (msp->watch_stereo) { - del_timer(&msp->wake_stereo); - msp->wake_stereo.expires = jiffies + 5*HZ; - add_timer(&msp->wake_stereo); + msp3400c_setstereo(client,VIDEO_SOUND_MONO); } + if (once) + msp->watch_stereo = 0; + if (msp->watch_stereo) + mod_timer(&msp->wake_stereo, jiffies+5*HZ); } static int msp3400c_thread(void *data) { - struct msp3400c *msp = data; + struct i2c_client *client = data; + struct msp3400c *msp = client->data; struct CARRIER_DETECT *cd; int count, max1,max2,val1,val2, val,this; - LOCK_FLAGS; -#ifdef __SMP__ +#ifdef CONFIG_SMP lock_kernel(); #endif @@ -638,7 +691,7 @@ msp->thread = current; -#ifdef __SMP__ +#ifdef CONFIG_SMP unlock_kernel(); #endif @@ -663,15 +716,21 @@ msp->active = 1; if (msp->watch_stereo) { - watch_stereo(msp); + watch_stereo(client); msp->active = 0; continue; } - + + /* some time for the tuner to sync */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/5); + if (signal_pending(current)) + goto done; + restart: - LOCK_I2C_BUS(msp->bus); - msp3400c_setvolume(msp->bus, 0, 0); - msp3400c_setmode(msp, MSP_MODE_AM_DETECT /* +1 */ ); + msp->restart = 0; + msp3400c_setvolume(client, 0, 0); + msp3400c_setmode(client, MSP_MODE_AM_DETECT /* +1 */ ); val1 = val2 = 0; max1 = max2 = -1; del_timer(&msp->wake_stereo); @@ -679,26 +738,32 @@ /* carrier detect pass #1 -- main carrier */ cd = carrier_detect_main; count = CARRIER_COUNT(carrier_detect_main); + + if (amsound && (msp->norm == VIDEO_MODE_SECAM)) { + /* autodetect doesn't work well with AM ... */ + max1 = 3; + count = 0; + dprintk("msp3400: AM sound override\n"); + } + for (this = 0; this < count; this++) { - msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ/25); + schedule_timeout(HZ/10); if (signal_pending(current)) goto done; - if (msp->restart) { + if (msp->restart) msp->restart = 0; - goto restart; - } - LOCK_I2C_BUS(msp->bus); - val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b); + val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); + if (val > 32768) + val -= 65536; if (val1 < val) val1 = val, max1 = this; dprintk("msp3400: carrier1 val: %5d / %s\n", val,cd[this].name); } - + /* carrier detect pass #2 -- second (stereo) carrier */ switch (max1) { case 1: /* 5.5 */ @@ -713,21 +778,24 @@ cd = NULL; count = 0; break; } + + if (amsound && (msp->norm == VIDEO_MODE_SECAM)) { + /* autodetect doesn't work well with AM ... */ + cd = NULL; count = 0; max2 = 0; + } for (this = 0; this < count; this++) { - msp3400c_setcarrier(msp->bus, cd[this].cdo,cd[this].cdo); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setcarrier(client, cd[this].cdo,cd[this].cdo); current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ/25); + schedule_timeout(HZ/10); if (signal_pending(current)) goto done; - if (msp->restart) { - msp->restart = 0; + if (msp->restart) goto restart; - } - LOCK_I2C_BUS(msp->bus); - val = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b); + val = msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b); + if (val > 32768) + val -= 65536; if (val2 < val) val2 = val, max2 = this; dprintk("msp3400: carrier2 val: %5d / %s\n", val,cd[this].name); @@ -740,16 +808,16 @@ if (max2 == 0) { /* B/G FM-stereo */ msp->second = carrier_detect_55[max2].cdo; - msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setmode(client, MSP_MODE_FM_TERRA); msp->nicam_on = 0; - msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + msp3400c_setstereo(client, VIDEO_SOUND_MONO); msp->watch_stereo = 1; } else if (max2 == 1 && msp->nicam) { /* B/G NICAM */ msp->second = carrier_detect_55[max2].cdo; - msp3400c_setmode(msp, MSP_MODE_FM_NICAM1); + msp3400c_setmode(client, MSP_MODE_FM_NICAM1); msp->nicam_on = 1; - msp3400c_setcarrier(msp->bus, msp->second, msp->main); + msp3400c_setcarrier(client, msp->second, msp->main); msp->watch_stereo = 1; } else { goto no_second; @@ -758,25 +826,34 @@ case 2: /* 6.0 */ /* PAL I NICAM */ msp->second = MSP_CARRIER(6.552); - msp3400c_setmode(msp, MSP_MODE_FM_NICAM2); + msp3400c_setmode(client, MSP_MODE_FM_NICAM2); msp->nicam_on = 1; - msp3400c_setcarrier(msp->bus, msp->second, msp->main); + msp3400c_setcarrier(client, msp->second, msp->main); msp->watch_stereo = 1; break; case 3: /* 6.5 */ if (max2 == 1 || max2 == 2) { /* D/K FM-stereo */ msp->second = carrier_detect_65[max2].cdo; - msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setmode(client, MSP_MODE_FM_TERRA); + msp->nicam_on = 0; + msp3400c_setstereo(client, VIDEO_SOUND_MONO); + msp->watch_stereo = 1; + } else if (max2 == 0 && + msp->norm == VIDEO_MODE_SECAM) { + /* L NICAM or AM-mono */ + msp->second = carrier_detect_65[max2].cdo; + msp3400c_setmode(client, MSP_MODE_AM_NICAM); msp->nicam_on = 0; - msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + msp3400c_setstereo(client, VIDEO_SOUND_MONO); + msp3400c_setcarrier(client, msp->second, msp->main); msp->watch_stereo = 1; } else if (max2 == 0 && msp->nicam) { /* D/K NICAM */ msp->second = carrier_detect_65[max2].cdo; - msp3400c_setmode(msp, MSP_MODE_FM_NICAM1); + msp3400c_setmode(client, MSP_MODE_FM_NICAM1); msp->nicam_on = 1; - msp3400c_setcarrier(msp->bus, msp->second, msp->main); + msp3400c_setcarrier(client, msp->second, msp->main); msp->watch_stereo = 1; } else { goto no_second; @@ -786,23 +863,19 @@ default: no_second: msp->second = carrier_detect_main[max1].cdo; - msp3400c_setmode(msp, MSP_MODE_FM_TERRA); + msp3400c_setmode(client, MSP_MODE_FM_TERRA); msp->nicam_on = 0; - msp3400c_setcarrier(msp->bus, msp->second, msp->main); + msp3400c_setcarrier(client, msp->second, msp->main); msp->stereo = VIDEO_SOUND_MONO; - msp3400c_setstereo(msp, VIDEO_SOUND_MONO); + msp3400c_setstereo(client, VIDEO_SOUND_MONO); break; } /* unmute */ - msp3400c_setvolume(msp->bus, msp->left, msp->right); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setvolume(client, msp->left, msp->right); - if (msp->watch_stereo) { - del_timer(&msp->wake_stereo); - msp->wake_stereo.expires = jiffies + 5*HZ; - add_timer(&msp->wake_stereo); - } + if (msp->watch_stereo) + mod_timer(&msp->wake_stereo, jiffies+5*HZ); if (debug) msp3400c_print_mode(msp); @@ -820,86 +893,210 @@ return 0; } -#if 0 /* not finished yet */ +/* ----------------------------------------------------------------------- */ +/* this one uses the automatic sound standard detection of newer */ +/* msp34xx chip versions */ +static struct MODES { + int retval; + int main, second; + char *name; +} modelist[] = { + { 0x0000, 0, 0, "ERROR" }, + { 0x0001, 0, 0, "autodetect start" }, + { 0x0002, MSP_CARRIER(4.5), MSP_CARRIER(4.72), "4.5/4.72 M Dual FM-Stereo" }, + { 0x0003, MSP_CARRIER(5.5), MSP_CARRIER(5.7421875), "5.5/5.74 B/G Dual FM-Stereo" }, + { 0x0004, MSP_CARRIER(6.5), MSP_CARRIER(6.2578125), "6.5/6.25 D/K1 Dual FM-Stereo" }, + { 0x0005, MSP_CARRIER(6.5), MSP_CARRIER(6.7421875), "6.5/6.74 D/K2 Dual FM-Stereo" }, + { 0x0006, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 D/K FM-Mono (HDEV3)" }, + { 0x0008, MSP_CARRIER(5.5), MSP_CARRIER(5.85), "5.5/5.85 B/G NICAM FM" }, + { 0x0009, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 L NICAM AM" }, + { 0x000a, MSP_CARRIER(6.0), MSP_CARRIER(6.55), "6.0/6.55 I NICAM FM" }, + { 0x000b, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM" }, + { 0x000c, MSP_CARRIER(6.5), MSP_CARRIER(5.85), "6.5/5.85 D/K NICAM FM (HDEV2)" }, + { 0x0020, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Stereo" }, + { 0x0021, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M BTSC-Mono + SAP" }, + { 0x0030, MSP_CARRIER(4.5), MSP_CARRIER(4.5), "4.5 M EIA-J Japan Stereo" }, + { 0x0040, MSP_CARRIER(10.7), MSP_CARRIER(10.7), "10.7 FM-Stereo Radio" }, + { 0x0050, MSP_CARRIER(6.5), MSP_CARRIER(6.5), "6.5 SAT-Mono" }, + { 0x0051, MSP_CARRIER(7.02), MSP_CARRIER(7.20), "7.02/7.20 SAT-Stereo" }, + { 0x0060, MSP_CARRIER(7.2), MSP_CARRIER(7.2), "7.2 SAT ADR" }, + { -1, 0, 0, NULL }, /* EOF */ +}; + static int msp3410d_thread(void *data) { - unsigned long flags; - struct msp3400c *msp = data; - struct semaphore sem = MUTEX_LOCKED; - int i, val; - - /* lock_kernel(); */ + struct i2c_client *client = data; + struct msp3400c *msp = client->data; + int mode,val,i,std; + +#ifdef CONFIG_SMP + lock_kernel(); +#endif exit_mm(current); current->session = 1; current->pgrp = 1; sigfillset(¤t->blocked); current->fs->umask = 0; - strcpy(current->comm,"msp3410 (nicam)"); + strcpy(current->comm,"msp3410 [auto]"); - msp->wait = &sem; msp->thread = current; - /* unlock_kernel(); */ +#ifdef CONFIG_SMP + unlock_kernel(); +#endif - dprintk("msp3410: thread: start\n"); + printk("msp3410: daemon started\n"); if(msp->notify != NULL) up(msp->notify); for (;;) { if (msp->rmmod) goto done; - dprintk("msp3410: thread: sleep\n"); - down_interruptible(&sem); - dprintk("msp3410: thread: wakeup\n"); - if (msp->rmmod) + if (debug > 1) + printk("msp3410: thread: sleep\n"); + interruptible_sleep_on(&msp->wq); + if (debug > 1) + printk("msp3410: thread: wakeup\n"); + if (msp->rmmod || signal_pending(current)) goto done; - + if (VIDEO_MODE_RADIO == msp->norm) continue; /* nothing to do */ msp->active = 1; - restart: - LOCK_I2C_BUS(msp->bus); - /* mute */ - msp3400c_setvolume(msp->bus, 0); - /* quick & dirty hack: - get the audio proccessor into some useful state */ - msp3400c_setmode(msp, MSP_MODE_FM_NICAM1); - /* kick autodetect */ - msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x20, 0x01); - msp3400c_write(msp->bus, I2C_MSP3400C_DFP, 0x21, 0x01); - UNLOCK_I2C_BUS(msp->bus); - - /* wait 1 sec */ - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + HZ; - schedule(); + if (msp->watch_stereo) { + watch_stereo(client); + msp->active = 0; + continue; + } + + /* some time for the tuner to sync */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/5); if (signal_pending(current)) goto done; - if (msp->restart) { - msp->restart = 0; - goto restart; + + restart: + msp->restart = 0; + del_timer(&msp->wake_stereo); + msp->watch_stereo = 0; + + /* put into sane state (and mute) */ + msp3400c_reset(client); + + /* start autodetect */ + switch (msp->norm) { + case VIDEO_MODE_PAL: + mode = 0x1003; + std = 1; + break; + case VIDEO_MODE_NTSC: /* BTSC */ + mode = 0x2003; + std = 0x0020; + break; + case VIDEO_MODE_SECAM: + mode = 0x0003; + std = 1; + break; + default: + mode = 0x0003; + std = 1; + break; } - - LOCK_I2C_BUS(msp->bus); - /* debug register dump */ - for (i = 0; i < sizeof(d1)/sizeof(struct REGISTER_DUMP); i++) { - val = msp3400c_read(msp->bus,I2C_MSP3400C_DEM,d1[i].addr); - printk(KERN_DEBUG "msp3400: %s = 0x%x\n",d1[i].name,val); - } + msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); + msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std); + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); + msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); + if (debug) { + int i; + for (i = 0; modelist[i].name != NULL; i++) + if (modelist[i].retval == std) + break; + printk("msp3410: setting mode: %s (0x%04x)\n", + modelist[i].name ? modelist[i].name : "unknown",std); + } + + if (std != 1) { + /* programmed some specific mode */ + val = std; + } else { + /* triggered autodetect */ + for (;;) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + if (signal_pending(current)) + goto done; + if (msp->restart) + goto restart; + + /* check results */ + val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); + if (val < 0x07ff) + break; + dprintk("msp3410: detection still in progress\n"); + } + } + for (i = 0; modelist[i].name != NULL; i++) + if (modelist[i].retval == val) + break; + dprintk("msp3410: current mode: %s (0x%04x)\n", + modelist[i].name ? modelist[i].name : "unknown", + val); + msp->main = modelist[i].main; + msp->second = modelist[i].second; + + if (amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { + /* autodetection has failed, let backup */ + dprintk("msp3410: autodetection failed, switching to backup mode: %s (0x%04x)\n", + modelist[8].name ? modelist[8].name : "unknown",val); + val = 0x0009; + msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val); + } + + /* set prescale / stereo */ + switch (val) { + case 0x0009: + msp->mode = MSP_MODE_AM_NICAM; + msp->stereo = VIDEO_SOUND_MONO; + msp3400c_setstereo(client,VIDEO_SOUND_MONO); + msp->watch_stereo = 1; + break; + case 0x0020: /* BTSC */ + /* just turn on stereo */ + msp->mode = MSP_MODE_BTSC; + msp->stereo = VIDEO_SOUND_STEREO; + msp->watch_stereo = 1; + msp3400c_setstereo(client,VIDEO_SOUND_STEREO); + /* set prescale */ + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ + break; + case 0x0003: + msp->mode = MSP_MODE_FM_TERRA; + msp->stereo = VIDEO_SOUND_MONO; + msp->watch_stereo = 1; + /* fall */ + default: + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ + msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* NICAM */ + break; + } + /* unmute */ - msp3400c_setvolume(msp->bus, msp->volume); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setbass(client, msp->bass); + msp3400c_settreble(client, msp->treble); + msp3400c_setvolume(client, msp->left, msp->right); + + if (msp->watch_stereo) + mod_timer(&msp->wake_stereo, jiffies+HZ); msp->active = 0; } done: dprintk("msp3410: thread: exit\n"); - msp->wait = NULL; msp->active = 0; msp->thread = NULL; @@ -907,23 +1104,17 @@ up(msp->notify); return 0; } -#endif /* ----------------------------------------------------------------------- */ /* mixer stuff -- with the modular sound driver in 2.1.x we can easily */ /* register the msp3400 as mixer device */ -#ifdef REGISTER_MIXER +#ifdef REGISTER_MIXER #include #include #include -static struct msp3400c *mspmix = NULL; /* ugly hack, should do something more sensible */ -static int mixer_num; -static int mixer_modcnt = 0; -static struct semaphore mixer_sem = MUTEX; - static int mix_to_v4l(int i) { int r; @@ -958,14 +1149,22 @@ static int msp3400c_mixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + struct i2c_client *client; + struct msp3400c *msp; int ret,val = 0; - LOCK_FLAGS; + client = file->private_data; + if (!client) + return -ENODEV; + msp = client->data; + if (!msp) + return -ENODEV; + if (cmd == SOUND_MIXER_INFO) { mixer_info info; strncpy(info.id, "MSP3400", sizeof(info.id)); strncpy(info.name, "MSP 3400", sizeof(info.name)); - info.modify_counter = mixer_modcnt; + info.modify_counter = msp->mixer_modcnt; if (copy_to_user((void *)arg, &info, sizeof(info))) return -EFAULT; return 0; @@ -985,12 +1184,6 @@ if (get_user(val, (int *)arg)) return -EFAULT; - down(&mixer_sem); - if (!mspmix) { - up(&mixer_sem); - return -ENODEV; - } - switch (cmd) { case MIXER_READ(SOUND_MIXER_RECMASK): case MIXER_READ(SOUND_MIXER_CAPS): @@ -1007,44 +1200,36 @@ break; case MIXER_WRITE(SOUND_MIXER_VOLUME): - mspmix->left = mix_to_v4l(val); - mspmix->right = mix_to_v4l(val >> 8); - LOCK_I2C_BUS(mspmix->bus); - msp3400c_setvolume(mspmix->bus,mspmix->left,mspmix->right); - UNLOCK_I2C_BUS(mspmix->bus); - mixer_modcnt++; + msp->left = mix_to_v4l(val); + msp->right = mix_to_v4l(val >> 8); + msp3400c_setvolume(client,msp->left,msp->right); + msp->mixer_modcnt++; /* fall */ case MIXER_READ(SOUND_MIXER_VOLUME): - ret = v4l_to_mix2(mspmix->left, mspmix->right); + ret = v4l_to_mix2(msp->left, msp->right); break; case MIXER_WRITE(SOUND_MIXER_BASS): - mspmix->bass = mix_to_v4l(val); - LOCK_I2C_BUS(mspmix->bus); - msp3400c_setbass(mspmix->bus,mspmix->bass); - UNLOCK_I2C_BUS(mspmix->bus); - mixer_modcnt++; + msp->bass = mix_to_v4l(val); + msp3400c_setbass(client,msp->bass); + msp->mixer_modcnt++; /* fall */ case MIXER_READ(SOUND_MIXER_BASS): - ret = v4l_to_mix(mspmix->bass); + ret = v4l_to_mix(msp->bass); break; case MIXER_WRITE(SOUND_MIXER_TREBLE): - mspmix->treble = mix_to_v4l(val); - LOCK_I2C_BUS(mspmix->bus); - msp3400c_settreble(mspmix->bus,mspmix->treble); - UNLOCK_I2C_BUS(mspmix->bus); - mixer_modcnt++; + msp->treble = mix_to_v4l(val); + msp3400c_settreble(client,msp->treble); + msp->mixer_modcnt++; /* fall */ case MIXER_READ(SOUND_MIXER_TREBLE): - ret = v4l_to_mix(mspmix->treble); + ret = v4l_to_mix(msp->treble); break; default: - up(&mixer_sem); return -EINVAL; } - up(&mixer_sem); if (put_user(ret, (int *)arg)) return -EFAULT; return 0; @@ -1053,6 +1238,27 @@ static int msp3400c_mixer_open(struct inode *inode, struct file *file) { + int minor = MINOR(inode->i_rdev); + struct i2c_client *client; + struct msp3400c *msp; + int i; + + /* search for the right one... */ + for (i = 0; i < MSP3400_MAX; i++) { + msp = msps[i]->data; + if (msp->mixer_num == minor) { + client = msps[i]; + file->private_data = client; + break; + } + } + if (MSP3400_MAX == i) + return -ENODEV; + + /* lock bttv in memory while the mixer is in use */ + if (client->adapter->inc_use) + client->adapter->inc_use(client->adapter); + MOD_INC_USE_COUNT; return 0; } @@ -1060,6 +1266,10 @@ static int msp3400c_mixer_release(struct inode *inode, struct file *file) { + struct i2c_client *client = file->private_data; + + if (client->adapter->dec_use) + client->adapter->dec_use(client->adapter); MOD_DEC_USE_COUNT; return 0; } @@ -1070,57 +1280,83 @@ return -ESPIPE; } -static /*const*/ struct file_operations msp3400c_mixer_fops = { - &msp3400c_mixer_llseek, - NULL, /* read */ - NULL, /* write */ - NULL, /* readdir */ - NULL, /* poll */ - &msp3400c_mixer_ioctl, - NULL, /* mmap */ - &msp3400c_mixer_open, - NULL, - &msp3400c_mixer_release, - NULL, /* fsync */ - NULL, /* fasync */ - NULL, /* check_media_change */ - NULL, /* revalidate */ - NULL, /* lock */ +static struct file_operations msp3400c_mixer_fops = { + llseek: msp3400c_mixer_llseek, + ioctl: msp3400c_mixer_ioctl, + open: msp3400c_mixer_open, + release: msp3400c_mixer_release, }; #endif /* ----------------------------------------------------------------------- */ -static int msp3400c_attach(struct i2c_device *device) +static int msp_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind); +static int msp_detach(struct i2c_client *client); +static int msp_probe(struct i2c_adapter *adap); +static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg); + +static struct i2c_driver driver = { + "i2c msp3400 driver", + I2C_DRIVERID_MSP3400, + I2C_DF_NOTIFY, + msp_probe, + msp_detach, + msp_command, +}; + +static struct i2c_client client_template = { - struct semaphore sem = MUTEX_LOCKED; + "unset", + -1, + 0, + 0, + NULL, + &driver +}; + +static int msp_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + DECLARE_MUTEX_LOCKED(sem); struct msp3400c *msp; - int rev1,rev2; - LOCK_FLAGS; + struct i2c_client *c; + int rev1,rev2,i; + + client_template.adapter = adap; + client_template.addr = addr; - device->data = msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL); - if (NULL == msp) + if (-1 == msp3400c_reset(&client_template)) { + dprintk("msp3400: no chip found\n"); + return -1; + } + + if (NULL == (c = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(c,&client_template,sizeof(struct i2c_client)); + if (NULL == (msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL))) { + kfree(c); return -ENOMEM; + } + memset(msp,0,sizeof(struct msp3400c)); - msp->bus = device->bus; msp->left = 65535; msp->right = 65535; msp->bass = 32768; msp->treble = 32768; + c->data = msp; + init_waitqueue_head(&msp->wq); - LOCK_I2C_BUS(msp->bus); - if (-1 == msp3400c_reset(msp->bus)) { - UNLOCK_I2C_BUS(msp->bus); + if (-1 == msp3400c_reset(c)) { kfree(msp); dprintk("msp3400: no chip found\n"); return -1; } - rev1 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1e); - rev2 = msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1f); + rev1 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1e); + rev2 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1f); if (0 == rev1 && 0 == rev2) { - UNLOCK_I2C_BUS(msp->bus); kfree(msp); printk("msp3400: error while reading chip version\n"); return -1; @@ -1128,49 +1364,67 @@ #if 0 /* this will turn on a 1kHz beep - might be useful for debugging... */ - msp3400c_write(msp->bus,I2C_MSP3400C_DFP, 0x0014, 0x1040); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0014, 0x1040); #endif - UNLOCK_I2C_BUS(msp->bus); - sprintf(device->name,"MSP34%02d%c-%c%d", + sprintf(c->name,"MSP34%02d%c-%c%d", (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0; + if (simple == -1) { + /* default mode */ + msp->simple = 0; + } else { + /* use insmod option */ + msp->simple = simple; + } + /* timer for stereo checking */ msp->wake_stereo.function = msp3400c_stereo_wake; msp->wake_stereo.data = (unsigned long)msp; + /* hello world :-) */ + printk(KERN_INFO "msp3400: init: chip=%s",c->name); + if (msp->nicam) + printk(", has NICAM support"); + printk("\n"); + /* startup control thread */ MOD_INC_USE_COUNT; - msp->wq = NULL; msp->notify = &sem; - kernel_thread(msp3400c_thread, (void *)msp, 0); + kernel_thread(msp->simple ? msp3410d_thread : msp3400c_thread, + (void *)c, 0); down(&sem); msp->notify = NULL; wake_up_interruptible(&msp->wq); - printk(KERN_INFO "msp3400: init: chip=%s",device->name); - if (msp->nicam) - printk(", has NICAM support"); #ifdef REGISTER_MIXER - down(&mixer_sem); - mspmix = msp; - up(&mixer_sem); + if ((msp->mixer_num = register_sound_mixer(&msp3400c_mixer_fops,mixer)) < 0) + printk(KERN_ERR "msp3400c: cannot allocate mixer device\n"); #endif - printk("\n"); + + /* update our own array */ + for (i = 0; i < MSP3400_MAX; i++) { + if (NULL == msps[i]) { + msps[i] = c; + break; + } + } + + /* done */ + i2c_attach_client(c); return 0; } -static int msp3400c_detach(struct i2c_device *device) +static int msp_detach(struct i2c_client *client) { - struct semaphore sem = MUTEX_LOCKED; - struct msp3400c *msp = (struct msp3400c*)device->data; - LOCK_FLAGS; - + DECLARE_MUTEX_LOCKED(sem); + struct msp3400c *msp = (struct msp3400c*)client->data; + int i; + #ifdef REGISTER_MIXER - down(&mixer_sem); - mspmix = NULL; - up(&mixer_sem); + if (msp->mixer_num >= 0) + unregister_sound_mixer(msp->mixer_num); #endif /* shutdown control thread */ @@ -1183,47 +1437,137 @@ down(&sem); msp->notify = NULL; } - - LOCK_I2C_BUS(msp->bus); - msp3400c_reset(msp->bus); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_reset(client); + /* update our own array */ + for (i = 0; i < MSP3400_MAX; i++) { + if (client == msps[i]) { + msps[i] = NULL; + break; + } + } + + i2c_detach_client(client); kfree(msp); + kfree(client); MOD_DEC_USE_COUNT; return 0; } -static int msp3400c_command(struct i2c_device *device, - unsigned int cmd, void *arg) +static int msp_probe(struct i2c_adapter *adap) { - struct msp3400c *msp = (struct msp3400c*)device->data; + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, msp_attach); + return 0; +} + +static int msp_command(struct i2c_client *client,unsigned int cmd, void *arg) +{ + struct msp3400c *msp = (struct msp3400c*)client->data; +#if 0 int *iarg = (int*)arg; __u16 *sarg = arg; - LOCK_FLAGS; +#endif switch (cmd) { - case MSP_SET_RADIO: + + case AUDC_SET_RADIO: msp->norm = VIDEO_MODE_RADIO; msp->watch_stereo=0; del_timer(&msp->wake_stereo); - LOCK_I2C_BUS(msp->bus); - msp3400c_setmode(msp,MSP_MODE_FM_RADIO); - msp3400c_setcarrier(msp->bus, MSP_CARRIER(10.7),MSP_CARRIER(10.7)); - msp3400c_setvolume(msp->bus,msp->left, msp->right); - UNLOCK_I2C_BUS(msp->bus); + if (msp->simple) { + msp3400c_reset(client); + msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, 0x0003); /* automatic */ + msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, 0x0040); /* FM Radio */ + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM prescale */ + msp3400c_setbass(client, msp->bass); + msp3400c_settreble(client, msp->treble); + msp3400c_setvolume(client, msp->left, msp->right); + } else { + msp3400c_setmode(client,MSP_MODE_FM_RADIO); + msp3400c_setcarrier(client, MSP_CARRIER(10.7),MSP_CARRIER(10.7)); + msp3400c_setvolume(client,msp->left, msp->right); + } + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + va->volume=MAX(msp->left,msp->right); + va->balance=(32768*MIN(msp->left,msp->right))/ + (va->volume ? va->volume : 1); + va->balance=(msp->leftright)? + (65535-va->balance) : va->balance; + va->bass = msp->bass; + va->treble = msp->treble; + + autodetect_stereo(client); + va->mode = msp->stereo; + break; + } + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + + msp->left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + msp->right = (MIN(va->balance,32768) * + va->volume) / 32768; + msp->bass = va->bass; + msp->treble = va->treble; + msp3400c_setvolume(client,msp->left, msp->right); + msp3400c_setbass(client,msp->bass); + msp3400c_settreble(client,msp->treble); + + if (va->mode != 0) { + msp->watch_stereo=0; + del_timer(&msp->wake_stereo); + msp->stereo = va->mode; + msp3400c_setstereo(client,va->mode); + } + break; + } + case VIDIOCSCHAN: + { + struct video_channel *vc = arg; + + msp->norm = vc->norm; + break; + } + case VIDIOCSFREQ: + { + /* new channel -- kick audio carrier scan */ + msp3400c_setvolume(client,0,0); + msp->watch_stereo=0; + del_timer(&msp->wake_stereo); + if (msp->active) + msp->restart = 1; + wake_up_interruptible(&msp->wq); break; - case MSP_SET_TVNORM: + } + + /* --- v4l2 ioctls --- */ + /* NOT YET */ + +#if 0 + /* --- old, obsolete interface --- */ + case AUDC_SET_TVNORM: msp->norm = *iarg; break; - case MSP_SWITCH_MUTE: + case AUDC_SWITCH_MUTE: /* channels switching step one -- mute */ msp->watch_stereo=0; del_timer(&msp->wake_stereo); - LOCK_I2C_BUS(msp->bus); - msp3400c_setvolume(msp->bus,0,0); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setvolume(client,0,0); break; - case MSP_NEWCHANNEL: + case AUDC_NEWCHANNEL: /* channels switching step two -- trigger sound carrier scan */ msp->watch_stereo=0; del_timer(&msp->wake_stereo); @@ -1232,96 +1576,79 @@ wake_up_interruptible(&msp->wq); break; - case MSP_GET_VOLUME: - *sarg = (msp->left > msp->right) ? msp->left : msp->right; + case AUDC_GET_VOLUME_LEFT: + *sarg = msp->left; + break; + case AUDC_GET_VOLUME_RIGHT: + *sarg = msp->right; break; - case MSP_SET_VOLUME: - msp->left = msp->right = *sarg; - LOCK_I2C_BUS(msp->bus); - msp3400c_setvolume(msp->bus,msp->left, msp->right); - UNLOCK_I2C_BUS(msp->bus); + case AUDC_SET_VOLUME_LEFT: + msp->left = *sarg; + msp3400c_setvolume(client,msp->left, msp->right); + break; + case AUDC_SET_VOLUME_RIGHT: + msp->right = *sarg; + msp3400c_setvolume(client,msp->left, msp->right); break; - case MSP_GET_BASS: + case AUDC_GET_BASS: *sarg = msp->bass; break; - case MSP_SET_BASS: + case AUDC_SET_BASS: msp->bass = *sarg; - LOCK_I2C_BUS(msp->bus); - msp3400c_setbass(msp->bus,msp->bass); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_setbass(client,msp->bass); break; - case MSP_GET_TREBLE: + case AUDC_GET_TREBLE: *sarg = msp->treble; break; - case MSP_SET_TREBLE: + case AUDC_SET_TREBLE: msp->treble = *sarg; - LOCK_I2C_BUS(msp->bus); - msp3400c_settreble(msp->bus,msp->treble); - UNLOCK_I2C_BUS(msp->bus); + msp3400c_settreble(client,msp->treble); break; - case MSP_GET_STEREO: - *sarg = msp->stereo; + case AUDC_GET_STEREO: + autodetect_stereo(client); + *sarg = msp->stereo; break; - case MSP_SET_STEREO: + case AUDC_SET_STEREO: if (*sarg) { msp->watch_stereo=0; del_timer(&msp->wake_stereo); - LOCK_I2C_BUS(msp->bus); - msp3400c_setstereo(msp,*sarg); - UNLOCK_I2C_BUS(msp->bus); + msp->stereo = *sarg; + msp3400c_setstereo(client,*sarg); } break; - case MSP_GET_DC: - LOCK_I2C_BUS(msp->bus); - *sarg = ((int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1b) + - (int)msp3400c_read(msp->bus, I2C_MSP3400C_DFP, 0x1c)); - UNLOCK_I2C_BUS(msp->bus); + case AUDC_GET_DC: + if (msp->simple) + break; /* fixme */ + *sarg = ((int)msp3400c_read(client, I2C_MSP3400C_DFP, 0x1b) + + (int)msp3400c_read(client, I2C_MSP3400C_DFP, 0x1c)); break; - +#endif default: - return -EINVAL; + /* nothing */ } return 0; } /* ----------------------------------------------------------------------- */ -struct i2c_driver i2c_driver_msp = { - "msp3400", /* name */ - I2C_DRIVERID_MSP3400, /* ID */ - I2C_MSP3400C, I2C_MSP3400C, /* addr range */ - - msp3400c_attach, - msp3400c_detach, - msp3400c_command -}; - #ifdef MODULE int init_module(void) #else - int msp3400c_init(void) +int msp3400c_init(void) #endif { - i2c_register_driver(&i2c_driver_msp); -#ifdef REGISTER_MIXER - if ((mixer_num = register_sound_mixer(&msp3400c_mixer_fops, -1)) < 0) - printk(KERN_ERR "msp3400c: cannot allocate mixer device\n"); -#endif + i2c_add_driver(&driver); return 0; } #ifdef MODULE void cleanup_module(void) { - i2c_unregister_driver(&i2c_driver_msp); -#ifdef REGISTER_MIXER - if (mixer_num >= 0) - unregister_sound_mixer(mixer_num); -#endif + i2c_del_driver(&driver); } #endif diff -urN vanilla-2.2.15pre17/drivers/char/saa5249.c bttv-2.2.15pre17/drivers/char/saa5249.c --- vanilla-2.2.15pre17/drivers/char/saa5249.c Fri Jan 15 07:53:02 1999 +++ bttv-2.2.15pre17/drivers/char/saa5249.c Mon May 1 23:02:30 2000 @@ -52,7 +52,7 @@ #include #include #include -#include +#include #include #include diff -urN vanilla-2.2.15pre17/drivers/char/saa7111.c bttv-2.2.15pre17/drivers/char/saa7111.c --- vanilla-2.2.15pre17/drivers/char/saa7111.c Mon May 1 22:59:45 2000 +++ bttv-2.2.15pre17/drivers/char/saa7111.c Mon May 1 23:02:30 2000 @@ -40,7 +40,7 @@ #include #include -#include +#include #include #define DEBUG(x) /* Debug driver */ diff -urN vanilla-2.2.15pre17/drivers/char/saa7185.c bttv-2.2.15pre17/drivers/char/saa7185.c --- vanilla-2.2.15pre17/drivers/char/saa7185.c Mon May 1 22:59:45 2000 +++ bttv-2.2.15pre17/drivers/char/saa7185.c Mon May 1 23:02:30 2000 @@ -40,7 +40,7 @@ #include #include -#include +#include #include #define DEBUG(x) x /* Debug driver */ diff -urN vanilla-2.2.15pre17/drivers/char/tda7432.c bttv-2.2.15pre17/drivers/char/tda7432.c --- vanilla-2.2.15pre17/drivers/char/tda7432.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/char/tda7432.c Mon May 1 23:03:21 2000 @@ -0,0 +1,505 @@ +/* + * For the STS-Thompson TDA7432 audio processor chip + * + * Handles audio functions: volume, balance, tone, loudness + * This driver will not complain if used with any + * other i2c device with the same address. + * + * Copyright (c) 2000 Eric Sandeen + * This code is placed under the terms of the GNU General Public License + * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * Which was based on tda8425.c by Greg Alexander (c) 1998 + * + * OPTIONS: + * debug - set to 1 if you'd like to see debug messages + * set to 2 if you'd like to be inundated with debug messages + * + * loudness - set between 0 and 15 for varying degrees of loudness effect + * + * TODO: + * Implement tone controls + * + * Revision: 0.3 - Fixed silly reversed volume controls. :) + * Revision: 0.2 - Cleaned up #defines + * fixed volume control + * Added I2C_DRIVERID_TDA7432 + * added loudness insmod control + * Revision: 0.1 - initial version + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + +/* This driver ID is brand new, so define it if it's not in i2c-id.h yet */ +#ifndef I2C_DRIVERID_TDA7432 + #define I2C_DRIVERID_TDA7432 27 +#endif + + +MODULE_AUTHOR("Eric Sandeen "); +MODULE_DESCRIPTION("bttv driver for the tda7432 audio processor chip"); + +MODULE_PARM(debug,"i"); +MODULE_PARM(loudness,"i"); +static int loudness = 0; /* disable loudness by default */ +static int debug = 0; /* insmod parameter */ + + +/* Address to scan (I2C address of this chip) */ +static unsigned short normal_i2c[] = { + I2C_TDA7432 >> 1, + I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + +/* Structure of address and subaddresses for the tda7432 */ + +struct tda7432 { + int addr; + int input; + int volume; + int tone; + int lf, lr, rf, rr; + int loud; +}; + +static struct i2c_driver driver; +static struct i2c_client client_template; + +#define dprintk if (debug) printk +#define d2printk if (debug > 1) printk + +/* The TDA7432 is made by STS-Thompson + * http://www.st.com + * http://us.st.com/stonline/books/pdf/docs/4056.pdf + * + * TDA7432: I2C-bus controlled basic audio processor + * + * The TDA7432 controls basic audio functions like volume, balance, + * and tone control (including loudness). It also has four channel + * output (for front and rear). Since most vidcap cards probably + * don't have 4 channel output, this driver will set front & rear + * together (no independent control). + */ + + /* Subaddresses for TDA7432 */ + +#define TDA7432_IN 0x00 /* Input select */ +#define TDA7432_VL 0x01 /* Volume */ +#define TDA7432_TN 0x02 /* Bass, Treble (Tone) */ +#define TDA7432_LF 0x03 /* Attenuation LF (Left Front) */ +#define TDA7432_LR 0x04 /* Attenuation LR (Left Rear) */ +#define TDA7432_RF 0x05 /* Attenuation RF (Right Front) */ +#define TDA7432_RR 0x06 /* Attenuation RR (Right Rear) */ +#define TDA7432_LD 0x07 /* Loudness */ + + + /* Masks for bits in TDA7432 subaddresses */ + +/* Many of these not used - just for documentation */ + +/* Subaddress 0x00 - Input selection and bass control */ + +/* Bits 0,1,2 control input: + * 0x00 - Stereo input + * 0x02 - Mono input + * 0x03 - Mute + * Mono probably isn't used - I'm guessing only the stereo + * input is connected on most cards, so we'll set it to stereo. + * + * Bit 3 controls bass cut: 0/1 is non-symmetric/symmetric bass cut + * Bit 4 controls bass range: 0/1 is extended/standard bass range + * + * Highest 3 bits not used + */ + +#define TDA7432_STEREO_IN 0 +#define TDA7432_MONO_IN 2 /* Probably won't be used */ +#define TDA7432_MUTE 3 /* Probably won't be used */ +#define TDA7432_BASS_SYM 1 << 3 +#define TDA7432_BASS_NORM 1 << 4 + +/* Subaddress 0x01 - Volume */ + +/* Lower 7 bits control volume from -79dB to +32dB in 1dB steps + * Recommended maximum is +20 dB + * + * +32dB: 0x00 + * +20dB: 0x0c + * 0dB: 0x20 + * -79dB: 0x6f + * + * MSB (bit 7) controls loudness: 1/0 is loudness on/off + */ + +#define TDA7432_VOL_0DB 0x20 +#define TDA7432_LD_ON 1 << 7 + + +/* Subaddress 0x02 - Tone control */ + +/* Bits 0,1,2 control absolute treble gain from 0dB to 14dB + * 0x0 is 14dB, 0x7 is 0dB + * + * Bit 3 controls treble attenuation/gain (sign) + * 1 = gain (+) + * 0 = attenuation (-) + * + * Bits 4,5,6 control absolute bass gain from 0dB to 14dB + * (This is only true for normal base range, set in 0x00) + * 0x0 << 4 is 14dB, 0x7 is 0dB + * + * Bit 7 controls bass attenuation/gain (sign) + * 1 << 7 = gain (+) + * 0 << 7 = attenuation (-) + * + * Example: + * 1 1 0 1 0 1 0 1 is +4dB bass, -4dB treble + */ + +#define TDA7432_TREBLE_0DB 0xf +#define TDA7432_TREBLE 7 +#define TDA7432_TREBLE_GAIN 1 << 3 +#define TDA7432_BASS_0DB 0xf << 4 +#define TDA7432_BASS 7 << 4 +#define TDA7432_BASS_GAIN 1 << 7 + + +/* Subaddress 0x03 - Left Front attenuation */ +/* Subaddress 0x04 - Left Rear attenuation */ +/* Subaddress 0x05 - Right Front attenuation */ +/* Subaddress 0x06 - Right Rear attenuation */ + +/* Bits 0,1,2,3,4 control attenuation from 0dB to -37.5dB + * in 1.5dB steps. + * + * 0x00 is 0dB + * 0x1f is -37.5dB + * + * Bit 5 mutes that channel when set (1 = mute, 0 = unmute) + * We'll use the mute on the input, though (above) + * Bits 6,7 unused + */ + +#define TDA7432_ATTEN_0DB 0x00 + + +/* Subaddress 0x07 - Loudness Control */ + +/* Bits 0,1,2,3 control loudness from 0dB to -15dB in 1dB steps + * when bit 4 is NOT set + * + * 0x0 is 0dB + * 0xf is -15dB + * + * If bit 4 is set, then there is a flat attenuation according to + * the lower 4 bits, as above. + * + * Bits 5,6,7 unused + */ + + + +/* Begin code */ + +static int tda7432_write(struct i2c_client *client, int subaddr, int val) +{ + unsigned char buffer[2]; + d2printk("tda7432: In tda7432_write\n"); + dprintk("tda7432: Writing %d 0x%x\n", subaddr, val); + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(client,buffer,2)) { + printk(KERN_WARNING "tda7432: I/O error, trying (write %d 0x%x)\n", + subaddr, val); + return -1; + } + return 0; +} + +/* I don't think we ever actually _read_ the chip... */ +#if 0 +static int tda7432_read(struct i2c_client *client) +{ + unsigned char buffer; + d2printk("tda7432: In tda7432_read\n"); + if (1 != i2c_master_recv(client,&buffer,1)) { + printk(KERN_WARNING "tda7432: I/O error, trying (read)\n"); + return -1; + } + dprintk("tda7432: Read 0x%02x\n", buffer); + return buffer; +} +#endif + +static int tda7432_set(struct i2c_client *client) +{ + struct tda7432 *t = client->data; + unsigned char buf[16]; + d2printk("tda7432: In tda7432_set\n"); + + dprintk(KERN_INFO + "tda7432: 7432_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", + t->input,t->volume,t->tone,t->lf,t->lr,t->rf,t->rr,t->loud); + buf[0] = TDA7432_IN; + buf[1] = t->input; + buf[2] = t->volume; + buf[3] = t->tone; + buf[4] = t->lf; + buf[5] = t->lr; + buf[6] = t->rf; + buf[7] = t->rr; + buf[8] = t->loud; + if (9 != i2c_master_send(client,buf,9)) { + printk(KERN_WARNING "tda7432: I/O error, trying tda7432_set\n"); + return -1; + } + + return 0; +} + +static void do_tda7432_init(struct i2c_client *client) +{ + struct tda7432 *t = client->data; + d2printk("tda7432: In tda7432_init\n"); + + t->input = TDA7432_STEREO_IN | /* Main (stereo) input */ + TDA7432_BASS_SYM | /* Symmetric bass cut */ + TDA7432_BASS_NORM; /* Normal bass range */ + t->volume = TDA7432_VOL_0DB; /* 0dB Volume */ + if (loudness) /* Turn loudness on? */ + t->volume |= TDA7432_LD_ON; + t->tone = TDA7432_TREBLE_0DB | /* 0dB Treble */ + TDA7432_BASS_0DB; /* 0dB Bass */ + t->lf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->lr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->rf = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->rr = TDA7432_ATTEN_0DB; /* 0dB attenuation */ + t->loud = loudness; /* insmod parameter */ + + tda7432_set(client); +} + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda7432_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tda7432 *t; + struct i2c_client *client; + d2printk("tda7432: In tda7432_attach\n"); + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = t = kmalloc(sizeof *t,GFP_KERNEL); + if (!t) + return -ENOMEM; + memset(t,0,sizeof *t); + do_tda7432_init(client); + MOD_INC_USE_COUNT; + strcpy(client->name,"TDA7432"); + printk(KERN_INFO "tda7432: init\n"); + + i2c_attach_client(client); + return 0; +} + +static int tda7432_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tda7432_attach); + return 0; +} + +static int tda7432_detach(struct i2c_client *client) +{ + struct tda7432 *t = client->data; + + do_tda7432_init(client); + i2c_detach_client(client); + + kfree(t); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int tda7432_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct tda7432 *t = client->data; + d2printk("tda7432: In tda7432_command\n"); +#if 0 + __u16 *sarg = arg; +#endif + + switch (cmd) { + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + + /* Query card - scale from TDA7432 settings to V4L settings */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + dprintk("tda7432: VIDIOCGAUDIO\n"); + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + + /* Master volume control + * V4L volume is min 0, max 65535 + * TDA7432 Volume: + * Min (-79dB) is 0x6f + * Max (+20dB) is 0x07 + * (Mask out bit 7 of vol - it's for the loudness setting) + */ + + va->volume = ( 0x6f - (t->volume & 0x7F) ) * 630; + + /* Balance depends on L,R attenuation + * V4L balance is 0 to 65535, middle is 32768 + * TDA7432 attenuation: min (0dB) is 0, max (-37.5dB) is 0x1f + * to scale up to V4L numbers, mult by 1057 + * attenuation exists for lf, lr, rf, rr + * we use only lf and rf (front channels) + */ + + if ( (t->lf) < (t->rf) ) + /* right is attenuated, balance shifted left */ + va->balance = (32768 - 1057*(t->rf)); + else + /* left is attenuated, balance shifted right */ + va->balance = (32768 + 1057*(t->lf)); + + /* Bass/treble */ + va->bass = 32768; /* brain hurts... set to middle for now */ + va->treble = 32768; /* brain hurts... set to middle for now */ + + break; /* VIDIOCGAUDIO case */ + } + + /* Set card - scale from V4L settings to TDA7432 settings */ + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + dprintk("tda7432: VIDEOCSAUDIO\n"); + + t->volume = 0x6f - ( (va->volume)/630 ); + + if (loudness) /* Turn on the loudness bit */ + t->volume |= TDA7432_LD_ON; + + if (va->balance < 32768) { + /* shifted to left, attenuate right */ + t->rr = (32768 - va->balance)/1057; + t->rf = t->rr; + } + else { + /* shifted to right, attenuate left */ + t->lf = (va->balance - 32768)/1057; + t->lr = t->lf; + } + + /* t->tone = 0xff; */ /* Brain hurts - no tone control for now... */ + + tda7432_write(client,TDA7432_VL, t->volume); + /* tda7432_write(client,TDA7432_TN, t->tone); */ + tda7432_write(client,TDA7432_LF, t->lf); + tda7432_write(client,TDA7432_LR, t->lr); + tda7432_write(client,TDA7432_RF, t->rf); + tda7432_write(client,TDA7432_RR, t->rr); + + break; + + } /* end of VIDEOCSAUDIO case */ + + default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */ + + /* nothing */ + d2printk("tda7432: Default\n"); + + } /* end of (cmd) switch */ + + return 0; +} + + +static struct i2c_driver driver = { + "i2c tda7432 driver", + I2C_DRIVERID_TDA7432, + I2C_DF_NOTIFY, + tda7432_probe, + tda7432_detach, + tda7432_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tda7432_init(void) +#endif +{ + + if ( (loudness < 0) || (loudness > 15) ) + { + printk(KERN_ERR "tda7432: loudness parameter must be between 0 and 15\n"); + return -EINVAL; + } + + + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN vanilla-2.2.15pre17/drivers/char/tda8425.c bttv-2.2.15pre17/drivers/char/tda8425.c --- vanilla-2.2.15pre17/drivers/char/tda8425.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/char/tda8425.c Mon May 1 23:03:21 2000 @@ -0,0 +1,324 @@ +/* + * for the TDA8425 chip (I don't know which cards have this) + * WARNING: THIS DRIVER WILL LOAD WITHOUT COMPLAINTS EVEN IF A DIFFERENT + * CHIP IS AT ADDRESS 0x82 (it relies on i2c to make sure that there is a + * device acknowledging that address) + * + * Copyright (c) 1998 Greg Alexander + * This code is placed under the terms of the GNU General Public License + * Code liberally copied from msp3400.c, which is by Gerd Knorr + * + * All of this should work, though it would be nice to eventually support + * balance (different left,right values). Also, the chip seems (?) to have + * two stereo inputs, so if someone has this card, could they tell me if the + * second one can be used for anything (i.e., does it have an external input + * that you can't hear even if you set input to composite?) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { + I2C_TDA8425 >> 1, + I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + +MODULE_PARM(debug,"i"); +static int debug = 0; /* insmod parameter */ +#define dprintk if (debug) printk + + +struct tda8425 { + int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */ + int stereo; + __u16 left,right; + __u16 bass,treble; +}; + +static struct i2c_driver driver; +static struct i2c_client client_template; + + +#define TDA8425_VL 0x00 /* volume left */ +#define TDA8425_VR 0x01 /* volume right */ +#define TDA8425_BA 0x02 /* bass */ +#define TDA8425_TR 0x03 /* treble */ +#define TDA8425_S1 0x08 /* switch functions */ + /* values for those registers: */ +#define TDA8425_S1_OFF 0xEE /* audio off (mute on) */ +#define TDA8425_S1_ON 0xCE /* audio on (mute off) - "linear stereo" mode */ + + +/* ******************************** * + * functions for talking to TDA8425 * + * ******************************** */ + +static int tda8425_write(struct i2c_client *client, int addr, int val) +{ + unsigned char buffer[2]; + + buffer[0] = addr; + buffer[1] = val; + if (2 != i2c_master_send(client,buffer,2)) { + printk(KERN_WARNING "tda8425: I/O error, trying (write %d 0x%x)\n", + addr, val); + return -1; + } + return 0; +} + +static void tda8425_set(struct i2c_client *client) +{ + struct tda8425 *tda = client->data; + + /* mode is ignored today */ + dprintk(KERN_DEBUG "tda8425_set(%04x,%04x,%04x,%04x)\n",tda->left>>10,tda->right>>10,tda->bass>>12,tda->treble>>12); + tda8425_write(client, TDA8425_VL, tda->left>>10 |0xC0); + tda8425_write(client, TDA8425_VR, tda->right>>10 |0xC0); + tda8425_write(client, TDA8425_BA, tda->bass>>12 |0xF0); + tda8425_write(client, TDA8425_TR, tda->treble>>12|0xF0); +} + +static void do_tda8425_init(struct i2c_client *client) +{ + struct tda8425 *tda = client->data; + + tda->left=tda->right =61440; /* 0dB */ + tda->bass=tda->treble=24576; /* 0dB */ + tda->mode=AUDIO_OFF; + tda->stereo=1; + /* left=right=0x27<<10, bass=treble=0x07<<12 */ + tda8425_write(client, TDA8425_S1, TDA8425_S1_OFF); /* mute */ + tda8425_set(client); +} + +static void tda8425_audio(struct i2c_client *client, int mode) +{ + struct tda8425 *tda = client->data; + + /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */ + dprintk(KERN_DEBUG "tda8425_audio:%d (T,R,E,I,O)\n",mode); + tda->mode=mode; + tda8425_write(client, TDA8425_S1, + (mode==AUDIO_OFF)?TDA8425_S1_OFF:TDA8425_S1_ON); + /* this is the function we'll need to change if it turns out the + * input-selecting capabilities should be used. */ +} + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda8425_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tda8425 *tda; + struct i2c_client *client; + + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = tda = kmalloc(sizeof *tda,GFP_KERNEL); + if (!tda) + return -ENOMEM; + memset(tda,0,sizeof *tda); + do_tda8425_init(client); + MOD_INC_USE_COUNT; + strcpy(client->name,"TDA8425"); + printk(KERN_INFO "tda8425: init\n"); + + i2c_attach_client(client); + return 0; +} + +static int tda8425_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tda8425_attach); + return 0; +} + + +static int tda8425_detach(struct i2c_client *client) +{ + struct tda8425 *tda = client->data; + + do_tda8425_init(client); + i2c_detach_client(client); + + kfree(tda); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int tda8425_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct tda8425 *tda = client->data; + __u16 *sarg = arg; + + switch (cmd) { + case AUDC_SET_RADIO: + tda8425_audio(client,AUDIO_RADIO); + break; + case AUDC_SET_INPUT: + tda8425_audio(client,*sarg); + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + va->volume=MAX(tda->left,tda->right); + va->balance=(32768*MIN(tda->left,tda->right))/ + (va->volume ? va->volume : 1); + va->balance=(tda->leftright)? + (65535-va->balance) : va->balance; + va->bass = tda->bass; + va->treble = tda->treble; + break; + } + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + + tda->left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + tda->right = (MIN(va->balance,32768) * + va->volume) / 32768; + tda->bass = va->bass; + tda->treble = va->treble; + tda8425_set(client); + break; + } + +#if 0 + /* --- old, obsolete interface --- */ + case AUDC_GET_VOLUME_LEFT: + *sarg = tda->left; + break; + case AUDC_GET_VOLUME_RIGHT: + *sarg = tda->right; + break; + case AUDC_SET_VOLUME_LEFT: + tda->left = *sarg; + tda8425_set(client); + break; + case AUDC_SET_VOLUME_RIGHT: + tda->right = *sarg; + tda8425_set(client); + break; + + case AUDC_GET_BASS: + *sarg = tda->bass; + break; + case AUDC_SET_BASS: + tda->bass = *sarg; + tda8425_set(client); + break; + + case AUDC_GET_TREBLE: + *sarg = tda->treble; + break; + case AUDC_SET_TREBLE: + tda->treble = *sarg; + tda8425_set(client); + break; + + case AUDC_GET_STEREO: + *sarg = tda->stereo?VIDEO_SOUND_STEREO:VIDEO_SOUND_MONO; + break; + case AUDC_SET_STEREO: + tda->stereo=(*sarg==VIDEO_SOUND_MONO)?0:1; + /* TODO: make this write to the TDA9850? */ + break; + +/* case AUDC_SWITCH_MUTE: someday, maybe -- not a lot of point to + case AUDC_NEWCHANNEL: it and it would require preserving state + case AUDC_GET_DC: huh?? (not used by bttv.c) +*/ +#endif + default: + /* nothing */ + } + return 0; +} + + +static struct i2c_driver driver = { + "i2c tda8424 driver", + I2C_DRIVERID_TDA8425, + I2C_DF_NOTIFY, + tda8425_probe, + tda8425_detach, + tda8425_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tda8425_init(void) +#endif +{ + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN vanilla-2.2.15pre17/drivers/char/tda985x.c bttv-2.2.15pre17/drivers/char/tda985x.c --- vanilla-2.2.15pre17/drivers/char/tda985x.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/char/tda985x.c Mon May 1 23:03:21 2000 @@ -0,0 +1,536 @@ +/* + * For the TDA9850 and TDA9855 chips + * (The TDA9855 is used on the Diamond DTV2000 and the TDA9850 is used + * on STB cards. Other cards probably use these chips as well.) + * This driver will not complain if used with any + * other i2c device with the same address. + * + * Copyright (c) 1999 Gerd Knorr + * TDA9850 code and TDA9855.c merger by Eric Sandeen (eric_sandeen@bigfoot.com) + * This code is placed under the terms of the GNU General Public License + * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * Which was based on tda8425.c by Greg Alexander (c) 1998 + * + * OPTIONS: + * debug - set to 1 if you'd like to see debug messages + * - set to 2 if you'd like to be flooded with debug messages + * chip - set to 9850 or 9855 to select your chip (default 9855) + * + * TODO: + * Fix channel change bug - sound goes out when changeing channels, mute + * and unmote to fix. - Is this still here? + * Fine tune sound + * Get rest of capabilities into video_audio struct... + * + * Revision 0.5 - cleaned up debugging messages, added debug level=2 + * Revision: 0.4 - check for correct chip= insmod value + * also cleaned up comments a bit + * Revision: 0.3 - took out extraneous tda985x_write in tda985x_command + * Revision: 0.2 - added insmod option chip= + * Revision: 0.1 - original version + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + +MODULE_PARM(debug,"i"); +MODULE_PARM(chip,"i"); +MODULE_PARM_DESC(chip, "Type of chip to handle: 9850 or 9855"); + +static int debug = 0; /* insmod parameter */ +static int chip = 9855; /* insmod parameter */ + +/* Addresses to scan */ +#define I2C_TDA985x_L 0xb4 +#define I2C_TDA985x_H 0xb6 +static unsigned short normal_i2c[] = {I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = { + I2C_TDA985x_L >> 1, + I2C_TDA985x_H >> 1, + I2C_CLIENT_END +}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + +/* This is a superset of the TDA9850 and TDA9855 members */ + +struct tda985x { + int addr; + int rvol, lvol; + int bass, treble, sub; + int c4, c5, c6, c7; + int a1, a2, a3; +}; + +static struct i2c_driver driver; +static struct i2c_client client_template; + +#define dprintk if (debug) printk +#define d2printk if (debug == 2) printk + +/* The TDA9850 and TDA9855 are both made by Philips Semiconductor + * http://www.semiconductors.philips.com + * TDA9850: I2C-bus controlled BTSC stereo/SAP decoder + * TDA9855: I2C-bus controlled BTSC stereo/SAP decoder and audio processor + * + * The TDA9850 has more or less a subset of the functions that the TDA9855 + * has. As a result, we can re-use many of these defines. Anything with + * TDA9855 is specific to that chip, anything with TDA9850 is specific + * to that chip, and anything with TDA985x is valid for either. + * + * To complicate things further, the TDA9850 uses labels C1 through C4 + * for subaddresses 0x04 through 0x07, while the TDA9855 uses + * C1 through C3 for subadresses 0x05 through 0x07 - quite confusing. + * To help keep things straight, I have renamed the various C[1,4] labels + * to C[4,7] so that the numerical label matches the hex value of the + * subaddress for both chips. At least the A[1,3] labels line up. :) + */ + + /* subaddresses for TDA9855 */ +#define TDA9855_VR 0x00 /* Volume, right */ +#define TDA9855_VL 0x01 /* Volume, left */ +#define TDA9855_BA 0x02 /* Bass */ +#define TDA9855_TR 0x03 /* Treble */ +#define TDA9855_SW 0x04 /* Subwoofer - not connected on DTV2000 */ + + /* subaddresses for TDA9850 */ +#define TDA9850_C4 0x04 /* Control 1 for TDA9850 */ + + /* subaddesses for both chips */ +#define TDA985x_C5 0x05 /* Control 2 for TDA9850, Control 1 for TDA9855 */ +#define TDA985x_C6 0x06 /* Control 3 for TDA9850, Control 2 for TDA9855 */ +#define TDA985x_C7 0x07 /* Control 4 for TDA9850, Control 3 for TDA9855 */ +#define TDA985x_A1 0x08 /* Alignment 1 for both chips */ +#define TDA985x_A2 0x09 /* Alignment 2 for both chips */ +#define TDA985x_A3 0x0a /* Alignment 3 for both chips */ + + /* Masks for bits in TDA9855 subaddresses */ +/* 0x00 - VR in TDA9855 */ +/* 0x01 - VL in TDA9855 */ +/* lower 7 bits control gain from -71dB (0x28) to 16dB (0x7f) + * in 1dB steps - mute is 0x27 */ + + +/* 0x02 - BA in TDA9855 */ +/* lower 5 bits control bass gain from -12dB (0x06) to 16.5dB (0x19) + * in .5dB steps - 0 is 0x0E */ + + +/* 0x03 - TR in TDA9855 */ +/* 4 bits << 1 control treble gain from -12dB (0x3) to 12dB (0xb) + * in 3dB steps - 0 is 0x7 */ + + /* Masks for bits in both chips' subaddresses */ +/* 0x04 - SW in TDA9855, C4/Control 1 in TDA9850 */ +/* Unique to TDA9855: */ +/* 4 bits << 2 control subwoofer/surround gain from -14db (0x1) to 14db (0xf) + * in 3dB steps - mute is 0x0 */ + +/* Unique to TDA9850: */ +/* lower 4 bits control stereo noise threshold, over which stereo turns off + * set to values of 0x00 through 0x0f for Ster1 through Ster16 */ + + +/* 0x05 - C5 - Control 1 in TDA9855 , Control 2 in TDA9850*/ +/* Unique to TDA9855: */ +#define TDA9855_MUTE 1<<7 /* GMU, Mute at outputs */ +#define TDA9855_AVL 1<<6 /* AVL, Automatic Volume Level */ +#define TDA9855_LOUD 1<<5 /* Loudness, 1==off */ +#define TDA9855_SUR 1<<3 /* Surround / Subwoofer 1==.5(L-R) 0==.5(L+R) */ + /* Bits 0 to 3 select various combinations + * of line in and line out, only the + * interesting ones are defined */ +#define TDA9855_EXT 1<<2 /* Selects inputs LIR and LIL. Pins 41 & 12 */ +#define TDA9855_INT 0 /* Selects inputs LOR and LOL. (internal) */ + +/* Unique to TDA9850: */ +/* lower 4 bits contol SAP noise threshold, over which SAP turns off + * set to values of 0x00 through 0x0f for SAP1 through SAP16 */ + + +/* 0x06 - C6 - Control 2 in TDA9855, Control 3 in TDA9850 */ +/* Common to TDA9855 and TDA9850: */ +#define TDA985x_SAP 3<<6 /* Selects SAP output, mute if not received */ +#define TDA985x_STEREO 1<<6 /* Selects Stereo ouput, mono if not received */ +#define TDA985x_MONO 0 /* Forces Mono output */ +#define TDA985x_LMU 1<<3 /* Mute (LOR/LOL for 9855, OUTL/OUTR for 9850) */ + +/* Unique to TDA9855: */ +#define TDA9855_TZCM 1<<5 /* If set, don't mute till zero crossing */ +#define TDA9855_VZCM 1<<4 /* If set, don't change volume till zero crossing*/ +#define TDA9855_LINEAR 0 /* Linear Stereo */ +#define TDA9855_PSEUDO 1 /* Pseudo Stereo */ +#define TDA9855_SPAT_30 2 /* Spatial Stereo, 30% anti-phase crosstalk */ +#define TDA9855_SPAT_50 3 /* Spatial Stereo, 52% anti-phase crosstalk */ +#define TDA9855_E_MONO 7 /* Forced mono - mono select elseware, so useless*/ + + +/* 0x07 - C7 - Control 3 in TDA9855, Control 4 in TDA9850 */ +/* Common to both TDA9855 and TDA9850: */ +/* lower 4 bits control input gain from -3.5dB (0x0) to 4dB (0xF) + * in .5dB steps - 0dB is 0x7 */ + + +/* 0x08, 0x09 - A1 and A2 (read/write) */ +/* Common to both TDA9855 and TDA9850: */ +/* lower 5 bites are wideband and spectral expander alignment + * from 0x00 to 0x1f - nominal at 0x0f and 0x10 (read/write) */ +#define TDA985x_STP 1<<5 /* Stereo Pilot/detect (read-only) */ +#define TDA985x_SAPP 1<<6 /* SAP Pilot/detect (read-only) */ +#define TDA985x_STS 1<<7 /* Stereo trigger 1= <35mV 0= <30mV (write-only)*/ + + +/* 0x0a - A3 */ +/* Common to both TDA9855 and TDA9850: */ +/* lower 3 bits control timing current for alignment: -30% (0x0), -20% (0x1), + * -10% (0x2), nominal (0x3), +10% (0x6), +20% (0x5), +30% (0x4) */ +#define TDA985x_ADJ 1<<7 /* Stereo adjust on/off (wideband and spectral */ + +/* Unique to TDA9855: */ +/* 2 bits << 5 control AVL attack time: 420ohm (0x0), 730ohm (0x2), + * 1200ohm (0x1), 2100ohm (0x3) */ + + +/* Begin code */ + +static int tda985x_write(struct i2c_client *client, int subaddr, int val) +{ + unsigned char buffer[2]; + d2printk("tda985x: In tda985x_write\n"); + dprintk("tda985x: Writing %d 0x%x\n", subaddr, val); + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(client,buffer,2)) { + printk(KERN_WARNING "tda985x: I/O error, trying (write %d 0x%x)\n", + subaddr, val); + return -1; + } + return 0; +} + +static int tda985x_read(struct i2c_client *client) +{ + unsigned char buffer; + d2printk("tda985x: In tda985x_read\n"); + if (1 != i2c_master_recv(client,&buffer,1)) { + printk(KERN_WARNING "tda985x: I/O error, trying (read)\n"); + return -1; + } + dprintk("tda985x: Read 0x%02x\n", buffer); + return buffer; +} + +static int tda985x_set(struct i2c_client *client) +{ + struct tda985x *t = client->data; + unsigned char buf[16]; + d2printk("tda985x: In tda985x_set\n"); + + if (chip == 9855) + { + dprintk(KERN_INFO + "tda985x: tda985x_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", + t->rvol,t->lvol,t->bass,t->treble,t->sub, + t->c5,t->c6,t->c7,t->a1,t->a2,t->a3); + buf[0] = TDA9855_VR; + buf[1] = t->rvol; + buf[2] = t->lvol; + buf[3] = t->bass; + buf[4] = t->treble; + buf[5] = t->sub; + buf[6] = t->c5; + buf[7] = t->c6; + buf[8] = t->c7; + buf[9] = t->a1; + buf[10] = t->a2; + buf[11] = t->a3; + if (12 != i2c_master_send(client,buf,12)) { + printk(KERN_WARNING "tda985x: I/O error, trying tda985x_set\n"); + return -1; + } + } + + else if (chip == 9850) + { + dprintk(KERN_INFO + "tda986x: tda985x_set(0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x)\n", + t->c4,t->c5,t->c6,t->c7,t->a1,t->a2,t->a3); + buf[0] = TDA9850_C4; + buf[1] = t->c4; + buf[2] = t->c5; + buf[3] = t->c6; + buf[4] = t->c7; + buf[5] = t->a1; + buf[6] = t->a2; + buf[7] = t->a3; + if (8 != i2c_master_send(client,buf,8)) { + printk(KERN_WARNING "tda985x: I/O error, trying tda985x_set\n"); + return -1; + } + } + + return 0; +} + +static void do_tda985x_init(struct i2c_client *client) +{ + struct tda985x *t = client->data; + d2printk("tda985x: In tda985x_init\n"); + + if (chip == 9855) + { + printk("tda985x: Using tda9855 options\n"); + t->rvol = 0x6f; /* 0dB */ + t->lvol = 0x6f; /* 0dB */ + t->bass = 0x0e; /* 0dB */ + t->treble = (0x07 << 1); /* 0dB */ + t->sub = 0x8 << 2; /* 0dB */ + t->c5 = TDA9855_MUTE | TDA9855_AVL | + TDA9855_LOUD | TDA9855_INT; + /* Set Mute, AVL, Loudness off, Internal sound */ + t->c6 = TDA985x_STEREO | TDA9855_LINEAR | + TDA9855_TZCM | TDA9855_VZCM; + /* Stereo linear mode, also wait til zero crossings */ + t->c7 = 0x07; /* 0dB input gain */ + } + + else if (chip == 9850) + { + printk("tda985x: Using tda9850 options\n"); + t->c4 = 0x08; /* Set stereo noise thresh to nominal */ + t->c5 = 0x08; /* Set SAP noise threshold to nominal */ + t->c6 = TDA985x_STEREO; /* Select Stereo mode for decoder */ + t->c7 = 0x07; /* 0dB input gain */ + } + + /* The following is valid for both chip types */ + t->a1 = 0x10; /* Select nominal wideband expander */ + t->a2 = 0x10; /* Select nominal spectral expander and 30mV trigger */ + t->a3 = 0x3; /* Set: nominal timing current, 420ohm AVL attack */ + + tda985x_set(client); +} + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda985x_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tda985x *t; + struct i2c_client *client; + d2printk("tda985x: In tda985x_attach\n"); + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = t = kmalloc(sizeof *t,GFP_KERNEL); + if (!t) + return -ENOMEM; + memset(t,0,sizeof *t); + do_tda985x_init(client); + MOD_INC_USE_COUNT; + strcpy(client->name,"TDA985x"); + printk(KERN_INFO "tda985x: init\n"); + + i2c_attach_client(client); + return 0; +} + +static int tda985x_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tda985x_attach); + return 0; +} + +static int tda985x_detach(struct i2c_client *client) +{ + struct tda985x *t = client->data; + + do_tda985x_init(client); + i2c_detach_client(client); + + kfree(t); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int tda985x_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct tda985x *t = client->data; + d2printk("tda985x: In tda985x_command\n"); +#if 0 + __u16 *sarg = arg; +#endif + + switch (cmd) { + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + dprintk("tda985x: VIDIOCGAUDIO\n"); + if (chip == 9855) + { + int left,right; + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + + /* min is 0x27 max is 0x7f, vstep is 2e8 */ + left = (t->lvol-0x27)*0x2e8; + right = (t->rvol-0x27)*0x2e8; + va->volume=MAX(left,right); + va->balance=(32768*MIN(left,right))/ + (va->volume ? va->volume : 1); + va->balance=(leftbalance) : va->balance; + va->bass = (t->bass-0x6)*0xccc; /* min 0x6 max 0x19 */ + va->treble = ((t->treble>>1)-0x3)*0x1c71; + } + + /* Valid for both chips: */ + { + va->mode = ((TDA985x_STP | TDA985x_SAPP) & + tda985x_read(client)) >> 4; + /* Add mono mode regardless of SAP and stereo */ + /* Allows forced mono */ + va->mode |= VIDEO_SOUND_MONO; + } + + break; /* VIDIOCGAUDIO case */ + } + + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + dprintk("tda985x: VIDEOCSAUDIO\n"); + if (chip == 9855) + { + int left,right; + + left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + right = (MIN(va->balance,32768) * + va->volume) / 32768; + t->lvol = left/0x2e8+0x27; + t->rvol = right/0x2e8+0x27; + t->bass = va->bass/0xccc+0x6; + t->treble = (va->treble/0x1c71+0x3)<<1; + tda985x_write(client,TDA9855_VL,t->lvol); + tda985x_write(client,TDA9855_VR,t->rvol); + tda985x_write(client,TDA9855_BA, t->bass); + tda985x_write(client,TDA9855_TR,t->treble); + } + + /* The following is valid for both chips */ + + switch (va->mode) { + case VIDEO_SOUND_MONO: + dprintk("tda985x: VIDEO_SOUND_MONO\n"); + t->c6= TDA985x_MONO | (t->c6 & 0x3f); + tda985x_write(client,TDA985x_C6,t->c6); + break; + case VIDEO_SOUND_STEREO: + dprintk("tda985x: VIDEO_SOUND_STEREO\n"); + t->c6= TDA985x_STEREO | (t->c6 & 0x3f); + tda985x_write(client,TDA985x_C6,t->c6); + break; + case VIDEO_SOUND_LANG1: + dprintk("tda985x: VIDEO_SOUND_LANG1\n"); + t->c6= TDA985x_SAP | (t->c6 & 0x3f); + tda985x_write(client,TDA985x_C6,t->c6); + break; + } /* End of (va->mode) switch */ + + break; + + } /* end of VIDEOCSAUDIO case */ + + default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */ + + /* nothing */ + d2printk("tda985x: Default\n"); + + } /* end of (cmd) switch */ + + return 0; +} + + +static struct i2c_driver driver = { + "i2c tda985x driver", + I2C_DRIVERID_TDA9855, /* Get new one for TDA985x? */ + I2C_DF_NOTIFY, + tda985x_probe, + tda985x_detach, + tda985x_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tda985x_init(void) +#endif +{ + if ( (chip != 9850) && (chip != 9855) ) + { + printk(KERN_ERR "tda985x: chip parameter must be 9850 or 9855\n"); + return -EINVAL; + } + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN vanilla-2.2.15pre17/drivers/char/tda9875.c bttv-2.2.15pre17/drivers/char/tda9875.c --- vanilla-2.2.15pre17/drivers/char/tda9875.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/char/tda9875.c Mon May 1 23:03:21 2000 @@ -0,0 +1,371 @@ +/* + * For the TDA9875 chip + * (The TDA9875 is used on the Diamond DTV2000 french version + * Other cards probably use these chips as well.) + * This driver will not complain if used with any + * other i2c device with the same address. + * + * Copyright (c) 2000 Guillamue Delvit based on Gerd Knorr source and + * Eric Sandeen + * This code is placed under the terms of the GNU General Public License + * Based on tda9855.c by Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * Which was based on tda8425.c by Greg Alexander (c) 1998 + * + * OPTIONS: + * debug - set to 1 if you'd like to see debug messages + * + * Revision: 0.1 - original version + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + +/* This driver ID is brand new, so define it if it's not in i2c-id.h yet */ +#ifndef I2C_DRIVERID_TDA9875 + #define I2C_DRIVERID_TDA9875 28 +#endif + + +MODULE_PARM(debug,"i"); + +static int debug = 0; /* insmod parameter */ + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { + I2C_TDA9875 >> 1, + I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + +/* This is a superset of the TDA9875 */ +struct tda9875 { + int mode; + int rvol, lvol; + int bass, treble; +}; + + +static struct i2c_driver driver; +static struct i2c_client client_template; + +#define dprintk if (debug) printk + +/* The TDA9875 is made by Philips Semiconductor + * http://www.semiconductors.philips.com + * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator + * + */ + + /* subaddresses for TDA9875 */ +#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ +#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ +#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ +#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ + +#define TDA9875_CH1V 0x0c /*Chanel 1 volume (mute)*/ +#define TDA9875_CH2V 0x0d /*Chanel 2 volume (mute)*/ +#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ +#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ + +#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ +#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ +#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ +#define TDA9875_MVL 0x1a /* Main volume gauche */ +#define TDA9875_MVR 0x1b /* Main volume droite */ +#define TDA9875_MBA 0x1d /* Main Basse */ +#define TDA9875_MTR 0x1e /* Main treble */ +#define TDA9875_ACS 0x1f /* Auxilary channel select (FM) 0b0000000*/ +#define TDA9875_AVL 0x20 /* Auxilary volume gauche */ +#define TDA9875_AVR 0x21 /* Auxilary volume droite */ +#define TDA9875_ABA 0x22 /* Auxilary Basse */ +#define TDA9875_ATR 0x23 /* Auxilary treble */ + +#define TDA9875_MSR 0x02 /* Monitor select register */ +#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ +#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ +#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ +#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ +#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ +#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ +#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ +#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ +#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ + +/* values */ +#define TDA9875_MUTE_ON 0xff /* general mute */ +#define TDA9875_MUTE_OFF 0xcc /* general no mute */ + + + +/* Begin code */ + +static int tda9875_write(struct i2c_client *client, int subaddr, int val) +{ + unsigned char buffer[2]; + dprintk("In tda9875_write\n"); + dprintk("Writing %d 0x%x\n", subaddr, val); + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(client,buffer,2)) { + printk(KERN_WARNING "tda9875: I/O error, trying (write %d 0x%x)\n", + subaddr, val); + return -1; + } + return 0; +} + +#if 0 +static int tda9875_read(struct i2c_client *client) +{ + unsigned char buffer; + dprintk("In tda9875_read\n"); + if (1 != i2c_master_recv(client,&buffer,1)) { + printk(KERN_WARNING "tda9875: I/O error, trying (read)\n"); + return -1; + } + dprintk("Read 0x%02x\n", buffer); + return buffer; +} +#endif + +static void tda9875_set(struct i2c_client *client) +{ + struct tda9875 *tda = client->data; + + dprintk(KERN_DEBUG "tda9875_set(%04x,%04x,%04x,%04x)\n",tda->lvol,tda->rvol,tda->bass,tda->treble); + tda9875_write(client, TDA9875_MVL, tda->lvol / 600 - 84); + tda9875_write(client, TDA9875_MVR, tda->rvol / 600 -84); + tda9875_write(client, TDA9875_MBA, tda->bass / 2340 -12); + tda9875_write(client, TDA9875_MTR, tda->treble / 2621 -12); +} + +static void do_tda9875_init(struct i2c_client *client) +{ + struct tda9875 *t = client->data; + dprintk("In tda9875_init\n"); + tda9875_write(client, TDA9875_CFG, 0xd0 ); /*reg de config 0 (reset)*/ + tda9875_write(client, TDA9875_MSR, 0x03 ); /* Monitor 0b00000XXX*/ + tda9875_write(client, TDA9875_C1MSB, 0x00 ); /*Car1(FM) MSB XMHz*/ + tda9875_write(client, TDA9875_C1MIB, 0x00 ); /*Car1(FM) MIB XMHz*/ + tda9875_write(client, TDA9875_C1LSB, 0x00 ); /*Car1(FM) LSB XMHz*/ + tda9875_write(client, TDA9875_C2MSB, 0x00 ); /*Car2(NICAM) MSB XMHz*/ + tda9875_write(client, TDA9875_C2MIB, 0x00 ); /*Car2(NICAM) MIB XMHz*/ + tda9875_write(client, TDA9875_C2LSB, 0x00 ); /*Car2(NICAM) LSB XMHz*/ + tda9875_write(client, TDA9875_DCR, 0x00 ); /*Demod config 0x00*/ + tda9875_write(client, TDA9875_DEEM, 0x44 ); /*DE-Emph 0b0100 0100*/ + tda9875_write(client, TDA9875_FMAT, 0x00 ); /*FM Matrix reg 0x00*/ + tda9875_write(client, TDA9875_SC1, 0x00 ); /* SCART 1 (SC1)*/ + tda9875_write(client, TDA9875_SC2, 0x01 ); /* SCART 2 (sc2)*/ + + tda9875_write(client, TDA9875_CH1V, 0x10 ); /* Chanel volume 1 mute*/ + tda9875_write(client, TDA9875_CH2V, 0x10 ); /* Chanel volume 2 mute */ + tda9875_write(client, TDA9875_DACOS, 0x02 ); /* sig DAC i/o(in:nicam)*/ + tda9875_write(client, TDA9875_ADCIS, 0x6f ); /* sig ADC input(in:mono)*/ + tda9875_write(client, TDA9875_LOSR, 0x00 ); /* line out (in:mono)*/ + tda9875_write(client, TDA9875_AER, 0x00 ); /*06 Effect (AVL+PSEUDO) */ + tda9875_write(client, TDA9875_MCS, 0x44 ); /* Main ch select (DAC) */ + tda9875_write(client, TDA9875_MVL, 0x03 ); /* Vol Main left 10dB */ + tda9875_write(client, TDA9875_MVR, 0x03 ); /* Vol Main right 10dB*/ + tda9875_write(client, TDA9875_MBA, 0x00 ); /* Main Bass Main 0dB*/ + tda9875_write(client, TDA9875_MTR, 0x00 ); /* Main Treble Main 0dB*/ + tda9875_write(client, TDA9875_ACS, 0x44 ); /* Aux chan select (dac)*/ + tda9875_write(client, TDA9875_AVL, 0x00 ); /* Vol Aux left 0dB*/ + tda9875_write(client, TDA9875_AVR, 0x00 ); /* Vol Aux right 0dB*/ + tda9875_write(client, TDA9875_ABA, 0x00 ); /* Aux Bass Main 0dB*/ + tda9875_write(client, TDA9875_ATR, 0x00 ); /* Aux Aigus Main 0dB*/ + + tda9875_write(client, TDA9875_MUT, 0xcc ); /* General mute */ + + t->mode=AUDIO_MUTE; + t->lvol=t->rvol =51000; /* 0dB */ + t->bass=30420; /* 0dB */ + t->treble=34073; /* 0dB */ + tda9875_set(client); + +} + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda9875_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tda9875 *t; + struct i2c_client *client; + dprintk("In tda9875_attach\n"); + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = t = kmalloc(sizeof *t,GFP_KERNEL); + if (!t) + return -ENOMEM; + memset(t,0,sizeof *t); + do_tda9875_init(client); + MOD_INC_USE_COUNT; + strcpy(client->name,"TDA9875"); + printk(KERN_INFO "tda9875: init\n"); + + i2c_attach_client(client); + return 0; +} + +static int tda9875_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tda9875_attach); + return 0; +} + +static int tda9875_detach(struct i2c_client *client) +{ + struct tda9875 *t = client->data; + + do_tda9875_init(client); + i2c_detach_client(client); + + kfree(t); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int tda9875_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct tda9875 *t = client->data; + + dprintk("In tda9875_command...\n"); + + switch (cmd) { + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + int left,right; + + dprintk("VIDIOCGAUDIO\n"); + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + + /* min is -84 max is 24 */ + left = (t->lvol+85)*600; + right = (t->rvol+85)*600; + va->volume=MAX(left,right); + va->balance=(32768*MIN(left,right))/ + (va->volume ? va->volume : 1); + va->balance=(leftbalance) : va->balance; + va->bass = (t->bass+13)*2340; /* min -12 max +15 */ + va->treble = (t->treble+13)*2621;/* min -12 max +12 */ + + va->mode |= VIDEO_SOUND_MONO; + + + break; /* VIDIOCGAUDIO case */ + } + + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + int left,right; + + dprintk("VIDEOCSAUDIO...\n"); + left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + right = (MIN(va->balance,32768) * + va->volume) / 32768; + t->lvol = left/600-84; + t->rvol = right/600-84; + t->bass = va->bass/2340-12; + t->treble = va->treble/2621-12; + tda9875_set(client); + + break; + + } /* end of VIDEOCSAUDIO case */ + + default: /* Not VIDEOCGAUDIO or VIDEOCSAUDIO */ + + /* nothing */ + dprintk("Default\n"); + + } /* end of (cmd) switch */ + + return 0; +} + + +static struct i2c_driver driver = { + "i2c tda9875 driver", + I2C_DRIVERID_TDA9875, /* Get new one for TDA9875 */ + I2C_DF_NOTIFY, + tda9875_probe, + tda9875_detach, + tda9875_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tda9875_init(void) +#endif +{ + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ + diff -urN vanilla-2.2.15pre17/drivers/char/tea6300.c bttv-2.2.15pre17/drivers/char/tea6300.c --- vanilla-2.2.15pre17/drivers/char/tea6300.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/char/tea6300.c Mon May 1 23:03:21 2000 @@ -0,0 +1,344 @@ +/* + * for the TEA6300 chip (only found on Gateway STB TV/FM cards tho the best + * of my knowledge) + * WARNING: THIS DRIVER WILL LOAD WITHOUT COMPLAINTS EVEN IF THE WRONG + * CHIP (i.e., an MSP3400) IS ON I2C ADDRESS 0x80 (it relies on i2c to + * make sure that there is a device acknowledging that address). This + * is a potential problem because the MSP3400 is very popular and does + * use this address! You have been warned! + * + * Copyright (c) 1998 Greg Alexander + * This code is placed under the terms of the GNU General Public License + * Code liberally copied from msp3400.c, which is by Gerd Knorr + * + * All of this should work, though it would be nice to eventually support + * balance (different left,right values) and, if someone ever finds a card + * with the support (or if you're careful with a soldering iron), fade + * (front/back). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + + +/* Addresses to scan */ +#define I2C_TEA6300 0x80 +static unsigned short normal_i2c[] = { + I2C_TEA6300 >> 1, + I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + + +MODULE_PARM(debug,"i"); +static int debug = 0; /* insmod parameter */ + +#define dprintk if (debug) printk + + +struct tea6300 { + int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */ + int stereo; + __u16 left,right; + __u16 bass,treble; +}; + +static struct i2c_driver driver; +static struct i2c_client client_template; + +#define TEA6300_VL 0x00 /* volume left */ +#define TEA6300_VR 0x01 /* volume right */ +#define TEA6300_BA 0x02 /* bass */ +#define TEA6300_TR 0x03 /* treble */ +#define TEA6300_FA 0x04 /* fader control */ +#define TEA6300_S 0x05 /* switch register */ + /* values for those registers: */ +#define TEA6300_S_SA 0x01 /* stereo A input */ +#define TEA6300_S_SB 0x02 /* stereo B */ +#define TEA6300_S_SC 0x04 /* stereo C */ +#define TEA6300_S_GMU 0x80 /* general mute */ + + +/* ******************************** * + * functions for talking to TEA6300 * + * ******************************** */ + +static int tea6300_write(struct i2c_client *client, int addr, int val) +{ + unsigned char buffer[2]; + + buffer[0] = addr; + buffer[1] = val; + if (2 != i2c_master_send(client,buffer,2)) { + printk(KERN_WARNING "tea6300: I/O error, trying (write %d 0x%x)\n", + addr, val); + return -1; + } + return 0; +} + +static void tea6300_set(struct i2c_client *client) +{ + struct tea6300 *tea = client->data; + + /* mode is ignored today */ + dprintk(KERN_DEBUG "tea6300_set(%04x,%04x,%04x,%04x)\n",tea->left>>10,tea->right>>10,tea->bass>>12,tea->treble>>12); + tea6300_write(client, TEA6300_VL, tea->left>>10 ); + tea6300_write(client, TEA6300_VR, tea->right>>10 ); + tea6300_write(client, TEA6300_BA, tea->bass>>12 ); + tea6300_write(client, TEA6300_TR, tea->treble>>12); +} + +static void do_tea6300_init(struct i2c_client *client) +{ + struct tea6300 *tea = client->data; + + tea->left=tea->right =49152; /* -10dB (loud enough, but not beyond + normal line levels - so as to avoid + clipping */ + tea->bass=tea->treble=28672; /* 0dB */ + tea->mode=AUDIO_OFF; + tea->stereo=1; + /* left=right=0x27<<10, bass=treble=0x07<<12 */ + tea6300_write(client, TEA6300_FA, 0x3f ); /* fader off */ + tea6300_write(client, TEA6300_S , TEA6300_S_GMU); /* mute */ + tea6300_set(client); +} + +static void tea6300_audio(struct i2c_client *client, int mode) +{ + struct tea6300 *tea = client->data; + + /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */ + dprintk(KERN_DEBUG "tea6300_audio:%d (T,R,E,I,O)\n",mode); + tea->mode=mode; + if (mode==AUDIO_OFF) { /* just mute it */ + tea6300_write(client, TEA6300_S, TEA6300_S_GMU); + return; + } + switch(mode) { + case AUDIO_TUNER: + tea6300_write(client, TEA6300_S, TEA6300_S_SA); + break; + case AUDIO_RADIO: + tea6300_write(client, TEA6300_S, TEA6300_S_SB); + break; + case AUDIO_EXTERN: + tea6300_write(client, TEA6300_S, TEA6300_S_SC); + break; + } +} + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tea6300_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tea6300 *tea; + struct i2c_client *client; + + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = tea = kmalloc(sizeof *tea,GFP_KERNEL); + if (!tea) + return -ENOMEM; + memset(tea,0,sizeof *tea); + do_tea6300_init(client); + + MOD_INC_USE_COUNT; + strcpy(client->name,"TEA6300T"); + printk(KERN_INFO "tea6300: initialized\n"); + + i2c_attach_client(client); + return 0; +} + +static int tea6300_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tea6300_attach); + return 0; +} + +static int tea6300_detach(struct i2c_client *client) +{ + struct tea6300 *tea = client->data; + + do_tea6300_init(client); + i2c_detach_client(client); + + kfree(tea); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int +tea6300_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct tea6300 *tea = client->data; + __u16 *sarg = arg; + + switch (cmd) { + case AUDC_SET_RADIO: + tea6300_audio(client,AUDIO_RADIO); + break; + case AUDC_SET_INPUT: + tea6300_audio(client,*sarg); + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; + va->volume=MAX(tea->left,tea->right); + va->balance=(32768*MIN(tea->left,tea->right))/ + (va->volume ? va->volume : 1); + va->balance=(tea->leftright)? + (65535-va->balance) : va->balance; + va->bass = tea->bass; + va->treble = tea->treble; + break; + } + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + + tea->left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + tea->right = (MIN(va->balance,32768) * + va->volume) / 32768; + tea->bass = va->bass; + tea->treble = va->treble; + tea6300_set(client); + break; + } +#if 0 + /* --- old, obsolete interface --- */ + case AUDC_GET_VOLUME_LEFT: + *sarg = tea->left; + break; + case AUDC_GET_VOLUME_RIGHT: + *sarg = tea->right; + break; + case AUDC_SET_VOLUME_LEFT: + tea->left = *sarg; + tea6300_set(client); + break; + case AUDC_SET_VOLUME_RIGHT: + tea->right = *sarg; + tea6300_set(client); + break; + + case AUDC_GET_BASS: + *sarg = tea->bass; + break; + case AUDC_SET_BASS: + tea->bass = *sarg; + tea6300_set(client); + break; + + case AUDC_GET_TREBLE: + *sarg = tea->treble; + break; + case AUDC_SET_TREBLE: + tea->treble = *sarg; + tea6300_set(client); + break; + + case AUDC_GET_STEREO: + *sarg = tea->stereo?VIDEO_SOUND_STEREO:VIDEO_SOUND_MONO; + break; + case AUDC_SET_STEREO: + tea->stereo=(*sarg==VIDEO_SOUND_MONO)?0:1; + /* TODO: make this write to the TDA9850? */ + break; + +/* case AUDC_SWITCH_MUTE: someday, maybe -- not a lot of point to + case AUDC_NEWCHANNEL: it and it would require preserving state + case AUDC_GET_DC: huh?? (not used by bttv.c) +*/ +#endif + default: + /* nothing */ + } + return 0; +} + +static struct i2c_driver driver = { + "i2c tea6300 driver", + I2C_DRIVERID_TEA6300, + I2C_DF_NOTIFY, + tea6300_probe, + tea6300_detach, + tea6300_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tea6300_init(void) +#endif +{ + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN vanilla-2.2.15pre17/drivers/char/tea6420.c bttv-2.2.15pre17/drivers/char/tea6420.c --- vanilla-2.2.15pre17/drivers/char/tea6420.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/char/tea6420.c Mon May 1 23:03:21 2000 @@ -0,0 +1,268 @@ +/* + * for the TEA6420 chip (only found on 3DFX (STB) TV/FM cards to the best + * of my knowledge) + * Copyright (C) 2000 Dave Stuart + * This code is placed under the terms of the GNU General Public License + * Code liberally copied from tea6300 by . . . + * + * Copyright (c) 1998 Greg Alexander + * This code is placed under the terms of the GNU General Public License + * Code liberally copied from msp3400.c, which is by Gerd Knorr + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + + +/* Addresses to scan */ +#define I2C_TEA6420 0x98 +static unsigned short normal_i2c[] = { + I2C_TEA6420 >> 1, + I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; + + +MODULE_PARM(debug,"i"); +static int debug = 0; /* insmod parameter */ + +#define dprintk if (debug) printk + + +struct tea6420 { + int mode; /* set to AUDIO_{OFF,TUNER,RADIO,EXTERN} */ + int stereo; +}; + +static struct i2c_driver driver; +static struct i2c_client client_template; + +#define TEA6420_S_SA 0x00 /* stereo A input */ +#define TEA6420_S_SB 0x01 /* stereo B */ +#define TEA6420_S_SC 0x02 /* stereo C */ +#define TEA6420_S_SD 0x03 /* stereo D */ +#define TEA6420_S_SE 0x04 /* stereo E */ +#define TEA6420_S_GMU 0x05 /* general mute */ + + +/* ******************************** * + * functions for talking to TEA6420 * + * ******************************** */ + +static int tea6420_write(struct i2c_client *client, int val) +{ + unsigned char buffer[2]; + int result; + +/* buffer[0] = addr; */ + buffer[0] = val; + result = i2c_master_send(client,buffer,1); + if (1 != result) { + printk(KERN_WARNING "tea6420: I/O error, trying (write +0x%x) result = %d\n", val, result); + return -1; + } + return 0; +} + + +static void do_tea6420_init(struct i2c_client *client) +{ + struct tea6420 *tea = client->data; + + tea->mode=AUDIO_OFF; + tea->stereo=1; + tea6420_write(client, TEA6420_S_GMU); /* mute */ +} + +static void tea6420_audio(struct i2c_client *client, int mode) +{ + struct tea6420 *tea = client->data; + + /* valid for AUDIO_TUNER, RADIO, EXTERN, OFF */ + dprintk(KERN_DEBUG "tea6420_audio:%d (T,R,E,I,O)\n",mode); + tea->mode=mode; + if (mode==AUDIO_OFF) { /* just mute it */ + tea6420_write(client, TEA6420_S_GMU); + return; + } + switch(mode) { + case AUDIO_TUNER: + tea6420_write(client, TEA6420_S_SA); + break; + case AUDIO_RADIO: + tea6420_write(client, TEA6420_S_SB); + break; + case AUDIO_EXTERN: + tea6420_write(client, TEA6420_S_SC); + break; + } +} + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tea6420_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct tea6420 *tea; + struct i2c_client *client; + + client = kmalloc(sizeof *client,GFP_KERNEL); + if (!client) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->adapter = adap; + client->addr = addr; + + client->data = tea = kmalloc(sizeof *tea,GFP_KERNEL); + if (!tea) + return -ENOMEM; + memset(tea,0,sizeof *tea); + do_tea6420_init(client); + + MOD_INC_USE_COUNT; + strcpy(client->name,"TEA6420"); + printk(KERN_INFO "tea6420: initialized\n"); + + i2c_attach_client(client); + return 0; +} + +static int tea6420_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tea6420_attach); + return 0; +} + +static int tea6420_detach(struct i2c_client *client) +{ + struct tea6420 *tea = client->data; + + do_tea6420_init(client); + i2c_detach_client(client); + + kfree(tea); + kfree(client); + MOD_DEC_USE_COUNT; + return 0; +} + +static int +tea6420_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + __u16 *sarg = arg; + + switch (cmd) { + case AUDC_SET_RADIO: + tea6420_audio(client,AUDIO_RADIO); + break; + case AUDC_SET_INPUT: + tea6420_audio(client,*sarg); + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + va->flags |= VIDEO_AUDIO_VOLUME | + VIDEO_AUDIO_BASS | + VIDEO_AUDIO_TREBLE; +/* va->volume=MAX(tea->left,tea->right); + va->balance=(32768*MIN(tea->left,tea->right))/ + (va->volume ? va->volume : 1); + va->balance=(tea->leftright)? + (65535-va->balance) : va->balance; + va->bass = tea->bass; + va->treble = tea->treble; +*/ break; + } + case VIDIOCSAUDIO: + { + +/* tea->left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + tea->right = (MIN(va->balance,32768) * + va->volume) / 32768; + tea->bass = va->bass; + tea->treble = va->treble; + tea6420_set(client); +*/ break; + } + +default: + /* nothing */ + } + return 0; +} + +static struct i2c_driver driver = { + "i2c tea6420 driver", + I2C_DRIVERID_TEA6420, + I2C_DF_NOTIFY, + tea6420_probe, + tea6420_detach, + tea6420_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +#ifdef MODULE +int init_module(void) +#else +int tea6420_init(void) +#endif +{ + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN vanilla-2.2.15pre17/drivers/char/tuner.c bttv-2.2.15pre17/drivers/char/tuner.c --- vanilla-2.2.15pre17/drivers/char/tuner.c Mon May 1 22:59:52 2000 +++ bttv-2.2.15pre17/drivers/char/tuner.c Mon May 1 23:03:21 2000 @@ -6,42 +6,54 @@ #include #include #include -#include - +#include #include +#include #include #include "tuner.h" +#include "audiochip.h" + +/* Addresses to scan */ +static unsigned short normal_i2c[] = {I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {0x60,0x6f,I2C_CLIENT_END}; +static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static struct i2c_client_address_data addr_data = { + normal_i2c, normal_i2c_range, + probe, probe_range, + ignore, ignore_range, + force +}; static int debug = 0; /* insmod parameter */ -static int type = -1; /* tuner type */ +static int type = -1; /* insmod parameter */ + +static int addr = 0; +static int this_adap; #define dprintk if (debug) printk -#if LINUX_VERSION_CODE > 0x020100 MODULE_PARM(debug,"i"); MODULE_PARM(type,"i"); -#endif - -#if LINUX_VERSION_CODE < 0x02017f -void schedule_timeout(int j) -{ - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + j; - schedule(); -} -#endif +MODULE_PARM(addr,"i"); -struct tuner +struct tuner { - struct i2c_bus *bus; /* where is our chip */ - int addr; - int type; /* chip type */ int freq; /* keep track of the current settings */ + int std; + int radio; + int mode; /* PAL(0)/SECAM(1) mode (PHILIPS_SECAM only) */ }; +static struct i2c_driver driver; +static struct i2c_client client_template; + /* ---------------------------------------------------------------------- */ struct tunertype @@ -56,8 +68,12 @@ unsigned char VHF_H; unsigned char UHF; unsigned char config; - unsigned char I2C; unsigned short IFPCoff; + unsigned char mode; /* mode change value (tested PHILIPS_SECAM only) */ + /* 0x01 -> ??? no change ??? */ + /* 0x02 -> PAL BDGHI / SECAM L */ + /* 0x04 -> ??? PAL others / SECAM others ??? */ + int capability; }; /* @@ -66,59 +82,80 @@ * "no float in kernel" rule. */ static struct tunertype tuners[] = { - {"Temic PAL", TEMIC, PAL, - 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2,623}, - {"Philips PAL_I", Philips, PAL_I, - 16*140.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc0,623}, - {"Philips NTSC", Philips, NTSC, - 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0,732}, - {"Philips SECAM", Philips, SECAM, - 16*168.25,16*447.25,0xA7,0x97,0x37,0x8e,0xc0,623}, - {"NoTuner", NoTuner, NOTUNER, - 0 ,0 ,0x00,0x00,0x00,0x00,0x00,000}, - {"Philips PAL", Philips, PAL, - 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0,623}, - {"Temic NTSC", TEMIC, NTSC, - 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2,732}, - {"TEMIC PAL_I", TEMIC, PAL_I, - // 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,0xc2,623}, - 16*170.00,16*450.00,0x02,0x04,0x01,0x8e,0xc2,623}, - {"Temic 4036 FY5 NTSC", TEMIC, NTSC, - 16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,0xc2,732}, - {"Alps TSBH1",TEMIC,NTSC, - 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732}, - {"Alps TSBE1",TEMIC,PAL, - 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,0xc2,732}, + { "Temic PAL", TEMIC, PAL, + 16*140.25,16*463.25,0x02,0x04,0x01,0x8e,623}, + { "Philips PAL_I", Philips, PAL_I, + 16*140.25,16*463.25,0xa0,0x90,0x30,0x8e,623}, + { "Philips NTSC", Philips, NTSC, + 16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,732}, + { "Philips SECAM", Philips, SECAM, + 16*168.25,16*447.25,0xA7,0x97,0x37,0x8e,623,0x02}, + { "NoTuner", NoTuner, NOTUNER, + 0,0,0x00,0x00,0x00,0x00,0x00,000}, + { "Philips PAL", Philips, PAL, + 16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,623}, + { "Temic NTSC", TEMIC, NTSC, + 16*157.25,16*463.25,0x02,0x04,0x01,0x8e,732}, + { "Temic PAL_I", TEMIC, PAL_I, + 16*170.00,16*450.00,0x02,0x04,0x01,0x8e,623}, + { "Temic 4036 FY5 NTSC", TEMIC, NTSC, + 16*157.25,16*463.25,0xa0,0x90,0x30,0x8e,732}, + { "Alps HSBH1", TEMIC, NTSC, + 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732}, + { "Alps TSBE1",TEMIC,PAL, + 16*137.25,16*385.25,0x01,0x02,0x08,0x8e,732}, + { "Alps TSBB5", Alps, PAL_I, /* tested (UK UHF) with Modtec MM205 */ + 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,632}, + { "Alps TSBE5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */ + 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,622}, + { "Alps TSBC5", Alps, PAL, /* untested - data sheet guess. Only IF differs. */ + 16*133.25,16*351.25,0x01,0x02,0x08,0x8e,608}, + { "Temic 4006FH5", TEMIC, PAL_I, + 16*170.00,16*450.00,0xa0,0x90,0x30,0x8e,623}, }; +#define TUNERS (sizeof(tuners)/sizeof(struct tunertype)) /* ---------------------------------------------------------------------- */ -static int tuner_getstatus (struct tuner *t) +static int tuner_getstatus(struct i2c_client *c) { - return i2c_read(t->bus,t->addr+1); + unsigned char byte; + + if (1 != i2c_master_recv(c,&byte,1)) + return 0; + return byte; } #define TUNER_POR 0x80 #define TUNER_FL 0x40 +#define TUNER_MODE 0x38 #define TUNER_AFC 0x07 -static int tuner_islocked (struct tuner *t) +static int tuner_islocked (struct i2c_client *c) { - return (tuner_getstatus (t) & TUNER_FL); + return (tuner_getstatus (c) & TUNER_FL); } -static int tuner_afcstatus (struct tuner *t) +static int tuner_afcstatus (struct i2c_client *c) { - return (tuner_getstatus (t) & TUNER_AFC) - 2; + return (tuner_getstatus (c) & TUNER_AFC) - 2; } +#if 0 /* unused */ +static int tuner_mode (struct i2c_client *c) +{ + return (tuner_getstatus (c) & TUNER_MODE) >> 3; +} +#endif -static void set_tv_freq(struct tuner *t, int freq) +static void set_tv_freq(struct i2c_client *c, int freq) { u8 config; u16 div; struct tunertype *tun; - LOCK_FLAGS; + struct tuner *t = c->data; + unsigned char buffer[4]; + int rc; if (t->type == -1) { printk("tuner: tuner type not set\n"); @@ -133,25 +170,58 @@ else config = tun->UHF; +#if 1 // Fix colorstandard mode change + if (t->type == TUNER_PHILIPS_SECAM + /*&& t->std == V4L2_STANDARD_DDD*/ ) + config |= tun->mode; + else + config &= ~tun->mode; +#else + config &= ~tun->mode; +#endif + div=freq + tun->IFPCoff; - div&=0x7fff; - LOCK_I2C_BUS(t->bus); - if (i2c_write(t->bus, t->addr, (div>>8)&0x7f, div&0xff, 1)<0) { - printk("tuner: i2c i/o error #1\n"); + /* + * Philips FI1216MK2 remark from specification : + * for channel selection involving band switching, and to ensure + * smooth tuning to the desired channel without causing + * unnecessary charge pump action, it is recommended to consider + * the difference between wanted channel frequency and the + * current channel frequency. Unnecessary charge pump action + * will result in very low tuning voltage which may drive the + * oscillator to extreme conditions. + */ + /* + * Progfou: specification says to send config data before + * frequency in case (wanted frequency < current frequency). + */ + + if (t->type == TUNER_PHILIPS_SECAM && freq < t->freq) { + buffer[0] = tun->config; + buffer[1] = config; + buffer[2] = (div>>8) & 0x7f; + buffer[3] = div & 0xff; } else { - if (i2c_write(t->bus, t->addr, tun->config, config, 1)) - printk("tuner: i2c i/o error #2\n"); + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; + buffer[2] = tun->config; + buffer[3] = config; } - UNLOCK_I2C_BUS(t->bus); + + if (4 != (rc = i2c_master_send(c,buffer,4))) + printk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc); + } -static void set_radio_freq(struct tuner *t, int freq) +static void set_radio_freq(struct i2c_client *c, int freq) { u8 config; u16 div; struct tunertype *tun; - LOCK_FLAGS; + struct tuner *t = (struct tuner*)c->data; + unsigned char buffer[4]; + int rc; if (t->type == -1) { printk("tuner: tuner type not set\n"); @@ -163,118 +233,197 @@ div=freq + (int)(16*10.7); div&=0x7fff; - LOCK_I2C_BUS(t->bus); - if (i2c_write(t->bus, t->addr, (div>>8)&0x7f, div&0xff, 1)<0) { - printk("tuner: i2c i/o error #1\n"); - } else { - if (i2c_write(t->bus, t->addr, tun->config, config, 1)) - printk("tuner: i2c i/o error #2\n"); - } + buffer[0] = (div>>8) & 0x7f; + buffer[1] = div & 0xff; + buffer[2] = tun->config; + buffer[3] = config; + if (4 != (rc = i2c_master_send(c,buffer,4))) + printk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc); + if (debug) { - UNLOCK_I2C_BUS(t->bus); current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/10); - LOCK_I2C_BUS(t->bus); - if (tuner_islocked (t)) + if (tuner_islocked (c)) printk ("tuner: PLL locked\n"); else printk ("tuner: PLL not locked\n"); - printk ("tuner: AFC: %d\n", tuner_afcstatus (t)); + printk ("tuner: AFC: %d\n", tuner_afcstatus (c)); } - UNLOCK_I2C_BUS(t->bus); } - /* ---------------------------------------------------------------------- */ -static int tuner_attach(struct i2c_device *device) + +static int tuner_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) { struct tuner *t; + struct i2c_client *client; - /* - * For now we only try and attach these tuners to the BT848 - * or ZORAN bus. This same module will however work different - * species of card using these chips. Just change the constraints - * (i2c doesn't have a totally clash free 'address' space) - */ - - if(device->bus->id!=I2C_BUSID_BT848 && - device->bus->id!=I2C_BUSID_ZORAN) - return -EINVAL; - - device->data = t = kmalloc(sizeof(struct tuner),GFP_KERNEL); - if (NULL == t) - return -ENOMEM; - memset(t,0,sizeof(struct tuner)); - strcpy(device->name,"tuner"); - t->bus = device->bus; - t->addr = device->addr; - t->type = type; - dprintk("tuner: type is %d (%s)\n",t->type, - (t->type == -1 ) ? "autodetect" : tuners[t->type].name); + if (this_adap > 0) + return -1; + this_adap++; + client_template.adapter = adap; + client_template.addr = addr; + + printk("tuner: chip found @ 0x%x\n",addr); + + if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) + return -ENOMEM; + memcpy(client,&client_template,sizeof(struct i2c_client)); + client->data = t = kmalloc(sizeof(struct tuner),GFP_KERNEL); + if (NULL == t) { + kfree(client); + return -ENOMEM; + } + memset(t,0,sizeof(struct tuner)); + if (type >= 0 && type < TUNERS) { + t->type = type; + strncpy(client->name, tuners[t->type].name, sizeof(client->name)); + } else { + t->type = -1; + } + i2c_attach_client(client); MOD_INC_USE_COUNT; + + return 0; +} + +static int tuner_probe(struct i2c_adapter *adap) +{ + if (0 != addr) { + normal_i2c_range[0] = addr; + normal_i2c_range[1] = addr; + } + this_adap = 0; + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, tuner_attach); return 0; } -static int tuner_detach(struct i2c_device *device) +static int tuner_detach(struct i2c_client *client) { - struct tuner *t = (struct tuner*)device->data; + struct tuner *t = (struct tuner*)client->data; + + i2c_detach_client(client); kfree(t); + kfree(client); MOD_DEC_USE_COUNT; return 0; } -static int tuner_command(struct i2c_device *device, - unsigned int cmd, void *arg) +static int +tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) { - struct tuner *t = (struct tuner*)device->data; - int *iarg = (int*)arg; + struct tuner *t = (struct tuner*)client->data; + int *iarg = (int*)arg; +#if 0 + __u16 *sarg = (__u16*)arg; +#endif + + switch (cmd) { - switch (cmd) + /* --- configuration --- */ + case TUNER_SET_TYPE: + if (t->type != -1) + return 0; + if (*iarg < 0 || *iarg >= TUNERS) + return 0; + t->type = *iarg; + dprintk("tuner: type set to %d (%s)\n", + t->type,tuners[t->type].name); + strncpy(client->name, tuners[t->type].name, sizeof(client->name)); + break; + case AUDC_SET_RADIO: + t->radio = 1; + break; + + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCSCHAN: + { + struct video_channel *vc = arg; + + t->radio = 0; + if (t->type == TUNER_PHILIPS_SECAM) { + t->mode = (vc->norm == VIDEO_MODE_SECAM) ? 1 : 0; + set_tv_freq(client,t->freq); + } + return 0; + } + case VIDIOCSFREQ: { - case TUNER_SET_TYPE: - if (t->type != -1) - return 0; - t->type = *iarg; - dprintk("tuner: type set to %d (%s)\n", - t->type,tuners[t->type].name); - break; + unsigned long *v = arg; - case TUNER_SET_TVFREQ: - dprintk("tuner: tv freq set to %d.%02d\n", - (*iarg)/16,(*iarg)%16*100/16); - set_tv_freq(t,*iarg); - t->radio = 0; - t->freq = *iarg; - break; - - case TUNER_SET_RADIOFREQ: + t->freq = *v; + if (t->radio) { dprintk("tuner: radio freq set to %d.%02d\n", (*iarg)/16,(*iarg)%16*100/16); - set_radio_freq(t,*iarg); - t->radio = 1; - t->freq = *iarg; - break; - - default: - return -EINVAL; + set_radio_freq(client,t->freq); + } else { + dprintk("tuner: tv freq set to %d.%02d\n", + (*iarg)/16,(*iarg)%16*100/16); + set_tv_freq(client,t->freq); + } + return 0; + } +#if 0 + /* --- old, obsolete interface --- */ + case TUNER_SET_TVFREQ: + dprintk("tuner: tv freq set to %d.%02d\n", + (*iarg)/16,(*iarg)%16*100/16); + set_tv_freq(client,*iarg); + t->radio = 0; + t->freq = *iarg; + break; + + case TUNER_SET_RADIOFREQ: + dprintk("tuner: radio freq set to %d.%02d\n", + (*iarg)/16,(*iarg)%16*100/16); + set_radio_freq(client,*iarg); + t->radio = 1; + t->freq = *iarg; + break; + case TUNER_SET_MODE: + if (t->type != TUNER_PHILIPS_SECAM) { + dprintk("tuner: trying to change mode for other than TUNER_PHILIPS_SECAM\n"); + } else { + int mode=(*sarg==VIDEO_MODE_SECAM)?1:0; + dprintk("tuner: mode set to %d\n", *sarg); + t->mode = mode; + set_tv_freq(client,t->freq); + } + break; +#endif + default: + /* nothing */ } + return 0; } /* ----------------------------------------------------------------------- */ -struct i2c_driver i2c_driver_tuner = +static struct i2c_driver driver = { + "i2c TV tuner driver", + I2C_DRIVERID_TUNER, + I2C_DF_NOTIFY, + tuner_probe, + tuner_detach, + tuner_command, +}; + +static struct i2c_client client_template = { - "tuner", /* name */ - I2C_DRIVERID_TUNER, /* ID */ - 0xc0, 0xce, /* addr range */ - - tuner_attach, - tuner_detach, - tuner_command + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver }; EXPORT_NO_SYMBOLS; @@ -285,14 +434,14 @@ int i2c_tuner_init(void) #endif { - i2c_register_driver(&i2c_driver_tuner); + i2c_add_driver(&driver); return 0; } #ifdef MODULE void cleanup_module(void) { - i2c_unregister_driver(&i2c_driver_tuner); + i2c_del_driver(&driver); } #endif diff -urN vanilla-2.2.15pre17/drivers/char/tuner.h bttv-2.2.15pre17/drivers/char/tuner.h --- vanilla-2.2.15pre17/drivers/char/tuner.h Mon May 1 22:59:29 2000 +++ bttv-2.2.15pre17/drivers/char/tuner.h Mon May 1 23:03:21 2000 @@ -31,7 +31,11 @@ #define TUNER_TEMIC_NTSC 6 #define TUNER_TEMIC_PAL_I 7 #define TUNER_TEMIC_4036FY5_NTSC 8 -#define TUNER_ALPS_TSBH1_NTSC 9 +#define TUNER_ALPS_TSBH1_NTSC 9 +#define TUNER_ALPS_TSBE1_PAL 10 +#define TUNER_ALPS_TSBB5_PAL_I 11 +#define TUNER_ALPS_TSBE5_PAL 12 +#define TUNER_ALPS_TSBC5_PAL 13 #define NOTUNER 0 #define PAL 1 @@ -43,9 +47,11 @@ #define Philips 1 #define TEMIC 2 #define Sony 3 +#define Alps 4 #define TUNER_SET_TYPE _IOW('t',1,int) /* set tuner type */ #define TUNER_SET_TVFREQ _IOW('t',2,int) /* set tv freq */ #define TUNER_SET_RADIOFREQ _IOW('t',3,int) /* set radio freq */ +#define TUNER_SET_MODE _IOW('t',4,int) /* set tuner mode */ #endif diff -urN vanilla-2.2.15pre17/drivers/i2c/Config.in bttv-2.2.15pre17/drivers/i2c/Config.in --- vanilla-2.2.15pre17/drivers/i2c/Config.in Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/Config.in Mon May 1 23:02:30 2000 @@ -0,0 +1,29 @@ +# +# Character device configuration +# +mainmenu_option next_comment +comment 'I2C support' + +tristate 'I2C support' CONFIG_I2C + +if [ "$CONFIG_I2C" != "n" ]; then + + dep_tristate 'I2C bit-banging interfaces' CONFIG_I2C_ALGOBIT $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOBIT" != "n" ]; then + dep_tristate ' Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT + dep_tristate ' ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT + dep_tristate ' Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT + fi + + dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C + if [ "$CONFIG_I2C_ALGOPCF" != "n" ]; then + dep_tristate ' Elektor ISA card' CONFIG_I2C_ELEKTOR $CONFIG_I2C_ALGOPCF + fi + +# This is needed for automatic patch generation: sensors code starts here +# This is needed for automatic patch generation: sensors code ends here + + dep_tristate 'I2C device interface' CONFIG_I2C_CHARDEV $CONFIG_I2C + +fi +endmenu diff -urN vanilla-2.2.15pre17/drivers/i2c/Makefile bttv-2.2.15pre17/drivers/i2c/Makefile --- vanilla-2.2.15pre17/drivers/i2c/Makefile Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/Makefile Mon May 1 23:02:30 2000 @@ -0,0 +1,98 @@ +# +# Makefile for the kernel i2c bus driver. +# + +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) +MOD_LIST_NAME := I2C_MODULES + +L_TARGET := i2c.a +MX_OBJS := +M_OBJS := +LX_OBJS := +L_OBJS := + +# ----- +# i2c core components +# ----- + +ifeq ($(CONFIG_I2C),y) + LX_OBJS += i2c-core.o +else + ifeq ($(CONFIG_I2C),m) + MX_OBJS += i2c-core.o + endif +endif + +ifeq ($(CONFIG_I2C_CHARDEV),y) + L_OBJS += i2c-dev.o +else + ifeq ($(CONFIG_I2C_CHARDEV),m) + M_OBJS += i2c-dev.o + endif +endif + +# ----- +# Bit banging adapters... +# ----- + +ifeq ($(CONFIG_I2C_ALGOBIT),y) + LX_OBJS += i2c-algo-bit.o +else + ifeq ($(CONFIG_I2C_ALGOBIT),m) + MX_OBJS += i2c-algo-bit.o + endif +endif + +ifeq ($(CONFIG_I2C_PHILIPSPAR),y) + L_OBJS += i2c-philips-par.o +else + ifeq ($(CONFIG_I2C_PHILIPSPAR),m) + M_OBJS += i2c-philips-par.o + endif +endif + +ifeq ($(CONFIG_I2C_ELV),y) + L_OBJS += i2c-elv.o +else + ifeq ($(CONFIG_I2C_ELV),m) + M_OBJS += i2c-elv.o + endif +endif + +ifeq ($(CONFIG_I2C_VELLEMAN),y) + L_OBJS += i2c-velleman.o +else + ifeq ($(CONFIG_I2C_VELLEMAN),m) + M_OBJS += i2c-velleman.o + endif +endif + + + +# ----- +# PCF components +# ----- + +ifeq ($(CONFIG_I2C_ALGOPCF),y) + LX_OBJS += i2c-algo-pcf.o +else + ifeq ($(CONFIG_I2C_ALGOPCF),m) + MX_OBJS += i2c-algo-pcf.o + endif +endif + +ifeq ($(CONFIG_I2C_ELEKTOR),y) + L_OBJS += i2c-elektor.o +else + ifeq ($(CONFIG_I2C_ELEKTOR),m) + M_OBJS += i2c-elektor.o + endif +endif + +# This is needed for automatic patch generation: sensors code starts here +# This is needed for automatic patch generation: sensors code ends here + +include $(TOPDIR)/Rules.make + diff -urN vanilla-2.2.15pre17/drivers/i2c/i2c-algo-bit.c bttv-2.2.15pre17/drivers/i2c/i2c-algo-bit.c --- vanilla-2.2.15pre17/drivers/i2c/i2c-algo-bit.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/i2c-algo-bit.c Mon May 1 23:02:30 2000 @@ -0,0 +1,640 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-bit.c i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-2000 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-algo-bit.c,v 1.26 2000/01/24 02:06:33 mds Exp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; +#define DEBSTAT(x) if (i2c_debug>=3) x; /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) { x; } + /* debug the protocol by showing transferred bits */ + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=0; +static int bit_test=0; /* see if the line-setting functions work */ +static int bit_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define setsda(adap,val) adap->setsda(adap->data, val) +#define setscl(adap,val) adap->setscl(adap->data, val) +#define getsda(adap) adap->getsda(adap->data) +#define getscl(adap) adap->getscl(adap->data) + +static inline void sdalo(struct i2c_algo_bit_data *adap) +{ + setsda(adap,0); + udelay(adap->udelay); +} + +static inline void sdahi(struct i2c_algo_bit_data *adap) +{ + setsda(adap,1); + udelay(adap->udelay); +} + +static inline void scllo(struct i2c_algo_bit_data *adap) +{ + setscl(adap,0); + udelay(adap->udelay); +#ifdef SLO_IO + SLO_IO +#endif +} + +/* + * Raise scl line, and do checking for delays. This is necessary for slower + * devices. + */ +static inline int sclhi(struct i2c_algo_bit_data *adap) +{ + int start=jiffies; + + setscl(adap,1); + + udelay(adap->udelay); + + /* Not all adapters have scl sense line... */ + if (adap->getscl == NULL ) + return 0; + + while (! getscl(adap) ) { + /* the hw knows how to read the clock line, + * so we wait until it actually gets high. + * This is safer as some chips may hold it low + * while they are processing data internally. + */ + setscl(adap,1); + if (start+adap->timeout <= jiffies) { + return -ETIMEDOUT; + } + if (current->need_resched) + schedule(); + } + DEBSTAT(printk("needed %ld jiffies\n", jiffies-start)); +#ifdef SLO_IO + SLO_IO +#endif + return 0; +} + + +/* --- other auxiliary functions -------------------------------------- */ +static void i2c_start(struct i2c_algo_bit_data *adap) +{ + /* assert: scl, sda are high */ + DEBPROTO(printk("S ")); + sdalo(adap); + scllo(adap); +} + +static void i2c_repstart(struct i2c_algo_bit_data *adap) +{ + /* scl, sda may not be high */ + DEBPROTO(printk(" Sr ")); + setsda(adap,1); + setscl(adap,1); + udelay(adap->udelay); + + sdalo(adap); + scllo(adap); +} + + +static void i2c_stop(struct i2c_algo_bit_data *adap) +{ + DEBPROTO(printk("P\n")); + /* assert: scl is low */ + sdalo(adap); + sclhi(adap); + sdahi(adap); +} + + + +/* send a byte without start cond., look for arbitration, + check ackn. from slave */ +/* returns: + * 1 if the device acknowledged + * 0 if the device did not ack + * -ETIMEDOUT if an error occured (while raising the scl line) + */ +static int i2c_outb(struct i2c_adapter *i2c_adap, char c) +{ + int i; + int sb; + int ack; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + DEB2(printk(" i2c_outb:%2.2X\n",c&0xff)); + for ( i=7 ; i>=0 ; i-- ) { + sb = c & ( 1 << i ); + setsda(adap,sb); + udelay(adap->udelay); + DEBPROTO(printk("%d",sb!=0)); + if (sclhi(adap)<0) { /* timed out */ + sdahi(adap); /* we don't want to block the net */ + return -ETIMEDOUT; + }; + /* do arbitration here: + * if ( sb && ! getsda(adap) ) -> ouch! Get out of here. + */ + setscl(adap, 0 ); + udelay(adap->udelay); + } + sdahi(adap); + if (sclhi(adap)<0){ /* timeout */ + return -ETIMEDOUT; + }; + /* read ack: SDA should be pulled down by slave */ + ack=getsda(adap); /* ack: sda is pulled low ->success. */ + DEB2(printk(" i2c_outb: getsda() = 0x%2.2x\n", ~ack )); + + DEBPROTO( printk("[%2.2x]",c&0xff) ); + DEBPROTO(if (0==ack){ printk(" A ");} else printk(" NA ") ); + scllo(adap); + return 0==ack; /* return 1 if device acked */ + /* assert: scl is low (sda undef) */ +} + + +static int i2c_inb(struct i2c_adapter *i2c_adap) +{ + /* read byte via i2c port, without start/stop sequence */ + /* acknowledge is sent in i2c_read. */ + int i; + unsigned char indata=0; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + /* assert: scl is low */ + DEB2(printk("i2c_inb.\n")); + + sdahi(adap); + for (i=0;i<8;i++) { + if (sclhi(adap)<0) { /* timeout */ + return -ETIMEDOUT; + }; + indata *= 2; + if ( getsda(adap) ) + indata |= 0x01; + scllo(adap); + } + /* assert: scl is low */ + DEBPROTO(printk(" %2.2x", indata & 0xff)); + return (int) (indata & 0xff); +} + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_algo_bit_data *adap, char* name) { + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("i2c-algo-bit.o: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("i2c-algo-bit.o: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("i2c-algo-bit.o: %s seems to be busy.\n",name); + goto bailout; + } + sdalo(adap); + printk("i2c-algo-bit.o:1 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("i2c-algo-bit.o:2 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL unexpected low while SDA high!\n", + name); + goto bailout; + } + scllo(adap); + printk("i2c-algo-bit.o:3 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 != getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL stuck high!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("i2c-algo-bit.o:4 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("i2c-algo-bit.o: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-bit.o: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("i2c-algo-bit.o: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +} + +/* ----- Utility functions + */ + +/* try_address tries to contact a chip for a number of + * times before it gives up. + * return values: + * 1 chip answered + * 0 chip did not answer + * -x transmission error + */ +static inline int try_address(struct i2c_adapter *i2c_adap, + unsigned char addr, int retries) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + int i,ret = -1; + for (i=0;i<=retries;i++) { + ret = i2c_outb(i2c_adap,addr); + if (ret==1) + break; /* success! */ + i2c_stop(adap); + udelay(5/*adap->udelay*/); + if (i==retries) /* no success */ + break; + i2c_start(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("i2c-algo-bit.o: needed %d retries for %d\n", + i,addr)); + return ret; +} + +static int sendbytes(struct i2c_adapter *i2c_adap,const char *buf, int count) +{ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + char c; + const char *temp = buf; + int retval; + int wrcount=0; + + while (count > 0) { + c = *temp; + DEB2(printk("i2c-algo-bit.o: %s i2c_write: writing %2.2X\n", + i2c_adap->name, c&0xff)); + retval = i2c_outb(i2c_adap,c); + if (retval>0) { + count--; + temp++; + wrcount++; + } else { /* arbitration or no acknowledge */ + printk("i2c-algo-bit.o: %s i2c_write: error - bailout.\n", + i2c_adap->name); + i2c_stop(adap); + return (retval<0)? retval : -EFAULT; + /* got a better one ?? */ + } +#if 0 + /* from asm/delay.h */ + __delay(adap->mdelay * (loops_per_sec / 1000) ); +#endif + } + return wrcount; +} + +static inline int readbytes(struct i2c_adapter *i2c_adap,char *buf,int count) +{ + char *temp = buf; + int inval; + int rdcount=0; /* counts bytes read */ + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + while (count > 0) { + inval = i2c_inb(i2c_adap); +/*printk("%#02x ",inval); if ( ! (count % 16) ) printk("\n"); */ + if (inval>=0) { + *temp = inval; + rdcount++; + } else { /* read timed out */ + printk("i2c-algo-bit.o: i2c_read: i2c_inb timed out.\n"); + break; + } + + if ( count > 1 ) { /* send ack */ + sdalo(adap); + DEBPROTO(printk(" Am ")); + } else { + sdahi(adap); /* neg. ack on last byte */ + DEBPROTO(printk(" NAm ")); + } + if (sclhi(adap)<0) { /* timeout */ + sdahi(adap); + printk("i2c-algo-bit.o: i2c_read: Timeout at ack\n"); + return -ETIMEDOUT; + }; + scllo(adap); + sdahi(adap); + temp++; + count--; + } + return rdcount; +} + +/* doAddress initiates the transfer by generating the start condition (in + * try_address) and transmits the address in the necessary format to handle + * reads, writes as well as 10bit-addresses. + * returns: + * 0 everything went okay, the chip ack'ed + * -x an error occured (like: -EREMOTEIO if the device did not answer, or + * -ETIMEDOUT, for example if the lines are stuck...) + */ +static inline int bit_doAddress(struct i2c_adapter *i2c_adap, + struct i2c_msg *msg, int retries) +{ + unsigned short flags = msg->flags; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + unsigned char addr; + int ret; + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + ret = i2c_outb(i2c_adap,msg->addr & 0x7f); + if (ret != 1) { + /* the chip did not ack / xmission error occured */ + printk("died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; + ret = try_address(i2c_adap, addr, retries); + if (ret!=1) { + return -EREMOTEIO; + } + } + return 0; +} + +static int bit_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct i2c_msg *pmsg; + struct i2c_algo_bit_data *adap = i2c_adap->algo_data; + + int i,ret; + + i2c_start(adap); + for (i=0;iflags & I2C_M_NOSTART)) { + if (i) { + i2c_repstart(adap); + } + ret = bit_doAddress(i2c_adap,pmsg,i2c_adap->retries); + if (ret != 0) { + DEB2(printk("i2c-algo-bit.o: NAK from device adr %#2x msg #%d\n" + ,msgs[i].addr,i)); + return (ret<0) ? ret : -EREMOTEIO; + } + } + if (pmsg->flags & I2C_M_RD ) { + /* read bytes into buffer*/ + ret = readbytes(i2c_adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c-algo-bit.o: read %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0)? ret : -EREMOTEIO; + } + } else { + /* write bytes from buffer */ + ret = sendbytes(i2c_adap,pmsg->buf,pmsg->len); + DEB2(printk("i2c-algo-bit.o: wrote %d bytes.\n",ret)); + if (ret < pmsg->len ) { + return (ret<0) ? ret : -EREMOTEIO; + } + } + } + i2c_stop(adap); + return num; +} + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 bit_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm i2c_bit_algo = { + "Bit-shift algorithm", + I2C_ALGO_BIT, + bit_xfer, + NULL, + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + bit_func, /* functionality */ +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_bit_add_bus(struct i2c_adapter *adap) +{ + int i; + struct i2c_algo_bit_data *bit_adap = adap->algo_data; + + if (bit_test) { + int ret = test_bus(bit_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-bit.o: hw routines for %s registered.\n", + adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= i2c_bit_algo.id; + adap->algo = &i2c_bit_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + + /* scan bus */ + if (bit_scan) { + int ack; + printk(KERN_INFO " i2c-algo-bit.o: scanning bus %s.\n", + adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_start(bit_adap); + ack = i2c_outb(adap,i); + i2c_stop(bit_adap); + if (ack>0) { + printk("(%02x)",i>>1); + } else + printk("."); + } + printk("\n"); + } + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + i2c_add_adapter(adap); + + return 0; +} + + +int i2c_bit_del_bus(struct i2c_adapter *adap) +{ + + i2c_del_adapter(adap); + + DEB2(printk("i2c-algo-bit.o: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +int __init i2c_algo_bit_init (void) +{ + printk("i2c-algo-bit.o: i2c bit algorithm module\n"); + return 0; +} + + + +EXPORT_SYMBOL(i2c_bit_add_bus); +EXPORT_SYMBOL(i2c_bit_del_bus); + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus bit-banging algorithm"); + +MODULE_PARM(bit_test, "i"); +MODULE_PARM(bit_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(bit_test, "Test the lines of the bus to see if it is stuck"); +MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 bit-protocol"); + +int init_module(void) +{ + return i2c_algo_bit_init(); +} + +void cleanup_module(void) +{ +} +#endif diff -urN vanilla-2.2.15pre17/drivers/i2c/i2c-algo-pcf.c bttv-2.2.15pre17/drivers/i2c/i2c-algo-pcf.c --- vanilla-2.2.15pre17/drivers/i2c/i2c-algo-pcf.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/i2c-algo-pcf.c Mon May 1 23:02:30 2000 @@ -0,0 +1,616 @@ + +/* ------------------------------------------------------------------------- */ +/* i2c-algo-pcf.c i2c driver algorithms for PCF8584 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-1997 Simon G. Vogl + 1998-2000 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and + Frodo Looijaard ,and also from Martin Bailey + */ + +/* $Id: i2c-algo-pcf.c,v 1.23 2000/03/24 21:02:11 frodo Exp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "i2c-pcf8584.h" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) x; + /* debug the protocol by showing transferred bits */ +#define DEF_TIMEOUT 16 + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=1; +static int pcf_test=0; /* see if the line-setting functions work */ +static int pcf_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define set_pcf(adap, ctl, val) adap->setpcf(adap->data, ctl, val) +#define get_pcf(adap, ctl) adap->getpcf(adap->data, ctl) +#define get_own(adap) adap->getown(adap->data) +#define get_clock(adap) adap->getclock(adap->data) +#define i2c_outb(adap, val) adap->setpcf(adap->data, 0, val) +#define i2c_inb(adap) adap->getpcf(adap->data, 0) + + +/* --- other auxiliary functions -------------------------------------- */ + +static void i2c_start(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk("S ")); + set_pcf(adap, 1, I2C_PCF_START); +} + +static void i2c_repstart(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk(" Sr ")); + set_pcf(adap, 1, I2C_PCF_REPSTART); +} + + +static void i2c_stop(struct i2c_algo_pcf_data *adap) +{ + DEBPROTO(printk("P\n")); + set_pcf(adap, 1, I2C_PCF_STOP); +} + + +static int wait_for_bb(struct i2c_algo_pcf_data *adap) { + + int timeout = DEF_TIMEOUT; + int status; + + status = get_pcf(adap, 1); +#ifndef STUB_I2C + while (timeout-- && !(status & I2C_PCF_BB)) { + udelay(1000); /* How much is this? */ + status = get_pcf(adap, 1); + } +#endif + if (timeout<=0) + printk("Timeout waiting for Bus Busy\n"); + /* + set_pcf(adap, 1, I2C_PCF_STOP); + */ + return(timeout<=0); +} + + +static inline void pcf_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} + + +static int wait_for_pin(struct i2c_algo_pcf_data *adap, int *status) { + + int timeout = DEF_TIMEOUT; + + *status = get_pcf(adap, 1); +#ifndef STUB_I2C + while (timeout-- && (*status & I2C_PCF_PIN)) { + adap->waitforpin(); + *status = get_pcf(adap, 1); + } +#endif + if (timeout <= 0) + return(-1); + else + return(0); +} + + +/* + * This should perform the 'PCF8584 initialization sequence' as described + * in the Philips IC12 data book (1995, Aug 29). + * There should be a 30 clock cycle wait after reset, I assume this + * has been fulfilled. + * There should be a delay at the end equal to the longest I2C message + * to synchronize the BB-bit (in multimaster systems). How long is + * this? I assume 1 second is always long enough. + */ +static int pcf_init_8584 (struct i2c_algo_pcf_data *adap) +{ + + /* S1=0x80: S0 selected, serial interface off */ + set_pcf(adap, 1, I2C_PCF_PIN); + + /* load own address in S0, effective address is (own << 1) */ + i2c_outb(adap, get_own(adap)); + + /* S1=0xA0, next byte in S2 */ + set_pcf(adap, 1, I2C_PCF_PIN | I2C_PCF_ES1); + + /* load clock register S2 */ + i2c_outb(adap, get_clock(adap)); + + /* Enable serial interface, idle, S0 selected */ + set_pcf(adap, 1, I2C_PCF_IDLE); + + DEB2(printk("i2c-algo-pcf.o: irq: Initialized 8584.\n")); + return 0; +} + + +/* + * Sanity check for the adapter hardware - check the reaction of + * the bus lines only if it seems to be idle. + */ +static int test_bus(struct i2c_algo_pcf_data *adap, char *name) { +#if 0 + int scl,sda; + sda=getsda(adap); + if (adap->getscl==NULL) { + printk("i2c-algo-pcf.o: Warning: Adapter can't read from clock line - skipping test.\n"); + return 0; + } + scl=getscl(adap); + printk("i2c-algo-pcf.o: Adapter: %s scl: %d sda: %d -- testing...\n", + name,getscl(adap),getsda(adap)); + if (!scl || !sda ) { + printk("i2c-algo-pcf.o: %s seems to be busy.\n",adap->name); + goto bailout; + } + sdalo(adap); + printk("i2c-algo-pcf.o:1 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 != getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA stuck high!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL unexpected low while pulling SDA low!\n", + name); + goto bailout; + } + sdahi(adap); + printk("i2c-algo-pcf.o:2 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA stuck low!\n",name); + sdahi(adap); + goto bailout; + } + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL unexpected low while SDA high!\n", + adap->name); + goto bailout; + } + scllo(adap); + printk("i2c-algo-pcf.o:3 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 != getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL stuck high!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA unexpected low while pulling SCL low!\n", + name); + goto bailout; + } + sclhi(adap); + printk("i2c-algo-pcf.o:4 scl: %d sda: %d \n",getscl(adap), + getsda(adap)); + if ( 0 == getscl(adap) ) { + printk("i2c-algo-pcf.o: %s SCL stuck low!\n",name); + sclhi(adap); + goto bailout; + } + if ( 0 == getsda(adap) ) { + printk("i2c-algo-pcf.o: %s SDA unexpected low while SCL high!\n", + name); + goto bailout; + } + printk("i2c-algo-pcf.o: %s passed test.\n",name); + return 0; +bailout: + sdahi(adap); + sclhi(adap); + return -ENODEV; +#endif + return (0); +} + +/* ----- Utility functions + */ + +static inline int try_address(struct i2c_algo_pcf_data *adap, + unsigned char addr, int retries) +{ + int i, status, ret = -1; + for (i=0;i= 0) { + if ((status & I2C_PCF_LRB) == 0) { + i2c_stop(adap); + break; /* success! */ + } + } + i2c_stop(adap); + udelay(adap->udelay); + } + DEB2(if (i) printk("i2c-algo-pcf.o: needed %d retries for %d\n",i, + addr)); + return ret; +} + + +static int pcf_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, + int count) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + int wrcount, status, timeout; + + for (wrcount=0; wrcountname, buf[wrcount]&0xff)); + i2c_outb(adap, buf[wrcount]); + timeout = wait_for_pin(adap, &status); + if (timeout) { + i2c_stop(adap); + printk("i2c-algo-pcf.o: %s i2c_write: " + "error - timeout.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } +#ifndef STUB_I2C + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + printk("i2c-algo-pcf.o: %s i2c_write: " + "error - no ack.\n", i2c_adap->name); + return -EREMOTEIO; /* got a better one ?? */ + } +#endif + } + i2c_stop(adap); + return (wrcount); +} + + +static int pcf_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count) +{ + int rdcount=0, i, status, timeout, dummy=1; + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + + for (i=0; iflags; + unsigned char addr; + int ret; + if ( (flags & I2C_M_TEN) ) { + /* a ten bit address */ + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk("addr0: %d\n",addr)); + /* try extended address code...*/ + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + /* the remaining 8 bit address */ + i2c_outb(adap,msg->addr & 0x7f); +/* Status check comes here */ + if (ret != 1) { + printk("died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + /* okay, now switch into reading mode */ + addr |= 0x01; + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk("died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else { /* normal 7bit address */ + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; + i2c_outb(adap, addr); + } + return 0; +} + +static int pcf_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + struct i2c_algo_pcf_data *adap = i2c_adap->algo_data; + struct i2c_msg *pmsg; + int i = 0; + int ret, timeout, status; + + pmsg = &msgs[i]; + + /* Send address here if Read */ + if (pmsg->flags & I2C_M_RD) { + ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); + } + + /* Check for bus busy */ + timeout = wait_for_bb(adap); + if (timeout) { + DEB2(printk("i2c-algo-pcf.o: " + "Timeout waiting for BB in pcf_xfer\n");) + return -EIO; + } + + /* Send address here if Write */ + if (!(pmsg->flags & I2C_M_RD)) { + ret = pcf_doAddress(adap, pmsg, i2c_adap->retries); + } + /* Send START */ + i2c_start(adap); + + /* Wait for PIN (pending interrupt NOT) */ + timeout = wait_for_pin(adap, &status); + if (timeout) { + i2c_stop(adap); + DEB2(printk("i2c-algo-pcf.o: Timeout waiting " + "for PIN(1) in pcf_xfer\n");) + return (-EREMOTEIO); + } + +#ifndef STUB_I2C + /* Check LRB (last rcvd bit - slave ack) */ + if (status & I2C_PCF_LRB) { + i2c_stop(adap); + DEB2(printk("i2c-algo-pcf.o: No LRB(1) in pcf_xfer\n");) + return (-EREMOTEIO); + } +#endif + + DEB3(printk("i2c-algo-pcf.o: Msg %d, addr=0x%x, flags=0x%x, len=%d\n", + i, msgs[i].addr, msgs[i].flags, msgs[i].len);) + + /* Read */ + if (pmsg->flags & I2C_M_RD) { + + /* read bytes into buffer*/ + ret = pcf_readbytes(i2c_adap, pmsg->buf, pmsg->len); + + if (ret != pmsg->len) { + DEB2(printk("i2c-algo-pcf.o: fail: " + "only read %d bytes.\n",ret)); + } else { + DEB2(printk("i2c-algo-pcf.o: read %d bytes.\n",ret)); + } + } else { /* Write */ + + /* Write bytes from buffer */ + ret = pcf_sendbytes(i2c_adap, pmsg->buf, pmsg->len); + + if (ret != pmsg->len) { + DEB2(printk("i2c-algo-pcf.o: fail: " + "only wrote %d bytes.\n",ret)); + } else { + DEB2(printk("i2c-algo-pcf.o: wrote %d bytes.\n",ret)); + } + } + return (num); +} + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 pcf_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING; +} + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm pcf_algo = { + "PCF8584 algorithm", + I2C_ALGO_PCF, + pcf_xfer, + NULL, + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + pcf_func, /* functionality */ +}; + +/* + * registering functions to load algorithms at runtime + */ +int i2c_pcf_add_bus(struct i2c_adapter *adap) +{ + int i, status; + struct i2c_algo_pcf_data *pcf_adap = adap->algo_data; + + if (pcf_test) { + int ret = test_bus(pcf_adap, adap->name); + if (ret<0) + return -ENODEV; + } + + DEB2(printk("i2c-algo-pcf.o: hw routines for %s registered.\n", + adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= pcf_algo.id; + adap->algo = &pcf_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + i2c_add_adapter(adap); + pcf_init_8584(pcf_adap); + + /* scan bus */ + if (pcf_scan) { + printk(KERN_INFO " i2c-algo-pcf.o: scanning bus %s.\n", + adap->name); + for (i = 0x00; i < 0xff; i+=2) { + i2c_outb(pcf_adap, i); + i2c_start(pcf_adap); + if ((wait_for_pin(pcf_adap, &status) >= 0) && + ((status & I2C_PCF_LRB) == 0)) { + printk("(%02x)",i>>1); + } else { + printk("."); + } + i2c_stop(pcf_adap); + udelay(pcf_adap->udelay); + } + printk("\n"); + } + return 0; +} + + +int i2c_pcf_del_bus(struct i2c_adapter *adap) +{ + i2c_del_adapter(adap); + DEB2(printk("i2c-algo-pcf.o: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + +int __init i2c_algo_pcf_init (void) +{ + printk("i2c-algo-pcf.o: i2c pcf8584 algorithm module\n"); + return 0; +} + + +EXPORT_SYMBOL(i2c_pcf_add_bus); +EXPORT_SYMBOL(i2c_pcf_del_bus); + +#ifdef MODULE +MODULE_AUTHOR("Hans Berglund "); +MODULE_DESCRIPTION("I2C-Bus PCF8584 algorithm"); + +MODULE_PARM(pcf_test, "i"); +MODULE_PARM(pcf_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(pcf_test, "Test if the I2C bus is available"); +MODULE_PARM_DESC(pcf_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 pcf-protocol"); + + +int init_module(void) +{ + return i2c_algo_pcf_init(); +} + +void cleanup_module(void) +{ +} +#endif diff -urN vanilla-2.2.15pre17/drivers/i2c/i2c-core.c bttv-2.2.15pre17/drivers/i2c/i2c-core.c --- vanilla-2.2.15pre17/drivers/i2c/i2c-core.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/i2c-core.c Mon May 1 23:02:30 2000 @@ -0,0 +1,1339 @@ +/* i2c-core.c - a device driver for the iic-bus interface */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki . + All SMBus-related things are written by Frodo Looijaard */ + +/* $Id: i2c-core.c,v 1.53 2000/04/18 08:48:56 simon Exp $ */ + +#include +#include +#include +#include +#include +#include + +#include + +/* ----- compatibility stuff ----------------------------------------------- */ + +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,1) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#include + +/* ----- global defines ---------------------------------------------------- */ + +/* exclusive access to the bus */ +#define I2C_LOCK(adap) down(&adap->lock) +#define I2C_UNLOCK(adap) up(&adap->lock) + +#define ADAP_LOCK() down(&adap_lock) +#define ADAP_UNLOCK() up(&adap_lock) + +#define DRV_LOCK() down(&driver_lock) +#define DRV_UNLOCK() up(&driver_lock) + +#define DEB(x) if (i2c_debug>=1) x; +#define DEB2(x) if (i2c_debug>=2) x; + +/* ----- global variables -------------------------------------------------- */ + +/**** lock for writing to global variables: the adapter & driver list */ +struct semaphore adap_lock; +struct semaphore driver_lock; + +/**** adapter list */ +static struct i2c_adapter *adapters[I2C_ADAP_MAX]; +static int adap_count; + +/**** drivers list */ +static struct i2c_driver *drivers[I2C_DRIVER_MAX]; +static int driver_count; + +/**** debug level */ +static int i2c_debug=1; +static void i2c_dummy_adapter(struct i2c_adapter *adapter); +static void i2c_dummy_client(struct i2c_client *client); + +/* --------------------------------------------------- + * /proc entry declarations + *---------------------------------------------------- + */ + +#ifdef CONFIG_PROC_FS + +static int i2cproc_init(void); +static int i2cproc_cleanup(void); + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +static void monitor_bus_i2c(struct inode *inode, int fill); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ + +static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos); +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int *eof , void *private); + +/* To implement the dynamic /proc/bus/i2c-? files, we need our own + implementation of the read hook */ +static struct file_operations i2cproc_operations = { + read: i2cproc_bus_read, +}; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,48)) +static struct inode_operations i2cproc_inode_operations = { + &i2cproc_operations +}; +#endif + +static int i2cproc_initialized = 0; + +#else /* undef CONFIG_PROC_FS */ + +#define i2cproc_init() +#define i2cproc_cleanup() + +#endif /* CONFIG_PROC_FS */ + + +/* --------------------------------------------------- + * registering functions + * --------------------------------------------------- + */ + +/* ----- + * i2c_add_adapter is called from within the algorithm layer, + * when a new hw adapter registers. A new device is register to be + * available for clients. + */ +int i2c_add_adapter(struct i2c_adapter *adap) +{ + int i,j; + + ADAP_LOCK(); + for (i = 0; i < I2C_ADAP_MAX; i++) + if (NULL == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: register_adapter(%s) - enlarge I2C_ADAP_MAX.\n", + adap->name); + ADAP_UNLOCK(); + return -ENOMEM; + } + + adapters[i] = adap; + adap_count++; + ADAP_UNLOCK(); + + /* init data types */ + init_MUTEX(&adap->lock); + + i2c_dummy_adapter(adap); /* actually i2c_dummy->add_adapter */ +#ifdef CONFIG_PROC_FS + + if (i2cproc_initialized) { + char name[8]; + struct proc_dir_entry *proc_entry; + + sprintf(name,"i2c-%d", i); + + proc_entry = create_proc_entry(name,0,proc_bus); + if (! proc_entry) { + printk("i2c-core.o: Could not create /proc/bus/%s\n", + name); + return -ENOENT; + } + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,48)) + proc_entry->proc_fops = &i2cproc_operations; +#else + proc_entry->ops = &i2cproc_inode_operations; +#endif +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) + proc_entry->owner = THIS_MODULE; +#else + proc_entry->fill_inode = &monitor_bus_i2c; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */ + adap->inode = proc_entry->low_ino; + } + +#endif /* def CONFIG_PROC_FS */ + + /* inform drivers of new adapters */ + DRV_LOCK(); + for (j=0;jflags&I2C_DF_NOTIFY) + drivers[j]->attach_adapter(adap); + DRV_UNLOCK(); + + DEB(printk("i2c-core.o: adapter %s registered as adapter %d.\n", + adap->name,i)); + + return 0; +} + + +int i2c_del_adapter(struct i2c_adapter *adap) +{ + int i,j; + ADAP_LOCK(); + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adap == adapters[i]) + break; + if (I2C_ADAP_MAX == i) { + printk( " i2c-core.o: unregister_adapter adap [%s] not found.\n", + adap->name); + ADAP_UNLOCK(); + return -ENODEV; + } + + i2c_dummy_adapter(adap); /* actually i2c_dummy->del_adapter */ +#ifdef CONFIG_PROC_FS + if (i2cproc_initialized) { + char name[8]; + sprintf(name,"i2c-%d", i); + remove_proc_entry(name,proc_bus); + } +#endif /* def CONFIG_PROC_FS */ + + /* detach any active clients */ + for (j=0;jclients[j]; + if ( (client!=NULL) + /* && (client->driver->flags & I2C_DF_NOTIFY) */ ) + /* detaching devices is unconditional of the set notify + * flag, as _all_ clients that reside on the adapter + * must be deleted, as this would cause invalid states. + */ + client->driver->detach_client(client); + /* i2c_detach_client(client); --- frodo */ + } + /* all done, now unregister */ + adapters[i] = NULL; + adap_count--; + + ADAP_UNLOCK(); + DEB(printk("i2c-core.o: adapter unregistered: %s\n",adap->name)); + return 0; +} + + +/* ----- + * What follows is the "upwards" interface: commands for talking to clients, + * which implement the functions to access the physical information of the + * chips. + */ + +int i2c_add_driver(struct i2c_driver *driver) +{ + int i,j; + DRV_LOCK(); + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (NULL == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: register_driver(%s) - enlarge I2C_DRIVER_MAX.\n", + driver->name); + DRV_UNLOCK(); + return -ENOMEM; + } + + drivers[i] = driver; + driver_count++; + + DRV_UNLOCK(); /* driver was successfully added */ + + DEB(printk("i2c-core.o: driver %s registered.\n",driver->name)); + + /* Notify all existing adapters and clients to dummy driver */ + ADAP_LOCK(); + if (driver->flags&I2C_DF_DUMMY) { + for (i=0; iattach_adapter(adapters[i]); + for (j=0; jclients[j]) + driver->detach_client( + adapters[i]->clients[j]); + } + } + ADAP_UNLOCK(); + return 0; + } + + /* now look for instances of driver on our adapters + */ + if ( driver->flags&I2C_DF_NOTIFY ) { + for (i=0;iattach_adapter(adapters[i]); + } + ADAP_UNLOCK(); + return 0; +} + +int i2c_del_driver(struct i2c_driver *driver) +{ + int i,j,k; + + DRV_LOCK(); + for (i = 0; i < I2C_DRIVER_MAX; i++) + if (driver == drivers[i]) + break; + if (I2C_DRIVER_MAX == i) { + printk(KERN_WARNING " i2c-core.o: unregister_driver: [%s] not found\n", + driver->name); + DRV_UNLOCK(); + return -ENODEV; + } + /* Have a look at each adapter, if clients of this driver are still + * attached. If so, detach them to be able to kill the driver + * afterwards. + */ + DEB2(printk("i2c-core.o: unregister_driver - looking for clients.\n")); + /* removing clients does not depend on the notify flag, else + * invalid operation might (will!) result, when using stale client + * pointers. + */ + ADAP_LOCK(); /* should be moved inside the if statement... */ + if ((driver->flags&I2C_DF_DUMMY)==0) + for (k=0;kname)); + for (j=0;jclients[j]; + if (client != NULL && client->driver == driver) { + DEB2(printk("i2c-core.o: detaching client %s:\n", + client->name)); + /*i2c_detach_client(client);*/ + driver->detach_client(client); + } + } + } + ADAP_UNLOCK(); + drivers[i] = NULL; + driver_count--; + DRV_UNLOCK(); + + DEB(printk("i2c-core.o: driver unregistered: %s\n",driver->name)); + return 0; +} + +int i2c_check_addr (struct i2c_adapter *adapter, int addr) +{ + int i; + for (i = 0; i < I2C_CLIENT_MAX ; i++) + if (adapter->clients[i] && (adapter->clients[i]->addr == addr)) + return -EBUSY; + return 0; +} + +int i2c_attach_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + int i; + + if (i2c_check_addr(client->adapter,client->addr)) + return -EBUSY; + + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (NULL == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING + " i2c-core.o: attach_client(%s) - enlarge I2C_CLIENT_MAX.\n", + client->name); + return -ENOMEM; + } + + adapter->clients[i] = client; + adapter->client_count++; + i2c_dummy_client(client); + + if (adapter->client_register != NULL) + adapter->client_register(client); + DEB(printk("i2c-core.o: client [%s] registered to adapter [%s](pos. %d).\n", + client->name, adapter->name,i)); + + if(client->flags & I2C_CLIENT_ALLOW_USE) + client->usage_count = 0; + + return 0; +} + + +int i2c_detach_client(struct i2c_client *client) +{ + struct i2c_adapter *adapter = client->adapter; + int i; + + for (i = 0; i < I2C_CLIENT_MAX; i++) + if (client == adapter->clients[i]) + break; + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING " i2c-core.o: unregister_client [%s] not found\n", + client->name); + return -ENODEV; + } + + if( (client->flags & I2C_CLIENT_ALLOW_USE) && + (client->usage_count>0)) + return -EBUSY; + + if (adapter->client_unregister != NULL) + adapter->client_unregister(client); + /* client->driver->detach_client(client);*/ + + adapter->clients[i] = NULL; + adapter->client_count--; + i2c_dummy_client(client); + + DEB(printk("i2c-core.o: client [%s] unregistered.\n",client->name)); + return 0; +} + +void i2c_inc_use_client(struct i2c_client *client) +{ + + if (client->driver->inc_use != NULL) + client->driver->inc_use(client); + + if (client->adapter->inc_use != NULL) + client->adapter->inc_use(client->adapter); +} + +void i2c_dec_use_client(struct i2c_client *client) +{ + + if (client->driver->dec_use != NULL) + client->driver->dec_use(client); + + if (client->adapter->dec_use != NULL) + client->adapter->dec_use(client->adapter); +} + +struct i2c_client *i2c_get_client(int driver_id, int adapter_id, + struct i2c_client *prev) +{ + int i,j; + struct i2c_adapter *adapter=0; + + /* Will iterate through the list of clients in each adapter of adapters-list + in search for a client that matches the search criteria. driver_id or + adapter_id are ignored if set to 0. If both are ignored this returns + first client found. */ + + i = j = 0; + + /* set starting point */ + if(prev) + { + if(!(prev->adapter)) + return (struct i2c_client *) -EINVAL; + + for(j=0; j < I2C_ADAP_MAX; j++) + if(prev->adapter == adapters[j]) + break; + + /* invalid starting point? */ + if (I2C_ADAP_MAX == j) { + printk(KERN_WARNING " i2c-core.o: get_client adapter for client:[%s] not found\n", + prev->name); + return (struct i2c_client *) -ENODEV; + } + + for(i=0; i < I2C_CLIENT_MAX; i++) + if(prev == adapters[j]->clients[i]) + break; + + /* invalid starting point? */ + if (I2C_CLIENT_MAX == i) { + printk(KERN_WARNING " i2c-core.o: get_client client:[%s] not found\n", + prev->name); + return (struct i2c_client *) -ENODEV; + } + + i++; /* start from one after prev */ + } + + for(; j < I2C_ADAP_MAX; j++) + { + if(!adapters[j]) + continue; + + if(adapter_id && (adapters[j]->id != adapter_id)) + continue; + + for(; i < I2C_CLIENT_MAX; i++) + { + if(!adapters[j]->clients[i]) + continue; + + if(driver_id && (adapters[j]->clients[i]->driver->id != driver_id)) + continue; + if(adapters[j]->clients[i]->flags & I2C_CLIENT_ALLOW_USE) + return adapters[j]->clients[i]; + } + } + + return 0; +} + +int i2c_use_client(struct i2c_client *client) +{ + if(client->flags & I2C_CLIENT_ALLOW_USE) + if (client->flags & I2C_CLIENT_ALLOW_MULTIPLE_USE) + client->usage_count++; + else + if(client->usage_count > 0) + return -EBUSY; + else + client->usage_count++; + + i2c_inc_use_client(client); + + return 0; +} + +int i2c_release_client(struct i2c_client *client) +{ + if(client->flags & I2C_CLIENT_ALLOW_USE) + if(client->usage_count>0) + client->usage_count--; + else + { + printk(KERN_WARNING " i2c-core.o: dec_use_client used one too many times\n"); + return -EPERM; + } + + i2c_dec_use_client(client); + + return 0; +} + +/* ---------------------------------------------------- + * The /proc functions + * ---------------------------------------------------- + */ + +#ifdef CONFIG_PROC_FS + +#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,27)) +/* Monitor access to /proc/bus/i2c*; make unloading i2c-proc impossible + if some process still uses it or some file in it */ +void monitor_bus_i2c(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} +#endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION(2,3,37)) */ + +/* This function generates the output for /proc/bus/i2c */ +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, + void *private) +{ + int i; + int nr = 0; + /* Note that it is safe to write a `little' beyond len. Yes, really. */ + for (i = 0; (i < I2C_ADAP_MAX) && (nr < len); i++) + if (adapters[i]) { + nr += sprintf(buf+nr, "i2c-%d\t", i); + if (adapters[i]->algo->smbus_xfer) { + if (adapters[i]->algo->master_xfer) + nr += sprintf(buf+nr,"smbus/i2c"); + else + nr += sprintf(buf+nr,"smbus "); + } else if (adapters[i]->algo->master_xfer) + nr += sprintf(buf+nr,"i2c "); + else + nr += sprintf(buf+nr,"dummy "); + nr += sprintf(buf+nr,"\t%-32s\t%-32s\n", + adapters[i]->name, + adapters[i]->algo->name); + } + return nr; +} + +/* This function generates the output for /proc/bus/i2c-? */ +ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count, + loff_t *ppos) +{ + struct inode * inode = file->f_dentry->d_inode; + char *kbuf; + struct i2c_client *client; + int i,j,k,order_nr,len=0,len_total; + int order[I2C_CLIENT_MAX]; + + if (count < 0) + return -EINVAL; + len_total = file->f_pos + count; + /* Too bad if this gets longer (unlikely) */ + if (len_total > 4000) + len_total = 4000; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adapters[i]->inode == inode->i_ino) { + /* We need a bit of slack in the kernel buffer; this makes the + sprintf safe. */ + if (! (kbuf = kmalloc(count + 80,GFP_KERNEL))) + return -ENOMEM; + /* Order will hold the indexes of the clients + sorted by address */ + order_nr=0; + for (j = 0; j < I2C_CLIENT_MAX; j++) { + if ((client = adapters[i]->clients[j]) && + (client->driver->id != I2C_DRIVERID_I2CDEV)) { + for(k = order_nr; + (k > 0) && + adapters[i]->clients[order[k-1]]-> + addr > client->addr; + k--) + order[k] = order[k-1]; + order[k] = j; + order_nr++; + } + } + + + for (j = 0; (j < order_nr) && (len < len_total); j++) { + client = adapters[i]->clients[order[j]]; + len += sprintf(kbuf+len,"%02x\t%-32s\t%-32s\n", + client->addr, + client->name, + client->driver->name); + } + len = len - file->f_pos; + if (len > count) + len = count; + if (len < 0) + len = 0; + if (copy_to_user (buf,kbuf+file->f_pos, len)) { + kfree(kbuf); + return -EFAULT; + } + file->f_pos += len; + kfree(kbuf); + return len; + } + return -ENOENT; +} + +int i2cproc_init(void) +{ + + struct proc_dir_entry *proc_bus_i2c; + + i2cproc_initialized = 0; + + if (! proc_bus) { + printk("i2c-core.o: /proc/bus/ does not exist"); + i2cproc_cleanup(); + return -ENOENT; + } + proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); + if (!proc_bus_i2c) { + printk("i2c-core.o: Could not create /proc/bus/i2c"); + i2cproc_cleanup(); + return -ENOENT; + } + proc_bus_i2c->read_proc = &read_bus_i2c; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) + proc_bus_i2c->owner = THIS_MODULE; +#else + proc_bus_i2c->fill_inode = &monitor_bus_i2c; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,27)) */ + i2cproc_initialized += 2; + return 0; +} + +int i2cproc_cleanup(void) +{ + + if (i2cproc_initialized >= 1) { + remove_proc_entry("i2c",proc_bus); + i2cproc_initialized -= 2; + } + return 0; +} + + +#endif /* def CONFIG_PROC_FS */ + +/* --------------------------------------------------- + * dummy driver notification + * --------------------------------------------------- + */ + +static void i2c_dummy_adapter(struct i2c_adapter *adap) +{ + int i; + for (i=0; iflags & I2C_DF_DUMMY)) + drivers[i]->attach_adapter(adap); +} + +static void i2c_dummy_client(struct i2c_client *client) +{ + int i; + for (i=0; iflags & I2C_DF_DUMMY)) + drivers[i]->detach_client(client); +} + + +/* ---------------------------------------------------- + * the functional interface to the i2c busses. + * ---------------------------------------------------- + */ + +int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg msgs[],int num) +{ + int ret; + + if (adap->algo->master_xfer) { + DEB2(printk("i2c-core.o: master_xfer: %s with %d msgs.\n", + adap->name,num)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,msgs,num); + I2C_UNLOCK(adap); + + return ret; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + adap->id); + return -ENOSYS; + } +} + +int i2c_master_send(struct i2c_client *client,const char *buf ,int count) +{ + int ret; + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + + if (client->adapter->algo->master_xfer) { + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.len = count; + (const char *)msg.buf = buf; + + DEB2(printk("i2c-core.o: master_send: writing %d bytes on %s.\n", + count,client->adapter->name)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,&msg,1); + I2C_UNLOCK(adap); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + client->adapter->id); + return -ENOSYS; + } +} + +int i2c_master_recv(struct i2c_client *client, char *buf ,int count) +{ + struct i2c_adapter *adap=client->adapter; + struct i2c_msg msg; + int ret; + if (client->adapter->algo->master_xfer) { + msg.addr = client->addr; + msg.flags = client->flags & I2C_M_TEN; + msg.flags |= I2C_M_RD; + msg.len = count; + msg.buf = buf; + + DEB2(printk("i2c-core.o: master_recv: reading %d bytes on %s.\n", + count,client->adapter->name)); + + I2C_LOCK(adap); + ret = adap->algo->master_xfer(adap,&msg,1); + I2C_UNLOCK(adap); + + DEB2(printk("i2c-core.o: master_recv: return:%d (count:%d, addr:0x%02x)\n", + ret, count, client->addr)); + + /* if everything went ok (i.e. 1 msg transmitted), return #bytes + * transmitted, else error code. + */ + return (ret == 1 )? count : ret; + } else { + printk("i2c-core.o: I2C adapter %04x: I2C level transfers not supported\n", + client->adapter->id); + return -ENOSYS; + } +} + + +int i2c_control(struct i2c_client *client, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + struct i2c_adapter *adap = client->adapter; + + DEB2(printk("i2c-core.o: i2c ioctl, cmd: 0x%x, arg: %#lx\n", cmd, arg)); + switch ( cmd ) { + case I2C_RETRIES: + adap->retries = arg; + break; + case I2C_TIMEOUT: + adap->timeout = arg; + break; + default: + if (adap->algo->algo_control!=NULL) + ret = adap->algo->algo_control(adap,cmd,arg); + } + return ret; +} + +/* ---------------------------------------------------- + * the i2c address scanning function + * Will not work for 10-bit addresses! + * ---------------------------------------------------- + */ +int i2c_probe(struct i2c_adapter *adapter, + struct i2c_client_address_data *address_data, + i2c_client_found_addr_proc *found_proc) +{ + int addr,i,found,err; + int adap_id = i2c_adapter_id(adapter); + + /* Forget it if we can't probe using SMBUS_QUICK */ + if (! i2c_check_functionality(adapter,I2C_FUNC_SMBUS_QUICK)) + return -1; + + for (addr = 0x00; addr <= 0x7f; addr++) { + + /* Skip if already in use */ + if (i2c_check_addr(adapter,addr)) + continue; + + /* If it is in one of the force entries, we don't do any detection + at all */ + found = 0; + + for (i = 0; !found && (address_data->force[i] != I2C_CLIENT_END); i += 3) { + if (((adap_id == address_data->force[i]) || + (address_data->force[i] == ANY_I2C_BUS)) && + (addr == address_data->force[i+1])) { + DEB2(printk("i2c-core.o: found force parameter for adapter %d, addr %04x\n", + adap_id,addr)); + if ((err = found_proc(adapter,addr,0,0))) + return err; + found = 1; + } + } + if (found) + continue; + + /* If this address is in one of the ignores, we can forget about + it right now */ + for (i = 0; + !found && (address_data->ignore[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->ignore[i]) || + ((address_data->ignore[i] == ANY_I2C_BUS))) && + (addr == address_data->ignore[i+1])) { + DEB2(printk("i2c-core.o: found ignore parameter for adapter %d, " + "addr %04x\n", adap_id ,addr)); + found = 1; + } + } + for (i = 0; + !found && (address_data->ignore_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->ignore_range[i]) || + ((address_data->ignore_range[i]==ANY_I2C_BUS))) && + (addr >= address_data->ignore_range[i+1]) && + (addr <= address_data->ignore_range[i+2])) { + DEB2(printk("i2c-core.o: found ignore_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + found = 1; + } + } + if (found) + continue; + + /* Now, we will do a detection, but only if it is in the normal or + probe entries */ + for (i = 0; + !found && (address_data->normal_i2c[i] != I2C_CLIENT_END); + i += 1) { + if (addr == address_data->normal_i2c[i]) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c entry for adapter %d, " + "addr %02x", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->normal_i2c_range[i] != I2C_CLIENT_END); + i += 2) { + if ((addr >= address_data->normal_i2c_range[i]) && + (addr <= address_data->normal_i2c_range[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found normal i2c_range entry for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + + for (i = 0; + !found && (address_data->probe[i] != I2C_CLIENT_END); + i += 2) { + if (((adap_id == address_data->probe[i]) || + ((address_data->probe[i] == ANY_I2C_BUS))) && + (addr == address_data->probe[i+1])) { + found = 1; + DEB2(printk("i2c-core.o: found probe parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + for (i = 0; + !found && (address_data->probe_range[i] != I2C_CLIENT_END); + i += 3) { + if (((adap_id == address_data->probe_range[i]) || + (address_data->probe_range[i] == ANY_I2C_BUS)) && + (addr >= address_data->probe_range[i+1]) && + (addr <= address_data->probe_range[i+2])) { + found = 1; + DEB2(printk("i2c-core.o: found probe_range parameter for adapter %d, " + "addr %04x\n", adap_id,addr)); + } + } + if (!found) + continue; + + /* OK, so we really should examine this address. First check + whether there is some client here at all! */ + if (i2c_smbus_xfer(adapter,addr,0,0,0,I2C_SMBUS_QUICK,NULL) >= 0) + if ((err = found_proc(adapter,addr,0,-1))) + return err; + } + return 0; +} + +/* + * return id number for a specific adapter + */ +int i2c_adapter_id(struct i2c_adapter *adap) +{ + int i; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (adap == adapters[i]) + return i; + return -1; +} + +/* The SMBus parts */ + +extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value) +{ + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + value,0,I2C_SMBUS_QUICK,NULL); +} + +extern s32 i2c_smbus_read_byte(struct i2c_client * client) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value) +{ + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); +} + +extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, u8 command, + u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_BYTE_DATA,&data); +} + +extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data)) + return -1; + else + return 0x0FFFF & data.word; +} + +extern s32 i2c_smbus_write_word_data(struct i2c_client * client, + u8 command, u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_WORD_DATA,&data); +} + +extern s32 i2c_smbus_process_call(struct i2c_client * client, + u8 command, u16 value) +{ + union i2c_smbus_data data; + data.word = value; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_PROC_CALL, &data)) + return -1; + else + return 0x0FFFF & data.word; +} + +/* Returns the number of read bytes */ +extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values) +{ + union i2c_smbus_data data; + int i; + if (i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_READ,command, + I2C_SMBUS_BLOCK_DATA,&data)) + return -1; + else { + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; + } +} + +extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > 32) + length = 32; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + data.block[0] = length; + return i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE,command, + I2C_SMBUS_BLOCK_DATA,&data); +} + +/* Simulate a SMBus command using the i2c protocol + No checking of parameters is done! */ +static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + /* So we need to generate a series of msgs. In the case of writing, we + need to use only one message; when reading, we need two. We initialize + most things with sane defaults, to keep the code below somewhat + simpler. */ + unsigned char msgbuf0[33]; + unsigned char msgbuf1[33]; + int num = read_write == I2C_SMBUS_READ?2:1; + struct i2c_msg msg[2] = { { addr, flags, 1, msgbuf0 }, + { addr, flags | I2C_M_RD, 0, msgbuf1 } + }; + int i; + + msgbuf0[0] = command; + switch(size) { + case I2C_SMBUS_QUICK: + msg[0].len = 0; + /* Special case: The read/write field is used as data */ + msg[0].flags = flags | (read_write==I2C_SMBUS_READ)?I2C_M_RD:0; + num = 1; + break; + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_READ) { + /* Special case: only a read! */ + msg[0].flags = I2C_M_RD | flags; + num = 1; + } + break; + case I2C_SMBUS_BYTE_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 1; + else { + msg[0].len = 2; + msgbuf0[1] = data->byte; + } + break; + case I2C_SMBUS_WORD_DATA: + if (read_write == I2C_SMBUS_READ) + msg[1].len = 2; + else { + msg[0].len=3; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = (data->word >> 8) & 0xff; + } + break; + case I2C_SMBUS_PROC_CALL: + num = 2; /* Special case */ + msg[0].len = 3; + msg[1].len = 2; + msgbuf0[1] = data->word & 0xff; + msgbuf0[2] = (data->word >> 8) & 0xff; + break; + case I2C_SMBUS_BLOCK_DATA: + if (read_write == I2C_SMBUS_READ) { + printk("i2c-core.o: Block read not supported under " + "I2C emulation!\n"); + return -1; + } else { + msg[1].len = data->block[0] + 1; + if (msg[1].len > 32) { + printk("i2c-core.o: smbus_access called with " + "invalid block write size (%d)\n", + msg[1].len); + return -1; + } + for (i = 1; i <= msg[1].len; i++) + msgbuf0[i] = data->block[i]; + } + break; + default: + printk("i2c-core.o: smbus_access called with invalid size (%d)\n", + size); + return -1; + } + + if (i2c_transfer(adapter, msg, num) < 0) + return -1; + + if (read_write == I2C_SMBUS_READ) + switch(size) { + case I2C_SMBUS_BYTE: + data->byte = msgbuf0[0]; + break; + case I2C_SMBUS_BYTE_DATA: + data->byte = msgbuf1[0]; + break; + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + data->word = msgbuf1[0] | (msgbuf1[1] << 8); + break; + } + return 0; +} + + +s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + s32 res; + flags = flags & I2C_M_TEN; + if (adapter->algo->smbus_xfer) { + I2C_LOCK(adapter); + res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, + command,size,data); + I2C_UNLOCK(adapter); + } else + res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, + command,size,data); + return res; +} + + +/* You should always define `functionality'; the 'else' is just for + backward compatibility. */ +u32 i2c_get_functionality (struct i2c_adapter *adap) +{ + if (adap->algo->functionality) + return adap->algo->functionality(adap); + else + return 0xffffffff; +} + +int i2c_check_functionality (struct i2c_adapter *adap, u32 func) +{ + u32 adap_func = i2c_get_functionality (adap); + return (func & adap_func) == func; +} + + +static int __init i2c_init(void) +{ + printk("i2c-core.o: i2c core module\n"); + memset(adapters,0,sizeof(adapters)); + memset(drivers,0,sizeof(drivers)); + adap_count=0; + driver_count=0; + + init_MUTEX(&adap_lock); + init_MUTEX(&driver_lock); + + i2cproc_init(); + + return 0; +} + +#ifndef MODULE +#ifdef CONFIG_I2C_CHARDEV + extern int i2c_dev_init(void); +#endif +#ifdef CONFIG_I2C_ALGOBIT + extern int algo_bit_init(void); +#endif +#ifdef CONFIG_I2C_BITLP + extern int bitlp_init(void); +#endif +#ifdef CONFIG_I2C_BITELV + extern int bitelv_init(void); +#endif +#ifdef CONFIG_I2C_BITVELLE + extern int bitvelle_init(void); +#endif +#ifdef CONFIG_I2C_BITVIA + extern int bitvia_init(void); +#endif + +#ifdef CONFIG_I2C_ALGOPCF + extern int algo_pcf_init(void); +#endif +#ifdef CONFIG_I2C_PCFISA + extern int pcfisa_init(void); +#endif + +/* This is needed for automatic patch generation: sensors code starts here */ +/* This is needed for automatic patch generation: sensors code ends here */ + +int __init i2c_init_all(void) +{ + /* --------------------- global ----- */ + i2c_init(); + +#ifdef CONFIG_I2C_CHARDEV + i2c_dev_init(); +#endif + /* --------------------- bit -------- */ +#ifdef CONFIG_I2C_ALGOBIT + i2c_algo_bit_init(); +#endif +#ifdef CONFIG_I2C_PHILIPSPAR + i2c_bitlp_init(); +#endif +#ifdef CONFIG_I2C_ELV + i2c_bitelv_init(); +#endif +#ifdef CONFIG_I2C_VELLEMAN + i2c_bitvelle_init(); +#endif + + /* --------------------- pcf -------- */ +#ifdef CONFIG_I2C_ALGOPCF + i2c_algo_pcf_init(); +#endif +#ifdef CONFIG_I2C_ELEKTOR + i2c_pcfisa_init(); +#endif +/* This is needed for automatic patch generation: sensors code starts here */ +/* This is needed for automatic patch generation: sensors code ends here */ + + return 0; +} + +#endif + + + +EXPORT_SYMBOL(i2c_add_adapter); +EXPORT_SYMBOL(i2c_del_adapter); +EXPORT_SYMBOL(i2c_add_driver); +EXPORT_SYMBOL(i2c_del_driver); +EXPORT_SYMBOL(i2c_attach_client); +EXPORT_SYMBOL(i2c_detach_client); +EXPORT_SYMBOL(i2c_inc_use_client); +EXPORT_SYMBOL(i2c_dec_use_client); +EXPORT_SYMBOL(i2c_get_client); +EXPORT_SYMBOL(i2c_use_client); +EXPORT_SYMBOL(i2c_release_client); +EXPORT_SYMBOL(i2c_check_addr); + + +EXPORT_SYMBOL(i2c_master_send); +EXPORT_SYMBOL(i2c_master_recv); +EXPORT_SYMBOL(i2c_control); +EXPORT_SYMBOL(i2c_transfer); +EXPORT_SYMBOL(i2c_adapter_id); +EXPORT_SYMBOL(i2c_probe); + +EXPORT_SYMBOL(i2c_smbus_xfer); +EXPORT_SYMBOL(i2c_smbus_write_quick); +EXPORT_SYMBOL(i2c_smbus_read_byte); +EXPORT_SYMBOL(i2c_smbus_write_byte); +EXPORT_SYMBOL(i2c_smbus_read_byte_data); +EXPORT_SYMBOL(i2c_smbus_write_byte_data); +EXPORT_SYMBOL(i2c_smbus_read_word_data); +EXPORT_SYMBOL(i2c_smbus_write_word_data); +EXPORT_SYMBOL(i2c_smbus_process_call); +EXPORT_SYMBOL(i2c_smbus_read_block_data); +EXPORT_SYMBOL(i2c_smbus_write_block_data); + +EXPORT_SYMBOL(i2c_get_functionality); +EXPORT_SYMBOL(i2c_check_functionality); + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus main module"); +MODULE_PARM(i2c_debug, "i"); +MODULE_PARM_DESC(i2c_debug,"debug level"); + +int init_module(void) +{ + return i2c_init(); +} + +void cleanup_module(void) +{ + i2cproc_cleanup(); +} +#endif diff -urN vanilla-2.2.15pre17/drivers/i2c/i2c-dev.c bttv-2.2.15pre17/drivers/i2c/i2c-dev.c --- vanilla-2.2.15pre17/drivers/i2c/i2c-dev.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/i2c-dev.c Mon May 1 23:02:30 2000 @@ -0,0 +1,501 @@ +/* + i2c-dev.c - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard + + 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. +*/ + +/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module. + But I have used so much of his original code and ideas that it seems + only fair to recognize him as co-author -- Frodo */ + +/* The I2C_RDWR ioctl code is written by Kolja Waschk */ + +/* $Id: i2c-dev.c,v 1.30 2000/02/28 21:35:05 frodo Exp $ */ + +#include +#include +#include +#include +#include + +/* If you want debugging uncomment: */ +/* #define DEBUG */ + +#include +#include + +#include +#include + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* def MODULE */ + +/* struct file_operations changed too often in the 2.1 series for nice code */ + +static loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin); + +static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, + loff_t *offset); +static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, + loff_t *offset); + +static int i2cdev_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); +static int i2cdev_open (struct inode *inode, struct file *file); + +static int i2cdev_release (struct inode *inode, struct file *file); + +static int i2cdev_attach_adapter(struct i2c_adapter *adap); +static int i2cdev_detach_client(struct i2c_client *client); +static int i2cdev_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +#ifdef MODULE +static +#else +extern +#endif + int __init i2c_dev_init(void); +static int i2cdev_cleanup(void); + +static struct file_operations i2cdev_fops = { + llseek: i2cdev_lseek, + read: i2cdev_read, + write: i2cdev_write, + ioctl: i2cdev_ioctl, + open: i2cdev_open, + release: i2cdev_release, +}; + +#define I2CDEV_ADAPS_MAX I2C_ADAP_MAX +static struct i2c_adapter *i2cdev_adaps[I2CDEV_ADAPS_MAX]; + +static struct i2c_driver i2cdev_driver = { + /* name */ "i2c-dev dummy driver", + /* id */ I2C_DRIVERID_I2CDEV, + /* flags */ I2C_DF_DUMMY, + /* attach_adapter */ i2cdev_attach_adapter, + /* detach_client */ i2cdev_detach_client, + /* command */ i2cdev_command, + /* inc_use */ NULL, + /* dec_use */ NULL, +}; + +static struct i2c_client i2cdev_client_template = { + /* name */ "I2C /dev entry", + /* id */ 1, + /* flags */ 0, + /* addr */ -1, + /* adapter */ NULL, + /* driver */ &i2cdev_driver, + /* data */ NULL +}; + +static int i2cdev_initialized; + +/* Note that the lseek function is called llseek in 2.1 kernels. But things + are complicated enough as is. */ +loff_t i2cdev_lseek (struct file *file, loff_t offset, int origin) +{ +#ifdef DEBUG + struct inode *inode = file->f_dentry->d_inode; + printk("i2c-dev,o: i2c-%d lseek to %ld bytes relative to %d.\n", + MINOR(inode->i_rdev),(long) offset,origin); +#endif /* DEBUG */ + return -ESPIPE; +} + +static ssize_t i2cdev_read (struct file *file, char *buf, size_t count, + loff_t *offset) +{ + char *tmp; + int ret; + +#ifdef DEBUG + struct inode *inode = file->f_dentry->d_inode; +#endif /* DEBUG */ + + struct i2c_client *client = (struct i2c_client *)file->private_data; + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + +#ifdef DEBUG + printk("i2c-dev,o: i2c-%d reading %d bytes.\n",MINOR(inode->i_rdev), + count); +#endif + + ret = i2c_master_recv(client,tmp,count); + if (ret >= 0) + ret = copy_to_user(buf,tmp,count)?-EFAULT:ret; + kfree(tmp); + return ret; +} + +static ssize_t i2cdev_write (struct file *file, const char *buf, size_t count, + loff_t *offset) +{ + int ret; + char *tmp; + struct i2c_client *client = (struct i2c_client *)file->private_data; + +#ifdef DEBUG + struct inode *inode = file->f_dentry->d_inode; +#endif /* DEBUG */ + + /* copy user space data to kernel space. */ + tmp = kmalloc(count,GFP_KERNEL); + if (tmp==NULL) + return -ENOMEM; + if (copy_from_user(tmp,buf,count)) { + kfree(tmp); + return -EFAULT; + } + +#ifdef DEBUG + printk("i2c-dev,o: i2c-%d writing %d bytes.\n",MINOR(inode->i_rdev), + count); +#endif + ret = i2c_master_send(client,tmp,count); + kfree(tmp); + return ret; +} + +int i2cdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct i2c_client *client = (struct i2c_client *)file->private_data; + struct i2c_rdwr_ioctl_data rdwr_arg; + struct i2c_smbus_ioctl_data data_arg; + union i2c_smbus_data temp; + struct i2c_msg *rdwr_pa; + int i,datasize,res; + unsigned long funcs; + +#ifdef DEBUG + printk("i2c-dev.o: i2c-%d ioctl, cmd: 0x%x, arg: %lx.\n", + MINOR(inode->i_rdev),cmd, arg); +#endif /* DEBUG */ + + switch ( cmd ) { + case I2C_SLAVE: + case I2C_SLAVE_FORCE: + if ((arg > 0x3ff) || + (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) + return -EINVAL; + if ((cmd == I2C_SLAVE) && i2c_check_addr(client->adapter,arg)) + return -EBUSY; + client->addr = arg; + return 0; + case I2C_TENBIT: + if (arg) + client->flags |= I2C_M_TEN; + else + client->flags &= ~I2C_M_TEN; + return 0; + case I2C_FUNCS: + funcs = i2c_get_functionality(client->adapter); + return (copy_to_user((unsigned long *)arg,&funcs, + sizeof(unsigned long)))?-EFAULT:0; + + case I2C_RDWR: + copy_from_user_ret(&rdwr_arg, + (struct i2c_rdwr_ioctl_data *)arg, + sizeof(rdwr_arg), + -EFAULT); + + rdwr_pa = (struct i2c_msg *) + kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), + GFP_KERNEL); + + if (rdwr_pa == NULL) return -ENOMEM; + + res = 0; + for( i=0; iadapter, + rdwr_pa, + rdwr_arg.nmsgs); + } + while(i-- > 0) + { + if( res>=0 && (rdwr_pa[i].flags & I2C_M_RD)) + { + if(copy_to_user( + rdwr_arg.msgs[i].buf, + rdwr_pa[i].buf, + rdwr_pa[i].len)) + { + res = -EFAULT; + } + } + kfree(rdwr_pa[i].buf); + } + kfree(rdwr_pa); + return res; + + case I2C_SMBUS: + copy_from_user_ret(&data_arg, + (struct i2c_smbus_ioctl_data *) arg, + sizeof(struct i2c_smbus_ioctl_data), + -EFAULT); + if ((data_arg.size != I2C_SMBUS_BYTE) && + (data_arg.size != I2C_SMBUS_QUICK) && + (data_arg.size != I2C_SMBUS_BYTE_DATA) && + (data_arg.size != I2C_SMBUS_WORD_DATA) && + (data_arg.size != I2C_SMBUS_PROC_CALL) && + (data_arg.size != I2C_SMBUS_BLOCK_DATA)) { +#ifdef DEBUG + printk("i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.size); +#endif + return -EINVAL; + } + /* Note that I2C_SMBUS_READ and I2C_SMBUS_WRITE are 0 and 1, + so the check is valid if size==I2C_SMBUS_QUICK too. */ + if ((data_arg.read_write != I2C_SMBUS_READ) && + (data_arg.read_write != I2C_SMBUS_WRITE)) { +#ifdef DEBUG + printk("i2c-dev.o: read_write out of range (%x) in ioctl I2C_SMBUS.\n", + data_arg.read_write); +#endif + return -EINVAL; + } + + /* Note that command values are always valid! */ + + if ((data_arg.size == I2C_SMBUS_QUICK) || + ((data_arg.size == I2C_SMBUS_BYTE) && + (data_arg.read_write == I2C_SMBUS_WRITE))) + /* These are special: we do not use data */ + return i2c_smbus_xfer(client->adapter, client->addr, + client->flags, + data_arg.read_write, + data_arg.command, + data_arg.size, NULL); + + if (data_arg.data == NULL) { +#ifdef DEBUG + printk("i2c-dev.o: data is NULL pointer in ioctl I2C_SMBUS.\n"); +#endif + return -EINVAL; + } + + if ((data_arg.size == I2C_SMBUS_BYTE_DATA) || + (data_arg.size == I2C_SMBUS_BYTE)) + datasize = sizeof(data_arg.data->byte); + else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || + (data_arg.size == I2C_SMBUS_PROC_CALL)) + datasize = sizeof(data_arg.data->word); + else /* size == I2C_SMBUS_BLOCK_DATA */ + datasize = sizeof(data_arg.data->block); + + if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_WRITE)) + copy_from_user_ret(&temp,data_arg.data,datasize, + -EFAULT); + res = i2c_smbus_xfer(client->adapter,client->addr,client->flags, + data_arg.read_write, + data_arg.command,data_arg.size,&temp); + if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.read_write == I2C_SMBUS_READ))) + copy_to_user_ret(data_arg.data,&temp,datasize,-EFAULT); + return res; + + default: + return i2c_control(client,cmd,arg); + } + return 0; +} + +int i2cdev_open (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct i2c_client *client; + + if ((minor >= I2CDEV_ADAPS_MAX) || ! (i2cdev_adaps[minor])) { +#ifdef DEBUG + printk("i2c-dev.o: Trying to open unattached adapter i2c-%d\n", + minor); +#endif + return -ENODEV; + } + + /* Note that we here allocate a client for later use, but we will *not* + register this client! Yes, this is safe. No, it is not very clean. */ + if(! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(client,&i2cdev_client_template,sizeof(struct i2c_client)); + client->adapter = i2cdev_adaps[minor]; + file->private_data = client; + + if (i2cdev_adaps[minor]->inc_use) + i2cdev_adaps[minor]->inc_use(i2cdev_adaps[minor]); + MOD_INC_USE_COUNT; + +#ifdef DEBUG + printk("i2c-dev.o: opened i2c-%d\n",minor); +#endif + return 0; +} + +static int i2cdev_release (struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + kfree(file->private_data); + file->private_data=NULL; +#ifdef DEBUG + printk("i2c-dev.o: Closed: i2c-%d\n", minor); +#endif + MOD_DEC_USE_COUNT; + if (i2cdev_adaps[minor]->dec_use) + i2cdev_adaps[minor]->dec_use(i2cdev_adaps[minor]); + return 0; +} + +int i2cdev_attach_adapter(struct i2c_adapter *adap) +{ + int i; + + if ((i = i2c_adapter_id(adap)) < 0) { + printk("i2c-dev.o: Unknown adapter ?!?\n"); + return -ENODEV; + } + if (i >= I2CDEV_ADAPS_MAX) { + printk("i2c-dev.o: Adapter number too large?!? (%d)\n",i); + return -ENODEV; + } + + if (! i2cdev_adaps[i]) { + i2cdev_adaps[i] = adap; + printk("i2c-dev.o: Registered '%s' as minor %d\n",adap->name,i); + } else { + i2cdev_adaps[i] = NULL; +#ifdef DEBUG + printk("i2c-dev.o: Adapter unregistered: %s\n",adap->name); +#endif + } + + return 0; +} + +int i2cdev_detach_client(struct i2c_client *client) +{ + return 0; +} + +static int i2cdev_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + return -1; +} + +int __init i2c_dev_init(void) +{ + int res; + + printk("i2c-dev.o: i2c /dev entries driver module\n"); + + i2cdev_initialized = 0; + if (register_chrdev(I2C_MAJOR,"i2c",&i2cdev_fops)) { + printk("i2c-dev.o: unable to get major %d for i2c bus\n", + I2C_MAJOR); + return -EIO; + } + i2cdev_initialized ++; + + if ((res = i2c_add_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver registration failed, module not inserted.\n"); + i2cdev_cleanup(); + return res; + } + i2cdev_initialized ++; + return 0; +} + +int i2cdev_cleanup(void) +{ + int res; + + if (i2cdev_initialized >= 2) { + if ((res = i2c_del_driver(&i2cdev_driver))) { + printk("i2c-dev.o: Driver deregistration failed, " + "module not removed.\n"); + return res; + } + i2cdev_initialized ++; + } + + if (i2cdev_initialized >= 1) { + if ((res = unregister_chrdev(I2C_MAJOR,"i2c"))) { + printk("i2c-dev.o: unable to release major %d for i2c bus\n", + I2C_MAJOR); + return res; + } + i2cdev_initialized --; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard and Simon G. Vogl "); +MODULE_DESCRIPTION("I2C /dev entries driver"); + +int init_module(void) +{ + return i2c_dev_init(); +} + +int cleanup_module(void) +{ + return i2cdev_cleanup(); +} + +#endif /* def MODULE */ + diff -urN vanilla-2.2.15pre17/drivers/i2c/i2c-elektor.c bttv-2.2.15pre17/drivers/i2c/i2c-elektor.c --- vanilla-2.2.15pre17/drivers/i2c/i2c-elektor.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/i2c-elektor.c Mon May 1 23:02:30 2000 @@ -0,0 +1,298 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-elektor.c,v 1.18 2000/03/22 22:59:54 frodo Exp $ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "i2c-pcf8584.h" + +#define DEFAULT_BASE 0x300 +#define DEFAULT_IRQ 0 +#define DEFAULT_CLOCK 0x1c +#define DEFAULT_OWN 0x55 + +static int base = 0; +static int irq = 0; +static int clock = 0; +static int own = 0; +static int i2c_debug=0; +static struct i2c_pcf_isa gpi; +#if (LINUX_VERSION_CODE < 0x020301) +static struct wait_queue *pcf_wait = NULL; +#else +static wait_queue_head_t pcf_wait; +#endif +static int pcf_pending; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x +#define DEBE(x) x /* error messages */ + + +/* --- Convenience defines for the i2c port: */ +#define BASE ((struct i2c_pcf_isa *)(data))->pi_base +#define DATA BASE /* Adapter data port */ +#define CTRL (BASE+1) /* Adapter control port */ + +/* ----- local functions ---------------------------------------------- */ + +static void pcf_isa_setbyte(void *data, int ctl, int val) +{ + unsigned long j = jiffies + 10; + + if (ctl) { + if (gpi.pi_irq > 0) { + DEB3(printk("i2c-elektor.o: Write Ctrl 0x%02X\n", + val|I2C_PCF_ENI)); + DEB3({while (jiffies < j) schedule();}) + outb(val | I2C_PCF_ENI, CTRL); + } else { + DEB3(printk("i2c-elektor.o: Write Ctrl 0x%02X\n", val|I2C_PCF_ENI)); + DEB3({while (jiffies < j) schedule();}) + outb(val|I2C_PCF_ENI, CTRL); + } + } else { + DEB3(printk("i2c-elektor.o: Write Data 0x%02X\n", val&0xff)); + DEB3({while (jiffies < j) schedule();}) + outb(val, DATA); + } +} + +static int pcf_isa_getbyte(void *data, int ctl) +{ + int val; + + if (ctl) { + val = inb(CTRL); + DEB3(printk("i2c-elektor.o: Read Ctrl 0x%02X\n", val)); + } else { + val = inb(DATA); + DEB3(printk("i2c-elektor.o: Read Data 0x%02X\n", val)); + } + return (val); +} + +static int pcf_isa_getown(void *data) +{ + return (gpi.pi_own); +} + + +static int pcf_isa_getclock(void *data) +{ + return (gpi.pi_clock); +} + + + +#if 0 +static void pcf_isa_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} +#endif + + +static void pcf_isa_waitforpin(void) { + + int timeout = 2; + + if (gpi.pi_irq > 0) { + cli(); + if (pcf_pending == 0) { + interruptible_sleep_on_timeout(&pcf_wait, timeout*HZ ); + } else + pcf_pending = 0; + sti(); + } else { + udelay(100); + } +} + + +static void pcf_isa_handler(int this_irq, void *dev_id, struct pt_regs *regs) { + pcf_pending = 1; + wake_up_interruptible(&pcf_wait); +} + + +static int pcf_isa_init(void) +{ + if (check_region(gpi.pi_base, 2) < 0 ) { + return -ENODEV; + } else { + request_region(gpi.pi_base, 2, "i2c (isa bus adapter)"); + } + if (gpi.pi_irq > 0) { + if (request_irq(gpi.pi_irq, pcf_isa_handler, 0, "PCF8584", 0) + < 0) { + printk("i2c-elektor.o: Request irq%d failed\n", gpi.pi_irq); + gpi.pi_irq = 0; + } else + enable_irq(gpi.pi_irq); + } + return 0; +} + + +static void pcf_isa_exit(void) +{ + if (gpi.pi_irq > 0) { + disable_irq(gpi.pi_irq); + free_irq(gpi.pi_irq, 0); + } + release_region(gpi.pi_base , 2); +} + + +static int pcf_isa_reg(struct i2c_client *client) +{ + return 0; +} + + +static int pcf_isa_unreg(struct i2c_client *client) +{ + return 0; +} + +static void pcf_isa_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void pcf_isa_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_pcf_data pcf_isa_data = { + NULL, + pcf_isa_setbyte, + pcf_isa_getbyte, + pcf_isa_getown, + pcf_isa_getclock, + pcf_isa_waitforpin, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter pcf_isa_ops = { + "PCF8584 ISA adapter", + I2C_HW_P_ELEK, + NULL, + &pcf_isa_data, + pcf_isa_inc_use, + pcf_isa_dec_use, + pcf_isa_reg, + pcf_isa_unreg, +}; + +static int __init i2c_pcfisa_init(void) +{ + + struct i2c_pcf_isa *pisa = &gpi; + + printk("i2c-elektor.o: i2c pcf8584-isa adapter module\n"); + if (base == 0) + pisa->pi_base = DEFAULT_BASE; + else + pisa->pi_base = base; + + if (irq == 0) + pisa->pi_irq = DEFAULT_IRQ; + else + pisa->pi_irq = irq; + + if (clock == 0) + pisa->pi_clock = DEFAULT_CLOCK; + else + pisa->pi_clock = clock; + + if (own == 0) + pisa->pi_own = DEFAULT_OWN; + else + pisa->pi_own = own; + + pcf_isa_data.data = (void *)pisa; +#if (LINUX_VERSION_CODE >= 0x020301) + init_waitqueue_head(&pcf_wait); +#endif + if (pcf_isa_init() == 0) { + if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + printk("i2c-elektor.o: found device at %#x.\n", pisa->pi_base); + return 0; +} + + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Hans Berglund "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for PCF8584 ISA bus adapter"); + +MODULE_PARM(base, "i"); +MODULE_PARM(irq, "i"); +MODULE_PARM(clock, "i"); +MODULE_PARM(own, "i"); +MODULE_PARM(i2c_debug,"i"); + +int init_module(void) +{ + return i2c_pcfisa_init(); +} + +void cleanup_module(void) +{ + i2c_pcf_del_bus(&pcf_isa_ops); + pcf_isa_exit(); +} + +#endif diff -urN vanilla-2.2.15pre17/drivers/i2c/i2c-elv.c bttv-2.2.15pre17/drivers/i2c/i2c-elv.c --- vanilla-2.2.15pre17/drivers/i2c/i2c-elv.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/i2c-elv.c Mon May 1 23:02:30 2000 @@ -0,0 +1,218 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elv.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-2000 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-elv.c,v 1.16 2000/01/18 23:54:07 frodo Exp $ */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define DEFAULT_BASE 0x378 +static int base=0; +static unsigned char PortData = 0; + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ +#define DEBINIT(x) x /* detection status messages */ + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + + +/* ----- local functions ---------------------------------------------- */ + + +static void bit_elv_setscl(void *data, int state) +{ + if (state) { + PortData &= 0xfe; + } else { + PortData |=1; + } + outb(PortData, DATA); +} + +static void bit_elv_setsda(void *data, int state) +{ + if (state) { + PortData &=0xfd; + } else { + PortData |=2; + } + outb(PortData, DATA); +} + +static int bit_elv_getscl(void *data) +{ + return ( 0 == ( (inb_p(STAT)) & 0x08 ) ); +} + +static int bit_elv_getsda(void *data) +{ + return ( 0 == ( (inb_p(STAT)) & 0x40 ) ); +} + +static int bit_elv_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + /* test for ELV adap. */ + if (inb(base+1) & 0x80) { /* BUSY should be high */ + DEBINIT(printk("i2c-elv.o: Busy was low.\n")); + return -ENODEV; + } else { + outb(0x0c,base+2); /* SLCT auf low */ + udelay(400); + if ( !(inb(base+1) && 0x10) ) { + outb(0x04,base+2); + DEBINIT(printk("i2c-elv.o: Select was high.\n")); + return -ENODEV; + } + } + request_region(base,(base == 0x3bc)? 3 : 8, + "i2c (ELV adapter)"); + PortData = 0; + bit_elv_setsda((void*)base,1); + bit_elv_setscl((void*)base,1); + } + return 0; +} + +static void bit_elv_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + +static int bit_elv_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_elv_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_elv_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void bit_elv_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ +static struct i2c_algo_bit_data bit_elv_data = { + NULL, + bit_elv_setsda, + bit_elv_setscl, + bit_elv_getsda, + bit_elv_getscl, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_elv_ops = { + "ELV Parallel port adaptor", + I2C_HW_B_ELV, + NULL, + &bit_elv_data, + bit_elv_inc_use, + bit_elv_dec_use, + bit_elv_reg, + bit_elv_unreg, +}; + +int __init i2c_bitelv_init(void) +{ + printk("i2c-elv.o: i2c ELV parallel port adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_elv_data.data=(void*)DEFAULT_BASE; + if (bit_elv_init()==0) { + if(i2c_bit_add_bus(&bit_elv_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_elv_ops.data=(void*)base; + if (bit_elv_init()==0) { + if(i2c_bit_add_bus(&bit_elv_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-elv.o: found device at %#x.\n",base); + return 0; +} + + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for ELV parallel port adapter") +; + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitelv_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_elv_ops); + bit_elv_exit(); +} + +#endif diff -urN vanilla-2.2.15pre17/drivers/i2c/i2c-pcf8584.h bttv-2.2.15pre17/drivers/i2c/i2c-pcf8584.h --- vanilla-2.2.15pre17/drivers/i2c/i2c-pcf8584.h Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/i2c-pcf8584.h Mon May 1 23:02:30 2000 @@ -0,0 +1,78 @@ +/* -------------------------------------------------------------------- */ +/* i2c-pcf8584.h: PCF 8584 global defines */ +/* -------------------------------------------------------------------- */ +/* Copyright (C) 1996 Simon G. Vogl + 1999 Hans Berglund + + 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. */ +/* -------------------------------------------------------------------- */ + +/* With some changes from Frodo Looijaard */ + +/* $Id: i2c-pcf8584.h,v 1.3 2000/01/18 23:54:07 frodo Exp $ */ + +#ifndef I2C_PCF8584_H +#define I2C_PCF8584_H 1 + +/* ----- Control register bits ---------------------------------------- */ +#define I2C_PCF_PIN 0x80 +#define I2C_PCF_ESO 0x40 +#define I2C_PCF_ES1 0x20 +#define I2C_PCF_ES2 0x10 +#define I2C_PCF_ENI 0x08 +#define I2C_PCF_STA 0x04 +#define I2C_PCF_STO 0x02 +#define I2C_PCF_ACK 0x01 + +#define I2C_PCF_START (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_STOP (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_STO | I2C_PCF_ACK) +#define I2C_PCF_REPSTART ( I2C_PCF_ESO | I2C_PCF_STA | I2C_PCF_ACK) +#define I2C_PCF_IDLE (I2C_PCF_PIN | I2C_PCF_ESO | I2C_PCF_ACK) + +/* ----- Status register bits ----------------------------------------- */ +/*#define I2C_PCF_PIN 0x80 as above*/ + +#define I2C_PCF_INI 0x40 /* 1 if not initialized */ +#define I2C_PCF_STS 0x20 +#define I2C_PCF_BER 0x10 +#define I2C_PCF_AD0 0x08 +#define I2C_PCF_LRB 0x08 +#define I2C_PCF_AAS 0x04 +#define I2C_PCF_LAB 0x02 +#define I2C_PCF_BB 0x01 + +/* ----- Chip clock frequencies --------------------------------------- */ +#define I2C_PCF_CLK3 0x00 +#define I2C_PCF_CLK443 0x10 +#define I2C_PCF_CLK6 0x14 +#define I2C_PCF_CLK 0x18 +#define I2C_PCF_CLK12 0x1c + +/* ----- transmission frequencies ------------------------------------- */ +#define I2C_PCF_TRNS90 0x00 /* 90 kHz */ +#define I2C_PCF_TRNS45 0x01 /* 45 kHz */ +#define I2C_PCF_TRNS11 0x02 /* 11 kHz */ +#define I2C_PCF_TRNS15 0x03 /* 1.5 kHz */ + + +/* ----- Access to internal registers according to ES1,ES2 ------------ */ +/* they are mapped to the data port ( a0 = 0 ) */ +/* available when ESO == 0 : */ + +#define I2C_PCF_OWNADR 0 +#define I2C_PCF_INTREG I2C_PCF_ES2 +#define I2C_PCF_CLKREG I2C_PCF_ES1 + +#endif I2C_PCF8584_H diff -urN vanilla-2.2.15pre17/drivers/i2c/i2c-philips-par.c bttv-2.2.15pre17/drivers/i2c/i2c-philips-par.c --- vanilla-2.2.15pre17/drivers/i2c/i2c-philips-par.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/i2c-philips-par.c Mon May 1 23:02:30 2000 @@ -0,0 +1,218 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-philips-par.c i2c-hw access for philips style parallel port adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-2000 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-philips-par.c,v 1.16 2000/01/18 23:54:07 frodo Exp $ */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEFAULT_BASE 0x378 +static int base=0; + +/* Note: all we need to know is the base address of the parallel port, so + * instead of having a dedicated struct to store this value, we store this + * int in the pointer field (=bit_lp_ops.data) itself. + */ + +/* Note2: as the hw that implements the i2c bus on the parallel port is + * incompatible with other epp stuff etc., we access the port exclusively + * and don't cooperate with parport functions. + */ + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + +/* ----- printer port defines ------------------------------------------*/ + /* Pin Port Inverted name */ +#define I2C_ON 0x20 /* 12 status N paper */ + /* ... only for phil. not used */ +#define I2C_SDA 0x80 /* 9 data N data7 */ +#define I2C_SCL 0x08 /* 17 ctrl N dsel */ + +#define I2C_SDAIN 0x80 /* 11 stat Y busy */ +#define I2C_SCLIN 0x08 /* 15 stat Y enable */ + +#define I2C_DMASK 0x7f +#define I2C_CMASK 0xf7 + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +/* ----- local functions ---------------------------------------------- */ + +static void bit_lp_setscl(void *data, int state) +{ + /*be cautious about state of the control register - + touch only the one bit needed*/ + if (state) { + outb(inb(CTRL)|I2C_SCL, CTRL); + } else { + outb(inb(CTRL)&I2C_CMASK, CTRL); + } +} + +static void bit_lp_setsda(void *data, int state) +{ + if (state) { + outb(I2C_DMASK , DATA); + } else { + outb(I2C_SDA , DATA); + } +} + +static int bit_lp_getscl(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SCLIN ) ); +} + +static int bit_lp_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +static int bit_lp_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + return -ENODEV; + } else { + request_region(base,(base == 0x3bc)? 3 : 8, + "i2c (parallel port adapter)"); + /* reset hardware to sane state */ + bit_lp_setsda((void*)base,1); + bit_lp_setscl((void*)base,1); + } + return 0; +} + +static void bit_lp_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + +static int bit_lp_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_lp_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_lp_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void bit_lp_dec_use(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +static struct i2c_algo_bit_data bit_lp_data = { + NULL, + bit_lp_setsda, + bit_lp_setscl, + bit_lp_getsda, + bit_lp_getscl, + 80, 80, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_lp_ops = { + "Philips Parallel port adapter", + I2C_HW_B_LP, + NULL, + &bit_lp_data, + bit_lp_inc_use, + bit_lp_dec_use, + bit_lp_reg, + bit_lp_unreg, +}; + + +int __init i2c_bitlp_init(void) +{ + printk("i2c-philips-par.o: i2c Philips parallel port adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_lp_data.data=(void*)DEFAULT_BASE; + if (bit_lp_init()==0) { + if (i2c_bit_add_bus(&bit_lp_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_lp_data.data=(void*)base; + if (bit_lp_init()==0) { + if (i2c_bit_add_bus(&bit_lp_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-philips-par.o: found device at %#x.\n",base); + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Philips parallel port adapter"); + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitlp_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_lp_ops); + bit_lp_exit(); +} + +#endif + + + diff -urN vanilla-2.2.15pre17/drivers/i2c/i2c-velleman.c bttv-2.2.15pre17/drivers/i2c/i2c-velleman.c --- vanilla-2.2.15pre17/drivers/i2c/i2c-velleman.c Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/drivers/i2c/i2c-velleman.c Mon May 1 23:02:30 2000 @@ -0,0 +1,206 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-velleman.c i2c-hw access for Velleman K9000 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-96, 2000 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* $Id: i2c-velleman.c,v 1.19 2000/01/24 02:06:33 mds Exp $ */ + +#include +#include +#include +#include +#include /* for 2.0 kernels to get NULL */ +#include /* for 2.0 kernels to get ENODEV */ +#include + +#include +#include + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) /* should be reasonable open, close &c. */ +#define DEB2(x) /* low level debugging - very slow */ +#define DEBE(x) x /* error messages */ + + /* Pin Port Inverted name */ +#define I2C_SDA 0x02 /* ctrl bit 1 (inv) */ +#define I2C_SCL 0x08 /* ctrl bit 3 (inv) */ + +#define I2C_SDAIN 0x10 /* stat bit 4 */ +#define I2C_SCLIN 0x08 /* ctrl bit 3 (inv)(reads own output)*/ + +#define I2C_DMASK 0xfd +#define I2C_CMASK 0xf7 + + +/* --- Convenience defines for the parallel port: */ +#define BASE (unsigned int)(data) +#define DATA BASE /* Centronics data port */ +#define STAT (BASE+1) /* Centronics status port */ +#define CTRL (BASE+2) /* Centronics control port */ + +#define DEFAULT_BASE 0x378 +static int base=0; + +/* ----- local functions --------------------------------------------------- */ + +static void bit_velle_setscl(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_CMASK, CTRL); + } else { + outb(inb(CTRL) | I2C_SCL, CTRL); + } + +} + +static void bit_velle_setsda(void *data, int state) +{ + if (state) { + outb(inb(CTRL) & I2C_DMASK , CTRL); + } else { + outb(inb(CTRL) | I2C_SDA, CTRL); + } + +} + +static int bit_velle_getscl(void *data) +{ + return ( 0 == ( (inb(CTRL)) & I2C_SCLIN ) ); +} + +static int bit_velle_getsda(void *data) +{ + return ( 0 != ( (inb(STAT)) & I2C_SDAIN ) ); +} + +static int bit_velle_init(void) +{ + if (check_region(base,(base == 0x3bc)? 3 : 8) < 0 ) { + DEBE(printk("i2c-velleman.o: Port %#x already in use.\n", + base)); + return -ENODEV; + } else { + request_region(base, (base == 0x3bc)? 3 : 8, + "i2c (Vellemann adapter)"); + bit_velle_setsda((void*)base,1); + bit_velle_setscl((void*)base,1); + } + return 0; +} + +static void bit_velle_exit(void) +{ + release_region( base , (base == 0x3bc)? 3 : 8 ); +} + + +static int bit_velle_reg(struct i2c_client *client) +{ + return 0; +} + +static int bit_velle_unreg(struct i2c_client *client) +{ + return 0; +} + +static void bit_velle_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void bit_velle_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* ------------------------------------------------------------------------ + * Encapsulate the above functions in the correct operations structure. + * This is only done when more than one hardware adapter is supported. + */ + +static struct i2c_algo_bit_data bit_velle_data = { + NULL, + bit_velle_setsda, + bit_velle_setscl, + bit_velle_getsda, + bit_velle_getscl, + 10, 10, 100, /* waits, timeout */ +}; + +static struct i2c_adapter bit_velle_ops = { + "Velleman K8000", + I2C_HW_B_VELLE, + NULL, + &bit_velle_data, + bit_velle_inc_use, + bit_velle_dec_use, + bit_velle_reg, + bit_velle_unreg, +}; + +int __init i2c_bitvelle_init(void) +{ + printk("i2c-velleman.o: i2c Velleman K8000 adapter module\n"); + if (base==0) { + /* probe some values */ + base=DEFAULT_BASE; + bit_velle_data.data=(void*)DEFAULT_BASE; + if (bit_velle_init()==0) { + if(i2c_bit_add_bus(&bit_velle_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } else { + bit_velle_data.data=(void*)base; + if (bit_velle_init()==0) { + if(i2c_bit_add_bus(&bit_velle_ops) < 0) + return -ENODEV; + } else { + return -ENODEV; + } + } + printk("i2c-velleman.o: found device at %#x.\n",base); + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Simon G. Vogl "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for Velleman K8000 adapter"); + +MODULE_PARM(base, "i"); + +int init_module(void) +{ + return i2c_bitvelle_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_velle_ops); + bit_velle_exit(); +} + +#endif diff -urN vanilla-2.2.15pre17/include/linux/i2c-algo-bit.h bttv-2.2.15pre17/include/linux/i2c-algo-bit.h --- vanilla-2.2.15pre17/include/linux/i2c-algo-bit.h Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/include/linux/i2c-algo-bit.h Mon May 1 23:02:30 2000 @@ -0,0 +1,55 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-bit.h i2c driver algorithms for bit-shift adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-99 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-algo-bit.h,v 1.7 1999/12/21 23:45:58 frodo Exp $ */ + +#ifndef I2C_ALGO_BIT_H +#define I2C_ALGO_BIT_H 1 + +#include + +/* --- Defines for bit-adapters --------------------------------------- */ +/* + * This struct contains the hw-dependent functions of bit-style adapters to + * manipulate the line states, and to init any hw-specific features. This is + * only used if you have more than one hw-type of adapter running. + */ +struct i2c_algo_bit_data { + void *data; /* private data for lowlevel routines */ + void (*setsda) (void *data, int state); + void (*setscl) (void *data, int state); + int (*getsda) (void *data); + int (*getscl) (void *data); + + /* local settings */ + int udelay; + int mdelay; + int timeout; +}; + +#define I2C_BIT_ADAP_MAX 16 + +int i2c_bit_add_bus(struct i2c_adapter *); +int i2c_bit_del_bus(struct i2c_adapter *); + +#endif /* I2C_ALGO_BIT_H */ diff -urN vanilla-2.2.15pre17/include/linux/i2c-algo-pcf.h bttv-2.2.15pre17/include/linux/i2c-algo-pcf.h --- vanilla-2.2.15pre17/include/linux/i2c-algo-pcf.h Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/include/linux/i2c-algo-pcf.h Mon May 1 23:02:30 2000 @@ -0,0 +1,52 @@ +/* ------------------------------------------------------------------------- */ +/* adap-pcf.h i2c driver algorithms for PCF8584 adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-algo-pcf.h,v 1.7 2000/02/27 23:02:45 frodo Exp $ */ + +#ifndef I2C_ALGO_PCF_H +#define I2C_ALGO_PCF_H 1 + +/* --- Defines for pcf-adapters --------------------------------------- */ +#include + +struct i2c_algo_pcf_data { + void *data; /* private data for lolevel routines */ + void (*setpcf) (void *data, int ctl, int val); + int (*getpcf) (void *data, int ctl); + int (*getown) (void *data); + int (*getclock) (void *data); + void (*waitforpin) (void); + + /* local settings */ + int udelay; + int mdelay; + int timeout; +}; + +#define I2C_PCF_ADAP_MAX 16 + +int i2c_pcf_add_bus(struct i2c_adapter *); +int i2c_pcf_del_bus(struct i2c_adapter *); + +#endif /* I2C_ALGO_PCF_H */ diff -urN vanilla-2.2.15pre17/include/linux/i2c-dev.h bttv-2.2.15pre17/include/linux/i2c-dev.h --- vanilla-2.2.15pre17/include/linux/i2c-dev.h Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/include/linux/i2c-dev.h Mon May 1 23:02:30 2000 @@ -0,0 +1,167 @@ +/* + i2c-dev.h - i2c-bus driver, char device interface + + Copyright (C) 1995-97 Simon G. Vogl + Copyright (C) 1998-99 Frodo Looijaard + + 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. +*/ + +/* $Id: i2c-dev.h,v 1.7 2000/02/15 17:57:27 frodo Exp $ */ + +#ifndef I2C_DEV_H +#define I2C_DEV_H + + +#include +#include + +/* Some IOCTL commands are defined in */ +/* Note: 10-bit addresses are NOT supported! */ + +/* This is the structure as used in the I2C_SMBUS ioctl call */ +struct i2c_smbus_ioctl_data { + char read_write; + __u8 command; + int size; + union i2c_smbus_data *data; +}; + +/* This is the structure as used in the I2C_RDWR ioctl call */ +struct i2c_rdwr_ioctl_data { + struct i2c_msg *msgs; /* pointers to i2c_msgs */ + int nmsgs; /* number of i2c_msgs */ +}; + +#ifndef __KERNEL__ + +#include + +extern inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_smbus_ioctl_data args; + + args.read_write = read_write; + args.command = command; + args.size = size; + args.data = data; + return ioctl(file,I2C_SMBUS,&args); +} + + +extern inline __s32 i2c_smbus_write_quick(int file, __u8 value) +{ + return i2c_smbus_access(file,value,0,I2C_SMBUS_QUICK,NULL); +} + +extern inline __s32 i2c_smbus_read_byte(int file) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,0,I2C_SMBUS_BYTE,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern inline __s32 i2c_smbus_write_byte(int file, __u8 value) +{ + return i2c_smbus_access(file,I2C_SMBUS_WRITE,value, + I2C_SMBUS_BYTE,NULL); +} + +extern inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command, + I2C_SMBUS_BYTE_DATA,&data)) + return -1; + else + return 0x0FF & data.byte; +} + +extern inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, + __u8 value) +{ + union i2c_smbus_data data; + data.byte = value; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, + I2C_SMBUS_BYTE_DATA, &data); +} + +extern inline __s32 i2c_smbus_read_word_data(int file, __u8 command) +{ + union i2c_smbus_data data; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command, + I2C_SMBUS_WORD_DATA,&data)) + return -1; + else + return 0x0FFFF & data.word; +} + +extern inline __s32 i2c_smbus_write_word_data(int file, __u8 command, + __u16 value) +{ + union i2c_smbus_data data; + data.word = value; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, + I2C_SMBUS_WORD_DATA, &data); +} + +extern inline __s32 i2c_smbus_process_call(int file, __u8 command, __u16 value) +{ + union i2c_smbus_data data; + data.word = value; + if (i2c_smbus_access(file,I2C_SMBUS_WRITE,command, + I2C_SMBUS_PROC_CALL,&data)) + return -1; + else + return 0x0FFFF & data.word; +} + + +/* Returns the number of read bytes */ +extern inline __s32 i2c_smbus_read_block_data(int file, __u8 command, + __u8 *values) +{ + union i2c_smbus_data data; + int i; + if (i2c_smbus_access(file,I2C_SMBUS_READ,command, + I2C_SMBUS_BLOCK_DATA,&data)) + return -1; + else { + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; + } +} + +extern inline __s32 i2c_smbus_write_block_data(int file, __u8 command, + __u8 length, __u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > 32) + length = 32; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + data.block[0] = length; + return i2c_smbus_access(file,I2C_SMBUS_WRITE,command, + I2C_SMBUS_BLOCK_DATA, &data); +} + +#endif /* ndef __KERNEL__ */ + +#endif diff -urN vanilla-2.2.15pre17/include/linux/i2c-elektor.h bttv-2.2.15pre17/include/linux/i2c-elektor.h --- vanilla-2.2.15pre17/include/linux/i2c-elektor.h Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/include/linux/i2c-elektor.h Mon May 1 23:02:30 2000 @@ -0,0 +1,43 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-elektor.c i2c-hw access for PCF8584 style isa bus adaptes */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* $Id: i2c-elektor.h,v 1.4 2000/01/18 23:54:07 frodo Exp $ */ + +#ifndef I2C_PCF_ELEKTOR_H +#define I2C_PCF_ELEKTOR_H 1 + +/* + * This struct contains the hw-dependent functions of PCF8584 adapters to + * manipulate the registers, and to init any hw-specific features. + */ + +struct i2c_pcf_isa { + int pi_base; + int pi_irq; + int pi_clock; + int pi_own; +}; + + +#endif /* PCF_ELEKTOR_H */ diff -urN vanilla-2.2.15pre17/include/linux/i2c-id.h bttv-2.2.15pre17/include/linux/i2c-id.h --- vanilla-2.2.15pre17/include/linux/i2c-id.h Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/include/linux/i2c-id.h Mon May 1 23:02:30 2000 @@ -0,0 +1,143 @@ +/* ------------------------------------------------------------------------- */ +/* */ +/* i2c.h - definitions for the i2c-bus interface */ +/* */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-1999 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* $Id: i2c-id.h,v 1.16 2000/04/03 18:57:42 frodo Exp $ */ + +#ifndef I2C_ID_H +#define I2C_ID_H +/* + * This file is part of the i2c-bus package and contains the identifier + * values for drivers, adapters and other folk populating these serial + * worlds. + * + * These will change often (i.e. additions) , therefore this has been + * separated from the functional interface definitions of the i2c api. + * + */ + +/* + * ---- Driver types ----------------------------------------------------- + * device id name + number function description, i2c address(es) + * + * Range 1000-1999 range is defined in sensors/sensors.h + * Range 0x100 - 0x1ff is for V4L2 Common Components + * Range 0xf000 - 0xffff is reserved for local experimentation, and should + * never be used in official drivers + */ + +#define I2C_DRIVERID_MSP3400 1 +#define I2C_DRIVERID_TUNER 2 +#define I2C_DRIVERID_VIDEOTEX 3 /* please rename */ +#define I2C_DRIVERID_TDA8425 4 /* stereo sound processor */ +#define I2C_DRIVERID_TEA6420 5 /* audio matrix switch */ +#define I2C_DRIVERID_TEA6415C 6 /* video matrix switch */ +#define I2C_DRIVERID_TDA9840 7 /* stereo sound processor */ +#define I2C_DRIVERID_SAA7111A 8 /* video input processor */ +#define I2C_DRIVERID_SAA5281 9 /* videotext decoder */ +#define I2C_DRIVERID_SAA7112 10 /* video decoder, image scaler */ +#define I2C_DRIVERID_SAA7120 11 /* video encoder */ +#define I2C_DRIVERID_SAA7121 12 /* video encoder */ +#define I2C_DRIVERID_SAA7185B 13 /* video encoder */ +#define I2C_DRIVERID_CH7003 14 /* digital pc to tv encoder */ +#define I2C_DRIVERID_PCF8574A 15 /* i2c expander - 8 bit in/out */ +#define I2C_DRIVERID_PCF8582C 16 /* eeprom */ +#define I2C_DRIVERID_AT24Cxx 17 /* eeprom 1/2/4/8/16 K */ +#define I2C_DRIVERID_TEA6300 18 /* audio mixer */ +#define I2C_DRIVERID_BT829 19 /* pc to tv encoder */ +#define I2C_DRIVERID_TDA9850 20 /* audio mixer */ +#define I2C_DRIVERID_TDA9855 21 /* audio mixer */ +#define I2C_DRIVERID_SAA7110 22 /* */ +#define I2C_DRIVERID_MGATVO 23 /* Matrox TVOut */ +#define I2C_DRIVERID_SAA5249 24 /* SAA5249 and compatibles */ +#define I2C_DRIVERID_PCF8583 25 /* real time clock */ +#define I2C_DRIVERID_SAB3036 26 /* SAB3036 tuner */ +#define I2C_DRIVERID_TDA7432 27 /* Stereo sound processor */ + +#define I2C_DRIVERID_EXP0 0xF0 /* experimental use id's */ +#define I2C_DRIVERID_EXP1 0xF1 +#define I2C_DRIVERID_EXP2 0xF2 +#define I2C_DRIVERID_EXP3 0xF3 + +#define I2C_DRIVERID_I2CDEV 900 +#define I2C_DRIVERID_I2CPROC 901 + +/* + * ---- Adapter types ---------------------------------------------------- + * + * First, we distinguish between several algorithms to access the hardware + * interface types, as a PCF 8584 needs other care than a bit adapter. + */ + +#define I2C_ALGO_NONE 0x000000 +#define I2C_ALGO_BIT 0x010000 /* bit style adapters */ +#define I2C_ALGO_PCF 0x020000 /* PCF 8584 style adapters */ +#define I2C_ALGO_ATI 0x030000 /* ATI video card */ +#define I2C_ALGO_SMBUS 0x040000 +#define I2C_ALGO_ISA 0x050000 /* lm_sensors ISA pseudo-adapter */ +#define I2C_ALGO_SAA7146 0x060000 /* SAA 7146 video decoder bus */ + +#define I2C_ALGO_EXP 0x800000 /* experimental */ + +#define I2C_ALGO_MASK 0xff0000 /* Mask for algorithms */ +#define I2C_ALGO_SHIFT 0x10 /* right shift to get index values */ + +#define I2C_HW_ADAPS 0x10000 /* # adapter types */ +#define I2C_HW_MASK 0xffff + + +/* hw specific modules that are defined per algorithm layer + */ + +/* --- Bit algorithm adapters */ +#define I2C_HW_B_LP 0x00 /* Parallel port Philips style adapter */ +#define I2C_HW_B_LPC 0x01 /* Parallel port, over control reg. */ +#define I2C_HW_B_SER 0x02 /* Serial line interface */ +#define I2C_HW_B_ELV 0x03 /* ELV Card */ +#define I2C_HW_B_VELLE 0x04 /* Vellemann K8000 */ +#define I2C_HW_B_BT848 0x05 /* BT848 video boards */ +#define I2C_HW_B_WNV 0x06 /* Winnov Videums */ +#define I2C_HW_B_VIA 0x07 /* Via vt82c586b */ +#define I2C_HW_B_HYDRA 0x08 /* Apple Hydra Mac I/O */ +#define I2C_HW_B_G400 0x09 /* Matrox G400 */ +#define I2C_HW_B_I810 0x0a /* Intel I810 */ +#define I2C_HW_B_VOO 0x0b /* 3dfx Voodoo 3 / Banshee */ +#define I2C_HW_B_RIVA 0x10 /* Riva based graphics cards */ +#define I2C_HW_B_IOC 0x11 /* IOC bit-wiggling */ + +/* --- PCF 8584 based algorithms */ +#define I2C_HW_P_LP 0x00 /* Parallel port interface */ +#define I2C_HW_P_ISA 0x01 /* generic ISA Bus inteface card */ +#define I2C_HW_P_ELEK 0x02 /* Elektor ISA Bus inteface card */ + +/* --- SMBus only adapters */ +#define I2C_HW_SMBUS_PIIX4 0x00 +#define I2C_HW_SMBUS_ALI15X3 0x01 +#define I2C_HW_SMBUS_VIA2 0x02 +#define I2C_HW_SMBUS_VOODOO3 0x03 +#define I2C_HW_SMBUS_I801 0x04 +#define I2C_HW_SMBUS_AMD756 0x05 +#define I2C_HW_SMBUS_SIS5595 0x06 + +/* --- ISA pseudo-adapter */ +#define I2C_HW_ISA 0x00 + +#endif /* I2C_ID_H */ diff -urN vanilla-2.2.15pre17/include/linux/i2c-old.h bttv-2.2.15pre17/include/linux/i2c-old.h --- vanilla-2.2.15pre17/include/linux/i2c-old.h Thu Jan 1 01:00:00 1970 +++ bttv-2.2.15pre17/include/linux/i2c-old.h Mon May 1 23:02:30 2000 @@ -0,0 +1,190 @@ +#ifndef I2C_H +#define I2C_H + +/* + * linux i2c interface. Works a little bit like the scsi subsystem. + * There are: + * + * i2c the basic control module (like scsi_mod) + * bus driver a driver with a i2c bus (hostadapter driver) + * chip driver a driver for a chip connected + * to a i2c bus (cdrom/hd driver) + * + * A device will be attached to one bus and one chip driver. Every chip + * driver gets a unique ID. + * + * A chip driver can provide a ioctl-like callback for the + * communication with other parts of the kernel (not every i2c chip is + * useful without other devices, a TV card tuner for example). + * + * "i2c internal" parts of the structs: only the i2c module is allowed to + * write to them, for others they are read-only. + * + */ + +#define I2C_BUS_MAX 4 /* max # of bus drivers */ +#define I2C_DRIVER_MAX 8 /* max # of chip drivers */ +#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */ + +struct i2c_bus; +struct i2c_driver; +struct i2c_device; + +#define I2C_DRIVERID_MSP3400 1 +#define I2C_DRIVERID_TUNER 2 +#define I2C_DRIVERID_VIDEOTEXT 3 +#define I2C_DRIVERID_VIDEODECODER 4 +#define I2C_DRIVERID_VIDEOENCODER 5 + +#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */ + /* 2 is used in 2.3.x */ +#define I2C_BUSID_BUZ 3 /* I2C bus on a BUZ */ +#define I2C_BUSID_ZORAN 4 /* I2C bus on a Zoran */ +#define I2C_BUSID_SGIVWFB 5 /* Moved to be unique */ + +/* + * struct for a driver for a i2c chip (tuner, soundprocessor, + * videotext, ... ). + * + * a driver will register within the i2c module. The i2c module will + * callback the driver (i2c_attach) for every device it finds on a i2c + * bus at the specified address. If the driver decides to "accept" + * the, device, it must return a struct i2c_device, and NULL + * otherwise. + * + * i2c_detach = i2c_attach ** -1 + * + * i2c_command will be used to pass commands to the driver in a + * ioctl-line manner. + * + */ + +struct i2c_driver +{ + char name[32]; /* some useful label */ + int id; /* device type ID */ + unsigned char addr_l, addr_h; /* address range of the chip */ + + int (*attach)(struct i2c_device *device); + int (*detach)(struct i2c_device *device); + int (*command)(struct i2c_device *device,unsigned int cmd, void *arg); + + /* i2c internal */ + struct i2c_device *devices[I2C_DEVICE_MAX]; + int devcount; +}; + + +/* + * this holds the informations about a i2c bus available in the system. + * + * a chip with a i2c bus interface (like bt848) registers the bus within + * the i2c module. This struct provides functions to access the i2c bus. + * + * One must hold the spinlock to access the i2c bus (XXX: is the irqsave + * required? Maybe better use a semaphore?). + * [-AC-] having a spinlock_irqsave is only needed if we have drivers wishing + * to bang their i2c bus from an interrupt. + * + * attach/detach_inform is a callback to inform the bus driver about + * attached chip drivers. + * + */ + +/* needed: unsigned long flags */ + +#include + +#if LINUX_VERSION_CODE >= 0x020100 +# if 0 +# define LOCK_FLAGS unsigned long flags; +# define LOCK_I2C_BUS(bus) spin_lock_irqsave(&(bus->bus_lock),flags); +# define UNLOCK_I2C_BUS(bus) spin_unlock_irqrestore(&(bus->bus_lock),flags); +# else +# define LOCK_FLAGS +# define LOCK_I2C_BUS(bus) spin_lock(&(bus->bus_lock)); +# define UNLOCK_I2C_BUS(bus) spin_unlock(&(bus->bus_lock)); +# endif +#else +# define LOCK_FLAGS unsigned long flags; +# define LOCK_I2C_BUS(bus) { save_flags(flags); cli(); } +# define UNLOCK_I2C_BUS(bus) { restore_flags(flags); } +#endif + +struct i2c_bus +{ + char name[32]; /* some useful label */ + int id; + void *data; /* free for use by the bus driver */ + +#if LINUX_VERSION_CODE >= 0x020100 + spinlock_t bus_lock; +#endif + + /* attach/detach inform callbacks */ + void (*attach_inform)(struct i2c_bus *bus, int id); + void (*detach_inform)(struct i2c_bus *bus, int id); + + /* Software I2C */ + void (*i2c_setlines)(struct i2c_bus *bus, int ctrl, int data); + int (*i2c_getdataline)(struct i2c_bus *bus); + + /* Hardware I2C */ + int (*i2c_read)(struct i2c_bus *bus, unsigned char addr); + int (*i2c_write)(struct i2c_bus *bus, unsigned char addr, + unsigned char b1, unsigned char b2, int both); + + /* internal data for i2c module */ + struct i2c_device *devices[I2C_DEVICE_MAX]; + int devcount; +}; + + +/* + * This holds per-device data for a i2c device + */ + +struct i2c_device +{ + char name[32]; /* some useful label */ + void *data; /* free for use by the chip driver */ + unsigned char addr; /* chip addr */ + + /* i2c internal */ + struct i2c_bus *bus; + struct i2c_driver *driver; +}; + + +/* ------------------------------------------------------------------- */ +/* i2c module functions */ + +/* register/unregister a i2c bus */ +int i2c_register_bus(struct i2c_bus *bus); +int i2c_unregister_bus(struct i2c_bus *bus); + +/* register/unregister a chip driver */ +int i2c_register_driver(struct i2c_driver *driver); +int i2c_unregister_driver(struct i2c_driver *driver); + +/* send a command to a chip using the ioctl-like callback interface */ +int i2c_control_device(struct i2c_bus *bus, int id, + unsigned int cmd, void *arg); + +/* i2c bus access functions */ +void i2c_start(struct i2c_bus *bus); +void i2c_stop(struct i2c_bus *bus); +void i2c_one(struct i2c_bus *bus); +void i2c_zero(struct i2c_bus *bus); +int i2c_ack(struct i2c_bus *bus); + +int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack); +unsigned char i2c_readbyte(struct i2c_bus *bus,int last); + +/* i2c (maybe) hardware functions */ +int i2c_read(struct i2c_bus *bus, unsigned char addr); +int i2c_write(struct i2c_bus *bus, unsigned char addr, + unsigned char b1, unsigned char b2, int both); + +int i2c_init(void); +#endif /* I2C_H */ diff -urN vanilla-2.2.15pre17/include/linux/i2c.h bttv-2.2.15pre17/include/linux/i2c.h --- vanilla-2.2.15pre17/include/linux/i2c.h Mon May 1 23:00:00 2000 +++ bttv-2.2.15pre17/include/linux/i2c.h Mon May 1 23:02:30 2000 @@ -1,190 +1,549 @@ +/* ------------------------------------------------------------------------- */ +/* */ +/* i2c.h - definitions for the i2c-bus interface */ +/* */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-2000 Simon G. Vogl + + 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. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and + Frodo Looijaard */ + +/* $Id: i2c.h,v 1.38 2000/04/18 08:48:56 simon Exp $ */ + #ifndef I2C_H #define I2C_H -/* - * linux i2c interface. Works a little bit like the scsi subsystem. - * There are: - * - * i2c the basic control module (like scsi_mod) - * bus driver a driver with a i2c bus (hostadapter driver) - * chip driver a driver for a chip connected - * to a i2c bus (cdrom/hd driver) - * - * A device will be attached to one bus and one chip driver. Every chip - * driver gets a unique ID. - * - * A chip driver can provide a ioctl-like callback for the - * communication with other parts of the kernel (not every i2c chip is - * useful without other devices, a TV card tuner for example). - * - * "i2c internal" parts of the structs: only the i2c module is allowed to - * write to them, for others they are read-only. - * - */ - -#define I2C_BUS_MAX 4 /* max # of bus drivers */ -#define I2C_DRIVER_MAX 8 /* max # of chip drivers */ -#define I2C_DEVICE_MAX 8 /* max # if devices per bus/driver */ - -struct i2c_bus; -struct i2c_driver; -struct i2c_device; +#include /* id values of adapters et. al. */ -#define I2C_DRIVERID_MSP3400 1 -#define I2C_DRIVERID_TUNER 2 -#define I2C_DRIVERID_VIDEOTEXT 3 -#define I2C_DRIVERID_VIDEODECODER 4 -#define I2C_DRIVERID_VIDEOENCODER 5 - -#define I2C_BUSID_BT848 1 /* I2C bus on a BT848 */ - /* 2 is used in 2.3.x */ -#define I2C_BUSID_BUZ 3 /* I2C bus on a BUZ */ -#define I2C_BUSID_ZORAN 4 /* I2C bus on a Zoran */ -#define I2C_BUSID_SGIVWFB 5 /* Moved to be unique */ - -/* - * struct for a driver for a i2c chip (tuner, soundprocessor, - * videotext, ... ). - * - * a driver will register within the i2c module. The i2c module will - * callback the driver (i2c_attach) for every device it finds on a i2c - * bus at the specified address. If the driver decides to "accept" - * the, device, it must return a struct i2c_device, and NULL - * otherwise. - * - * i2c_detach = i2c_attach ** -1 - * - * i2c_command will be used to pass commands to the driver in a - * ioctl-line manner. - * - */ - -struct i2c_driver -{ - char name[32]; /* some useful label */ - int id; /* device type ID */ - unsigned char addr_l, addr_h; /* address range of the chip */ - - int (*attach)(struct i2c_device *device); - int (*detach)(struct i2c_device *device); - int (*command)(struct i2c_device *device,unsigned int cmd, void *arg); - - /* i2c internal */ - struct i2c_device *devices[I2C_DEVICE_MAX]; - int devcount; -}; - - -/* - * this holds the informations about a i2c bus available in the system. - * - * a chip with a i2c bus interface (like bt848) registers the bus within - * the i2c module. This struct provides functions to access the i2c bus. - * - * One must hold the spinlock to access the i2c bus (XXX: is the irqsave - * required? Maybe better use a semaphore?). - * [-AC-] having a spinlock_irqsave is only needed if we have drivers wishing - * to bang their i2c bus from an interrupt. - * - * attach/detach_inform is a callback to inform the bus driver about - * attached chip drivers. - * - */ +#ifdef __KERNEL__ -/* needed: unsigned long flags */ +/* --- Includes and compatibility declarations ------------------------ */ #include +#ifndef KERNEL_VERSION +#define KERNEL_VERSION(a,b,c) (((a) << 16) | ((b) << 8) | (c)) +#endif -#if LINUX_VERSION_CODE >= 0x020100 -# if 0 -# define LOCK_FLAGS unsigned long flags; -# define LOCK_I2C_BUS(bus) spin_lock_irqsave(&(bus->bus_lock),flags); -# define UNLOCK_I2C_BUS(bus) spin_unlock_irqrestore(&(bus->bus_lock),flags); -# else -# define LOCK_FLAGS -# define LOCK_I2C_BUS(bus) spin_lock(&(bus->bus_lock)); -# define UNLOCK_I2C_BUS(bus) spin_unlock(&(bus->bus_lock)); -# endif +#include /* for 2.2.xx */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,25) +#include #else -# define LOCK_FLAGS unsigned long flags; -# define LOCK_I2C_BUS(bus) { save_flags(flags); cli(); } -# define UNLOCK_I2C_BUS(bus) { restore_flags(flags); } +#include #endif +#include +#include + +/* --- General options ------------------------------------------------ */ + +#define I2C_ALGO_MAX 4 /* control memory consumption */ +#define I2C_ADAP_MAX 16 +#define I2C_DRIVER_MAX 16 +#define I2C_CLIENT_MAX 32 +#define I2C_DUMMY_MAX 4 + +struct i2c_msg; +struct i2c_algorithm; +struct i2c_adapter; +struct i2c_client; +struct i2c_driver; +struct i2c_client_address_data; +union i2c_smbus_data; + + +/* + * The master routines are the ones normally used to transmit data to devices + * on a bus (or read from them). Apart from two basic transfer functions to + * transmit one message at a time, a more complex version can be used to + * transmit an arbitrary number of messages without interruption. + */ +extern int i2c_master_send(struct i2c_client *,const char* ,int); +extern int i2c_master_recv(struct i2c_client *,char* ,int); -struct i2c_bus -{ - char name[32]; /* some useful label */ - int id; - void *data; /* free for use by the bus driver */ +/* Transfer num messages. + */ +extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[],int num); + +/* + * Some adapter types (i.e. PCF 8584 based ones) may support slave behaviuor. + * This is not tested/implemented yet and will change in the future. + */ +extern int i2c_slave_send(struct i2c_client *,char*,int); +extern int i2c_slave_recv(struct i2c_client *,char*,int); -#if LINUX_VERSION_CODE >= 0x020100 - spinlock_t bus_lock; + +/* + * I2C Message - could be used in the current interface to + */ +struct i2c_msg { + u16 addr; /* slave address */ + unsigned short flags; +#define I2C_M_TEN 0x10 /* we have a ten bit chip address */ +#define I2C_M_RD 0x01 +#define I2C_M_NOSTART 0x4000 +#define I2C_M_REV_DIR_ADDR 0x2000 +#if 0 +#define I2C_M_PROBE 0x20 #endif + short len; /* msg length */ + char *buf; /* pointer to msg data */ +}; - /* attach/detach inform callbacks */ - void (*attach_inform)(struct i2c_bus *bus, int id); - void (*detach_inform)(struct i2c_bus *bus, int id); - /* Software I2C */ - void (*i2c_setlines)(struct i2c_bus *bus, int ctrl, int data); - int (*i2c_getdataline)(struct i2c_bus *bus); +/* This is the very generalized SMBus access routine. You probably do not + want to use this, though; one of the functions below may be much easier, + and probably just as fast. + Note that we use i2c_adapter here, because you do not need a specific + smbus adapter to call this function. */ +extern s32 i2c_smbus_xfer (struct i2c_adapter * adapter, u16 addr, + unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data); + +/* Now follow the 'nice' access routines. These also document the calling + conventions of smbus_access. */ + +extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value); +extern s32 i2c_smbus_read_byte(struct i2c_client * client); +extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value); +extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command); +extern s32 i2c_smbus_write_byte_data(struct i2c_client * client, + u8 command, u8 value); +extern s32 i2c_smbus_read_word_data(struct i2c_client * client, u8 command); +extern s32 i2c_smbus_write_word_data(struct i2c_client * client, + u8 command, u16 value); +extern s32 i2c_smbus_process_call(struct i2c_client * client, + u8 command, u16 value); +/* Returns the number of read bytes */ +extern s32 i2c_smbus_read_block_data(struct i2c_client * client, + u8 command, u8 *values); +extern s32 i2c_smbus_write_block_data(struct i2c_client * client, + u8 command, u8 length, + u8 *values); - /* Hardware I2C */ - int (*i2c_read)(struct i2c_bus *bus, unsigned char addr); - int (*i2c_write)(struct i2c_bus *bus, unsigned char addr, - unsigned char b1, unsigned char b2, int both); - /* internal data for i2c module */ - struct i2c_device *devices[I2C_DEVICE_MAX]; - int devcount; +/* + * A driver is capable of handling one or more physical devices present on + * I2C adapters. This information is used to inform the driver of adapter + * events. + */ + +struct i2c_driver { + char name[32]; + int id; + unsigned int flags; /* div., see below */ + + /* Notifies the driver that a new bus has appeared. This routine + * can be used by the driver to test if the bus meets its conditions + * & seek for the presence of the chip(s) it supports. If found, it + * registers the client(s) that are on the bus to the i2c admin. via + * i2c_attach_client. + */ + int (*attach_adapter)(struct i2c_adapter *); + + /* tells the driver that a client is about to be deleted & gives it + * the chance to remove its private data. Also, if the client struct + * has been dynamically allocated by the driver in the function above, + * it must be freed here. + */ + int (*detach_client)(struct i2c_client *); + + /* a ioctl like command that can be used to perform specific functions + * with the device. + */ + int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); + + /* These two are mainly used for bookkeeping & dynamic unloading of + * kernel modules. inc_use tells the driver that a client is being + * used by another module & that it should increase its ref. counter. + * dec_use is the inverse operation. + * NB: Make sure you have no circular dependencies, or else you get a + * deadlock when trying to unload the modules. + * You should use the i2c_{inc,dec}_use_client functions instead of + * calling this function directly. + */ + void (*inc_use)(struct i2c_client *client); + void (*dec_use)(struct i2c_client *client); +}; + +/* + * i2c_client identifies a single device (i.e. chip) that is connected to an + * i2c bus. The behaviour is defined by the routines of the driver. This + * function is mainly used for lookup & other admin. functions. + */ +struct i2c_client { + char name[32]; + int id; + unsigned int flags; /* div., see below */ + unsigned int addr; /* chip address - NOTE: 7bit */ + /* addresses are stored in the */ + /* _LOWER_ 7 bits of this char */ + /* addr: unsigned int to make lm_sensors i2c-isa adapter work + more cleanly. It does not take any more memory space, due to + alignment considerations */ + struct i2c_adapter *adapter; /* the adapter we sit on */ + struct i2c_driver *driver; /* and our access routines */ + void *data; /* for the clients */ + int usage_count; /* How many accesses currently */ + /* to the client */ }; /* - * This holds per-device data for a i2c device + * The following structs are for those who like to implement new bus drivers: + * i2c_algorithm is the interface to a class of hardware solutions which can + * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 + * to name two of the most common. */ +struct i2c_algorithm { + char name[32]; /* textual description */ + unsigned int id; + + /* If a adapter algorithm can't to I2C-level access, set master_xfer + to NULL. If an adapter algorithm can do SMBus access, set + smbus_xfer. If set to NULL, the SMBus protocol is simulated + using common I2C messages */ + int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg msgs[], + int num); + int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data); + + /* --- these optional/future use for some adapter types.*/ + int (*slave_send)(struct i2c_adapter *,char*,int); + int (*slave_recv)(struct i2c_adapter *,char*,int); -struct i2c_device -{ - char name[32]; /* some useful label */ - void *data; /* free for use by the chip driver */ - unsigned char addr; /* chip addr */ + /* --- ioctl like call to set div. parameters. */ + int (*algo_control)(struct i2c_adapter *, unsigned int, unsigned long); - /* i2c internal */ - struct i2c_bus *bus; - struct i2c_driver *driver; + /* To determine what the adapter supports */ + u32 (*functionality) (struct i2c_adapter *); }; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29) +struct proc_dir_entry; +#endif + +/* + * i2c_adapter is the structure used to identify a physical i2c bus along + * with the access algorithms necessary to access it. + */ +struct i2c_adapter { + char name[32]; /* some useful name to identify the adapter */ + unsigned int id;/* == is algo->id | hwdep.struct->id, */ + /* for registered values see below */ + struct i2c_algorithm *algo;/* the algorithm to access the bus */ + void *algo_data; + + /* --- These may be NULL, but should increase the module use count */ + void (*inc_use)(struct i2c_adapter *); + void (*dec_use)(struct i2c_adapter *); + + /* --- administration stuff. */ + int (*client_register)(struct i2c_client *); + int (*client_unregister)(struct i2c_client *); + + void *data; /* private data for the adapter */ + /* some data fields that are used by all types */ + /* these data fields are readonly to the public */ + /* and can be set via the i2c_ioctl call */ + + /* data fields that are valid for all devices */ + struct semaphore lock; + unsigned int flags;/* flags specifying div. data */ + + struct i2c_client *clients[I2C_CLIENT_MAX]; + int client_count; + + int timeout; + int retries; + +#ifdef CONFIG_PROC_FS + /* No need to set this when you initialize the adapter */ + int inode; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29) + struct proc_dir_entry *proc_entry; +#endif +#endif /* def CONFIG_PROC_FS */ +}; + +/*flags for the driver struct: */ +#define I2C_DF_NOTIFY 0x01 /* notify on bus (de/a)ttaches */ +#define I2C_DF_DUMMY 0x02 /* do not connect any clients */ + +/*flags for the client struct: */ +#define I2C_CLIENT_ALLOW_USE 0x01 /* Client allows access */ +#define I2C_CLIENT_ALLOW_MULTIPLE_USE 0x02 /* Allow multiple access-locks */ + /* on an i2c_client */ + +/* i2c_client_address_data is the struct for holding default client + * addresses for a driver and for the parameters supplied on the + * command line + */ +struct i2c_client_address_data { + unsigned short *normal_i2c; + unsigned short *normal_i2c_range; + unsigned short *probe; + unsigned short *probe_range; + unsigned short *ignore; + unsigned short *ignore_range; + unsigned short *force; +}; + +/* Internal numbers to terminate lists */ +#define I2C_CLIENT_END 0xfffe + +/* The numbers to use to set I2C bus address */ +#define ANY_I2C_BUS 0xffff + +/* The length of the option lists */ +#define I2C_CLIENT_MAX_OPTS 48 + + +/* ----- functions exported by i2c.o */ + +/* administration... + */ +extern int i2c_add_adapter(struct i2c_adapter *); +extern int i2c_del_adapter(struct i2c_adapter *); + +extern int i2c_add_driver(struct i2c_driver *); +extern int i2c_del_driver(struct i2c_driver *); + +extern int i2c_attach_client(struct i2c_client *); +extern int i2c_detach_client(struct i2c_client *); + +/* Only call these if you grab a resource that makes unloading the + client and the adapter it is on completely impossible. Like when a + /proc directory is entered. */ +extern void i2c_inc_use_client(struct i2c_client *); +extern void i2c_dec_use_client(struct i2c_client *); + +/* New function: This is to get an i2c_client-struct for controlling the + client either by using i2c_control-function or having the + client-module export functions that can be used with the i2c_client + -struct. */ +extern struct i2c_client *i2c_get_client(int driver_id, int adapter_id, + struct i2c_client *prev); + +/* Should be used with new function + extern struct i2c_client *i2c_get_client(int,int,struct i2c_client *); + to make sure that client-struct is valid and that it is okay to access + the i2c-client. + returns -EACCES if client doesn't allow use (default) + returns -EBUSY if client doesn't allow multiple use (default) and + usage_count >0 */ +extern int i2c_use_client(struct i2c_client *); +extern int i2c_release_client(struct i2c_client *); + +/* returns -EBUSY if address has been taken, 0 if not. Note that the only + other place at which this is called is within i2c_attach_client; so + you can cheat by simply not registering. Not recommended, of course! */ +extern int i2c_check_addr (struct i2c_adapter *adapter, int addr); + +/* Detect function. It itterates over all possible addresses itself. + * It will only call found_proc if some client is connected at the + * specific address (unless a 'force' matched); + */ +typedef int i2c_client_found_addr_proc (struct i2c_adapter *adapter, + int addr, unsigned short flags,int kind); + +extern int i2c_probe(struct i2c_adapter *adapter, + struct i2c_client_address_data *address_data, + i2c_client_found_addr_proc *found_proc); -/* ------------------------------------------------------------------- */ -/* i2c module functions */ +/* An ioctl like call to set div. parameters of the adapter. + */ +extern int i2c_control(struct i2c_client *,unsigned int, unsigned long); -/* register/unregister a i2c bus */ -int i2c_register_bus(struct i2c_bus *bus); -int i2c_unregister_bus(struct i2c_bus *bus); +/* This call returns a unique low identifier for each registered adapter, + * or -1 if the adapter was not regisitered. + */ +extern int i2c_adapter_id(struct i2c_adapter *adap); -/* register/unregister a chip driver */ -int i2c_register_driver(struct i2c_driver *driver); -int i2c_unregister_driver(struct i2c_driver *driver); -/* send a command to a chip using the ioctl-like callback interface */ -int i2c_control_device(struct i2c_bus *bus, int id, - unsigned int cmd, void *arg); -/* i2c bus access functions */ -void i2c_start(struct i2c_bus *bus); -void i2c_stop(struct i2c_bus *bus); -void i2c_one(struct i2c_bus *bus); -void i2c_zero(struct i2c_bus *bus); -int i2c_ack(struct i2c_bus *bus); +/* Return the functionality mask */ +extern u32 i2c_get_functionality (struct i2c_adapter *adap); + +/* Return 1 if adapter supports everything we need, 0 if not. */ +extern int i2c_check_functionality (struct i2c_adapter *adap, u32 func); + +#endif /* __KERNEL__ */ + +/* To determine what functionality is present */ + +#define I2C_FUNC_I2C 0x00000001 +#define I2C_FUNC_10BIT_ADDR 0x00000002 +#define I2C_FUNC_PROTOCOL_MANGLING 0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART} */ +#define I2C_FUNC_SMBUS_QUICK 0x00010000 +#define I2C_FUNC_SMBUS_READ_BYTE 0x00020000 +#define I2C_FUNC_SMBUS_WRITE_BYTE 0x00040000 +#define I2C_FUNC_SMBUS_READ_BYTE_DATA 0x00080000 +#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA 0x00100000 +#define I2C_FUNC_SMBUS_READ_WORD_DATA 0x00200000 +#define I2C_FUNC_SMBUS_WRITE_WORD_DATA 0x00400000 +#define I2C_FUNC_SMBUS_PROC_CALL 0x00800000 +#define I2C_FUNC_SMBUS_READ_BLOCK_DATA 0x01000000 +#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 +#define I2C_FUNC_SMBUS_READ_I2C_BLOCK 0x04000000 /* New I2C-like block */ +#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK 0x08000000 /* transfer */ + +#define I2C_FUNC_SMBUS_BYTE I2C_FUNC_SMBUS_READ_BYTE | \ + I2C_FUNC_SMBUS_WRITE_BYTE +#define I2C_FUNC_SMBUS_BYTE_DATA I2C_FUNC_SMBUS_READ_BYTE_DATA | \ + I2C_FUNC_SMBUS_WRITE_BYTE_DATA +#define I2C_FUNC_SMBUS_WORD_DATA I2C_FUNC_SMBUS_READ_WORD_DATA | \ + I2C_FUNC_SMBUS_WRITE_WORD_DATA +#define I2C_FUNC_SMBUS_BLOCK_DATA I2C_FUNC_SMBUS_READ_BLOCK_DATA | \ + I2C_FUNC_SMBUS_WRITE_BLOCK_DATA +#define I2C_FUNC_SMBUS_I2C_BLOCK I2C_FUNC_SMBUS_READ_I2C_BLOCK | \ + I2C_FUNC_SMBUS_WRITE_I2C_BLOCK + +#define I2C_FUNC_SMBUS_EMUL I2C_FUNC_SMBUS_QUICK | \ + I2C_FUNC_SMBUS_BYTE | \ + I2C_FUNC_SMBUS_BYTE_DATA | \ + I2C_FUNC_SMBUS_WORD_DATA | \ + I2C_FUNC_SMBUS_PROC_CALL | \ + I2C_FUNC_SMBUS_READ_BLOCK_DATA + +/* + * Data for SMBus Messages + */ +union i2c_smbus_data { + __u8 byte; + __u16 word; + __u8 block[33]; /* block[0] is used for length */ +}; + +/* smbus_access read or write markers */ +#define I2C_SMBUS_READ 1 +#define I2C_SMBUS_WRITE 0 + +/* SMBus transaction types (size parameter in the above functions) + Note: these no longer correspond to the (arbitrary) PIIX4 internal codes! */ +#define I2C_SMBUS_QUICK 0 +#define I2C_SMBUS_BYTE 1 +#define I2C_SMBUS_BYTE_DATA 2 +#define I2C_SMBUS_WORD_DATA 3 +#define I2C_SMBUS_PROC_CALL 4 +#define I2C_SMBUS_BLOCK_DATA 5 + + +/* ----- commands for the ioctl like i2c_command call: + * note that additional calls are defined in the algorithm and hw + * dependent layers - these can be listed here, or see the + * corresponding header files. + */ + /* -> bit-adapter specific ioctls */ +#define I2C_RETRIES 0x0701 /* number times a device adress should */ + /* be polled when not acknowledging */ +#define I2C_TIMEOUT 0x0702 /* set timeout - call with int */ + + +/* this is for i2c-dev.c */ +#define I2C_SLAVE 0x0703 /* Change slave address */ + /* Attn.: Slave address is 7 or 10 bits */ +#define I2C_SLAVE_FORCE 0x0706 /* Change slave address */ + /* Attn.: Slave address is 7 or 10 bits */ + /* This changes the address, even if it */ + /* is already taken! */ +#define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */ + +#define I2C_FUNCS 0x0705 /* Get the adapter functionality */ +#define I2C_RDWR 0x0707 /* Combined R/W transfer (one stop only)*/ +#if 0 +#define I2C_ACK_TEST 0x0710 /* See if a slave is at a specific adress */ +#endif -int i2c_sendbyte(struct i2c_bus *bus,unsigned char data,int wait_for_ack); -unsigned char i2c_readbyte(struct i2c_bus *bus,int last); +#define I2C_SMBUS 0x0720 /* SMBus-level access */ -/* i2c (maybe) hardware functions */ -int i2c_read(struct i2c_bus *bus, unsigned char addr); -int i2c_write(struct i2c_bus *bus, unsigned char addr, - unsigned char b1, unsigned char b2, int both); +/* ... algo-bit.c recognizes */ +#define I2C_UDELAY 0x0705 /* set delay in microsecs between each */ + /* written byte (except address) */ +#define I2C_MDELAY 0x0706 /* millisec delay between written bytes */ + +/* ----- I2C-DEV: char device interface stuff ------------------------- */ + +#define I2C_MAJOR 89 /* Device major number */ + +#ifdef __KERNEL__ + +# ifndef NULL +# define NULL ( (void *) 0 ) +# endif + +# ifndef ENODEV +# include +# endif + +/* These defines are used for probing i2c client addresses */ +/* Default fill of many variables */ +#define I2C_CLIENT_DEFAULTS {I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END, \ + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END} + +/* This is ugly. We need to evaluate I2C_CLIENT_MAX_OPTS before it is + stringified */ +#define I2C_CLIENT_MODPARM_AUX1(x) "1-" #x "h" +#define I2C_CLIENT_MODPARM_AUX(x) I2C_CLIENT_MODPARM_AUX1(x) +#define I2C_CLIENT_MODPARM I2C_CLIENT_MODPARM_AUX(I2C_CLIENT_MAX_OPTS) + +/* I2C_CLIENT_MODULE_PARM creates a module parameter, and puts it in the + module header */ + +#define I2C_CLIENT_MODULE_PARM(var,desc) \ + static unsigned short var[I2C_CLIENT_MAX_OPTS] = I2C_CLIENT_DEFAULTS; \ + MODULE_PARM(var,I2C_CLIENT_MODPARM); \ + MODULE_PARM_DESC(var,desc) + +/* This is the one you want to use in your own modules */ +#define I2C_CLIENT_INSMOD \ + I2C_CLIENT_MODULE_PARM(probe, \ + "List of adapter,address pairs to scan additionally"); \ + I2C_CLIENT_MODULE_PARM(probe_range, \ + "List of adapter,start-addr,end-addr triples to scan " \ + "additionally"); \ + I2C_CLIENT_MODULE_PARM(ignore, \ + "List of adapter,address pairs not to scan"); \ + I2C_CLIENT_MODULE_PARM(ignore_range, \ + "List of adapter,start-addr,end-addr triples not to " \ + "scan"); \ + I2C_CLIENT_MODULE_PARM(force, \ + "List of adapter,address pairs to boldly assume " \ + "to be present"); \ + static struct i2c_client_address_data addr_data = \ + {normal_i2c, normal_i2c_range, \ + probe, probe_range, \ + ignore, ignore_range, \ + force} -int i2c_init(void); +#endif /* def __KERNEL__ */ #endif /* I2C_H */