diff -urN linux-2.2.16.SuSE/drivers/char/Makefile linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/Makefile --- linux-2.2.16.SuSE/drivers/char/Makefile Sun Jun 25 17:30:13 2000 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/Makefile Sun Jun 25 19:29:10 2000 @@ -392,22 +392,29 @@ endif endif +BTTV_OBJS = bttv-driver.o bttv-cards.o bttv-if.o ifeq ($(CONFIG_VIDEO_BT848),y) -L_OBJS += bttv.o tuner.o +O_OBJS += bttv-cards.o bttv-driver.o +OX_OBJS += bttv-if.o +L_OBJS += bttv.o tuner.o ir.o tvaudio.o tvmixer.o +LX_OBJS += kcompat24.o L_I2C=y else ifeq ($(CONFIG_VIDEO_BT848),m) - M_OBJS += bttv.o tuner.o + O_OBJS += bttv-cards.o bttv-driver.o + OX_OBJS += bttv-if.o + M_OBJS += bttv.o tuner.o ir.o tvaudio.o tvmixer.o + MX_OBJS += kcompat24.o M_I2C=y endif endif ifeq ($(CONFIG_VIDEO_MSP3400),y) -L_OBJS += msp3400.o +L_OBJS += msp3400.o tea6300.o tea6420.o tda7432.o tda8425.o tda985x.o tda9875.o dpl3518.o L_I2C=y else ifeq ($(CONFIG_VIDEO_MSP3400),m) - M_OBJS += msp3400.o + M_OBJS += msp3400.o tea6300.o tea6420.o tda7432.o tda8425.o tda985x.o tda9875.o dpl3518.o M_I2C=y endif endif @@ -655,4 +662,7 @@ defkeymap.c: defkeymap.map loadkeys --mktable defkeymap.map > defkeymap.c + +bttv.o: $(BTTV_OBJS) + $(LD) -r -o $@ $(BTTV_OBJS) diff -urN linux-2.2.16.SuSE/drivers/char/audiochip.h linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/audiochip.h --- linux-2.2.16.SuSE/drivers/char/audiochip.h Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/audiochip.h Sat Nov 20 21:26:24 1999 @@ -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 linux-2.2.16.SuSE/drivers/char/bt848.h linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/bt848.h --- linux-2.2.16.SuSE/drivers/char/bt848.h Sun Jun 25 17:30:22 2000 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/bt848.h Sun Feb 20 11:06:38 2000 @@ -336,7 +336,7 @@ -/* Bt848A and Bt849 only !! */ +/* Bt848A and higher only !! */ #define BT848_TGLB 0x080 #define BT848_TGCTRL 0x084 #define BT848_FCAP 0x0E8 diff -urN linux-2.2.16.SuSE/drivers/char/bttv-cards.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/bttv-cards.c --- linux-2.2.16.SuSE/drivers/char/bttv-cards.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/bttv-cards.c Sun Jun 25 18:51:57 2000 @@ -0,0 +1,764 @@ +/* + bttv-cards.c -- this file has card-specific stuff + + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#define __NO_VERSION__ 1 + +#include +#include +#include +#include +#include + +#include + +#include "bttv.h" +#include "tuner.h" + +/* fwd decl */ +static void hauppauge_eeprom(struct bttv *btv); +static void hauppauge_boot_msp34xx(struct bttv *btv); +static void init_PXC200(struct bttv *btv); +static void init_tea5757(struct bttv *btv); + +MODULE_PARM(card,"1-4i"); +MODULE_PARM(pll,"1-4i"); +MODULE_PARM(autoload,"i"); + +static unsigned int card[4] = { -1, -1, -1, -1 }; +static unsigned int pll[4] = { -1, -1, -1, -1 }; +#ifdef MODULE +static unsigned int autoload = 1; +#else +static unsigned int autoload = 0; +#endif + +/* ----------------------------------------------------------------------- */ +/* list of card IDs for bt878+ cards */ + +static struct CARD { + unsigned id; + int cardnr; + char *name; +} cards[] __devinitdata = { + { 0x00011002, BTTV_HAUPPAUGE878, "ATI TV Wonder" }, + { 0x00011461, BTTV_AVPHONE98, "AVerMedia TVPhone98" }, + { 0x00021461, BTTV_AVERMEDIA98, "Avermedia TVCapture 98" }, + { 0x00031461, BTTV_AVPHONE98, "AVerMedia TVPhone98" }, + { 0x00041461, BTTV_AVPHONE98, "AVerMedia TVPhone98" }, + { 0x10b42636, BTTV_HAUPPAUGE878, "STB ???" }, + { 0x1118153b, BTTV_TERRATVALUE, "Terratec TV Value" }, + { 0x1123153b, BTTV_TERRATVRADIO, "Terratec TV/Radio+" }, + { 0x1200bd11, BTTV_PINNACLERAVE, "Pinnacle PCTV Rave" }, + { 0x13eb0070, BTTV_HAUPPAUGE878, "Hauppauge WinTV" }, +#if 0 /* probably wrong */ + { 0x14610002, BTTV_AVERMEDIA98, "Avermedia TVCapture 98" }, +#endif + { 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" }, + { 0x400a15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" }, + { 0x402010fc, 0 /* no tvcards entry yet */, "I-O Data Co. GV-BCV3/PCI" }, + { 0x6606217d, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, + { 0, -1, NULL } +}; + +/* ----------------------------------------------------------------------- */ +/* array with description for bt848 / bt878 tv/grabber cards */ + +struct tvcard bttv_tvcards[] = +{ + /* 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, -1, 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, + /* 3, 1, 0, 2, 0x3004F, { 2, 3, 1, 1}, {1, 0x10011, 5, 0,10}, 0x3004F, */ + 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, 3, 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 }, + { "AVerMedia TVPhone 98", + 3, 4, 0, 2, 4, { 2, 3, 1, 1}, { 13, 14, 11, 7, 0, 0},0, + 1,1,1,1,0,0,0,1, PLL_28, 5 }, + { "ProVideo PV951", /* pic16c54 */ + 3, 1, 0, 2, 0, { 2, 3, 1, 1}, { 0, 0, 0, 0, 0},0, + 0,0,0,0,0,0,0,0, PLL_28, 1 }, + { "Little OnAir TV", + 3, 1, 0, 2, 0xe00b, {2, 3, 1, 1}, + {0xff9ff6, 0xff9ff6, 0xff1ff7, 0, 0xff3ffc},0, + 0,0,0,0,0,0,0,0, PLL_NONE, -1 }, + + { "Sigma TVII-FM", + 2, 1, 0, -1, 3, {2, 3, 1, 1}, {1, 1, 0, 2, 3},0, + 0,0,0,0,0,0,0,0, PLL_NONE, -1 }, + { "MATRIX-Vision MV-Delta 2", + 5, 1, -1, 3, 0, { 2, 3, 1, 0, 0},{0 }, 0, + 0,0,0,0,0,0,0,0, PLL_28, -1 }, + { "Zoltrix Genie TV", + 3, 1, 0, 2, 0xbcf03f, { 2, 3, 1, 1}, + { 0xbc803f, 0, 0xbcb03f, 0, 0xbcb03f}, 0, + 0,0,0,0,0,0,0,0, PLL_28, 5 }, + { "Terratec TV/Radio+", /* Radio ?? */ + 3, 1, 0, 2, 0x1f0000, { 2, 3, 1, 1}, + { 0xe2ffff, 0, 0, 0, 0xe0ffff, 0xe2ffff },0, + 0,0,0,0,0,0,0,0, PLL_35, 1 }, +}; +const int bttv_num_tvcards = (sizeof(bttv_tvcards)/sizeof(struct tvcard)); + +/* ----------------------------------------------------------------------- */ + +static unsigned char eeprom_data[256]; + +static void __devinit bttv_dump_eeprom(struct bttv *btv,int addr) +{ + int i; + + if (bttv_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 __devinit bttv_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 */ + btv->cardid = id; + 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: card id: %s (0x%08x) => card=%d\n", + btv->nr,cards[n].name,id,cards[n].cardnr); + 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; + } +} + +void __devinit bttv_idcard(struct bttv *btv) +{ + int type,eeprom = 0; + + btwrite(0, BT848_GPIO_OUT_EN); + + /* try to autodetect the card */ + /* many bt878 cards have a eeprom @ 0xa0 => read ID + and try to identify it */ + if (bttv_I2CRead(btv, I2C_HAUPEE, "eeprom") >= 0) { + eeprom = 0xa0; + bttv_readee(btv,eeprom_data,0xa0); + bttv_dump_eeprom(btv,0xa0); /* DEBUG */ + type = bttv_idcard_eeprom(btv); + if (-1 != type) { + btv->type = type; + } else if (btv->id <= 849) { + /* for unknown bt848, assume old Hauppauge */ + btv->type=BTTV_HAUPPAUGE; + } + + /* STB cards have a eeprom @ 0xae (old bt848) */ + } else if (bttv_I2CRead(btv, I2C_STBEE, "eeprom")>=0) { + btv->type=BTTV_STB; + } + + /* let the user override the autodetected type */ + if (card[btv->nr] >= 0 && card[btv->nr] < bttv_num_tvcards) + btv->type=card[btv->nr]; + + /* print which card config we are using */ + sprintf(btv->video_dev.name,"BT%d%s(%.23s)", + btv->id, + (btv->id==848 && btv->revision==0x12) ? "A" : "", + bttv_tvcards[btv->type].name); + printk(KERN_INFO "bttv%d: model: %s [%s]\n",btv->nr,btv->video_dev.name, + (card[btv->nr] >= 0 && card[btv->nr] < bttv_num_tvcards) ? + "insmod option" : "autodetected"); + + /* 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 0 + if (btv->type == BTTV_MIROPRO) { + if (bttv_verbose) + printk(KERN_INFO "Initializing TEA5757...\n"); + init_tea5757(btv); + } +#endif + } + if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) { + /* pick up some config infos from the eeprom */ + if (0xa0 != eeprom) { + eeprom = 0xa0; + bttv_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 == bttv_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 != bttv_tvcards[btv->type].tuner_type) + btv->tuner_type = bttv_tvcards[btv->type].tuner_type; + if (btv->tuner_type != -1) + bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); + + /* try to detect audio/fader chips */ + if (bttv_tvcards[btv->type].msp34xx && + bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx") >=0) { + if (autoload) + request_module("msp3400"); + } + + if (bttv_tvcards[btv->type].tda8425 && + bttv_I2CRead(btv, I2C_TDA8425, "TDA8425") >=0) { + if (autoload) + request_module("tda8425"); + } + +#if 0 + if (bttv_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); + } +#endif + + if (bttv_tvcards[btv->type].tda985x && + bttv_I2CRead(btv, I2C_TDA9850, "TDA985x") >=0) { + if (autoload) + request_module("tda985x"); + } + + if (bttv_tvcards[btv->type].tda9875 && + bttv_I2CRead(btv, I2C_TDA9875, "TDA9875") >=0) { + if (autoload) + request_module("tda9875"); + } + + if (bttv_tvcards[btv->type].tda7432 && + bttv_I2CRead(btv, I2C_TDA7432, "TDA7432") >=0) { + if (autoload) + request_module("tda7432"); + } + + if (bttv_tvcards[btv->type].tea63xx) { + if (autoload) + request_module("tea6300"); + } + + if (bttv_tvcards[btv->type].tea64xx) { + if (autoload) + request_module("tea6420"); + } + + if (bttv_tvcards[btv->type].tuner != -1) { + if (autoload) + request_module("tuner"); + } +} + + +/* ----------------------------------------------------------------------- */ +/* some hauppauge specific stuff */ + +static struct HAUPPAUGE_TUNER +{ + int id; + char *name; +} +hauppauge_tuner[] __devinitdata = +{ + { TUNER_ABSENT, "" }, + { TUNER_ABSENT, "External" }, + { TUNER_ABSENT, "Unspecified" }, + { TUNER_ABSENT, "Philips FI1216" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF" }, + { TUNER_PHILIPS_NTSC, "Philips FI1236" }, + { TUNER_ABSENT, "Philips FI1246" }, + { TUNER_ABSENT, "Philips FI1256" }, + { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, + { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, + { TUNER_ABSENT, "Philips FI1256 MK2" }, + { TUNER_ABSENT, "Temic 4032FY5" }, + { TUNER_TEMIC_PAL, "Temic 4002FH5" }, + { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, + { TUNER_ABSENT, "Philips FR1216 MK2" }, + { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, + { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, + { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, + { TUNER_ABSENT, "Philips FR1256 MK2" }, + { 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 __devinit hauppauge_eeprom(struct bttv *btv) +{ + if (eeprom_data[9] < sizeof(hauppauge_tuner)/sizeof(struct HAUPPAUGE_TUNER)) + { + btv->tuner_type = hauppauge_tuner[eeprom_data[9]].id; + if (bttv_verbose) + printk("bttv%d: Hauppauge eeprom: tuner=%s (%d)\n",btv->nr, + hauppauge_tuner[eeprom_data[9]].name,btv->tuner_type); + } +} + +static void __devinit hauppauge_boot_msp34xx(struct bttv *btv) +{ + int i; + + /* reset/enable the MSP on some Hauppauge cards */ + /* Thanks to Kyösti Mälkki (kmalkki@cc.hut.fi)! */ + btaor(32, ~32, BT848_GPIO_OUT_EN); + btaor(0, ~32, BT848_GPIO_DATA); + udelay(2500); + btaor(32, ~32, BT848_GPIO_DATA); + + if (bttv_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 (bttv_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 __devinit 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"); + bttv_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=bttv_I2CWrite(btv,0x1E,vals[i],0,1); + printk(KERN_INFO "I2C Write(0x08) = %i\nI2C Read () = %x\n\n", + tmp,bttv_I2CRead(btv,0x1F,NULL)); + } + printk(KERN_INFO "PXC200 Initialised.\n"); +} + +/* ----------------------------------------------------------------------- */ +/* Miro Pro radio stuff -- the tea5757 is connected to some GPIO ports */ +/* + * Copyright (c) 1999 Csaba Halasz + * This code is placed under the terms of the GNU General Public License + * + * Brutally hacked by Dan Sheridan djs52 8/3/00 + */ + +/* bus bits on the GPIO port */ +#define TEA_WE 6 +#define TEA_DATA 9 +#define TEA_CLK 8 +#define TEA_MOST 7 + +#define BUS_LOW(bit) btand(~(1<> TEA_##bit) & 1) + +/* TEA5757 register bits */ +#define TEA_FREQ 0:14 +#define TEA_BUFFER 15:15 + +#define TEA_SIGNAL_STRENGTH 16:17 + +#define TEA_PORT1 18:18 +#define TEA_PORT0 19:19 + +#define TEA_BAND 20:21 +#define TEA_BAND_FM 0 +#define TEA_BAND_MW 1 +#define TEA_BAND_LW 2 +#define TEA_BAND_SW 3 + +#define TEA_MONO 22:22 +#define TEA_ALLOW_STEREO 0 +#define TEA_FORCE_MONO 1 + +#define TEA_SEARCH_DIRECTION 23:23 +#define TEA_SEARCH_DOWN 0 +#define TEA_SEARCH_UP 1 + +#define TEA_STATUS 24:24 +#define TEA_STATUS_TUNED 0 +#define TEA_STATUS_SEARCHING 1 + +/* Low-level stuff */ +static int tea_read(struct bttv *btv) +{ + int value = 0; + long timeout; + int i; + + /* better safe than sorry */ + btaor((1< + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HACKING +# include +#endif + +#include "bttv.h" +#include "tuner.h" + +#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)) + + +static void bt848_set_risc_jmps(struct bttv *btv, int state); + +int bttv_num; /* number of Bt848s in use */ +struct bttv bttvs[BTTV_MAX]; + + +/* insmod args */ +MODULE_PARM(triton1,"i"); +MODULE_PARM(radio,"1-4i"); +MODULE_PARM(bigendian,"i"); +MODULE_PARM(fieldnr,"i"); +MODULE_PARM(bttv_verbose,"i"); +MODULE_PARM(bttv_debug,"i"); +MODULE_PARM(gbuffers,"i"); +MODULE_PARM(gbufsize,"i"); + +MODULE_DESCRIPTION("bttv - v4l driver module for bt848/878 based cards"); +MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); + +#if defined(__sparc__) || defined(__powerpc__) +static unsigned int bigendian=1; +#else +static unsigned int bigendian=0; +#endif +static int triton1=0; +static unsigned int radio[BTTV_MAX]; +static unsigned int fieldnr = 0; +static unsigned int gbuffers = 2; +static unsigned int gbufsize = BTTV_MAX_FBUF; +unsigned int bttv_debug = 0; +unsigned int bttv_verbose = 1; + +#define I2C_TIMING (0x7<<4) +#define I2C_DELAY 10 + +#define I2C_SET(CTRL,DATA) \ + { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); } +#define I2C_GET() (btread(BT848_I2C)&1) + +#define BURSTOFFSET 76 +#define BTTV_ERRORS 5 + + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +#define MDEBUG(x) do { } while(0) /* Debug memory management */ + +/* [DaveM] I've recoded most of this so that: + * 1) It's easier to tell what is happening + * 2) It's more portable, especially for translating things + * out of vmalloc mapped areas in the kernel. + * 3) Less unnecessary translations happen. + * + * The code used to assume that the kernel vmalloc mappings + * existed in the page tables of every process, this is simply + * not guarenteed. We now use pgd_offset_k which is the + * defined way to get at the kernel page tables. + */ + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if(pte_present(pte)) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,23) + ret = (pte_page(pte)|(adr&(PAGE_SIZE-1))); +#else + ret = (page_address(pte_page(pte))|(adr&(PAGE_SIZE-1))); +#endif + } + } + MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + unsigned long kva, ret; + + kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long kvirt_to_bus(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); + return ret; +} + +static void * rvmalloc(signed long size) +{ + void * mem; + unsigned long adr, page; + + mem=vmalloc_32(size); + if (mem) + { + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_pa(adr); + mem_map_reserve(MAP_NR(__va(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + } + return mem; +} + +static void rvfree(void * mem, signed long size) +{ + unsigned long adr, page; + + if (mem) + { + adr=(unsigned long) mem; + while (size > 0) + { + page = kvirt_to_pa(adr); + mem_map_unreserve(MAP_NR(__va(page))); + adr+=PAGE_SIZE; + size-=PAGE_SIZE; + } + vfree(mem); + } +} + + + +/* + * Create the giant waste of buffer space we need for now + * until we get DMA to user space sorted out (probably 2.3.x) + * + * We only create this as and when someone uses mmap + */ + +static int fbuffer_alloc(struct bttv *btv) +{ + if(!btv->fbuffer) + btv->fbuffer=(unsigned char *) rvmalloc(gbuffers*gbufsize); + else + printk(KERN_ERR "bttv%d: Double alloc of fbuffer!\n", + btv->nr); + if(!btv->fbuffer) + return -ENOBUFS; + return 0; +} + + +static int __devinit init_bttv_i2c(struct bttv *btv) +{ + /* i2c bit_adapter */ + memcpy(&btv->i2c_adap, &bttv_i2c_adap_template, sizeof(struct i2c_adapter)); + memcpy(&btv->i2c_algo, &bttv_i2c_algo_template, sizeof(struct i2c_algo_bit_data)); + memcpy(&btv->i2c_client, &bttv_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; + +#if 0 + bttv_bit_setscl(btv,1); + bttv_bit_setsda(btv,1); +#endif + + btv->i2c_ok = i2c_bit_add_bus(&btv->i2c_adap); + return btv->i2c_ok; +} + + +/* ----------------------------------------------------------------------- */ + +static void audio(struct bttv *btv, int mode, int no_irq_context) +{ + btaor(bttv_tvcards[btv->type].gpiomask, ~bttv_tvcards[btv->type].gpiomask, + BT848_GPIO_OUT_EN); + + switch (mode) + { + case AUDIO_MUTE: + btv->audio|=AUDIO_MUTE; + break; + case AUDIO_UNMUTE: + btv->audio&=~AUDIO_MUTE; + mode=btv->audio; + break; + case AUDIO_OFF: + mode=AUDIO_OFF; + break; + case AUDIO_ON: + mode=btv->audio; + break; + default: + btv->audio&=AUDIO_MUTE; + btv->audio|=mode; + break; + } + /* if audio mute or not in H-lock, turn audio off */ + if ((btv->audio&AUDIO_MUTE)) + mode=AUDIO_OFF; + if ((mode == AUDIO_TUNER) && (btv->radio)) + mode = AUDIO_RADIO; + btaor(bttv_tvcards[btv->type].audiomux[mode], + ~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA); + if (no_irq_context) + bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mode)); +} + + +extern inline void bt848_dma(struct bttv *btv, uint state) +{ + if (state) + btor(3, BT848_GPIO_DMA_CTL); + else + btand(~3, BT848_GPIO_DMA_CTL); +} + + +/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC*/ + +/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C + PLL_X = Reference pre-divider (0=1, 1=2) + PLL_C = Post divider (0=6, 1=4) + PLL_I = Integer input + PLL_F = Fractional input + + F_input = 28.636363 MHz: + PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0 +*/ + +static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout) +{ + unsigned char fl, fh, fi; + + /* prevent overflows */ + fin/=4; + fout/=4; + + fout*=12; + fi=fout/fin; + + fout=(fout%fin)*256; + fh=fout/fin; + + fout=(fout%fin)*256; + fl=fout/fin; + + /*printk("0x%02x 0x%02x 0x%02x\n", fi, fh, fl);*/ + btwrite(fl, BT848_PLL_F_LO); + btwrite(fh, BT848_PLL_F_HI); + btwrite(fi|BT848_PLL_X, BT848_PLL_XCI); +} + +static int set_pll(struct bttv *btv) +{ + int i; + unsigned long tv; + + if (!btv->pll.pll_crystal) + return 0; + + if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) { + /* no PLL needed */ + if (btv->pll.pll_current == 0) { + /* printk ("bttv%d: PLL: is off\n",btv->nr); */ + return 0; + } + printk ("bttv%d: PLL: switching off\n",btv->nr); + btwrite(0x00,BT848_TGCTRL); + btwrite(0x00,BT848_PLL_XCI); + btv->pll.pll_current = 0; + return 0; + } + + if (btv->pll.pll_ofreq == btv->pll.pll_current) { + /* printk("bttv%d: PLL: no change required\n",btv->nr); */ + return 1; + } + + if (bttv_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); + + /* Let other people run while the PLL stabilizes */ + tv=jiffies+HZ/10; /* .1 seconds */ + do + { + schedule(); + } + while(time_before(jiffies,tv)); + + for (i=0; i<100; i++) + { + if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK)) + btwrite(0,BT848_DSTATUS); + else + { + btwrite(0x08,BT848_TGCTRL); + btv->pll.pll_current = btv->pll.pll_ofreq; + if (bttv_verbose) + printk("ok\n"); + return 1; + } + mdelay(10); + } + btv->pll.pll_current = 0; + if (bttv_verbose) + printk("oops\n"); + return -1; +} + +static void bt848_muxsel(struct bttv *btv, unsigned int input) +{ + btaor(bttv_tvcards[btv->type].gpiomask2,~bttv_tvcards[btv->type].gpiomask2, + BT848_GPIO_OUT_EN); + + /* This seems to get rid of some synchronization problems */ + btand(~(3<<5), BT848_IFORM); + mdelay(10); + + + input %= bttv_tvcards[btv->type].video_inputs; + if (input==bttv_tvcards[btv->type].svhs) + { + btor(BT848_CONTROL_COMP, BT848_E_CONTROL); + btor(BT848_CONTROL_COMP, BT848_O_CONTROL); + } + else + { + btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); + btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); + } + btaor((bttv_tvcards[btv->type].muxsel[input&7]&3)<<5, ~(3<<5), BT848_IFORM); + audio(btv, (input!=bttv_tvcards[btv->type].tuner) ? + AUDIO_EXTERN : AUDIO_TUNER, 1); + btaor(bttv_tvcards[btv->type].muxsel[input]>>4, + ~bttv_tvcards[btv->type].gpiomask2, BT848_GPIO_DATA); +} + + +struct tvnorm +{ + u32 Fsc; + u16 swidth, sheight; /* scaled standard width, height */ + u16 totalwidth; + u8 adelay, bdelay, iform; + u32 scaledtwidth; + u16 hdelayx1, hactivex1; + u16 vdelay; + u8 vbipack; +}; + +static struct tvnorm tvnorms[] = { + /* 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 */ + { 35468950, + 924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), + 1135, 186, 924, 0x20, 255}, + + /* NTSC */ + { 28636363, + 768, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0), + 910, 128, 910, 0x1a, 144}, +#if 0 + /* SECAM EAST */ + { 35468950, + 768, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1), + 944, 186, 922, 0x20, 255}, +#else + /* SECAM L */ + { 35468950, + 924, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1), + 1135, 186, 922, 0x20, 255}, +#endif + /* PAL-NC */ + { 28636363, + 640, 576, 910, 0x68, 0x5d, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0), + 780, 130, 734, 0x1a, 144}, + /* PAL-M */ + { 28636363, + 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0), + 780, 135, 754, 0x1a, 144}, + /* PAL-N */ + { 35468950, + 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1), + 944, 186, 922, 0x20, 144}, + /* NTSC-Japan */ + { 28636363, + 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0), + 780, 135, 754, 0x16, 144}, +}; +#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm)) +#define VBI_SPL 2044 + +/* RISC command to write one VBI data line */ +#define VBI_RISC BT848_RISC_WRITE|VBI_SPL|BT848_RISC_EOL|BT848_RISC_SOL + +static void make_vbitab(struct bttv *btv) +{ + int i; + unsigned int *po=(unsigned int *) btv->vbi_odd; + unsigned int *pe=(unsigned int *) btv->vbi_even; + + if (bttv_debug > 1) + 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++) + { + *(po++)=cpu_to_le32(VBI_RISC); + *(po++)=cpu_to_le32(kvirt_to_bus((unsigned long)btv->vbibuf+i*2048)); + } + *(po++)=cpu_to_le32(BT848_RISC_JUMP); + *(po++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+4)); + + *(pe++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(pe++)=0; + for (i=16; i<32; i++) + { + *(pe++)=cpu_to_le32(VBI_RISC); + *(pe++)=cpu_to_le32(kvirt_to_bus((unsigned long)btv->vbibuf+i*2048)); + } + *(pe++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16)); + *(pe++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+10)); + + if (bttv_debug > 1) + printk("bttv%d: vbi2: po=%08lx pe=%08lx\n", + btv->nr,virt_to_bus(po), virt_to_bus(pe)); +} + +static int fmtbppx2[16] = { + 8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0 +}; + +static int palette2fmt[] = { + 0, + BT848_COLOR_FMT_Y8, + BT848_COLOR_FMT_RGB8, + BT848_COLOR_FMT_RGB16, + BT848_COLOR_FMT_RGB24, + BT848_COLOR_FMT_RGB32, + BT848_COLOR_FMT_RGB15, + BT848_COLOR_FMT_YUY2, + BT848_COLOR_FMT_BtYUV, + -1, + -1, + -1, + BT848_COLOR_FMT_RAW, + BT848_COLOR_FMT_YCrCb422, + BT848_COLOR_FMT_YCrCb411, + BT848_COLOR_FMT_YCrCb422, + BT848_COLOR_FMT_YCrCb411, +}; +#define PALETTEFMT_MAX (sizeof(palette2fmt)/sizeof(int)) + +static int make_rawrisctab(struct bttv *btv, unsigned int *ro, + unsigned int *re, unsigned int *vbuf) +{ + unsigned long line; + 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++)=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. + We'll have to handle this inside the IRQ handler ... */ + + for (line=0; line < 640; line++) + { + *(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+gbufsize/2)); + vadr+=bpl; + } + + *(ro++)=cpu_to_le32(BT848_RISC_JUMP); + *(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); + + return 0; +} + +static int make_prisctab(struct bttv *btv, unsigned int *ro, + unsigned int *re, + unsigned int *vbuf, unsigned short width, + unsigned short height, unsigned short fmt) +{ + unsigned long line, lmask; + unsigned long bl, blcr, blcb, rcmd; + unsigned long todo; + unsigned int **rp; + int inter; + unsigned long cbadr, cradr; + unsigned long vadr=(unsigned long) vbuf; + int shift, csize; + + if (bttv_debug > 1) + printk("bttv%d: prisc1: ro=%08lx re=%08lx\n", + btv->nr,virt_to_bus(ro), virt_to_bus(re)); + + switch(fmt) + { + case VIDEO_PALETTE_YUV422P: + csize=(width*height)>>1; + shift=1; + lmask=0; + break; + + case VIDEO_PALETTE_YUV411P: + csize=(width*height)>>2; + shift=2; + lmask=0; + break; + + case VIDEO_PALETTE_YUV420P: + csize=(width*height)>>2; + shift=1; + lmask=1; + break; + + case VIDEO_PALETTE_YUV410P: + csize=(width*height)>>4; + shift=2; + lmask=3; + break; + + default: + return -1; + } + cbadr=vadr+(width*height); + cradr=cbadr+csize; + 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; + + for (line=0; line < (height<<(1^inter)); line++) + { + if(line==height) + { + vadr+=csize<<1; + cbadr=vadr+(width*height); + cradr=cbadr+csize; + } + if (inter) + rp= (line&1) ? &re : &ro; + else + rp= (line>=height) ? &ro : &re; + + + if(line&lmask) + rcmd=BT848_RISC_WRITE1S23|BT848_RISC_SOL; + else + rcmd=BT848_RISC_WRITE123|BT848_RISC_SOL; + + todo=width; + while(todo) + { + bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr); + blcr=(PAGE_SIZE-((PAGE_SIZE-1)&cradr))<todo) ? todo : bl; + blcr=bl>>shift; + blcb=blcr; + /* bl now containts the longest row that can be written */ + todo-=bl; + if(!todo) rcmd|=BT848_RISC_EOL; /* if this is the last EOL */ + + *((*rp)++)=cpu_to_le32(rcmd|bl); + *((*rp)++)=cpu_to_le32(blcb|(blcr<<16)); + *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); + vadr+=bl; + if((rcmd&(15<<28))==BT848_RISC_WRITE123) + { + *((*rp)++)=(kvirt_to_bus(cbadr)); + cbadr+=blcb; + *((*rp)++)=cpu_to_le32(kvirt_to_bus(cradr)); + cradr+=blcr; + } + + rcmd&=~BT848_RISC_SOL; /* only the first has SOL */ + } + } + + *(ro++)=cpu_to_le32(BT848_RISC_JUMP); + *(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 (bttv_debug > 1) + printk("bttv%d: prisc2: ro=%08lx re=%08lx\n", + btv->nr,virt_to_bus(ro), virt_to_bus(re)); + + return 0; +} + +static int make_vrisctab(struct bttv *btv, unsigned int *ro, + unsigned int *re, + unsigned int *vbuf, unsigned short width, + unsigned short height, unsigned short palette) +{ + unsigned long line; + unsigned long bpl; /* bytes per line */ + unsigned long bl; + unsigned long todo; + unsigned int **rp; + int inter; + unsigned long vadr=(unsigned long) vbuf; + + if (palette==VIDEO_PALETTE_RAW) + return make_rawrisctab(btv, ro, re, vbuf); + if (palette>=VIDEO_PALETTE_PLANAR) + return make_prisctab(btv, ro, re, vbuf, width, height, palette); + + if (bttv_debug > 1) + 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++)=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) ? &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); + *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); + vadr+=bpl; + } + else + { + todo=bpl; + *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|bl); + *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); + vadr+=bl; + todo-=bl; + while (todo>PAGE_SIZE) + { + *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|PAGE_SIZE); + *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); + vadr+=PAGE_SIZE; + todo-=PAGE_SIZE; + } + *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|todo); + *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); + vadr+=todo; + } + } + + *(ro++)=cpu_to_le32(BT848_RISC_JUMP); + *(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 (bttv_debug > 1) + printk("bttv%d: vrisc2: ro=%08lx re=%08lx\n", + btv->nr,virt_to_bus(ro), virt_to_bus(re)); + + return 0; +} + +static unsigned char lmaskt[8] = +{ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80}; +static unsigned char rmaskt[8] = +{ 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; + +static void clip_draw_rectangle(unsigned char *clipmap, int x, int y, int w, int h) +{ + unsigned char lmask, rmask, *p; + int W, l, r; + int i; + + if (bttv_debug > 1) + printk("bttv clip: %dx%d+%d+%d\n",w,h,x,y); + + /* bitmap is fixed width, 128 bytes (1024 pixels represented) */ + if (x<0) + { + w+=x; + x=0; + } + if (y<0) + { + h+=y; + y=0; + } + if (w < 0 || h < 0) /* catch bad clips */ + return; + /* out of range data should just fall through */ + if (y+h>=625) + h=625-y; + if (x+w>=1024) + w=1024-x; + + l=x>>3; + r=(x+w-1)>>3; + W=r-l-1; + lmask=lmaskt[x&7]; + rmask=rmaskt[(x+w-1)&7]; + p=clipmap+128*y+l; + + if (W>0) + { + for (i=0; iwin.bpp is allowed here */ + bpp = fmtbppx2[btv->win.color_fmt&0xf]/2; + bpl=btv->win.bpl; + 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 (bttv_debug > 1) + 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 > 625) + height = 625; /* sanity check */ + ro=btv->risc_scr_odd; + re=btv->risc_scr_even; + + if (bttv_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); + *(ro++)=cpu_to_le32(btv->bus_vbi_even); + *(re++)=cpu_to_le32(BT848_RISC_JUMP); + *(re++)=cpu_to_le32(btv->bus_vbi_odd); + } + if (ncr < 0) { /* bitmap was pased */ + memcpy(clipmap, (unsigned char *)cr, VIDEO_CLIPMAP_SIZE); + } else { /* convert rectangular clips to a bitmap */ + memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ + for (i=0; iwin.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); + 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++)=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; + 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_scr_odd>RISCMEM_LEN/2 - 16) + outofmem++; + 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); + + if (bttv_debug > 1) + printk("bttv%d: clip2: pal=%d size=%dx%d, bpl=%d bpp=%d\n", + btv->nr,btv->picture.palette,width,height,bpl,bpp); +} + +/* + * 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(width&0xff, BT848_E_HACTIVE_LO+off); + btwrite(hdelay&0xff, BT848_E_HDELAY_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, + int no_irq_context) +{ + u16 ewidth, eheight, owidth, oheight; + u16 format, bswap; + struct tvnorm *tvn; + + tvn=&tvnorms[btv->win.norm]; + + btwrite(tvn->adelay, BT848_ADELAY); + btwrite(tvn->bdelay, BT848_BDELAY); + btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM); + btwrite(tvn->vbipack, BT848_VBI_PACK_SIZE); + btwrite(1, BT848_VBI_PACK_DEL); + + btv->pll.pll_ofreq = tvn->Fsc; + if (no_irq_context) + set_pll(btv); + + btv->win.interlace = (btv->win.height>tvn->sheight/2) ? 1 : 0; + + 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); + + btwrite(format, BT848_COLOR_FMT); + btwrite(bswap | BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL); +} + + +static int bpp2fmt[4] = { + BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16, + BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32 +}; + +static void bt848_set_winsize(struct bttv *btv) +{ + unsigned short format; + + 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 + * data sheet? [AC - because it's a standard format so I guess + * it never occurred to them] + * Enable dithering in this mode. + */ + + if (format==BT848_COLOR_FMT_RGB8) + btand(~BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); + else + btor(BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); + + bt848_set_geo(btv,1); +} + +#ifdef HACKING +/* playing with kiobufs and dma-to-userspace. 2.4.x only + Yes, I know: cut+paste programming is ugly. + will fix later, this is proof-of-concept right now. */ +static int make_vrisctab_kiobuf(struct bttv *btv, unsigned int *ro, + unsigned int *re, struct kiobuf *iobuf, + unsigned short width, unsigned short height, + unsigned short palette) +{ + unsigned long bpl; /* bytes per line */ + unsigned long bl; + unsigned long todo; + unsigned long pageaddr; + unsigned int **rp; + unsigned long line,inter,offset,page; + + 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++)=cpu_to_le32(0); + *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(re++)=cpu_to_le32(0); + + offset = iobuf->offset; + page = 0; + pageaddr = virt_to_bus((void*)page_address(iobuf->maplist[page])); + for (line=0; line < (height<<(1^inter)); line++) + { + if (inter) + rp= (line&1) ? &re : &ro; + else + rp= (line>=height) ? &ro : &re; + + bl = PAGE_SIZE - offset; + if (bpl <= bl) { + *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL| + BT848_RISC_EOL|bpl); + *((*rp)++)=cpu_to_le32(pageaddr+offset); + offset+=bpl; + } else { + todo = bpl; + *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|bl); + *((*rp)++)=cpu_to_le32(pageaddr+offset); + todo -= bl; + offset = 0; + page++; + pageaddr = virt_to_bus((void*)page_address(iobuf->maplist[page])); + while (todo>PAGE_SIZE) + { + *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|PAGE_SIZE); + *((*rp)++)=cpu_to_le32(pageaddr); + page++; + pageaddr = virt_to_bus((void*)page_address(iobuf->maplist[page])); + } + *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|todo); + *((*rp)++)=cpu_to_le32(pageaddr); + offset += todo; + } + } + + *(ro++)=cpu_to_le32(BT848_RISC_JUMP); + *(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); + + return 0; +} + +static int vgrab_kiobuf(struct bttv *btv, struct bttv_just_hacking *mp, + struct kiobuf *iobuf) +{ + unsigned int *ro, *re; + unsigned long flags; + + if(btv->gbuf[0].stat != GBUFFER_UNUSED) + return -EBUSY; + + if(mp->height < 32 || mp->width < 32) + return -EINVAL; + if (mp->format >= 12 /* more is'nt yet ... PALETTEFMT_MAX */) + return -EINVAL; + + if(-1 == palette2fmt[mp->format]) + return -EINVAL; + + /* + * Ok load up the BT848 + */ + + ro=btv->gbuf[0].risc; + re=ro+2048; + make_vrisctab_kiobuf(btv, ro, re, iobuf, mp->width, mp->height, mp->format); + + if (bttv_debug) + printk("bttv%d: cap vgrab_kiobuf: queue %d (%d:%dx%d)\n", + btv->nr,0,mp->format,mp->width,mp->height); + spin_lock_irqsave(&btv->s_lock, flags); + btv->gbuf[0].stat = GBUFFER_GRABBING; + btv->gbuf[0].fmt = palette2fmt[mp->format]; + btv->gbuf[0].width = mp->width; + btv->gbuf[0].height = mp->height; + btv->gbuf[0].ro = virt_to_bus(ro); + btv->gbuf[0].re = virt_to_bus(re); + +#if 1 + if (mp->height <= tvnorms[btv->win.norm].sheight/2 && + mp->format != VIDEO_PALETTE_RAW) + btv->gbuf[0].ro = 0; +#endif + + if (-1 == btv->gq_grab && btv->gq_in == btv->gq_out) { + btv->gq_start = 1; + btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); + } + btv->gqueue[btv->gq_in++] = 0; + btv->gq_in = btv->gq_in % MAX_GBUFFERS; + + btor(3, BT848_CAP_CTL); + btor(3, BT848_GPIO_DMA_CTL); + spin_unlock_irqrestore(&btv->s_lock, flags); + return 0; +} +#endif + +/* + * Grab into virtual memory. + */ + +static int vgrab(struct bttv *btv, struct video_mmap *mp) +{ + unsigned int *ro, *re; + unsigned int *vbuf; + unsigned long flags; + + if(btv->fbuffer==NULL) + { + if(fbuffer_alloc(btv)) + return -ENOBUFS; + } + + if(mp->frame >= gbuffers || mp->frame < 0) + return -EINVAL; + if(btv->gbuf[mp->frame].stat != GBUFFER_UNUSED) + return -EBUSY; + + 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 + > gbufsize) + return -EINVAL; + if(-1 == palette2fmt[mp->format]) + return -EINVAL; + + /* + * Ok load up the BT848 + */ + + 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); + + if (bttv_debug) + printk("bttv%d: cap vgrab: queue %d (%d:%dx%d)\n", + btv->nr,mp->frame,mp->format,mp->width,mp->height); + spin_lock_irqsave(&btv->s_lock, flags); + 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 (-1 == btv->gq_grab && btv->gq_in == btv->gq_out) { + btv->gq_start = 1; + btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); + } + btv->gqueue[btv->gq_in++] = mp->frame; + btv->gq_in = btv->gq_in % MAX_GBUFFERS; + + btor(3, BT848_CAP_CTL); + btor(3, BT848_GPIO_DMA_CTL); + spin_unlock_irqrestore(&btv->s_lock, flags); + return 0; +} + +static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock) +{ + return -EINVAL; +} + +static long bttv_read(struct video_device *v, char *buf, unsigned long count, int nonblock) +{ + struct bttv *btv= (struct bttv *)v; + int q,todo; + DECLARE_WAITQUEUE(wait, current); + + /* BROKEN: RETURNS VBI WHEN IT SHOULD RETURN GRABBED VIDEO FRAME */ + todo=count; + while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) + { + if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q)) + return -EFAULT; + todo-=q; + buf+=q; + + add_wait_queue(&btv->vbiq, &wait); + current->state = TASK_INTERRUPTIBLE; + if (todo && q==VBIBUF_SIZE-btv->vbip) + { + if(nonblock) + { + remove_wait_queue(&btv->vbiq, &wait); + current->state = TASK_RUNNING; + if(count==todo) + return -EWOULDBLOCK; + return count-todo; + } + schedule(); + if(signal_pending(current)) + { + remove_wait_queue(&btv->vbiq, &wait); + current->state = TASK_RUNNING; + + if(todo==count) + return -EINTR; + else + return count-todo; + } + } + remove_wait_queue(&btv->vbiq, &wait); + current->state = TASK_RUNNING; + } + if (todo) + { + if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo)) + return -EFAULT; + btv->vbip+=todo; + } + 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) +{ + unsigned long irq_flags; + + if (bttv_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; + spin_lock_irqsave(&btv->s_lock, irq_flags); + bt848_set_geo(btv,0); + bt848_set_risc_jmps(btv,-1); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); +} + +/* + * Open a bttv card. Right now the flags stuff is just playing + */ + +static int bttv_open(struct video_device *dev, int flags) +{ + struct bttv *btv = (struct bttv *)dev; + int i,ret; + + ret = -EBUSY; + + MOD_INC_USE_COUNT; + 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); + return 0; + + out_unlock: + up(&btv->lock); + MOD_DEC_USE_COUNT; + return ret; +} + +static void bttv_close(struct video_device *dev) +{ + struct bttv *btv=(struct bttv *)dev; + unsigned long irq_flags; + + down(&btv->lock); + btv->user--; + spin_lock_irqsave(&btv->s_lock, irq_flags); + btv->scr_on = 0; + btv->risc_cap_odd = 0; + btv->risc_cap_even = 0; + bt848_set_risc_jmps(btv,-1); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + + /* + * A word of warning. At this point the chip + * is still capturing because its FIFO hasn't emptied + * and the DMA control operations are posted PCI + * operations. + */ + + btread(BT848_I2C); /* This fixes the PCI posting delay */ + + if (-1 != btv->gq_grab) { + /* + * This is sucky but right now I can't find a good way to + * 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, gbuffers*gbufsize); + btv->fbuffer=0; + up(&btv->lock); + MOD_DEC_USE_COUNT; +} + + +/***********************************/ +/* ioctls and supporting functions */ +/***********************************/ + +extern inline void bt848_bright(struct bttv *btv, uint bright) +{ + btwrite(bright&0xff, BT848_BRIGHT); +} + +extern inline void bt848_hue(struct bttv *btv, uint hue) +{ + btwrite(hue&0xff, BT848_HUE); +} + +extern inline void bt848_contrast(struct bttv *btv, uint cont) +{ + unsigned int conthi; + + conthi=(cont>>6)&4; + btwrite(cont&0xff, BT848_CONTRAST_LO); + btaor(conthi, ~4, BT848_E_CONTROL); + btaor(conthi, ~4, BT848_O_CONTROL); +} + +extern inline void bt848_sat_u(struct bttv *btv, unsigned long data) +{ + u32 datahi; + + datahi=(data>>7)&2; + btwrite(data&0xff, BT848_SAT_U_LO); + btaor(datahi, ~2, BT848_E_CONTROL); + btaor(datahi, ~2, BT848_O_CONTROL); +} + +static inline void bt848_sat_v(struct bttv *btv, unsigned long data) +{ + u32 datahi; + + datahi=(data>>8)&1; + btwrite(data&0xff, BT848_SAT_V_LO); + btaor(datahi, ~1, BT848_E_CONTROL); + btaor(datahi, ~1, BT848_O_CONTROL); +} + +/* + * ioctl routine + */ + + +static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct bttv *btv=(struct bttv *)dev; + unsigned long irq_flags; + int i,ret = 0; + + if (bttv_debug > 1) + 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| + ((bttv_tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) | + VID_TYPE_OVERLAY| + VID_TYPE_CLIPPING| + VID_TYPE_FRAMERAM| + VID_TYPE_SCALES; + b.channels = bttv_tvcards[btv->type].video_inputs; + b.audios = bttv_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>=bttv_tvcards[btv->type].video_inputs) + return -EINVAL; + if(v.channel==bttv_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==bttv_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>bttv_tvcards[btv->type].video_inputs) + return -EINVAL; + if (v.norm > (sizeof(tvnorms)/sizeof(*tvnorms))) + return -EOPNOTSUPP; + + bttv_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); + spin_lock_irqsave(&btv->s_lock, irq_flags); + bt848_set_winsize(btv); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + } + 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; + bttv_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!=bttv_tvcards[btv->type].tuner) + return -EINVAL; + + if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC + &&v.mode!=VIDEO_MODE_SECAM) + return -EOPNOTSUPP; + bttv_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); + spin_lock_irqsave(&btv->s_lock, irq_flags); + bt848_set_winsize(btv); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + up(&btv->lock); + } + 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; + + down(&btv->lock); + if(vw.flags || vw.width < 16 || vw.height < 16) + { + spin_lock_irqsave(&btv->s_lock, irq_flags); + btv->scr_on = 0; + bt848_set_risc_jmps(btv,-1); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + return -EINVAL; + } + if (btv->win.bpp < 4) + { /* adjust and align writes */ + vw.x = (vw.x + 3) & ~3; + vw.width &= ~3; + } + 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; + + spin_lock_irqsave(&btv->s_lock, irq_flags); + bt848_set_risc_jmps(btv,0); + bt848_set_winsize(btv); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + + /* + * 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) + vfree(vcp); + spin_lock_irqsave(&btv->s_lock, irq_flags); + bt848_set_risc_jmps(btv,-1); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + 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; + spin_lock_irqsave(&btv->s_lock, irq_flags); + if (v == 1 && btv->win.vidadr != 0) + btv->scr_on = 1; + if (v == 0) + btv->scr_on = 0; + bt848_set_risc_jmps(btv,-1); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + 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(!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 (bttv_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; + bttv_call_i2c_clients(btv,cmd,&v); +#if 0 + if (btv->type == BTTV_MIROPRO && btv->radio) + tea5757_set_freq(btv,v); +#endif + 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; + bttv_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; + } + + 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; + 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 >= bttv_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); + + bttv_call_i2c_clients(btv,cmd,&v); + + 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<audio_dev=v; + up(&btv->lock); + return 0; + } + + case VIDIOCSYNC: + { + DECLARE_WAITQUEUE(wait, current); + + 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: + add_wait_queue(&btv->capq, &wait); + current->state = TASK_INTERRUPTIBLE; + while(btv->gbuf[i].stat==GBUFFER_GRABBING) { + if (bttv_debug) + printk("bttv%d: cap sync: sleep on %d\n",btv->nr,i); + schedule(); + if(signal_pending(current)) { + remove_wait_queue(&btv->capq, &wait); + current->state = TASK_RUNNING; + return -EINTR; + } + } + remove_wait_queue(&btv->capq, &wait); + current->state = TASK_RUNNING; + /* fall throuth */ + case GBUFFER_DONE: + case GBUFFER_ERROR: + ret = (btv->gbuf[i].stat == GBUFFER_ERROR) ? -EIO : 0; + if (bttv_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(!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; + 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=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; + vu.teletext=VIDEO_NO_UNIT; + if(copy_to_user((void *)arg, (void *)&vu, sizeof(vu))) + return -EFAULT; + return 0; + } + + case BTTV_BURST_ON: + { + burst(1); + return 0; + } + + case BTTV_BURST_OFF: + { + burst(0); + return 0; + } + + case BTTV_VERSION: + { + return BTTV_VERSION_CODE; + } + + case BTTV_PICNR: + { + /* return picture;*/ + return 0; + } + +#ifdef HACKING + /* playing with kiobufs and dma-to-userspace */ + case BTTV_JUST_HACKING: + { + DECLARE_WAITQUEUE(wait, current); + struct bttv_just_hacking hack; + struct kiobuf *iobuf; + int err; + + if(copy_from_user((void *) &hack, (void *) arg, sizeof(hack))) + return -EFAULT; + printk("bttv%d: hack: userland args: %dx%d, fmt=%d, buf=%lx, len=%d\n", + btv->nr,hack.width,hack.height,hack.format, + hack.buf,hack.len); + + /* pin down */ + err = alloc_kiovec(1,&iobuf); + if (err) + goto hack_oops; + err = map_user_kiobuf(READ, iobuf, hack.buf, hack.len); + if (err) + goto hack_oops; + err = lock_kiovec(1,&iobuf,1); + if (err) + goto hack_oops; + + /* have a look */ + printk("bttv%d: hack: kiobuf: nr_pages=%d, offset=%d, length=%d, locked=%d\n", + btv->nr,iobuf->nr_pages,iobuf->offset,iobuf->length, + iobuf->locked); + printk("bttv%d: hack: pages (bus addr):",btv->nr); + for (i = 0; i < iobuf->nr_pages; i++) { + printk(" %lx", virt_to_bus((void*)page_address(iobuf->maplist[i]))); + } + printk("\n"); + + /* start capture */ + err = -EINVAL; + if (hack.height * hack.width * 2 * /* fixme: *2 */ + fmtbppx2[palette2fmt[hack.format]&0x0f]/2 > hack.len) + goto hack_oops; + err = vgrab_kiobuf(btv,&hack,iobuf); + if (err) + goto hack_oops; + printk("bttv%d: hack: capture started\n",btv->nr); + + /* wait */ + add_wait_queue(&btv->capq, &wait); + current->state = TASK_INTERRUPTIBLE; + while(btv->gbuf[0].stat==GBUFFER_GRABBING) { + if (bttv_debug) + printk("bttv%d: hack: cap sync: sleep on %d\n",btv->nr,0); + schedule(); +#if 0 + if(signal_pending(current)) { + remove_wait_queue(&btv->capq, &wait); + current->state = TASK_RUNNING; + return -EINTR; + } +#endif + } + remove_wait_queue(&btv->capq, &wait); + current->state = TASK_RUNNING; + printk("bttv%d: hack: capture done\n",btv->nr); + + /* release */ + err = 0; + hack_oops: + free_kiovec(1,&iobuf); + return 0; + } +#endif + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int bttv_init_done(struct video_device *dev) +{ + return 0; +} + +/* + * This maps the vmalloced and reserved fbuffer to user space. + * + * FIXME: + * - PAGE_READONLY should suffice!? + * - remap_page_range is kind of inefficient for page by page remapping. + * But e.g. pte_alloc() does not work in modules ... :-( + */ + +static int do_bttv_mmap(struct bttv *btv, const char *adr, unsigned long size) +{ + unsigned long start=(unsigned long) adr; + unsigned long page,pos; + + 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", + VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT, + VID_HARDWARE_BT848, + bttv_open, + bttv_close, + bttv_read, + bttv_write, + NULL, + bttv_ioctl, + bttv_mmap, + bttv_init_done, + NULL, + 0, + -1 +}; + + +static long vbi_read(struct video_device *v, char *buf, unsigned long count, + int nonblock) +{ + struct bttv *btv=(struct bttv *)(v-2); + int q,todo; + DECLARE_WAITQUEUE(wait, current); + + 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; + buf+=q; + + add_wait_queue(&btv->vbiq, &wait); + current->state = TASK_INTERRUPTIBLE; + if (todo && q==VBIBUF_SIZE-btv->vbip) + { + if(nonblock) + { + remove_wait_queue(&btv->vbiq, &wait); + current->state = TASK_RUNNING; + if(count==todo) + return -EWOULDBLOCK; + return count-todo; + } + schedule(); + if(signal_pending(current)) + { + remove_wait_queue(&btv->vbiq, &wait); + current->state = TASK_RUNNING; + if(todo==count) + return -EINTR; + else + return count-todo; + } + } + remove_wait_queue(&btv->vbiq, &wait); + current->state = TASK_RUNNING; + } + if (todo) + { + if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo)) + return -EFAULT; + btv->vbip+=todo; + } + return count; +} + +static unsigned int vbi_poll(struct video_device *dev, struct file *file, + poll_table *wait) +{ + struct bttv *btv=(struct bttv *)(dev-2); + unsigned int mask = 0; + + poll_wait(file, &btv->vbiq, wait); + + if (btv->vbip < VBIBUF_SIZE) + mask |= (POLLIN | POLLRDNORM); + + return mask; +} + +static int vbi_open(struct video_device *dev, int flags) +{ + struct bttv *btv=(struct bttv *)(dev-2); + unsigned long irq_flags; + + MOD_INC_USE_COUNT; + down(&btv->lock); + if (btv->needs_restart) + bt848_restart(btv); + set_pll(btv); + btv->vbip=VBIBUF_SIZE; + spin_lock_irqsave(&btv->s_lock, irq_flags); + btv->vbi_on = 1; + bt848_set_risc_jmps(btv,-1); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + up(&btv->lock); + + return 0; +} + +static void vbi_close(struct video_device *dev) +{ + struct bttv *btv=(struct bttv *)(dev-2); + unsigned long irq_flags; + + spin_lock_irqsave(&btv->s_lock, irq_flags); + btv->vbi_on = 0; + bt848_set_risc_jmps(btv,-1); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + MOD_DEC_USE_COUNT; +} + +static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct bttv *btv=(struct bttv *)dev; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + strcpy(b.name,btv->vbi_dev.name); + b.type = ((bttv_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= +{ + "bttv vbi", + VID_TYPE_CAPTURE|VID_TYPE_TELETEXT, + VID_HARDWARE_BT848, + vbi_open, + vbi_close, + vbi_read, + bttv_write, + vbi_poll, + vbi_ioctl, + NULL, /* no mmap yet */ + bttv_init_done, + NULL, + 0, + -1 +}; + + +static int radio_open(struct video_device *dev, int flags) +{ + struct bttv *btv = (struct bttv *)(dev-1); + unsigned long v; + + MOD_INC_USE_COUNT; + down(&btv->lock); + if (btv->user) + goto busy_unlock; + btv->user++; + + btv->radio = 1; + v = 400*16; + bttv_call_i2c_clients(btv,VIDIOCSFREQ,&v); + bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type); + bt848_muxsel(btv,0); + up(&btv->lock); + + return 0; + + busy_unlock: + up(&btv->lock); + MOD_DEC_USE_COUNT; + 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; + up(&btv->lock); + MOD_DEC_USE_COUNT; +} + +static long radio_read(struct video_device *v, char *buf, unsigned long count, int nonblock) +{ + return -EINVAL; +} + +static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct bttv *btv=(struct bttv *)(dev-1); + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability v; + strcpy(v.name,btv->video_dev.name); + v.type = VID_TYPE_TUNER; + v.channels = 1; + v.audios = 1; + /* No we don't do pictures */ + v.maxwidth = 0; + v.maxheight = 0; + v.minwidth = 0; + v.minheight = 0; + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + return 0; + break; + } + 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, "Radio"); + 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))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: + { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + /* Only channel 0 has a tuner */ + if(v.tuner!=0 || btv->channel) + return -EINVAL; + /* XXX anything to do ??? */ + return 0; + } + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + bttv_ioctl((struct video_device *)btv,cmd,arg); + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static struct video_device radio_template= +{ + "bttv radio", + VID_TYPE_TUNER, + VID_HARDWARE_BT848, + radio_open, + radio_close, + radio_read, /* just returns -EINVAL */ + bttv_write, /* just returns -EINVAL */ + NULL, /* no poll */ + radio_ioctl, + NULL, /* no mmap */ + bttv_init_done, /* just returns 0 */ + NULL, + 0, + -1 +}; + + +#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 __devinit 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"); + } + + 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))) + { + printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n"); + triton1=BT848_INT_ETBF; + } +} + + +#if 0 +/* can tda9855.c handle this too maybe? */ +static void __devinit 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 */ +} +#endif + +static void bt848_set_risc_jmps(struct bttv *btv, int flags) +{ + if (-1 == flags) { + /* defaults */ + flags = 0; + if (btv->scr_on) + flags |= 0x03; + if (btv->vbi_on) + flags |= 0x0c; + } + + if (bttv_debug > 1) + 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]=cpu_to_le32(0); + + /* Jump to odd vbi sub */ + btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0xd<<20)); + if (flags&8) { + if (bttv_debug > 1) + printk(" ev=%08lx",virt_to_bus(btv->vbi_odd)); + btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->vbi_odd)); + } else { + if (bttv_debug > 1) + 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|(0xe<<20)); + if (0 != btv->risc_cap_odd) { + if (bttv_debug > 1) + 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 (bttv_debug > 1) + 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 (bttv_debug > 1) + 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]=cpu_to_le32(0); + + /* Jump to even vbi sub */ + btv->risc_jmp[8]=cpu_to_le32(BT848_RISC_JUMP); + if (flags&4) { + if (bttv_debug > 1) + printk(" ov=%08lx",virt_to_bus(btv->vbi_even)); + btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->vbi_even)); + } else { + if (bttv_debug > 1) + 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 (0 != btv->risc_cap_even) { + if (bttv_debug > 1) + 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 (bttv_debug > 1) + 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 (bttv_debug > 1) + printk(" -----------"); + btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_jmp+12)); + } + + if (btv->gq_start) { + btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); + } else { + btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP); + } + btv->risc_jmp[13]=cpu_to_le32(virt_to_bus(btv->risc_jmp)); + + /* enable cpaturing and DMA */ + if (bttv_debug > 1) + 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); + else + bt848_dma(btv, 0); +} + +static int __devinit init_video_dev(struct bttv *btv) +{ + 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)); + + bttv_idcard(btv); + audio(btv, AUDIO_MUTE, 1); + + 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[btv->nr]) + { + 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 __devinit init_bt848(struct bttv *btv) +{ + int j; + unsigned long irq_flags; + + 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 (bttv_verbose >= 2) + printk("bttv%d: gpio: out_enable=0x%x, data=0x%x, in=0x%x\n", + btv->nr, + 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", btv->nr, (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; + btv->win.x=0; + btv->win.y=0; + btv->win.width=768; /* 640 */ + btv->win.height=576; /* 480 */ + 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->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->field=btv->last_field=0; + + btv->errors=0; + btv->needs_restart=0; + + /* i2c */ + btv->tuner_type=-1; + init_bttv_i2c(btv); + + if (!(btv->risc_scr_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) + return -1; + 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; + btv->vbi_odd=btv->risc_jmp+16; + btv->vbi_even=btv->vbi_odd+256; + btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp+12); + btv->bus_vbi_even=virt_to_bus(btv->risc_jmp+6); + + btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD); + btv->vbibuf=(unsigned char *) vmalloc_32(VBIBUF_SIZE); + if (!btv->vbibuf) + return -1; + 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 */ + + btv->fbuffer=NULL; + + bt848_muxsel(btv, 1); + bt848_set_winsize(btv); + +/* btwrite(0, BT848_TDEC); */ + btwrite(0x10, BT848_COLOR_CTL); + btwrite(0x00, BT848_CAP_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_AUTO, + BT848_IFORM); + + btwrite(0xd8, BT848_CONTRAST_LO); + bt848_bright(btv, 0x10); + + btwrite(0x20, BT848_E_VSCALE_HI); + btwrite(0x20, BT848_O_VSCALE_HI); + btwrite(/*BT848_ADC_SYNC_T|*/ + BT848_ADC_RESERVED|BT848_ADC_CRUSH, BT848_ADC); + + btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL); + btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL); + + btv->picture.colour=254<<7; + btv->picture.brightness=128<<8; + btv->picture.hue=128<<8; + btv->picture.contrast=0xd8<<7; + + btwrite(0x00, BT848_E_SCLOOP); + btwrite(0x00, BT848_O_SCLOOP); + + /* clear interrupt status */ + btwrite(0xfffffUL, BT848_INT_STAT); + + /* set interrupt mask */ + btwrite(btv->triton1| + /*BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR| + BT848_INT_FDSR|BT848_INT_FTRGT|BT848_INT_FBUS|*/ + (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); + spin_lock_irqsave(&btv->s_lock, irq_flags); + bt848_set_risc_jmps(btv,-1); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + + /* + * Now add the template and register the device unit. + */ + init_video_dev(btv); + + return 0; +} + +static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) +{ + u32 stat,astat; + u32 dstat; + int count,i; + struct bttv *btv; + unsigned long irq_flags; + + btv=(struct bttv *)dev_id; + count=0; + while (1) + { + /* get/clear interrupt status bits */ + stat=btread(BT848_INT_STAT); + astat=stat&btread(BT848_INT_MASK); + if (!astat) + return; + btwrite(astat,BT848_INT_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)); + /*btv->win.norm&= + (dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */ + } + if (astat&BT848_INT_VPRES) + { + IDEBUG(printk ("bttv%d: IRQ_VPRES\n", btv->nr)); + } + if (astat&BT848_INT_VSYNC) + { + IDEBUG(printk ("bttv%d: IRQ_VSYNC\n", btv->nr)); + btv->field++; + } + if (astat&(BT848_INT_SCERR|BT848_INT_OCERR)) { + if (bttv_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) { + spin_lock_irqsave(&btv->s_lock, irq_flags); + 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); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + } else { + if (bttv_verbose) + printk("bttv%d: aiee: error loops\n",btv->nr); + /* cancel all outstanding grab requests */ + spin_lock_irqsave(&btv->s_lock, irq_flags); + 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; + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + + wake_up_interruptible(&btv->vbiq); + wake_up_interruptible(&btv->capq); + } + } + if (astat&BT848_INT_RISCI) + { + if (bttv_debug > 1) + printk("bttv%d: IRQ_RISCI\n",btv->nr); + + /* captured VBI frame */ + if (stat&(1<<28)) + { + btv->vbip=0; + /* inc vbi frame count for detecting drops */ + (*(u32 *)&(btv->vbibuf[VBIBUF_SIZE - 4]))++; + wake_up_interruptible(&btv->vbiq); + } + + /* captured full frame */ + if (stat&(2<<28) && btv->gq_grab != -1) + { + btv->last_field=btv->field; + if (bttv_debug) + printk("bttv%d: cap irq: done %d\n",btv->nr,btv->gq_grab); + do_gettimeofday(&btv->gbuf[btv->gq_grab].tv); + spin_lock_irqsave(&btv->s_lock, irq_flags); + btv->gbuf[btv->gq_grab].stat = GBUFFER_DONE; + btv->gq_grab = -1; + if (btv->gq_in != btv->gq_out) + { + btv->gq_grab = btv->gqueue[btv->gq_out++]; + btv->gq_out = btv->gq_out % MAX_GBUFFERS; + if (bttv_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 { + 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); + } + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + wake_up_interruptible(&btv->capq); + break; + } + if (stat&(8<<28)) + { + spin_lock_irqsave(&btv->s_lock, irq_flags); + btv->gq_start = 0; + btv->gq_grab = btv->gqueue[btv->gq_out++]; + btv->gq_out = btv->gq_out % MAX_GBUFFERS; + if (bttv_debug) + printk("bttv%d: cap irq: capture %d [start]\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); + spin_unlock_irqrestore(&btv->s_lock, irq_flags); + } + } + if (astat&BT848_INT_OCERR) + { + IDEBUG(printk ("bttv%d: IRQ_OCERR\n", btv->nr)); + } + if (astat&BT848_INT_PABORT) + { + IDEBUG(printk ("bttv%d: IRQ_PABORT\n", btv->nr)); + } + if (astat&BT848_INT_RIPERR) + { + IDEBUG(printk ("bttv%d: IRQ_RIPERR\n", btv->nr)); + } + if (astat&BT848_INT_PPERR) + { + IDEBUG(printk ("bttv%d: IRQ_PPERR\n", btv->nr)); + } + if (astat&BT848_INT_FDSR) + { + IDEBUG(printk ("bttv%d: IRQ_FDSR\n", btv->nr)); + } + if (astat&BT848_INT_FTRGT) + { + IDEBUG(printk ("bttv%d: IRQ_FTRGT\n", btv->nr)); + } + if (astat&BT848_INT_FBUS) + { + IDEBUG(printk ("bttv%d: IRQ_FBUS\n", btv->nr)); + } + if (astat&BT848_INT_HLOCK) + { + if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio)) + audio(btv, AUDIO_ON,0); + else + audio(btv, AUDIO_OFF,0); + } + + if (astat&BT848_INT_I2CDONE) + { + IDEBUG(printk ("bttv%d: IRQ_I2CDONE\n", btv->nr)); + } + count++; + if (count > 10) + printk (KERN_WARNING "bttv%d: irq loop %d\n", + btv->nr,count); + if (count > 20) + { + btwrite(0, BT848_INT_MASK); + printk(KERN_ERR + "bttv%d: IRQ lockup, cleared int mask\n", btv->nr); + } + } +} + + + +/* + * Scan for a Bt848 card, request the irq and map the io memory + */ + +static void __devinit bttv_remove(struct pci_dev *pci_dev) +{ + u8 command; + int j; + struct bttv *btv = PCI_GET_DRIVER_DATA(pci_dev); + + /* unregister i2c_bus */ + i2c_bit_del_bus(&btv->i2c_adap); + + /* turn off all capturing, DMA and IRQs */ + btand(~15, BT848_GPIO_DMA_CTL); + + /* first disable interrupts before unmapping the memory! */ + btwrite(0, BT848_INT_MASK); + btwrite(~0x0UL,BT848_INT_STAT); + btwrite(0x0, BT848_GPIO_OUT_EN); + + /* disable PCI bus-mastering */ + 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); + + /* unmap and free memory */ + 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_scr_odd) + kfree((void *) btv->risc_scr_odd); + + 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) + kfree((void *) btv->risc_jmp); + + DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%p.\n", btv->vbibuf)); + if (btv->vbibuf) + vfree((void *) btv->vbibuf); + + free_irq(btv->irq,btv); + DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%p.\n", btv->bt848_mem)); + if (btv->bt848_mem) + iounmap(btv->bt848_mem); + + if(btv->video_dev.minor!=-1) + video_unregister_device(&btv->video_dev); + if(btv->vbi_dev.minor!=-1) + video_unregister_device(&btv->vbi_dev); + if (radio[btv->nr] && btv->radio_dev.minor != -1) + video_unregister_device(&btv->radio_dev); + + release_mem_region(btv->bt848_adr, + pci_resource_len(btv->dev,0)); + /* 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); + + return; +} + + +static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) +{ + int result; + unsigned char command; + struct bttv *btv; +#if defined(__powerpc__) + unsigned int cmd; +#endif + + printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num); + + btv=&bttvs[bttv_num]; + btv->dev=dev; + btv->nr = bttv_num; + btv->bt848_mem=NULL; + btv->vbibuf=NULL; + btv->risc_jmp=NULL; + btv->vbi_odd=NULL; + btv->vbi_even=NULL; + init_waitqueue_head(&btv->vbiq); + init_waitqueue_head(&btv->capq); + btv->vbip=VBIBUF_SIZE; + btv->s_lock = SPIN_LOCK_UNLOCKED; + init_waitqueue_head(&btv->gpioq); + btv->shutdown=0; + + btv->id=dev->device; + btv->irq=dev->irq; + btv->bt848_adr=pci_resource_start(dev,0); + if (pci_enable_device(dev)) + return -EIO; + if (!request_mem_region(pci_resource_start(dev,0), + pci_resource_len(dev,0), + "bttv")) { + return -EBUSY; + } + if (btv->id >= 878) + btv->i2c_command = 0x83; + else + btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA); + + pci_read_config_byte(dev, 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, ",dev->bus->number, dev->devfn); + printk("irq: %d, ",btv->irq); + printk("memory: 0x%lx.\n", btv->bt848_adr); + +#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; +#else + btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); +#endif + + /* clear interrupt mask */ + btwrite(0, BT848_INT_MASK); + + 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); + goto fail; + } + if (result==-EBUSY) + { + printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq); + goto fail; + } + if (result < 0) + goto fail; + + pci_set_master(dev); + + btv->triton1=triton1 ? BT848_INT_ETBF : 0; + if (triton1 && btv->id >= 878) + { + btv->triton1 = 0; + printk("bttv: Enabling 430FX compatibilty for bt878\n"); + pci_read_config_byte(dev, BT878_DEVCTRL, &command); + command|=BT878_EN_TBFX; + pci_write_config_byte(dev, BT878_DEVCTRL, command); + pci_read_config_byte(dev, BT878_DEVCTRL, &command); + if (!(command&BT878_EN_TBFX)) + { + printk("bttv: 430FX compatibility could not be enabled\n"); + free_irq(btv->irq,btv); + result = -1; + goto fail; + } + } + + PCI_SET_DRIVER_DATA(dev,btv); + + if(init_bt848(btv) < 0) { + bttv_remove(dev); + return -EIO; + } + bttv_num++; + + return 0; + + fail: + release_mem_region(pci_resource_start(btv->dev,0), + pci_resource_len(btv->dev,0)); + return result; +} + +static struct pci_device_id bttv_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, bttv_pci_tbl); + +static struct pci_driver bttv_pci_driver = { + name: "bttv", + id_table: bttv_pci_tbl, + probe: bttv_probe, + remove: bttv_remove, +}; + +int bttv_init_module(void) +{ + bttv_num = 0; + + 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 (bttv_verbose) + printk(KERN_INFO "bttv: using %d buffers with %dk (%dk total) for capture\n", + gbuffers,gbufsize/1024,gbuffers*gbufsize/1024); + + handle_chipset(); + + return pci_module_init(&bttv_pci_driver); +} + +void bttv_cleanup_module(void) +{ + pci_unregister_driver(&bttv_pci_driver); + return; +} + +module_init(bttv_init_module); +module_exit(bttv_cleanup_module); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN linux-2.2.16.SuSE/drivers/char/bttv-if.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/bttv-if.c --- linux-2.2.16.SuSE/drivers/char/bttv-if.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/bttv-if.c Sun Jun 25 18:51:57 2000 @@ -0,0 +1,337 @@ +/* + bttv-if.c -- interfaces to other kernel modules + all the i2c code is here + also the gpio interface exported by bttv (used by lirc) + + + 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 + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#define __NO_VERSION__ 1 + +#include +#include +#include + +#include + +#include "bttv.h" +#include "tuner.h" + + +EXPORT_SYMBOL(bttv_get_cardinfo); +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); + +/* ----------------------------------------------------------------------- */ +/* Exported functions - for other modules which want to access the */ +/* gpio ports (IR for example) */ +/* see bttv.h for comments */ + +int bttv_get_cardinfo(unsigned int card, int *type, int *cardid) +{ + if (card >= bttv_num) { + return -1; + } + *type = bttvs[card].type; + *cardid = bttvs[card].cardid; + return 0; +} + +int bttv_get_id(unsigned int card) +{ + printk("bttv_get_id is obsolete, use bttv_get_cardinfo instead\n"); + 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_head_t* 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; +} + + +/* ----------------------------------------------------------------------- */ +/* I2C functions */ + +static void bttv_bit_setscl(void *data, int state) +{ + 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 void bttv_bit_setsda(void *data, int state) +{ + 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); +} + +static int bttv_bit_getscl(void *data) +{ + struct bttv *btv = (struct bttv*)data; + int state; + + state = btread(BT848_I2C) & 0x02 ? 1 : 0; + return state; +} + +static int bttv_bit_getsda(void *data) +{ + struct bttv *btv = (struct bttv*)data; + int state; + + state = btread(BT848_I2C) & 0x01; + return state; +} + +static void bttv_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void bttv_dec_use(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +static int attach_inform(struct i2c_client *client) +{ + 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; + } + } + if (btv->tuner_type != -1) + bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); + if (bttv_verbose) + printk("bttv%d: i2c attach [%s]\n",btv->nr,client->name); + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + struct bttv *btv = (struct bttv*)client->adapter->data; + int i; + + if (bttv_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; + } + } + return 0; +} + +void bttv_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); + } +} + +struct i2c_algo_bit_data bttv_i2c_algo_template = { + NULL, + bttv_bit_setsda, + bttv_bit_setscl, + bttv_bit_getsda, + bttv_bit_getscl, + 10, 10, 100, +}; + +struct i2c_adapter bttv_i2c_adap_template = { + "bt848", + I2C_HW_B_BT848, + NULL, + NULL, + bttv_inc_use, + bttv_dec_use, + attach_inform, + detach_inform, + NULL, +}; + +struct i2c_client bttv_i2c_client_template = { + "bttv internal", + -1, + 0, + 0, + NULL, + NULL +}; + + +/* read I2C */ +int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) +{ + unsigned char buffer = 0; + + if (0 != btv->i2c_ok) + return -1; + if (bttv_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 (bttv_verbose) + printk("not found\n"); + } else + printk(KERN_WARNING "bttv%d: i2c read 0x%x: error\n", + btv->nr,addr); + return -1; + } + if (bttv_verbose && NULL != probe_for) + printk("found\n"); + return buffer; +} + +/* write I2C */ +int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, + unsigned char b2, int both) +{ + 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 content */ +void __devinit bttv_readee(struct bttv *btv, unsigned char *eedata, int addr) +{ + int i; + + if (bttv_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; + } + } +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN linux-2.2.16.SuSE/drivers/char/bttv.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/bttv.c --- linux-2.2.16.SuSE/drivers/char/bttv.c Mon Jun 26 18:17:24 2000 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/bttv.c Thu Jan 1 01:00:00 1970 @@ -1,3957 +0,0 @@ - -/* - bttv - Bt848 frame grabber driver - - Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) - & Marcus Metzler (mocm@thp.uni-koeln.de) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if LINUX_VERSION_CODE >= 0x020100 -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if LINUX_VERSION_CODE >= 0x020100 -#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 IDEBUG(x) /* Debug interrupt handler */ - -#if LINUX_VERSION_CODE >= 0x020117 -MODULE_PARM(vidmem,"i"); -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 - -/* Anybody who uses more than four? */ -#define BTTV_MAX 4 - -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 -#endif - -static unsigned long remap[BTTV_MAX]; /* remap Bt848 */ -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 int bttv_num; /* number of Bt848s in use */ -static struct bttv bttvs[BTTV_MAX]; - -#define I2C_TIMING (0x7<<4) -#define I2C_DELAY 10 - -#define I2C_SET(CTRL,DATA) \ - { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); } -#define I2C_GET() (btread(BT848_I2C)&1) - -#define EEPROM_WRITE_DELAY 20000 -#define BURSTOFFSET 76 - -/*******************************/ -/* Memory management functions */ -/*******************************/ - -#define MDEBUG(x) do { } while(0) /* Debug memory management */ - -/* [DaveM] I've recoded most of this so that: - * 1) It's easier to tell what is happening - * 2) It's more portable, especially for translating things - * out of vmalloc mapped areas in the kernel. - * 3) Less unnecessary translations happen. - * - * The code used to assume that the kernel vmalloc mappings - * existed in the page tables of every process, this is simply - * not guarenteed. We now use pgd_offset_k which is the - * defined way to get at the kernel page tables. - */ - -/* Given PGD from the address space's page table, return the kernel - * virtual mapping of the physical memory mapped at ADR. - */ -static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) -{ - unsigned long ret = 0UL; - pmd_t *pmd; - pte_t *ptep, pte; - - if (!pgd_none(*pgd)) { - pmd = pmd_offset(pgd, adr); - if (!pmd_none(*pmd)) { - ptep = pte_offset(pmd, adr); - pte = *ptep; - if(pte_present(pte)) - ret = (pte_page(pte)|(adr&(PAGE_SIZE-1))); - } - } - MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); - return ret; -} - -static inline unsigned long uvirt_to_bus(unsigned long adr) -{ - unsigned long kva, ret; - - kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); - return ret; -} - -static inline unsigned long kvirt_to_bus(unsigned long adr) -{ - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); - ret = virt_to_bus((void *)kva); - MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); - return ret; -} - -/* Here we want the physical address of the memory. - * This is used when initializing the contents of the - * area and marking the pages as reserved. - */ -static inline unsigned long kvirt_to_pa(unsigned long adr) -{ - unsigned long va, kva, ret; - - va = VMALLOC_VMADDR(adr); - kva = uvirt_to_kva(pgd_offset_k(va), va); - ret = __pa(kva); - MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); - return ret; -} - -static void * rvmalloc(unsigned long size) -{ - void * mem; - unsigned long adr, page; - - mem=vmalloc(size); - if (mem) - { - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr=(unsigned long) mem; - while (size > 0) - { - page = kvirt_to_pa(adr); - mem_map_reserve(MAP_NR(__va(page))); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - } - return mem; -} - -static void rvfree(void * mem, unsigned long size) -{ - unsigned long adr, page; - - if (mem) - { - adr=(unsigned long) mem; - while (size > 0) - { - page = kvirt_to_pa(adr); - mem_map_unreserve(MAP_NR(__va(page))); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - vfree(mem); - } -} - - - -/* - * Create the giant waste of buffer space we need for now - * until we get DMA to user space sorted out (probably 2.3.x) - * - * We only create this as and when someone uses mmap - */ - -static int fbuffer_alloc(struct bttv *btv) -{ - if(!btv->fbuffer) - btv->fbuffer=(unsigned char *) rvmalloc(2*BTTV_MAX_FBUF); - else - printk(KERN_ERR "bttv%d: Double alloc of fbuffer!\n", - btv->nr); - if(!btv->fbuffer) - return -ENOBUFS; - return 0; -} - - -/* ----------------------------------------------------------------------- */ -/* I2C functions */ - -/* software I2C functions */ - -static void i2c_setlines(struct i2c_bus *bus,int ctrl,int data) -{ - struct bttv *btv = (struct bttv*)bus->data; - btwrite((ctrl<<1)|data, BT848_I2C); - btread(BT848_I2C); /* flush buffers */ - udelay(I2C_DELAY); -} - -static int i2c_getdataline(struct i2c_bus *bus) -{ - struct bttv *btv = (struct bttv*)bus->data; - return btread(BT848_I2C)&1; -} - -/* hardware I2C functions */ - -/* read I2C */ -static int I2CRead(struct i2c_bus *bus, unsigned char addr) -{ - 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; -} - -/* set both to write both bytes, reset it to write only b1 */ - -static int I2CWrite(struct i2c_bus *bus, unsigned char addr, unsigned char b1, - unsigned char b2, int both) -{ - 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); - - 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; - } - if (!(stat & BT848_INT_RACK)) - return -2; - - return 0; -} - -/* read EEPROM */ -static void readee(struct i2c_bus *bus, unsigned char *eedata) -{ - 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"); - break; - } - eedata[i]=k; - } -} - -/* write EEPROM */ -static void writeee(struct i2c_bus *bus, unsigned char *eedata) -{ - 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); - break; - } - udelay(EEPROM_WRITE_DELAY); - } -} - -static void attach_inform(struct i2c_bus *bus, int id) -{ - 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; - } -} - -static void detach_inform(struct i2c_bus *bus, int id) -{ - struct bttv *btv = (struct bttv*)bus->data; - - switch (id) - { - case I2C_DRIVERID_MSP3400: - btv->have_msp3400 = 0; - break; - case I2C_DRIVERID_TUNER: - btv->have_tuner = 0; - break; - } -} - -static struct i2c_bus bttv_i2c_bus_template = -{ - "bt848", - I2C_BUSID_BT848, - NULL, - -#if LINUX_VERSION_CODE >= 0x020100 - SPIN_LOCK_UNLOCKED, -#endif - - attach_inform, - detach_inform, - - i2c_setlines, - i2c_getdataline, - I2CRead, - I2CWrite, -}; - -/* ----------------------------------------------------------------------- */ -/* some hauppauge specific stuff */ - -static unsigned char eeprom_data[256]; -static struct HAUPPAUGE_TUNER -{ - int id; - char *name; -} -hauppauge_tuner[] = -{ - { TUNER_ABSENT, "" }, - { TUNER_ABSENT, "External" }, - { TUNER_ABSENT, "Unspecified" }, - { TUNER_ABSENT, "Philips FI1216" }, - { TUNER_ABSENT, "Philips FI1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FI1236" }, - { TUNER_ABSENT, "Philips FI1246" }, - { TUNER_ABSENT, "Philips FI1256" }, - { TUNER_PHILIPS_PAL, "Philips FI1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2" }, - { TUNER_PHILIPS_NTSC, "Philips FI1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2" }, - { TUNER_ABSENT, "Philips FI1256 MK2" }, - { TUNER_ABSENT, "Temic 4032FY5" }, - { TUNER_TEMIC_PAL, "Temic 4002FH5" }, - { TUNER_TEMIC_PAL_I, "Temic 4062FY5" }, - { TUNER_ABSENT, "Philips FR1216 MK2" }, - { TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2" }, - { TUNER_PHILIPS_NTSC, "Philips FR1236 MK2" }, - { TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2" }, - { TUNER_ABSENT, "Philips FR1256 MK2" }, - { TUNER_PHILIPS_PAL, "Philips FM1216" }, - { TUNER_ABSENT, "Philips FM1216MF" }, - { TUNER_PHILIPS_NTSC, "Philips FM1236" }, -}; - -static void -hauppauge_eeprom(struct i2c_bus *bus) -{ - 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); - } -} - -static void -hauppauge_msp_reset(struct bttv *btv) -{ - /* Reset 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); */ -} - -/* ----------------------------------------------------------------------- */ - - -struct tvcard -{ - 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 */ -}; - -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}} -}; -#define TVCARDS (sizeof(tvcards)/sizeof(tvcard)) - -static void audio(struct bttv *btv, int mode) -{ - btaor(tvcards[btv->type].gpiomask, ~tvcards[btv->type].gpiomask, - BT848_GPIO_OUT_EN); - - switch (mode) - { - case AUDIO_MUTE: - btv->audio|=AUDIO_MUTE; - break; - case AUDIO_UNMUTE: - btv->audio&=~AUDIO_MUTE; - mode=btv->audio; - break; - case AUDIO_OFF: - mode=AUDIO_OFF; - break; - case AUDIO_ON: - mode=btv->audio; - break; - default: - btv->audio&=AUDIO_MUTE; - btv->audio|=mode; - 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 - ) - mode=AUDIO_OFF; - if ((mode == 0) && (btv->radio)) - mode = 1; - btaor(tvcards[btv->type].audiomux[mode], - ~tvcards[btv->type].gpiomask, BT848_GPIO_DATA); -} - - -extern inline void bt848_dma(struct bttv *btv, uint state) -{ - if (state) - btor(3, BT848_GPIO_DMA_CTL); - else - btand(~3, BT848_GPIO_DMA_CTL); -} - - -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 - PLL_X = Reference pre-divider (0=1, 1=2) - PLL_C = Post divider (0=6, 1=4) - PLL_I = Integer input - PLL_F = Fractional input - - F_input = 28.636363 MHz: - PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0 -*/ - -static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout) -{ - unsigned char fl, fh, fi; - - /* prevent overflows */ - fin/=4; - fout/=4; - - fout*=12; - fi=fout/fin; - - fout=(fout%fin)*256; - fh=fout/fin; - - fout=(fout%fin)*256; - fl=fout/fin; - - /*printk("0x%02x 0x%02x 0x%02x\n", fi, fh, fl);*/ - btwrite(fl, BT848_PLL_F_LO); - btwrite(fh, BT848_PLL_F_HI); - btwrite(fi|BT848_PLL_X, BT848_PLL_XCI); -} - -static int set_pll(struct bttv *btv) -{ - int i; - unsigned long tv; - - if (!btv->pll.pll_crystal) - return 0; - - if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) { - /* no PLL needed */ - if (btv->pll.pll_current == 0) { - /* printk ("bttv%d: PLL: is off\n",btv->nr); */ - return 0; - } - printk ("bttv%d: PLL: switching off\n",btv->nr); - btwrite(0x00,BT848_TGCTRL); - btwrite(0x00,BT848_PLL_XCI); - btv->pll.pll_current = 0; - return 0; - } - - if (btv->pll.pll_ofreq == btv->pll.pll_current) { - /* 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); - - set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq); - - /* Let other people run while the PLL stabilizes */ - tv=jiffies+HZ/10; /* .1 seconds */ - do - { - schedule(); - } - while(time_before(jiffies,tv)); - - for (i=0; i<10; i++) - { - if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK)) - btwrite(0,BT848_DSTATUS); - else - { - btwrite(0x08,BT848_TGCTRL); - btv->pll.pll_current = btv->pll.pll_ofreq; - printk("ok\n"); - return 1; - } - mdelay(10); - } - btv->pll.pll_current = 0; - printk("oops\n"); - return -1; -} - -static void bt848_muxsel(struct bttv *btv, unsigned int input) -{ - btaor(tvcards[btv->type].gpiomask2,~tvcards[btv->type].gpiomask2, - BT848_GPIO_OUT_EN); - - /* This seems to get rid of some synchronization problems */ - btand(~(3<<5), BT848_IFORM); - mdelay(10); - - - input %= tvcards[btv->type].video_inputs; - if (input==tvcards[btv->type].svhs) - { - btor(BT848_CONTROL_COMP, BT848_E_CONTROL); - btor(BT848_CONTROL_COMP, BT848_O_CONTROL); - } - else - { - btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); - btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); - } - btaor((tvcards[btv->type].muxsel[input&7]&3)<<5, ~(3<<5), BT848_IFORM); - audio(btv, (input!=tvcards[btv->type].tuner) ? - AUDIO_EXTERN : AUDIO_TUNER); - 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; - u16 swidth, sheight; /* scaled standard width, height */ - u16 totalwidth; - u8 adelay, bdelay, iform; - u32 scaledtwidth; - u16 hdelayx1, hactivex1; - u16 vdelay; - u8 vbipack; -}; - -static struct tvnorm tvnorms[] = { - /* 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, - 768, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1), - 944, 186, 922, 0x20, 255}, -#else - /* SECAM L */ - { 35468950, - 924, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1), - 1135, 186, 922, 0x20, 255}, -#endif - /* PAL-NC */ - { 28636363, - 640, 576, 910, 0x68, 0x5d, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0), - 780, 130, 734, 0x1a, 144}, - /* PAL-M */ - { 28636363, - 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0), - 780, 135, 754, 0x1a, 144}, - /* PAL-N */ - { 35468950, - 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1), - 944, 186, 922, 0x20, 144}, - /* NTSC-Japan */ - { 28636363, - 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0), - 780, 135, 754, 0x16, 144}, -}; -#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm)) -#define VBI_SPL 2044 - -/* RISC command to write one VBI data line */ -#define VBI_RISC BT848_RISC_WRITE|VBI_SPL|BT848_RISC_EOL|BT848_RISC_SOL - -static void make_vbitab(struct bttv *btv) -{ - int i; - 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)); - - *(po++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(po++)=0; - for (i=0; i<16; i++) - { - *(po++)=cpu_to_le32(VBI_RISC); - *(po++)=cpu_to_le32(kvirt_to_bus((unsigned long)btv->vbibuf+i*2048)); - } - *(po++)=cpu_to_le32(BT848_RISC_JUMP); - *(po++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+4)); - - *(pe++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(pe++)=0; - for (i=16; i<32; i++) - { - *(pe++)=cpu_to_le32(VBI_RISC); - *(pe++)=cpu_to_le32(kvirt_to_bus((unsigned long)btv->vbibuf+i*2048)); - } - *(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)); -} - -int fmtbppx2[16] = { - 8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0 -}; - -int palette2fmt[] = { - 0, - BT848_COLOR_FMT_Y8, - BT848_COLOR_FMT_RGB8, - BT848_COLOR_FMT_RGB16, - BT848_COLOR_FMT_RGB24, - BT848_COLOR_FMT_RGB32, - BT848_COLOR_FMT_RGB15, - BT848_COLOR_FMT_YUY2, - BT848_COLOR_FMT_BtYUV, - -1, - -1, - -1, - BT848_COLOR_FMT_RAW, - BT848_COLOR_FMT_YCrCb422, - BT848_COLOR_FMT_YCrCb411, - BT848_COLOR_FMT_YCrCb422, - BT848_COLOR_FMT_YCrCb411, -}; -#define PALETTEFMT_MAX (sizeof(palette2fmt)/sizeof(int)) - -static int make_rawrisctab(struct bttv *btv, unsigned int *ro, - unsigned int *re, unsigned int *vbuf) -{ - unsigned long line; - 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; - - /* In PAL 650 blocks of 256 DWORDs are sampled, but only if VDELAY - is 2 and without separate VBI grabbing. - We'll have to handle this inside the IRQ handler ... */ - - for (line=0; line < 640; line++) - { - *(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)); - vadr+=bpl; - } - - *(ro++)=cpu_to_le32(BT848_RISC_JUMP); - *(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); - - return 0; -} - - -static int make_prisctab(struct bttv *btv, unsigned int *ro, - unsigned int *re, - unsigned int *vbuf, unsigned short width, - unsigned short height, unsigned short fmt) -{ - unsigned long line, lmask; - unsigned long bl, blcr, blcb, rcmd; - unsigned long todo; - unsigned int **rp; - int inter; - unsigned long cbadr, cradr; - unsigned long vadr=(unsigned long) vbuf; - int shift, csize; - - - switch(fmt) - { - case VIDEO_PALETTE_YUV422P: - csize=(width*height)>>1; - shift=1; - lmask=0; - break; - - case VIDEO_PALETTE_YUV411P: - csize=(width*height)>>2; - shift=2; - lmask=0; - break; - - case VIDEO_PALETTE_YUV420P: - csize=(width*height)>>2; - shift=1; - lmask=1; - break; - - case VIDEO_PALETTE_YUV410P: - csize=(width*height)>>4; - shift=2; - lmask=3; - break; - - default: - return -1; - } - cbadr=vadr+(width*height); - cradr=cbadr+csize; - inter = (height>btv->win.cropheight/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; - - for (line=0; line < (height<<(1^inter)); line++) - { - if(line==height) - { - vadr+=csize<<1; - cbadr=vadr+(width*height); - cradr=cbadr+csize; - } - if (inter) - rp= (line&1) ? &re : &ro; - else - rp= (line>=height) ? &re : &ro; - - - if(line&lmask) - rcmd=BT848_RISC_WRITE1S23|BT848_RISC_SOL; - else - rcmd=BT848_RISC_WRITE123|BT848_RISC_SOL; - - todo=width; - while(todo) - { - bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr); - blcr=(PAGE_SIZE-((PAGE_SIZE-1)&cradr))<todo) ? todo : bl; - blcr=bl>>shift; - blcb=blcr; - /* bl now containts the longest row that can be written */ - todo-=bl; - if(!todo) rcmd|=BT848_RISC_EOL; /* if this is the last EOL */ - - *((*rp)++)=cpu_to_le32(rcmd|bl); - *((*rp)++)=cpu_to_le32(blcb|(blcr<<16)); - *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); - vadr+=bl; - if((rcmd&(15<<28))==BT848_RISC_WRITE123) - { - *((*rp)++)=cpu_to_le32(kvirt_to_bus(cbadr)); - cbadr+=blcb; - *((*rp)++)=cpu_to_le32(kvirt_to_bus(cradr)); - cradr+=blcr; - } - - rcmd&=~BT848_RISC_SOL; /* only the first has SOL */ - } - } - - *(ro++)=cpu_to_le32(BT848_RISC_JUMP); - *(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); - - return 0; -} - -static int make_vrisctab(struct bttv *btv, unsigned int *ro, - unsigned int *re, - unsigned int *vbuf, unsigned short width, - unsigned short height, unsigned short palette) -{ - unsigned long line; - unsigned long bpl; /* bytes per line */ - unsigned long bl; - unsigned long todo; - unsigned int **rp; - int inter; - unsigned long vadr=(unsigned long) vbuf; - - if (palette==VIDEO_PALETTE_RAW) - 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; - 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; - - for (line=0; line < (height<<(1^inter)); line++) - { - if (inter) - rp= (line&1) ? &re : &ro; - else - rp= (line>=height) ? &re : &ro; - - bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr); - if (bpl<=bl) - { - *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL| - BT848_RISC_EOL|bpl); - *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); - vadr+=bpl; - } - else - { - todo=bpl; - *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|bl); - *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); - vadr+=bl; - todo-=bl; - while (todo>PAGE_SIZE) - { - *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|PAGE_SIZE); - *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); - vadr+=PAGE_SIZE; - todo-=PAGE_SIZE; - } - *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|todo); - *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr)); - vadr+=todo; - } - } - - *(ro++)=cpu_to_le32(BT848_RISC_JUMP); - *(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); - - return 0; -} - -static unsigned char lmaskt[8] = -{ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80}; -static unsigned char rmaskt[8] = -{ 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; - -static void clip_draw_rectangle(unsigned char *clipmap, int x, int y, int w, int h) -{ - unsigned char lmask, rmask, *p; - int W, l, r; - int i; - /* bitmap is fixed width, 128 bytes (1024 pixels represented) */ - if (x<0) - { - w+=x; - x=0; - } - if (y<0) - { - h+=y; - y=0; - } - if (w < 0 || h < 0) /* catch bad clips */ - return; - /* out of range data should just fall through */ - if (y+h>=625) - h=625-y; - if (x+w>=1024) - w=1024-x; - - l=x>>3; - r=(x+w)>>3; - W=r-l-1; - lmask=lmaskt[x&7]; - rmask=rmaskt[(x+w)&7]; - p=clipmap+128*y+l; - - if (W>0) - { - for (i=0; iwin.interlace&1)^1; - bpp=btv->win.bpp; - if (bpp==15) /* handle 15bpp as 16bpp in calculations */ - bpp++; - bpl=btv->win.bpl; - ro=btv->risc_odd; - re=btv->risc_even; - if((width=btv->win.width)>1023) - width = 1023; /* sanity check */ - if((height=btv->win.height)>625) - height = 625; /* sanity check */ - adr=btv->win.vidadr+btv->win.x*bpp+btv->win.y*bpl; - if ((clipmap=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) { - /* can't clip, don't generate any risc code */ - *(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); - } - if (ncr < 0) { /* bitmap was pased */ - memcpy(clipmap, (unsigned char *)cr, VIDEO_CLIPMAP_SIZE); - } else { /* convert rectangular clips to a bitmap */ - memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ - for (i=0; iwin.x+width>btv->win.swidth) ? - (btv->win.swidth-btv->win.x) : 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); - 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; - - /* 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) - dx++; - else { /* generate the dma controller code */ - len = dx * bpp; - flags = ((bpp==4) ? BT848_RISC_BYTE3 : 0); - flags |= ((!sx) ? BT848_RISC_SOL : 0); - flags |= ((sx + dx == width) ? BT848_RISC_EOL : 0); - if (!lastbit) { - *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|flags|len); - *((*rp)++)=cpu_to_le32(adr + bpp * sx); - } else - *((*rp)++)=cpu_to_le32(BT848_RISC_SKIP|flags|len); - lastbit=cbit; - sx += dx; - dx = 1; - if (ro - btv->risc_odd > RISCMEM_LEN/2 - 16) - outofmem++; - if (re - btv->risc_even > RISCMEM_LEN/2 - 16) - outofmem++; - } - } - 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. - -*/ -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) -{ - int off = odd ? 0x80 : 0x00; - - 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(hdelay&0xff, BT848_E_HDELAY_LO+off); - btwrite(vactive&0xff, BT848_E_VACTIVE_LO+off); - btwrite(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) -{ - u16 vscale, hscale; - u32 xsf, sr; - u16 hdelay, vdelay; - u16 hactive, vactive; - u16 inter; - u8 crop, vtc; - 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); - btwrite(tvn->vbipack, BT848_VBI_PACK_SIZE); - 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; - - 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; - - xsf = (hactive*tvn->scaledtwidth)/btv->win.cropwidth; - hscale = ((tvn->totalwidth*4096UL)/xsf-4096); - - hdelay=tvn->hdelayx1+btv->win.cropx; - hdelay=(hdelay*hactive)/btv->win.cropwidth; - hdelay&=0x3fe; - - 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, - BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32 -}; - -static void bt848_set_winsize(struct bttv *btv) -{ - unsigned short format; - - btv->win.color_fmt = format = - (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 : - bpp2fmt[(btv->win.bpp-1)&3]; - - /* RGB8 seems to be a 9x5x5 GRB color cube starting at - * color 16. Why the h... can't they even mention this in the - * data sheet? [AC - because it's a standard format so I guess - * it never occurred to them] - * Enable dithering in this mode. - */ - - if (format==BT848_COLOR_FMT_RGB8) - btand(~BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); - else - btor(BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); - - bt848_set_geo(btv, btv->win.width, btv->win.height, format, 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. - * Currently only does double buffering. Do we need more? - */ - -static int vgrab(struct bttv *btv, struct video_mmap *mp) -{ - 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) - return -EINVAL; - - 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) - return -EINVAL; - - if (mp->format >= PALETTEFMT_MAX) - return -EINVAL; - if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2 - > BTTV_MAX_FBUF) - 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); - 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); - } - btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); - } - restore_flags(flags); - 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; -} - -static long bttv_read(struct video_device *v, char *buf, unsigned long count, int nonblock) -{ - 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)) - { - if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q)) - return -EFAULT; - todo-=q; - buf+=q; - - cli(); - if (todo && q==VBIBUF_SIZE-btv->vbip) - { - if(nonblock) - { - sti(); - if(count==todo) - return -EWOULDBLOCK; - return count-todo; - } - interruptible_sleep_on(&btv->vbiq); - sti(); - if(signal_pending(current)) - { - if(todo==count) - return -EINTR; - else - return count-todo; - } - } - } - if (todo) - { - if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo)) - return -EFAULT; - btv->vbip+=todo; - } - return count; -} - -/* - * Open a bttv card. Right now the flags stuff is just playing - */ - -static int bttv_open(struct video_device *dev, int flags) -{ - struct bttv *btv = (struct bttv *)dev; - int users, i; - - 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; - - btv->user++; - MOD_INC_USE_COUNT; - return 0; -} - -static void bttv_close(struct video_device *dev) -{ - struct bttv *btv=(struct bttv *)dev; - - btv->user--; - audio(btv, AUDIO_INTERN); - btv->cap&=~3; - bt848_set_risc_jmps(btv); - - /* - * A word of warning. At this point the chip - * is still capturing because its FIFO hasn't emptied - * and the DMA control operations are posted PCI - * operations. - */ - - btread(BT848_I2C); /* This fixes the PCI posting delay */ - - /* - * This is sucky but right now I can't find a good way to - * 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); - btv->fbuffer=0; - MOD_DEC_USE_COUNT; -} - - -/***********************************/ -/* ioctls and supporting functions */ -/***********************************/ - -extern inline void bt848_bright(struct bttv *btv, uint bright) -{ - btwrite(bright&0xff, BT848_BRIGHT); -} - -extern inline void bt848_hue(struct bttv *btv, uint hue) -{ - btwrite(hue&0xff, BT848_HUE); -} - -extern inline void bt848_contrast(struct bttv *btv, uint cont) -{ - unsigned int conthi; - - conthi=(cont>>6)&4; - btwrite(cont&0xff, BT848_CONTRAST_LO); - btaor(conthi, ~4, BT848_E_CONTROL); - btaor(conthi, ~4, BT848_O_CONTROL); -} - -extern inline void bt848_sat_u(struct bttv *btv, unsigned long data) -{ - u32 datahi; - - datahi=(data>>7)&2; - btwrite(data&0xff, BT848_SAT_U_LO); - btaor(datahi, ~2, BT848_E_CONTROL); - btaor(datahi, ~2, BT848_O_CONTROL); -} - -static inline void bt848_sat_v(struct bttv *btv, unsigned long data) -{ - u32 datahi; - - datahi=(data>>8)&1; - btwrite(data&0xff, BT848_SAT_V_LO); - btaor(datahi, ~1, BT848_E_CONTROL); - btaor(datahi, ~1, BT848_O_CONTROL); -} - -/* - * ioctl routine - */ - - -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); - - 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; - bt848_muxsel(btv, v.channel); - if(v.norm!=VIDEO_MODE_PAL&&v.norm!=VIDEO_MODE_NTSC - &&v.norm!=VIDEO_MODE_SECAM) - return -EOPNOTSUPP; - btv->win.norm = v.norm; - 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; - - 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; - } - 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; - - 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(btv->win.height>btv->win.cropheight/2) - btv->win.interlace=1; - else - btv->win.interlace=0; - - on=(btv->cap&3); - - bt848_cap(btv,0); - bt848_set_winsize(btv); - - /* - * 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) - 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); - } - 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 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; - - 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; - - case BTTV_READEE: -#if LINUX_VERSION_CODE >= 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; - - 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; - - 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); - } - - 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 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 BTTV_BURST_ON: - { - tvnorms[0].scaledtwidth=1135-BURSTOFFSET-2; - tvnorms[0].hdelayx1=186-BURSTOFFSET; - return 0; - } - - case BTTV_BURST_OFF: - { - tvnorms[0].scaledtwidth=1135; - tvnorms[0].hdelayx1=186; - return 0; - } - - case BTTV_VERSION: - { - return BTTV_VERSION_CODE; - } - - case BTTV_PICNR: - { - /* return picture;*/ - return 0; - } - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static int bttv_init_done(struct video_device *dev) -{ - return 0; -} - -/* - * This maps the vmalloced and reserved fbuffer to user space. - * - * FIXME: - * - PAGE_READONLY should suffice!? - * - 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) -{ - struct bttv *btv=(struct bttv *)dev; - unsigned long start=(unsigned long) adr; - 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; -} - -static struct video_device bttv_template= -{ - "UNSET", - VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT, - VID_HARDWARE_BT848, - bttv_open, - bttv_close, - bttv_read, - bttv_write, -#if LINUX_VERSION_CODE >= 0x020100 - NULL, /* poll */ -#endif - bttv_ioctl, - bttv_mmap, - bttv_init_done, - NULL, - 0, - -1 -}; - - -static long vbi_read(struct video_device *v, char *buf, unsigned long count, - int nonblock) -{ - struct bttv *btv=(struct bttv *)(v-2); - int q,todo; - - todo=count; - while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) - { - if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q)) - return -EFAULT; - todo-=q; - buf+=q; - - cli(); - if (todo && q==VBIBUF_SIZE-btv->vbip) - { - if(nonblock) - { - sti(); - if(count==todo) - return -EWOULDBLOCK; - return count-todo; - } - interruptible_sleep_on(&btv->vbiq); - sti(); - if(signal_pending(current)) - { - if(todo==count) - return -EINTR; - else - return count-todo; - } - } - } - if (todo) - { - if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo)) - return -EFAULT; - btv->vbip+=todo; - } - return count; -} - -#if LINUX_VERSION_CODE >= 0x020100 -static unsigned int vbi_poll(struct video_device *dev, struct file *file, - poll_table *wait) -{ - struct bttv *btv=(struct bttv *)(dev-2); - unsigned int mask = 0; - - poll_wait(file, &btv->vbiq, wait); - - if (btv->vbip < VBIBUF_SIZE) - mask |= (POLLIN | POLLRDNORM); - - return mask; -} -#endif - -static int vbi_open(struct video_device *dev, int flags) -{ - struct bttv *btv=(struct bttv *)(dev-2); - - btv->vbip=VBIBUF_SIZE; - btv->cap|=0x0c; - bt848_set_risc_jmps(btv); - - MOD_INC_USE_COUNT; - return 0; -} - -static void vbi_close(struct video_device *dev) -{ - struct bttv *btv=(struct bttv *)(dev-2); - - btv->cap&=~0x0c; - bt848_set_risc_jmps(btv); - - MOD_DEC_USE_COUNT; -} - - -static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) -{ - return -EINVAL; -} - -static struct video_device vbi_template= -{ - "bttv vbi", - VID_TYPE_CAPTURE|VID_TYPE_TELETEXT, - VID_HARDWARE_BT848, - vbi_open, - vbi_close, - vbi_read, - bttv_write, -#if LINUX_VERSION_CODE >= 0x020100 - vbi_poll, -#endif - vbi_ioctl, - NULL, /* no mmap yet */ - bttv_init_done, - NULL, - 0, - -1 -}; - - -static int radio_open(struct video_device *dev, int flags) -{ - struct bttv *btv = (struct bttv *)(dev-1); - - if (btv->user) - return -EBUSY; - btv->user++; - set_freq(btv,400*16); - btv->radio = 1; - bt848_muxsel(btv,0); - audio(btv, AUDIO_UNMUTE); - - MOD_INC_USE_COUNT; - return 0; -} - -static void radio_close(struct video_device *dev) -{ - struct bttv *btv=(struct bttv *)(dev-1); - - btv->user--; - btv->radio = 0; - /*audio(btv, AUDIO_MUTE);*/ - MOD_DEC_USE_COUNT; -} - -static long radio_read(struct video_device *v, char *buf, unsigned long count, int nonblock) -{ - return -EINVAL; -} - -static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg) -{ - struct bttv *btv=(struct bttv *)(dev-1); - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability v; - strcpy(v.name,btv->video_dev.name); - v.type = VID_TYPE_TUNER; - v.channels = 1; - v.audios = 1; - /* No we don't do pictures */ - v.maxwidth = 0; - v.maxheight = 0; - v.minwidth = 0; - v.minheight = 0; - if (copy_to_user(arg, &v, sizeof(v))) - return -EFAULT; - return 0; - break; - } - 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, "Radio"); - v.rangelow=(int)(87.5*16); - v.rangehigh=(int)(108*16); - v.flags= 0; /* XXX */ - v.mode = 0; /* XXX */ - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner v; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - /* Only channel 0 has a tuner */ - if(v.tuner!=0 || btv->channel) - return -EINVAL; - /* XXX anything to do ??? */ - return 0; - } - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - bttv_ioctl((struct video_device *)btv,cmd,arg); - break; - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static struct video_device radio_template= -{ - "bttv radio", - VID_TYPE_TUNER, - VID_HARDWARE_BT848, - radio_open, - 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 */ -} - -static void init_tda8425(struct i2c_bus *bus) -{ - 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 */ -} - -static void init_tda9840(struct i2c_bus *bus) -{ - I2CWrite(bus, I2C_TDA9840, TDA9840_SW, 0x2A, 1); /* Sound mode switching */ -} - -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); -} - - - -/* Figure out card and tuner type */ - -static void idcard(int i) -{ - struct bttv *btv = &bttvs[i]; - - 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 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 - btv->type=BTTV_HAUPPAUGE; - - } else if (I2CRead(&(btv->i2c), I2C_STBEE)>=0) { - btv->type=BTTV_STB; - } else { - if (I2CRead(&(btv->i2c), 0x80)>=0) /* check for msp34xx */ - btv->type = BTTV_MIROPRO; - else - btv->type=BTTV_MIRO; - } - } - - /* 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 ... */ - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - } - } - - if (btv->type == BTTV_PIXVIEWPLAYTV) { - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - } - - if(btv->type==BTTV_AVERMEDIA98) - { - btv->pll.pll_ifreq=28636363; - btv->pll.pll_crystal=BT848_IFORM_XT0; - } - - 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 (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; - } - printk("%s\n",btv->video_dev.name); - audio(btv, AUDIO_INTERN); -} - - - -static void bt848_set_risc_jmps(struct bttv *btv) -{ - int flags=btv->cap; - - /* 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; - - /* Jump to odd vbi sub */ - btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0x5<<20)); - if (flags&8) - btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->vbi_odd)); - else - 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[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; - - /* Jump to even vbi sub */ - btv->risc_jmp[8]=cpu_to_le32(BT848_RISC_JUMP); - if (flags&4) - btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->vbi_even)); - else - 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 - 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 */ - btaor(flags, ~0x0f, BT848_CAP_CTL); - if (flags&0x0f) - bt848_dma(btv, 3); - else - bt848_dma(btv, 0); -} - -static int init_bt848(int i) -{ - struct bttv *btv = &bttvs[i]; - - btv->user=0; - - /* reset the bt848 */ - btwrite(0, BT848_SRESET); - DEBUG(printk(KERN_DEBUG "bttv%d: bt848_mem: 0x%lx\n",i,(unsigned long) btv->bt848_mem)); - - /* 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; - btv->win.x=0; - 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->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; - - /* i2c */ - memcpy(&(btv->i2c),&bttv_i2c_bus_template,sizeof(struct i2c_bus)); - sprintf(btv->i2c.name,"bt848-%d",i); - btv->i2c.data = btv; - - if (!(btv->risc_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) - return -1; - if (!(btv->risc_even=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL))) - return -1; - if (!(btv->risc_jmp =(unsigned int *) kmalloc(2048, GFP_KERNEL))) - return -1; - DEBUG(printk(KERN_DEBUG "risc_jmp: %p\n",btv->risc_jmp)); - btv->vbi_odd=btv->risc_jmp+16; - btv->vbi_even=btv->vbi_odd+256; - btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp+12); - btv->bus_vbi_even=virt_to_bus(btv->risc_jmp+6); - - btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD); - btv->vbibuf=(unsigned char *) vmalloc(VBIBUF_SIZE); - if (!btv->vbibuf) - return -1; - if (!(btv->grisc=(unsigned int *) kmalloc(32768, GFP_KERNEL))) - return -1; - - memset(btv->vbibuf, 0, VBIBUF_SIZE); /* We don't want to return random - memory to the user */ - - btv->fbuffer=NULL; - - bt848_muxsel(btv, 1); - bt848_set_winsize(btv); - -/* btwrite(0, BT848_TDEC); */ - btwrite(0x10, BT848_COLOR_CTL); - btwrite(0x00, BT848_CAP_CTL); - btwrite(0xac, BT848_GPIO_DMA_CTL); - - /* select direct input */ - btwrite(0x00, BT848_GPIO_REG_INP); - - btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_PAL_BDGHI, - BT848_IFORM); - - btwrite(0xd8, BT848_CONTRAST_LO); - bt848_bright(btv, 0x10); - - btwrite(0x20, BT848_E_VSCALE_HI); - btwrite(0x20, BT848_O_VSCALE_HI); - btwrite(/*BT848_ADC_SYNC_T|*/ - BT848_ADC_RESERVED|BT848_ADC_CRUSH, BT848_ADC); - - btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL); - btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL); - - btv->picture.colour=254<<7; - btv->picture.brightness=128<<8; - btv->picture.hue=128<<8; - btv->picture.contrast=0xd8<<7; - - btwrite(0x00, BT848_E_SCLOOP); - btwrite(0x00, BT848_O_SCLOOP); - - /* clear interrupt status */ - btwrite(0xfffffUL, BT848_INT_STAT); - - /* set interrupt mask */ - btwrite(btv->triton1| - /*BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR| - BT848_INT_FDSR|BT848_INT_FTRGT|BT848_INT_FBUS|*/ - BT848_INT_VSYNC| - 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); - - /* - * 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); - - return 0; -} - -static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) -{ - u32 stat,astat; - u32 dstat; - int count; - struct bttv *btv; - - btv=(struct bttv *)dev_id; - count=0; - while (1) - { - /* get/clear interrupt status bits */ - stat=btread(BT848_INT_STAT); - astat=stat&btread(BT848_INT_MASK); - if (!astat) - return; - btwrite(astat,BT848_INT_STAT); - IDEBUG(printk ("bttv%d: astat %08x stat %08x\n", btv->nr, astat, stat)); - - /* get device status bits */ - dstat=btread(BT848_DSTATUS); - - if (astat&BT848_INT_FMTCHG) - { - IDEBUG(printk ("bttv%d: IRQ_FMTCHG\n", btv->nr)); - /*btv->win.norm&= - (dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */ - } - if (astat&BT848_INT_VPRES) - { - IDEBUG(printk ("bttv%d: IRQ_VPRES\n", btv->nr)); - } - if (astat&BT848_INT_VSYNC) - { - 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_RISCI) - { - IDEBUG(printk ("bttv%d: IRQ_RISCI\n", btv->nr)); - - /* captured VBI frame */ - if (stat&(1<<28)) - { - btv->vbip=0; - /* inc vbi frame count for detecting drops */ - (*(u32 *)&(btv->vbibuf[VBIBUF_SIZE - 4]))++; - wake_up_interruptible(&btv->vbiq); - } - - /* captured full frame */ - if (stat&(2<<28)) - { - wake_up_interruptible(&btv->capq); - btv->last_field=btv->field; - btv->grab++; - btv->frame_stat[btv->grf] = GBUFFER_DONE; - if ((--btv->grabbing)) - { - 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); - } 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); - } - 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); - } - } - if (astat&BT848_INT_OCERR) - { - IDEBUG(printk ("bttv%d: IRQ_OCERR\n", btv->nr)); - } - if (astat&BT848_INT_PABORT) - { - IDEBUG(printk ("bttv%d: IRQ_PABORT\n", btv->nr)); - } - if (astat&BT848_INT_RIPERR) - { - IDEBUG(printk ("bttv%d: IRQ_RIPERR\n", btv->nr)); - } - if (astat&BT848_INT_PPERR) - { - IDEBUG(printk ("bttv%d: IRQ_PPERR\n", btv->nr)); - } - if (astat&BT848_INT_FDSR) - { - IDEBUG(printk ("bttv%d: IRQ_FDSR\n", btv->nr)); - } - if (astat&BT848_INT_FTRGT) - { - IDEBUG(printk ("bttv%d: IRQ_FTRGT\n", btv->nr)); - } - if (astat&BT848_INT_FBUS) - { - IDEBUG(printk ("bttv%d: IRQ_FBUS\n", btv->nr)); - } - if (astat&BT848_INT_HLOCK) - { - if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio)) - audio(btv, AUDIO_ON); - else - audio(btv, AUDIO_OFF); - } - - if (astat&BT848_INT_I2CDONE) - { - } - - count++; - if (count > 10) - printk (KERN_WARNING "bttv%d: irq loop %d\n", - btv->nr,count); - if (count > 20) - { - btwrite(0, BT848_INT_MASK); - printk(KERN_ERR - "bttv%d: IRQ lockup, cleared int mask\n", btv->nr); - } - } -} - - - -/* - * 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; - - btv=&bttvs[bttv_num]; - btv->dev=dev; - btv->nr = bttv_num; - 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; - - btv->id=dev->device; - btv->irq=dev->irq; - btv->bt848_adr=dev->base_address[0]; - if (btv->id >= 878) - btv->i2c_command = 0x83; - else - btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA); - - if (remap[bttv_num]) - { - unsigned int dw = btv->bt848_adr; - - 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%lx.\n", - bttv_num,remap[bttv_num]); - 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->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) ", - bttv_num,btv->id, btv->revision); - printk("bus: %d, devfn: %d, ",dev->bus->number, dev->devfn); - 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; - } - } - -#ifdef __sparc__ - btv->bt848_mem=(unsigned char *)btv->bt848_adr; -#else - btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); -#endif - - /* clear interrupt mask */ - btwrite(0, BT848_INT_MASK); - - 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; - - pci_set_master(dev); - - btv->triton1=triton1 ? BT848_INT_ETBF : 0; - if (triton1 && btv->id >= 878) - { - btv->triton1 = 0; - printk("bttv: Enabling 430FX compatibilty for bt878\n"); - pci_read_config_byte(dev, BT878_DEVCTRL, &command); - command|=BT878_EN_TBFX; - pci_write_config_byte(dev, BT878_DEVCTRL, command); - pci_read_config_byte(dev, BT878_DEVCTRL, &command); - if (!(command&BT878_EN_TBFX)) - { - printk("bttv: 430FX compatibility could not be enabled\n"); - return -1; - } - } - - return 0; -} - -static int find_bt848(void) -{ - struct pci_dev *dev = pci_devices; - int result=0; - - bttv_num=0; - - while (dev) - { - if (dev->vendor == PCI_VENDOR_ID_BROOKTREE) - if ((dev->device == PCI_DEVICE_ID_BT848)|| - (dev->device == PCI_DEVICE_ID_BT849)|| - (dev->device == PCI_DEVICE_ID_BT878)|| - (dev->device == PCI_DEVICE_ID_BT879)) - 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; - struct bttv *btv; - - for (i=0;ii2c)); - - /* 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); - - if (btv->risc_odd) - kfree((void *) btv->risc_odd); - - if (btv->risc_even) - kfree((void *) btv->risc_even); - - DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%p.\n", btv->risc_jmp)); - if (btv->risc_jmp) - kfree((void *) btv->risc_jmp); - - DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%p.\n", btv->vbibuf)); - if (btv->vbibuf) - vfree((void *) btv->vbibuf); - - - free_irq(btv->irq,btv); - DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%p.\n", btv->bt848_mem)); - if (btv->bt848_mem) - iounmap(btv->bt848_mem); - - if(btv->video_dev.minor!=-1) - video_unregister_device(&btv->video_dev); - if(btv->vbi_dev.minor!=-1) - video_unregister_device(&btv->vbi_dev); - if (radio[btv->nr] && btv->radio_dev.minor != -1) - video_unregister_device(&btv->radio_dev); - } -} - -#ifdef MODULE - -EXPORT_NO_SYMBOLS; - -int init_module(void) -{ -#else -int init_bttv_cards(struct video_init *unused) -{ -#endif - int i; - - handle_chipset(); - if (find_bt848()<0) - return -EIO; - - /* initialize Bt848s */ - for (i=0; idriver_data) +# define PCI_SET_DRIVER_DATA(pdev,data) (((pdev)->driver_data) = (data)) +#endif /* PCI_GET_DRIVER_DATA */ #include #include +#include +#include +#include -#include -#include "msp3400.h" +#include "audiochip.h" #include "bt848.h" -#include + +#ifdef __KERNEL__ + +/* fwd decl */ +struct bttv; + + +/* ---------------------------------------------------------- */ +/* exported by bttv-cards.c */ + +#define BTTV_UNKNOWN 0x00 +#define BTTV_MIRO 0x01 +#define BTTV_HAUPPAUGE 0x02 +#define BTTV_STB 0x03 +#define BTTV_INTEL 0x04 +#define BTTV_DIAMOND 0x05 +#define BTTV_AVERMEDIA 0x06 +#define BTTV_MATRIX_VISION 0x07 +#define BTTV_FLYVIDEO 0x08 +#define BTTV_TURBOTV 0x09 +#define BTTV_HAUPPAUGE878 0x0a +#define BTTV_MIROPRO 0x0b +#define BTTV_ADSTECH_TV 0x0c +#define BTTV_AVERMEDIA98 0x0d +#define BTTV_VHX 0x0e +#define BTTV_ZOLTRIX 0x0f +#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 BTTV_AVPHONE98 0x29 +#define BTTV_PV951 0x2a +#define BTTV_ONAIR_TV 0x2b +#define BTTV_SIGMA_TVII_FM 0x2c +#define BTTV_MATRIX_VISION2 0x2d +#define BTTV_ZOLTRIX_GENIE 0x2e +#define BTTV_TERRATVRADIO 0x2f + +struct tvcard +{ + char *name; + int video_inputs; + int audio_inputs; + int tuner; + int svhs; + u32 gpiomask; + u32 muxsel[8]; + 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; +#define PLL_NONE 0 +#define PLL_28 1 +#define PLL_35 2 + + int tuner_type; +}; + +extern struct tvcard bttv_tvcards[]; +extern const int bttv_num_tvcards; + +/* identification / initialization of the card */ +extern void bttv_idcard(struct bttv *btv); + +/* card-specific funtions */ +extern void tea5757_set_freq(struct bttv *btv, unsigned short freq); + + +/* ---------------------------------------------------------- */ +/* exported by bttv-if.c */ +/* interface for gpio access by other modules */ + +/* returns card type + card ID (for bt878-based ones) + for possible values see lines below beginning with #define BTTV_UNKNOWN + returns negative value if error ocurred +*/ +extern int bttv_get_cardinfo(unsigned int card, int *type, int *cardid); + +/* obsolete, use bttv_get_cardinfo instead */ +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_head_t* bttv_get_gpio_queue(unsigned int card); + +/* i2c */ +struct i2c_algo_bit_data bttv_i2c_algo_template; +struct i2c_adapter bttv_i2c_adap_template; +struct i2c_client bttv_i2c_client_template; +void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg); +int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for); +int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, + unsigned char b2, int both); +void bttv_readee(struct bttv *btv, unsigned char *eedata, int addr); + + +/* ---------------------------------------------------------- */ +/* bttv-driver.c */ + +/* insmod options */ +extern unsigned int bttv_verbose; +extern unsigned int bttv_debug; + +/* Anybody who uses more than four? */ +#define BTTV_MAX 4 +extern int bttv_num; /* number of Bt848s in use */ +extern struct bttv bttvs[BTTV_MAX]; + #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 - -#ifdef __KERNEL__ +#define BTTV_MAX_FBUF 0x208000 +#define I2C_CLIENTS_MAX 8 struct bttv_window { @@ -50,8 +225,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 +240,62 @@ 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 */ + spinlock_t s_lock; + 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 cardid; 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 +303,22 @@ 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_head_t vbiq; + wait_queue_head_t capq; 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,gq_start; char *fbuffer; - int gmode; - int grabbing; - int lastgrab; - int grab; - int grabcount; struct bttv_pll_info pll; unsigned int Fsc; @@ -163,15 +326,25 @@ unsigned int last_field; /* number of last grabbed field */ int i2c_command; int triton1; + + int errors; + int needs_restart; + + wait_queue_head_t 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,27 +364,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_UNKNOWN 0x00 -#define BTTV_MIRO 0x01 -#define BTTV_HAUPPAUGE 0x02 -#define BTTV_STB 0x03 -#define BTTV_INTEL 0x04 -#define BTTV_DIAMOND 0x05 -#define BTTV_AVERMEDIA 0x06 -#define BTTV_MATRIX_VISION 0x07 -#define BTTV_FLYVIDEO 0x08 -#define BTTV_TURBOTV 0x09 -#define BTTV_HAUPPAUGE878 0x0a -#define BTTV_MIROPRO 0x0b -#define BTTV_ADSTECH_TV 0x0c -#define BTTV_AVERMEDIA98 0x0d -#define BTTV_VHX 0x0e -#define BTTV_ZOLTRIX 0x0f -#define BTTV_PIXVIEWPLAYTV 0x10 -#define BTTV_WINVIEW_601 0x11 -#define BTTV_AVEC_INTERCAP 0x12 +#define BTTV_VBISIZE _IOR('v' , BASE_VIDIOCPRIVATE+8, int) #define AUDIO_TUNER 0x00 #define AUDIO_RADIO 0x01 @@ -223,61 +376,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 /* also used by 9855,9873 */ +#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 @@ -287,4 +406,19 @@ #define WINVIEW_PT2254_DATA 0x20 #define WINVIEW_PT2254_STROBE 0x80 +struct bttv_just_hacking { + int height,width; /* size */ + unsigned int format; /* should be VIDEO_PALETTE_* */ + long buf; + int len; +}; + +#define BTTV_JUST_HACKING _IOR('v' , BASE_VIDIOCPRIVATE+31,struct bttv_just_hacking) + #endif + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN linux-2.2.16.SuSE/drivers/char/dpl3518.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/dpl3518.c --- linux-2.2.16.SuSE/drivers/char/dpl3518.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/dpl3518.c Sun Jun 18 12:40:44 2000 @@ -0,0 +1,415 @@ +/* + * programming the dpl3518a Dolby Pro Logic Processor + * + * WARNING: THIS DRIVER WILL LOAD WITHOUT COMPLAINTS EVEN IF A DIFFERENT + * CHIP IS AT ADDRESS 0x84 (it relies on i2c to make sure that there is a + * device acknowledging that address) + * + * Copyright (C) 1999 Roland Jansen + * + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + * + * --- + * DOLBY and PRO LOGIC are trademarks of + * Dolby Laboratories Licensing Corporation. + */ + +/* FIXME */ +#define DPL_MAJOR 127 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +#include +#ifdef __SMP__ +#include +#include +#endif +#include +#endif + +#include "dpl3518.h" + +/* Addresses to scan */ +#define I2C_DPL3518 0x84 +static unsigned short normal_i2c[] = { + I2C_DPL3518 >> 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 +}; + +struct dpl3518 { + struct i2c_client *client; + int mode; /* dolby mode */ +}; + +static struct dpl3518 *dpl_device; +static struct i2c_driver driver; +static struct i2c_client client_template; + +/* ---------------------------------------------------------------------- */ + +/* insmod parameters */ +static int debug = 0; /* debug output */ +MODULE_PARM(debug, "i"); + +#define dprintk if (debug) printk + +/* ---------------------------------------------------------------------- */ + +#define I2C_DPL3518_MR 0x10 /* write address MODE_REG */ +#define I2C_DPL3518_DFP 0x12 /* write address DFP */ + +/* ----------------------------------------------------------------------- */ +/* functions for talking to the dpl3518 Sound processor */ + +static int dpl3518_reset(struct i2c_client *client) +{ + static char reset_off[3] = { 0x00, 0x80, 0x00 }; + static char reset_on[3] = { 0x00, 0x00, 0x00 }; + + i2c_master_send(client,reset_off,3); /* ignore errors here */ + if (3 != i2c_master_send(client,reset_on, 3)) { + printk(KERN_ERR "dpl3518: chip reset failed, penguin on i2c bus?\n"); + return -1; + } + return 0; +} + +static int dpl3518_write(struct i2c_client *client, int dev, int addr, int val) +{ + 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;) { + if (5 == i2c_master_send(client, buffer, 5)) + break; + err++; + printk(KERN_WARNING "dpl3518: I/O error #%d (write 0x%02x/0x%02x)\n", + err, dev, addr); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + } + if (3 == err) { + printk(KERN_WARNING "dpl3518: giving up, reseting chip. Sound will go off, sorry folks :-|\n"); + dpl3518_reset(client); + return -1; + } + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* in: 0..100 out: 0x00..0x7f */ +static int p100_to_7f(int i) +{ + int r; + + r = ((i & 0x7f) * 127 + 1) / 100; + if (r > 127) + r = 127; + if (r < 0) + r = 0; + return r; +} + +/* ----------------------------------------------------------------------- */ + +void dpl3518_ioc_init(struct i2c_client *client) +{ + dprintk("dpl3518 init\n"); + dpl3518_write(client, I2C_DPL3518_MR, 0x0083, 0x0008); /* mode_reg */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x000b, 0x0320); /*I2S1 src: RL */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0012, 0x0000); /*prescale2: off */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0016, 0x1000); /*prescale: 0 dB */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0038, 0x0420); /*I2S2 src: CS */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0040, 0x0000); /*adaptive,surround */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0041, 0x0520); /*i2s1, stereo */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0042, 0x1400); /*delay: 20ms */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0043, 0x0000); /*inp. Bal. auto */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0044, 0x4000); /*spatial 50% */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0045, 0x0000); /*panorama off */ + //dpl3518_write(client,I2C_DPL3518_DFP,0x0045,0x5400); /*panorama 66%*/ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0046, 0x0000); /*reverb off */ +} + +void dpl3518_prologic_init(struct i2c_client *client) +{ + dprintk("dpl3518 prologic_init\n"); + dpl3518_write(client, I2C_DPL3518_DFP, 0x000b, 0x0320); /*I2S1 src: RL */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0038, 0x0420); /*I2S2 src: CS */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0041, 0x0520); /*i2s1, stereo */ +} + +void dpl3518_noise_init(struct i2c_client *client) +{ + dprintk("dpl3518 noise_init\n"); + dpl3518_write(client, I2C_DPL3518_DFP, 0x000b, 0x0320); /*I2S1 src: RL */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0038, 0x0420); /*I2S2 src: CS */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0041, 0x0100); /*NOISE */ +} + +void dpl3518_through_init(struct i2c_client *client) +{ + dprintk("dpl3518 through_init\n"); + dpl3518_write(client, I2C_DPL3518_DFP, 0x000b, 0x0520); /*I2S1 src: S1 */ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0038, 0x0220); /*I2S2 src: scart*/ + dpl3518_write(client, I2C_DPL3518_DFP, 0x0041, 0x0520); /*i2s1, stereo */ +} + +void dpl3518_setmode(struct i2c_client *client, int mode) +{ + dprintk("dpl3518 setmode: %i\n", mode); + dpl3518_write(client, I2C_DPL3518_DFP, 0x0040, mode << 4); + dpl_device->mode = mode; +} + +void dpl3518_setnoisemode(struct i2c_client *client, int mode) +{ + static int noise_modes[5] = { 0x0000, 0x01a0, 0x01b0, 0x01c0, 0x01d0 }; + dprintk("dpl3518 setnoisemode: %i\n", mode); + dpl3518_write(client, I2C_DPL3518_DFP, 0x0041, noise_modes[mode]); +} + + +void dpl3518_delay(struct i2c_client *client, int delay) +{ + dprintk("dpl3518 setdelay: %i\n", delay); + dpl3518_write(client, I2C_DPL3518_DFP, 0x0042, delay << 8); +} + +void dpl3518_reverb(struct i2c_client *client, int rev) +{ + dprintk("dpl3518 setreverb: %i\n", rev); + dpl3518_write(client, I2C_DPL3518_DFP, 0x0046, p100_to_7f(rev) << 8); +} + +void dpl3518_panorama(struct i2c_client *client, int pan) +{ + dprintk("dpl3518 panorama: %i\n", pan); + dpl3518_write(client, I2C_DPL3518_DFP, 0x0045, p100_to_7f(pan) << 8); +} + +void dpl3518_spatial(struct i2c_client *client, int spatial) +{ + dprintk("dpl3518 setspatial: %i\n", spatial); + dpl3518_write(client, I2C_DPL3518_DFP, 0x0045, p100_to_7f(spatial) << 8); +} + +/* ----------------------------------------------------------------------- */ + +static int dpl3518_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct dpl3518 *dpl; + 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 = dpl = kmalloc(sizeof *dpl,GFP_KERNEL); + if (NULL == dpl) + return -ENOMEM; + memset(dpl, 0, sizeof(struct dpl3518)); + dpl->mode = 0; /* DOLBY_MODE_NONE */ + dpl->client = client; + + if (-1 == dpl3518_reset(client)) { + kfree(dpl); + kfree(client); + dprintk("dpl3518: no chip found\n"); + return -1; + } + + strcpy(client->name, "DPL3518"); + printk(KERN_INFO "dpl3518: init\n"); + + dpl_device = dpl; + dpl3518_ioc_init(client); + + /* done */ + i2c_attach_client(client); + return 0; +} + +static int dpl3518_probe(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, dpl3518_attach); +} + +static int dpl3518_detach(struct i2c_client *client) +{ + struct dpl3518 *dpl = client->data; + + dpl3518_reset(client); + i2c_detach_client(client); + + dpl_device = NULL; + kfree(dpl); + kfree(client); + return 0; +} + +static int dpl3518_dev_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ +// unsigned int minor = MINOR(inode->i_rdev); + int ret = 0; + struct i2c_client *client = dpl_device->client; + + switch (cmd) { + case DPL_IOC_RESET: + dpl3518_reset(client); + break; + case DPL_IOC_VERSION: + if (put_user(DPL_VERSION_CODE, (int *) arg)) + ret = -EFAULT; + break; + case DPL_IOC_INIT: + dpl3518_ioc_init(client); + break; + case DPL_IOC_PROLOGIC_INIT: + dpl3518_prologic_init(client); + break; + case DPL_IOC_NOISE_INIT: + dpl3518_noise_init(client); + break; + case DPL_IOC_THROUGH_INIT: + dpl3518_through_init(client); + break; + case DPL_IOC_MODE: + dpl3518_setmode(client, (int) arg); + break; + case DPL_IOC_GET_MODE: + if (put_user(dpl_device->mode, (int *) arg)) + ret = -EFAULT; + break; + case DPL_IOC_NOISE_MODE: + dpl3518_setnoisemode(client, (int) arg); + break; + case DPL_IOC_DELAY: + dpl3518_delay(client, (int) arg); + break; + case DPL_IOC_REVERB: + dpl3518_reverb(client, (int) arg); + break; + case DPL_IOC_PANORAMA: + dpl3518_panorama(client, (int) arg); + break; + case DPL_IOC_SPATIAL: + dpl3518_spatial(client, (int) arg); + break; + default: + dprintk("dpl3518_ioctl: default %i\n", (int) arg); + ret = -EINVAL; + } + return ret; +} + +static int dpl3518_dev_open(struct inode *inode, struct file *file) +{ + dprintk("dpl3518_dev_open\n"); + MOD_INC_USE_COUNT; + return 0; +} + +static int dpl3518_dev_release(struct inode *inode, struct file *file) +{ + dprintk("dpl3518_dev_release\n"); + MOD_DEC_USE_COUNT; + return 0; +} + +/* ---------------------------------------------------------------------- */ + +static struct i2c_driver driver = { + "i2c dpl3518 driver", + I2C_DRIVERID_EXP0, /* FIXME */ + I2C_DF_NOTIFY, + dpl3518_probe, + dpl3518_detach, + NULL, +}; + +static struct i2c_client client_template = +{ + "unset", + -1, + 0, + 0, + NULL, + &driver +}; + +static struct file_operations dpl3518_fops = +{ + ioctl: dpl3518_dev_ioctl, + open: dpl3518_dev_open, + release: dpl3518_dev_release, +}; + + +#ifdef MODULE +int init_module(void) +#else +int dpl3518_init(void) +#endif +{ + if (register_chrdev(DPL_MAJOR, "dpl3518", &dpl3518_fops)) { + printk("dpl3518: unable to get major %d\n", DPL_MAJOR); + return -EBUSY; + } + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); + unregister_chrdev(DPL_MAJOR, "dpl3518"); +} + +#endif diff -urN linux-2.2.16.SuSE/drivers/char/dpl3518.h linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/dpl3518.h --- linux-2.2.16.SuSE/drivers/char/dpl3518.h Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/dpl3518.h Sun Jun 25 18:46:03 2000 @@ -0,0 +1,44 @@ +#ifndef DPL3518_H +#define DPL3518_H + +#include + +#define DPL_VERSION_CODE 0x0002 /* Version 0.2 */ + + +/* IOCTL numbers */ + +/* Reset function (just in case...) */ +#define DPL_IOC_RESET _IO('d',1) + +/* Get driver version */ +#define DPL_IOC_VERSION _IOR('d',2,int) + +/* Dolby Pro Logic modes */ +#define DPL_IOC_INIT _IO('d',3) /* Must be done first */ + +#define DPL_IOC_PROLOGIC_INIT _IO('d',4) +#define DPL_IOC_NOISE_INIT _IO('d',5) +#define DPL_IOC_THROUGH_INIT _IO('d',6) + +#define DPL_IOC_MODE _IOW('d',7,int) /* 0 = Normal */ + /* 1 = Phantom */ + /* 2 = Wide */ + /* 3 = Dolby 3 Stereo */ + /* 4 = Center Off */ + /* 5 = Panorama */ + /* 6 = 3D-Panorama (virtual dolby) */ +#define DPL_IOC_GET_MODE _IOR('d',7,int) + +#define DPL_IOC_NOISE_MODE _IOW('d',8,int) +#define DPL_IOC_GET_NOISE_MODE _IOR('d',8,int) + +#define DPL_IOC_DELAY _IOW('d',9,int) /* 0 .. 15 */ +#define DPL_IOC_REVERB _IOW('d',10,int) /* 0 .. 100 % */ +#define DPL_IOC_PANORAMA _IOW('d',11,int) /* 0 .. 100 % */ +#define DPL_IOC_SPATIAL _IOW('d',12,int) /* 0 .. 100 % */ + + +/* ---------------------------------------------------------------------- */ + +#endif /* DPL3518_H */ diff -urN linux-2.2.16.SuSE/drivers/char/ir.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/ir.c --- linux-2.2.16.SuSE/drivers/char/ir.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/ir.c Sun Jun 18 12:41:48 2000 @@ -0,0 +1,512 @@ +/* + * hauppauge IR driver + * (c) 2000 Gerd Knorr + * + * parts are cut&pasted from lirc_haup.c + * + * this driver is useful only with the lirc package, see + * http://fsinfo.cs.uni-sb.de/~columbus/lirc/ + * + * it will be moved back to the lirc package in near future. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __SMP__ +#include +#include +#endif +/* kernel_thread */ +#define __KERNEL_SYSCALLS__ +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +# include "kcompat24.h" +#endif +#include "lirc.h" + +/* Addresses to scan */ +static unsigned short normal_i2c[] = {I2C_CLIENT_END}; +static unsigned short normal_i2c_range[] = {0x18,0x1a,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 +}; + +#define BUFLEN 256 +#define IR_MAX 4 + +struct IR { + struct task_struct *thread; + struct semaphore *notify; + int rmmod; + int open; + + struct semaphore lock; + wait_queue_head_t wait_poll; + unsigned short buffer[BUFLEN]; + int head,tail; +}; + +static struct i2c_client *irs[IR_MAX]; + +/* ----------------------------------------------------------------------- */ +/* insmod parameters */ + +static int debug = 0; /* debug output */ + +MODULE_PARM(debug,"i"); + +#define dprintk if (debug) printk + +/* ----------------------------------------------------------------------- */ + +#define DEVICE_NAME "hauppauge ir rc" +#define CODE_LENGTH 13 +#define LIRC_MAJOR 61 + +/* + * If this key changes, a new key was pressed. + */ +#define REPEAT_TOGGLE_0 192 +#define REPEAT_TOGGLE_1 224 + +/* + * KEY SEQUENCES. + * Hauppauge uses these definitions internally in transmitting keypresses. + * BYTE 2 contains the last key pressed. + */ +#define REMOTE_0 0x00 +#define REMOTE_1 0x04 +#define REMOTE_2 0x08 +#define REMOTE_3 0x0c +#define REMOTE_4 0x10 +#define REMOTE_5 0x14 +#define REMOTE_6 0x18 +#define REMOTE_7 0x1c +#define REMOTE_8 0x20 +#define REMOTE_9 0x24 +#define REMOTE_RADIO 0x30 +#define REMOTE_MUTE 0x34 +#define REMOTE_TV 0x3c +#define REMOTE_VOL_PLUS 0x40 +#define REMOTE_VOL_MINUS 0x44 +#define REMOTE_RESERVED 0x78 +#define REMOTE_CHAN_PLUS 0x80 +#define REMOTE_CHAN_MINUS 0x84 +#define REMOTE_SOURCE 0x88 +#define REMOTE_MINIMIZE 0x98 +#define REMOTE_FULL_SCREEN 0xb8 + +/* ----------------------------------------------------------------------- */ + +static char * +keyname(unsigned char v) +{ + switch (v) { + case REMOTE_0: return "0"; + case REMOTE_1: return "1"; + case REMOTE_2: return "2"; + case REMOTE_3: return "3"; + case REMOTE_4: return "4"; + case REMOTE_5: return "5"; + case REMOTE_6: return "6"; + case REMOTE_7: return "7"; + case REMOTE_8: return "8"; + case REMOTE_9: return "9"; + case REMOTE_RADIO: return "Radio"; + case REMOTE_MUTE: return "Mute"; + case REMOTE_TV: return "TV"; + case REMOTE_VOL_PLUS: return "Vol +"; + case REMOTE_VOL_MINUS: return "Vol -"; + case REMOTE_RESERVED: return "Reserved"; + case REMOTE_CHAN_PLUS: return "Chan +"; + case REMOTE_CHAN_MINUS: return "Chan -"; + case REMOTE_SOURCE: return "Source"; + case REMOTE_MINIMIZE: return "Minimize"; + case REMOTE_FULL_SCREEN: return "Full Screen"; + default: dprintk("Error, invalid key %d",v); + return ""; + } +} + +static int +ir_poll_thread(void *data) +{ + struct i2c_client *client = data; + struct IR *ir = client->data; + unsigned char b[3], last_b0 = 0; + __u16 repeat_bit, code; + +#ifdef __SMP__ + lock_kernel(); +#endif + + exit_mm(current); + current->session = 1; + current->pgrp = 1; + sigfillset(¤t->blocked); + current->fs->umask = 0; + strcpy(current->comm,"IR poll"); + + ir->thread = current; + +#ifdef __SMP__ + unlock_kernel(); +#endif + + printk("ir: thread started\n"); + if(ir->notify != NULL) + up(ir->notify); + + for (;;) { + if (ir->rmmod) + break; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + if (signal_pending(current)) + break; + + /* poll IR chip */ + if (3 != i2c_master_recv(client,b,3)) { + dprintk("ir: read error\n"); + continue; + } + + /* key pressed ? */ + if (b[0] != REPEAT_TOGGLE_0 && b[0] != REPEAT_TOGGLE_1) + continue; + + /* look what we have */ + dprintk("ir: key %s (0x%02x/0x%02x)\n", keyname(b[1]), b[0], b[1]); + if (b[0] == last_b0) { /* repeat */ + dprintk("Repeat\n"); + } else { + last_b0 = b[0]; + } + repeat_bit=(b[0]&0x20) ? 0x800:0; + code = (0x1000 | repeat_bit | (b[1]>>2)); + + /* put into fifo */ + down(&ir->lock); + if (((ir->tail+1)%BUFLEN) == ir->head) { + up(&ir->lock); + dprintk("ir: input buffer overflow\n"); + } else { + ir->buffer[ir->tail++]=code; + ir->tail%=BUFLEN; + up(&ir->lock); + wake_up_interruptible(&ir->wait_poll); + } + } + + dprintk("ir: thread done\n"); + ir->thread = NULL; + + if(ir->notify != NULL) + up(ir->notify); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static unsigned int lirc_haup_poll(struct file *file, + struct poll_table_struct * wait) +{ + struct i2c_client *client = file->private_data; + struct IR *ir = client->data; + + poll_wait(file, &ir->wait_poll, wait); + + if(ir->head != ir->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int lirc_haup_ioctl(struct inode *ino, struct file *fp, + unsigned int cmd, unsigned long arg) +{ + int result = 0; + unsigned long features = LIRC_CAN_REC_LIRCCODE; + unsigned long mode; + + switch (cmd) { + case LIRC_GET_FEATURES: + result = put_user(features,(unsigned long *) arg); + break; + case LIRC_SET_REC_MODE: + result = get_user(mode, (unsigned long *) arg); + if (result) { + break; + } + if ((LIRC_MODE2REC(mode) & features) == 0) { + result = -EINVAL; + } + break; + case LIRC_GET_LENGTH: + result = put_user(CODE_LENGTH, (unsigned long *)arg); + break; + default: + result = -ENOIOCTLCMD; + } + return result; +} + +/* This function is called whenever a process attempts to open the device + * file */ +static int lirc_haup_open(struct inode *inode, struct file *file) +{ + struct i2c_client *client = irs[0]; + struct IR *ir; + + if (NULL == client) + return -ENODEV; + file->private_data = client; + ir = client->data; + + down(&ir->lock); + if (0 == ir->open) { + ir->head = 0; + ir->tail = 0; + } + ir->open++; + up(&ir->lock); + + /* lock bttv in memory while /dev/lirc is in use */ + if (client->adapter->inc_use) + client->adapter->inc_use(client->adapter); + + MOD_INC_USE_COUNT; + return 0; +} + +/* This function is called when a process closes the device file. */ +static int lirc_haup_close(struct inode *inode, struct file *file) +{ + struct i2c_client *client = file->private_data; + struct IR *ir = client->data; + + ir->open--; + + if (client->adapter->dec_use) + client->adapter->dec_use(client->adapter); + MOD_DEC_USE_COUNT; + return 0; +} + +static ssize_t lirc_haup_read(struct file * file, char * buffer, + size_t length, loff_t *ppos) +{ + struct i2c_client *client = file->private_data; + struct IR *ir = client->data; + unsigned char data[2]; + + down(&ir->lock); + + /* we are only prepared to send 2 bytes */ + if(length != 2) { + up(&ir->lock); + return -EIO; + } + + /* while input buffer is empty, wait for input */ + while (ir->head==ir->tail) { + up(&ir->lock); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + if (signal_pending(current)) + return -ERESTARTSYS; + interruptible_sleep_on(&ir->wait_poll); + current->state = TASK_RUNNING; + down(&ir->lock); + } + + data[0]=(unsigned char) ((ir->buffer[ir->head]>>8)&0x1f); + data[1]=(unsigned char) (ir->buffer[ir->head++]&0xff); + ir->head%=BUFLEN; + up(&ir->lock); + + if (copy_to_user(buffer,data,2)) + return -EFAULT; + return length; +} + + +static ssize_t lirc_haup_write(struct file * file, const char * buffer, + size_t length, loff_t *ppos) +{ + return -EINVAL; +} + +static struct file_operations lirc_haup_fops = { + read: lirc_haup_read, + write: lirc_haup_write, + poll: lirc_haup_poll, + ioctl: lirc_haup_ioctl, + open: lirc_haup_open, + release: lirc_haup_close +}; + +/* ----------------------------------------------------------------------- */ + +static int ir_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind); +static int ir_detach(struct i2c_client *client); +static int ir_probe(struct i2c_adapter *adap); +static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg); + +static struct i2c_driver driver = { + "i2c ir driver", + /* I2C_DRIVERID_FIXME */ 42, + I2C_DF_NOTIFY, + ir_probe, + ir_detach, + ir_command, +}; + +static struct i2c_client client_template = +{ + "ir", + -1, + 0, + 0, + NULL, + &driver +}; + +static int ir_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + int i; + DECLARE_MUTEX_LOCKED(sem); + struct IR *ir; + struct i2c_client *c; + + client_template.adapter = adap; + client_template.addr = addr; + + if (NULL == (c = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + memcpy(c,&client_template,sizeof(struct i2c_client)); + if (NULL == (ir = kmalloc(sizeof(struct IR),GFP_KERNEL))) { + kfree(c); + return -ENOMEM; + } + + memset(ir,0,sizeof(struct IR)); + init_MUTEX(&ir->lock); + init_waitqueue_head(&ir->wait_poll); + c->data = ir; + + /* startup thread */ + ir->notify = &sem; + kernel_thread(ir_poll_thread, (void *)c, 0); + down(&sem); + ir->notify = NULL; + + /* register device */ + register_chrdev(LIRC_MAJOR, DEVICE_NAME, &lirc_haup_fops); + + /* update our own array */ + for (i = 0; i < IR_MAX; i++) { + if (NULL == irs[i]) { + irs[i] = c; + break; + } + } + + /* done */ + i2c_attach_client(c); + return 0; +} + +static int ir_detach(struct i2c_client *client) +{ + DECLARE_MUTEX_LOCKED(sem); + struct IR *ir = (struct IR*)client->data; + int i; + + /* unregister device */ + unregister_chrdev(LIRC_MAJOR, DEVICE_NAME); + + /* shutdown control thread */ + if (ir->thread) + { + ir->notify = &sem; + ir->rmmod = 1; + down(&sem); + ir->notify = NULL; + } + + /* update our own array */ + for (i = 0; i < IR_MAX; i++) { + if (client == irs[i]) { + irs[i] = NULL; + break; + } + } + + i2c_detach_client(client); + kfree(ir); + kfree(client); + return 0; +} + +static int ir_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, ir_attach); + return 0; +} + +static int ir_command(struct i2c_client *client,unsigned int cmd, void *arg) +{ + /* nothing */ + return 0; +} + +/* ----------------------------------------------------------------------- */ + +#ifdef MODULE +int init_module(void) +#else +int ir_init(void) +#endif +{ + i2c_add_driver(&driver); + return 0; +} + +#ifdef MODULE +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} +#endif + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN linux-2.2.16.SuSE/drivers/char/kcompat24.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/kcompat24.c --- linux-2.2.16.SuSE/drivers/char/kcompat24.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/kcompat24.c Mon Jun 12 17:36:10 2000 @@ -0,0 +1,379 @@ +/* + * see http://gtf.org/garzik/drivers/kcompat24/ + * this is version 0.9.3 + * -- kraxel + */ + +/* + * $Id: kcompat24.c,v 1.2 2000/06/01 22:18:36 jgarzik Exp $ + * + * PCI Bus Services, see include/linux/pci.h for further explanation. + * + * Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter, + * David Mosberger-Tang + * + * Copyright 1997 -- 2000 Martin Mares + */ + +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + +#ifndef KCOMPAT_INCLUDED +#include +#endif + +#include +#include +#include +#include +#include "kcompat24.h" + +/* + * Registration of PCI drivers and handling of hot-pluggable devices. + */ + +static LIST_HEAD(pci_drivers); + +struct pci_driver_mapping { + struct pci_dev *dev; + struct pci_driver *drv; + void *driver_data; +}; + +#define PCI_MAX_MAPPINGS 16 +static struct pci_driver_mapping drvmap [PCI_MAX_MAPPINGS] = { { NULL, } , }; + +struct net_device_mapping { + struct net_device *dev; + struct timer_list dev_watchdog; + int watchdog_timeo; + void (*tx_timeout) (struct net_device *dev); + int timer_started; +}; + +#define NETDEV_MAX_MAPPINGS 16 +static struct net_device_mapping netdevmap [NETDEV_MAX_MAPPINGS] = { { NULL, } , }; + + +static inline struct net_device_mapping *netif_map_find (struct net_device *dev) +{ + int i; + + for (i = 0; i < NETDEV_MAX_MAPPINGS; i++) + if (netdevmap[i].dev == dev) + return &netdevmap[i]; + + panic ("mapping not found"); +} + + +void netif_watchdog (unsigned long data) +{ + struct net_device_mapping *cdev = (struct net_device_mapping *) data; + struct net_device *dev = cdev->dev; + + if (netif_running(dev) && netif_queue_stopped(dev) && + ((jiffies - dev->trans_start) > cdev->watchdog_timeo)) { + printk(KERN_INFO "NETDEV WATCHDOG: %s: transmit timed out\n", dev->name); + cdev->tx_timeout(dev); + } + + mod_timer(&cdev->dev_watchdog, jiffies + cdev->watchdog_timeo); +} + + +void netif_init_watchdog(struct net_device *dev, int timeout, + void (*tx_timeout) (struct net_device *dev)) +{ + struct net_device_mapping *cdev; + + cdev = netif_map_find (NULL); + cdev->dev = dev; + cdev->watchdog_timeo = timeout; + cdev->tx_timeout = tx_timeout; + cdev->timer_started = 0; + + init_timer(&cdev->dev_watchdog); + cdev->dev_watchdog.data = (unsigned long) cdev; + cdev->dev_watchdog.function = netif_watchdog; +} + + +void netif_start_watchdog(struct net_device *dev) +{ + struct net_device_mapping *cdev = netif_map_find (dev); + + if (!cdev->timer_started) { + cdev->timer_started = 1; + mod_timer(&cdev->dev_watchdog, jiffies + cdev->watchdog_timeo); + } +} + + +void netif_stop_watchdog(struct net_device *dev) +{ + struct net_device_mapping *cdev = netif_map_find (dev); + + if (!cdev->timer_started) { + cdev->timer_started = 0; + del_timer(&cdev->dev_watchdog); + } +} + + +KCOMPINC void * pci_compat_get_driver_data (struct pci_dev *dev) +{ + int i; + + for (i = 0; i < PCI_MAX_MAPPINGS; i++) + if (drvmap[i].dev == dev) + return drvmap[i].driver_data; + + return NULL; +} + + +KCOMPINC void pci_compat_set_driver_data (struct pci_dev *dev, void *driver_data) +{ + int i; + + for (i = 0; i < PCI_MAX_MAPPINGS; i++) + if (drvmap[i].dev == dev) { + drvmap[i].driver_data = driver_data; + return; + } +} + + +KCOMPINC const struct pci_device_id * +pci_compat_match_device(const struct pci_device_id *ids, struct pci_dev *dev) +{ + u16 subsystem_vendor, subsystem_device; + + pci_read_config_word(dev, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor); + pci_read_config_word(dev, PCI_SUBSYSTEM_ID, &subsystem_device); + + while (ids->vendor || ids->subvendor || ids->class_mask) { + if ((ids->vendor == PCI_ANY_ID || ids->vendor == dev->vendor) && + (ids->device == PCI_ANY_ID || ids->device == dev->device) && + (ids->subvendor == PCI_ANY_ID || ids->subvendor == subsystem_vendor) && + (ids->subdevice == PCI_ANY_ID || ids->subdevice == subsystem_device) && + !((ids->class ^ dev->class) & ids->class_mask)) + return ids; + ids++; + } + return NULL; +} + +static int +pci_announce_device(struct pci_driver *drv, struct pci_dev *dev) +{ + const struct pci_device_id *id; + int found, i; + + if (drv->id_table) { + id = pci_compat_match_device(drv->id_table, dev); + if (!id) + return 0; + } else + id = NULL; + + found = 0; + for (i = 0; i < PCI_MAX_MAPPINGS && !found; i++) + if (!drvmap[i].dev) { + drvmap[i].dev = dev; + drvmap[i].drv = drv; + found = 1; + } + + if (drv->probe(dev, id) >= 0) { + if(found) + return 1; + } else + drvmap[i - 1].dev = NULL; + + return 0; +} + +KCOMPINC int +pci_compat_register_driver(struct pci_driver *drv) +{ + struct pci_dev *dev; + int count = 0, found, i; + + list_add_tail(&drv->node, &pci_drivers); + pci_for_each_dev(dev) { + found = 0; + for (i = 0; i < PCI_MAX_MAPPINGS && !found; i++) + if (drvmap[i].dev == dev) + found = 1; + if (!found) + count += pci_announce_device(drv, dev); + } + return count; +} + +KCOMPINC void +pci_compat_unregister_driver(struct pci_driver *drv) +{ + struct pci_dev *dev; + int i; + + list_del(&drv->node); + pci_for_each_dev(dev) { + for (i = 0; i < PCI_MAX_MAPPINGS; i++) + if (drvmap[i].dev == dev && + drvmap[i].drv == drv) + break; + if (PCI_MAX_MAPPINGS == i) + continue; + if (drv->remove) + drv->remove(dev); + drvmap[i].dev = NULL; + } +} + +KCOMPINC unsigned long pci_compat_get_size (struct pci_dev *dev, int n_base) +{ + u32 l, sz; + int reg = PCI_BASE_ADDRESS_0 + (n_base << 2); + + /* XXX temporarily disable I/O and memory decoding for this device? */ + + pci_read_config_dword (dev, reg, &l); + if (l == 0xffffffff) + return 0; + + pci_write_config_dword (dev, reg, ~0); + pci_read_config_dword (dev, reg, &sz); + pci_write_config_dword (dev, reg, l); + + if (!sz || sz == 0xffffffff) + return 0; + if ((l & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY) { + sz = ~(sz & PCI_BASE_ADDRESS_MEM_MASK); + } else { + sz = ~(sz & PCI_BASE_ADDRESS_IO_MASK) & 0xffff; + } + + return sz + 1; +} + +KCOMPINC int pci_compat_get_flags (struct pci_dev *dev, int n_base) +{ + unsigned long foo = dev->base_address[n_base] & PCI_BASE_ADDRESS_SPACE; + int flags = 0; + + if (foo == 0) + flags |= IORESOURCE_MEM; + if (foo == 1) + flags |= IORESOURCE_IO; + + return flags; +} + +/* + * Set power management state of a device. For transitions from state D3 + * it isn't as straightforward as one could assume since many devices forget + * their configuration space during wakeup. Returns old power state. + */ +KCOMPINC int +pci_compat_set_power_state(struct pci_dev *dev, int new_state) +{ + u32 base[5], romaddr; + u16 pci_command, pwr_command; + u8 pci_latency, pci_cacheline; + int i, old_state; + int pm = pci_compat_find_capability(dev, PCI_CAP_ID_PM); + + if (!pm) + return 0; + pci_read_config_word(dev, pm + PCI_PM_CTRL, &pwr_command); + old_state = pwr_command & PCI_PM_CTRL_STATE_MASK; + if (old_state == new_state) + return old_state; + if (old_state == 3) { + pci_read_config_word(dev, PCI_COMMAND, &pci_command); + pci_write_config_word(dev, PCI_COMMAND, pci_command & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)); + for (i = 0; i < 5; i++) + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0 + i*4, &base[i]); + pci_read_config_dword(dev, PCI_ROM_ADDRESS, &romaddr); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &pci_cacheline); + pci_write_config_word(dev, pm + PCI_PM_CTRL, new_state); + for (i = 0; i < 5; i++) + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0 + i*4, base[i]); + pci_write_config_dword(dev, PCI_ROM_ADDRESS, romaddr); + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, dev->irq); + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, pci_cacheline); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, pci_latency); + pci_write_config_word(dev, PCI_COMMAND, pci_command); + } else + pci_write_config_word(dev, pm + PCI_PM_CTRL, (pwr_command & ~PCI_PM_CTRL_STATE_MASK) | new_state); + return old_state; +} + +/* + * Initialize device before it's used by a driver. Ask low-level code + * to enable I/O and memory. Wake up the device if it was suspended. + * Beware, this function can fail. + */ +KCOMPINC int +pci_compat_enable_device(struct pci_dev *dev) +{ +#if 0 + pci_compat_set_power_state(dev, 0); +#endif + return 0; +} + +KCOMPINC int +pci_compat_find_capability(struct pci_dev *dev, int cap) +{ + u16 status; + u8 pos, id; + int ttl = 48; + + pci_read_config_word(dev, PCI_STATUS, &status); + if (!(status & PCI_STATUS_CAP_LIST)) + return 0; + pci_read_config_byte(dev, PCI_CAPABILITY_LIST, &pos); + while (ttl-- && pos >= 0x40) { + pos &= ~3; + pci_read_config_byte(dev, pos + PCI_CAP_LIST_ID, &id); + if (id == 0xff) + break; + if (id == cap) + return pos; + pci_read_config_byte(dev, pos + PCI_CAP_LIST_NEXT, &pos); + } + return 0; +} + + +/* note - assumes you only test for NULL, and not + * actually care about the return value */ +KCOMPINC void *compat_request_region (unsigned long start, unsigned long n, const char *name) +{ + if (check_region (start, n) != 0) + return NULL; + request_region (start, n, name); + return (void *) 1; +} + +#ifndef KCOMPAT_INCLUDED +EXPORT_SYMBOL(pci_compat_match_device); +EXPORT_SYMBOL(pci_compat_register_driver); +EXPORT_SYMBOL(pci_compat_unregister_driver); +EXPORT_SYMBOL(pci_compat_get_size); +EXPORT_SYMBOL(pci_compat_get_flags); +EXPORT_SYMBOL(pci_compat_set_power_state); +EXPORT_SYMBOL(pci_compat_enable_device); +EXPORT_SYMBOL(pci_compat_find_capability); +EXPORT_SYMBOL(pci_compat_get_driver_data); +EXPORT_SYMBOL(pci_compat_set_driver_data); +#endif + +#endif /* kernel version < 2.3.0 */ + diff -urN linux-2.2.16.SuSE/drivers/char/kcompat24.h linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/kcompat24.h --- linux-2.2.16.SuSE/drivers/char/kcompat24.h Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/kcompat24.h Sun Jun 25 18:38:03 2000 @@ -0,0 +1,349 @@ +/* + * see http://gtf.org/garzik/drivers/kcompat24/ + * this is version 0.9.3 + * -- kraxel + */ + +#ifndef __KCOMPAT24_H__ +#define __KCOMPAT24_H__ + +/* + + notes: + + 2.3.13 adds new resource allocation + + */ + +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) + +#define init_MUTEX(a) *(a) = MUTEX + +#define wait_queue_head_t struct wait_queue * +#define DECLARE_WAITQUEUE(a, b) struct wait_queue a = {b, NULL}; +#define init_waitqueue_head(a) init_waitqueue(a) +#define DECLARE_MUTEX(foo) struct semaphore foo = MUTEX +#define DECLARE_MUTEX_LOCKED(foo) struct semaphore foo = MUTEX_LOCKED + +#define in_irq in_interrupt + +#include + +#ifdef KCOMPAT_INCLUDED + #define KCOMPINC static +#else + #define KCOMPINC +#endif + +#define net_device device + +#define __exit +#define __exitdata +#define __devinit +#define __devinitdata +#define __devexit +#define __devexitdata + +/* Not sure what version aliases were introduced in, but certainly in 2.91.66. */ +#ifdef MODULE + #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 91) + #define module_init(x) int init_module(void) __attribute__((alias(#x))); + #define module_exit(x) void cleanup_module(void) __attribute__((alias(#x))); + #else + #define module_init(x) int init_module(void) { return x(); } + #define module_exit(x) void cleanup_module(void) { x(); } + #endif +#else + #define module_init(x) + #define module_exit(x) +#endif + +#define MODULE_DEVICE_TABLE(foo,bar) + + +/* + * Insert a new entry before the specified head.. + */ +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) + +#define IORESOURCE_IO 0x00000100 /* Resource type */ +#define IORESOURCE_MEM 0x00000200 + +#define request_region compat_request_region + +/* XXX provide real support for this, even though pre-2.3.x support + * doesn't exist */ +#define request_mem_region(x,y,z) (1) +#define release_mem_region(x,y) do {} while (0) + + +/* New-style probing supporting hot-pluggable devices */ + +#define PCI_PM_CTRL 4 /* PM control and status register */ +#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ + +#define PCI_ANY_ID (~0) + +#define PCI_GET_DRIVER_DATA pci_compat_get_driver_data +#define PCI_SET_DRIVER_DATA pci_compat_set_driver_data + +#define PCI_SET_DMA_MASK(dev, mask) +#define pci_dma_supported(dev, mask) 1 + +#define pci_enable_device pci_compat_enable_device +#define pci_register_driver pci_compat_register_driver +#define pci_unregister_driver pci_compat_unregister_driver + +#define pci_dev_g(n) list_entry(n, struct pci_dev, global_list) +#define pci_dev_b(n) list_entry(n, struct pci_dev, bus_list) + +#define pci_for_each_dev(dev) \ + for(dev = pci_devices; dev; dev = dev->next) + +#define pci_resource_start(dev,bar) \ +(((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_SPACE) ? \ + ((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_IO_MASK) : \ + ((dev)->base_address[(bar)] & PCI_BASE_ADDRESS_MEM_MASK)) +#define pci_resource_len pci_compat_get_size +#define pci_resource_end(dev,bar) \ + (pci_resource_len((dev),(bar)) == 0 ? \ + pci_resource_start(dev,bar) : \ + (pci_resource_start(dev,bar) + pci_resource_len((dev),(bar)) - 1) + +#define pci_resource_flags(dev,bar) (pci_compat_get_flags((dev),(bar))) + +struct pci_device_id { + unsigned int vendor, device; /* Vendor and device ID or PCI_ANY_ID */ + unsigned int subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */ + unsigned int class, class_mask; /* (class,subclass,prog-if) triplet */ + unsigned long driver_data; /* Data private to the driver */ +}; + +struct pci_driver { + struct list_head node; + struct pci_dev *dev; + char *name; + const struct pci_device_id *id_table; /* NULL if wants all devices */ + int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); /* New device inserted */ + void (*remove)(struct pci_dev *dev); /* Device removed (NULL if not a hot-plug capable driver) */ + void (*suspend)(struct pci_dev *dev); /* Device suspended */ + void (*resume)(struct pci_dev *dev); /* Device woken up */ +}; + + + +/* + * + */ +KCOMPINC const struct pci_device_id * pci_compat_match_device(const struct pci_device_id *ids, struct pci_dev *dev); +KCOMPINC int pci_compat_register_driver(struct pci_driver *drv); +KCOMPINC void pci_compat_unregister_driver(struct pci_driver *drv); +KCOMPINC unsigned long pci_compat_get_size (struct pci_dev *dev, int n_base); +KCOMPINC int pci_compat_get_flags (struct pci_dev *dev, int n_base); +KCOMPINC int pci_compat_set_power_state(struct pci_dev *dev, int new_state); +KCOMPINC int pci_compat_enable_device(struct pci_dev *dev); +KCOMPINC int pci_compat_find_capability(struct pci_dev *dev, int cap); +KCOMPINC void *compat_request_region (unsigned long start, unsigned long n, const char *name); +KCOMPINC void * pci_compat_get_driver_data (struct pci_dev *dev); +KCOMPINC void pci_compat_set_driver_data (struct pci_dev *dev, void *driver_data); + +#else /* if kernel version >= 2.3.0 */ + +#include + +#ifndef pci_resource_start + #define pci_resource_start(dev,bar) ((dev)->resource[(bar)].start) + #define pci_resource_end(dev,bar) ((dev)->resource[(bar)].end) + #define pci_resource_flags(dev,bar) ((dev)->resource[(bar)].flags) +#endif /* !pci_resource_start */ + +#ifndef PCI_GET_DRIVER_DATA + #define PCI_GET_DRIVER_DATA(pdev) ((pdev)->driver_data) + #define PCI_SET_DRIVER_DATA(pdev,data) (((pdev)->driver_data) = (data)) +#endif /* PCI_GET_DRIVER_DATA */ + +static inline void netif_init_watchdog(struct net_device *dev, int timeout, + void (*tx_timeout) (struct net_device *dev)) +{ + dev->watchdog_timeo = timeout; + dev->tx_timeout = tx_timeout; +} + +static inline void netif_start_watchdog(struct net_device *dev) +{ + /* network layer does it for us */ +} + +static inline void netif_stop_watchdog(struct net_device *dev) +{ + /* network layer does it for us */ +} + +#endif /* kernel version <=> 2.3.0 */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,47) +typedef __u32 dma_addr_t; + +/* Pure 2^n version of get_order */ +extern __inline__ int __compat_get_order(unsigned long size) +{ + int order; + + size = (size-1) >> (PAGE_SHIFT-1); + order = -1; + do { + size >>= 1; + order++; + } while (size); + return order; +} + +extern __inline__ void * +pci_alloc_consistent(struct pci_dev *hwdev, + size_t size, dma_addr_t *dma_handle) { + void *ret; + int gfp = GFP_ATOMIC; + + if (hwdev == NULL) + gfp |= GFP_DMA; + ret = (void *)__get_free_pages(gfp, __compat_get_order(size)); + + if (ret != NULL) { + memset(ret, 0, size); + *dma_handle = virt_to_bus(ret); + } + return ret; +} + +extern __inline__ void +pci_free_consistent(struct pci_dev *hwdev, size_t size, + void *vaddr, dma_addr_t dma_handle) +{ + free_pages((unsigned long)vaddr, __compat_get_order(size)); +} + +#define pci_unmap_single(cookie, address, size, dir) +#define pci_map_sg(cookie, sg, nents, dir) (nents) +#define pci_unmap_sg(cookie, sg, nents, dir) +#define sg_dma_len(slp) ((slp)->length) +#define pci_map_single(cookie, address, size, dir) virt_to_bus(address) +#define sg_dma_address(slp) virt_to_bus((slp)->address) +#define PCI_DMA_BIDIRECTIONAL 0 +#define PCI_DMA_TODEVICE 1 +#define PCI_DMA_FROMDEVICE 2 +#define PCI_DMA_NONE 3 +#define scsi_to_pci_dma_dir(dir) PCI_DMA_BIDIRECTIONAL +#endif /* version < v2.3.47 */ + + +/* + * SoftNet + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,40) + +#define dev_kfree_skb_irq(a) dev_kfree_skb(a) +#define netif_stop_queue(dev) set_bit(0, &dev->tbusy) +#define netif_queue_stopped(dev) (dev->tbusy) +#define netif_running(dev) (dev->start) + +static inline void netif_start_queue(struct net_device *dev) +{ + dev->interrupt = 0; + clear_bit(0, &dev->tbusy); + set_bit(0, &dev->start); +} + +static inline void netif_wake_queue(struct net_device *dev) +{ + clear_bit(0, &dev->tbusy); + mark_bh(NET_BH); +} + +static inline void netif_device_attach(struct net_device *dev) +{ + if (dev->tbusy && !netif_running(dev)) { + netif_start_queue(dev); + netif_wake_queue(dev); + } +} + +static inline void netif_device_detach(struct net_device *dev) +{ + if (netif_running(dev)) { + dev->start = 0; + netif_stop_queue(dev); + } +} + +void netif_init_watchdog(struct net_device *dev, int timeout, + void (*tx_timeout) (struct net_device *dev)); +void netif_stop_watchdog(struct net_device *dev); +void netif_start_watchdog(struct net_device *dev); + +#endif /* softnet compatibility -- version < 2.3.40 */ + + +/* + * pci.module.init (2.3.40 version is just a guess) + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,40) + +/* + * a helper function which helps ensure correct pci_driver + * setup and cleanup for commonly-encountered hotplug/modular cases + * + * This MUST stay in a header, as it checks for -DMODULE + */ +static inline int pci_module_init(struct pci_driver *drv) +{ + int rc = pci_register_driver (drv); + + if (rc > 0) + return 0; + + /* if we get here, we need to clean up pci driver instance + * and return some sort of error */ + pci_unregister_driver (drv); + + return -ENODEV; +} + +#endif /* pci.module.init compatibility -- version < 2.3.40 (a guess) */ + + +/* + * tasklet compat from Rui Sousa (2.3.40 version is just a guess) + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,40) + +#define tasklet_hi_schedule(t) queue_task((t), &tq_immediate); \ + mark_bh(IMMEDIATE_BH) + +#define tasklet_init(t,f,d) (t)->next = NULL; \ + (t)->sync = 0; \ + (t)->routine = (void (*)(void *))(f); \ + (t)->data = (void *)(d) + +#define tasklet_struct tq_struct + +#define tasklet_unlock_wait(t) while (test_bit(0, &(t)->sync)) { } + +#endif /* tasklet compatibility from Rui Sousa -- version < 2.3.40 (a guess) */ + +#ifdef KCOMPAT_INCLUDED + #include "kcompat24.c" +#endif + +#endif /* __KCOMPAT24_H__ */ + + diff -urN linux-2.2.16.SuSE/drivers/char/lirc.h linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/lirc.h --- linux-2.2.16.SuSE/drivers/char/lirc.h Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/lirc.h Sun Jun 25 18:45:48 2000 @@ -0,0 +1,95 @@ +/* $Id: lirc.h,v 5.3 1999/09/02 20:03:53 columbus Exp $ */ + +#ifndef _LINUX_LIRC_H +#define _LINUX_LIRC_H + +#include + +#include +#include + +#define PULSE_BIT 0x01000000 +#define PULSE_MASK 0x00FFFFFF + +#if INT_MAX>=PULSE_BIT +typedef int lirc_t; +#else +#error "No appropriate type for lirc data available." +#endif + + +/* + * lirc compatible hardware features + */ + + +#define LIRC_MODE2SEND(x) (x) +#define LIRC_SEND2MODE(x) (x) +#define LIRC_MODE2REC(x) ((x) << 16) +#define LIRC_REC2MODE(x) ((x) >> 16) + +#define LIRC_MODE_RAW 0x00000001 +#define LIRC_MODE_PULSE 0x00000002 +#define LIRC_MODE_MODE2 0x00000004 +#define LIRC_MODE_CODE 0x00000008 +#define LIRC_MODE_LIRCCODE 0x00000010 +#define LIRC_MODE_STRING 0x00000020 + + +#define LIRC_CAN_SEND_RAW LIRC_MODE2SEND(LIRC_MODE_RAW) +#define LIRC_CAN_SEND_PULSE LIRC_MODE2SEND(LIRC_MODE_PULSE) +#define LIRC_CAN_SEND_MODE2 LIRC_MODE2SEND(LIRC_MODE_MODE2) +#define LIRC_CAN_SEND_CODE LIRC_MODE2SEND(LIRC_MODE_CODE) +#define LIRC_CAN_SEND_LIRCCODE LIRC_MODE2SEND(LIRC_MODE_LIRCCODE) +#define LIRC_CAN_SEND_STRING LIRC_MODE2SEND(LIRC_MODE_STRING) + +#define LIRC_CAN_SEND_MASK 0x0000003f + +#define LIRC_CAN_SET_SEND_CARRIER 0x00000100 +#define LIRC_CAN_SET_SEND_PULSE_WIDTH 0x00000200 + +#define LIRC_CAN_REC_RAW LIRC_MODE2REC(LIRC_MODE_RAW) +#define LIRC_CAN_REC_PULSE LIRC_MODE2REC(LIRC_MODE_PULSE) +#define LIRC_CAN_REC_MODE2 LIRC_MODE2REC(LIRC_MODE_MODE2) +#define LIRC_CAN_REC_CODE LIRC_MODE2REC(LIRC_MODE_CODE) +#define LIRC_CAN_REC_LIRCCODE LIRC_MODE2REC(LIRC_MODE_LIRCCODE) +#define LIRC_CAN_REC_STRING LIRC_MODE2REC(LIRC_MODE_STRING) + +#define LIRC_CAN_REC_MASK LIRC_MODE2REC(LIRC_CAN_SEND_MASK) + +#define LIRC_CAN_SET_REC_CARRIER (LIRC_CAN_SET_SEND_CARRIER << 16) +#define LIRC_CAN_SET_REC_PULSE_WIDTH (LIRC_CAN_SET_SEND_PULSE_WIDTH << 16) + +#define LIRC_CAN_ADD_REC_CARRIER 0x80000000 + + +#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK) +#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK) + +/* + * IOCTL commands for lirc driver + */ + +#define LIRC_GET_FEATURES _IOR('i', 0x00000000, __u32) + +#define LIRC_GET_SEND_MODE _IOR('i', 0x00000001, __u32) +#define LIRC_GET_REC_MODE _IOR('i', 0x00000002, __u32) +#define LIRC_GET_SEND_CARRIER _IOR('i', 0x00000003, __u32) +#define LIRC_GET_REC_CARRIER _IOR('i', 0x00000004, __u32) +#define LIRC_GET_SEND_PUSE_WIDTH _IOR('i', 0x00000005, __u32) +#define LIRC_GET_REC_PUSE_WIDTH _IOR('i', 0x00000006, __u32) + +/* code length in bits, currently only for LIRC_MODE_LIRCCODE */ +#define LIRC_GET_LENGTH _IOR('i', 0x0000000f, __u32) + +#define LIRC_SET_SEND_MODE _IOW('i', 0x00000011, __u32) +#define LIRC_SET_REC_MODE _IOW('i', 0x00000012, __u32) +/* Note: these can reset the according pulse_width */ +#define LIRC_SET_SEND_CARRIER _IOW('i', 0x00000013, __u32) +#define LIRC_SET_REC_CARRIER _IOW('i', 0x00000014, __u32) +#define LIRC_SET_SEND_PULSE_WIDTH _IOW('i', 0x00000015, __u32) +#define LIRC_SET_REC_PULSE_WIDTH _IOW('i', 0x00000016, __u32) + +#define LIRC_ADD_REC_CARRIER _IOW('i', 0x0000001f, __u32) + +#endif diff -urN linux-2.2.16.SuSE/drivers/char/msp3400.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/msp3400.c --- linux-2.2.16.SuSE/drivers/char/msp3400.c Sun Jun 25 17:30:13 2000 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/msp3400.c Wed May 31 21:13:35 2000 @@ -1,12 +1,13 @@ /* * programming the msp34* sound processor family * - * (c) 1997,1998 Gerd Knorr + * (c) 1997-2000 Gerd Knorr * * 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,55 @@ #include #include #include -#ifdef __SMP__ +#include +#include +#include +#include + +#ifdef CONFIG_SMP #include #include #endif - /* kernel_thread */ #define __KERNEL_SYSCALLS__ #include -#include -#include - -#include "msp3400.h" - +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +# include "kcompat24.h" +#endif +#include "audiochip.h" /* 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 0 /* 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,0x40,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 +104,34 @@ /* thread */ struct task_struct *thread; - struct wait_queue *wq; + wait_queue_head_t 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 +142,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 +228,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 +266,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 +303,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 +315,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 +334,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 +441,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 +475,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 +499,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 +523,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 +551,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 +561,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 +577,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 +605,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 +651,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 +690,7 @@ msp->thread = current; -#ifdef __SMP__ +#ifdef CONFIG_SMP unlock_kernel(); #endif @@ -663,15 +715,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 +737,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 +777,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 +807,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 +825,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 +862,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 +892,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 +1103,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 +1148,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 +1183,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 +1199,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 +1237,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 +1265,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 +1279,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 = +{ + "unset", + -1, + 0, + 0, + NULL, + &driver +}; + +static int msp_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) { - struct semaphore sem = MUTEX_LOCKED; + DECLARE_MUTEX_LOCKED(sem); struct msp3400c *msp; - int rev1,rev2; - LOCK_FLAGS; + struct i2c_client *c; + int rev1,rev2,i; - device->data = msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL); - if (NULL == msp) + client_template.adapter = adap; + client_template.addr = addr; + + 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 +1363,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 = (((rev2>>8)&0xff) == 0) ? 0 : 1; + } 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 +1436,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,98 +1575,78 @@ 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 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_GET_VOLUME_RIGHT: + *sarg = msp->right; + break; + 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) -#endif +int msp3400_init_module(void) { - 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) +void msp3400_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 + +module_init(msp3400_init_module); +module_exit(msp3400_cleanup_module); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff -urN linux-2.2.16.SuSE/drivers/char/pic16c54.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/pic16c54.c --- linux-2.2.16.SuSE/drivers/char/pic16c54.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/pic16c54.c Wed Jun 7 21:49:37 2000 @@ -0,0 +1,584 @@ +/* + * The Kernel module driver for the PIC16C54 chip, found on ProVision PV951 TV/FM cards + * This driver provides the audio and lirc services. + * + * Copyright (c) 2000 Kefah T. Issa + * This code is placed under the terms of the GNU General Public License. + * + * In writting this code, I relied on tea6300 and ir drivers. + * + * Thanks to: + * ProVision (Arrow Hsu ) + * for the card access information, specifications, and support. + * Mark D. Studebaker + * for the valiable I2C bus speed control tip. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __SMP__ +#include +#include +#endif +/* kernel_thread */ +#define __KERNEL_SYSCALLS__ +#include + + +#include "bttv.h" +#include "audiochip.h" +#include "pic16c54.h" +#include "lirc.h" + +#define DEVICE_NAME "ProVedio 951 ir rc" +#define LIRC_MAJOR 61 +#define CODE_LENGTH 8 + + +// Define UDELAY to control i2c bus speed. +// 84 ===> 84 microseonds for half a signal ==> frequency = 1 / ( 84e-6 * 2) ~= 6KHz +#define UDELAY 84 + +#define SUCCESS 0 + +#define BUFLEN 256 +#define IR_MAX 4 + +struct IR +{ + struct task_struct *thread; + struct semaphore *notify; + int rmmod; + int open; + + struct semaphore lock; + wait_queue_head_t wait_poll; + unsigned char buffer[BUFLEN]; + int head,tail; +}; + +static struct i2c_client *irs[IR_MAX]; + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = {ID_16C54 >> 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 +}; + + +static int debug = 0; /* insmod parameter */ +MODULE_PARM(debug,"i"); + +#define dprintk if (debug) printk + +static struct i2c_driver driver; +static struct i2c_client client_template; + + +/* ********************************* * + * functions for talking to PIC16C54 * + * ********************************* */ + +static int pic16c54_write(struct i2c_client *client, int addr, int val) +{ + unsigned char buffer[2]; + int ret=0; + struct i2c_algo_bit_data* algo_data = client->adapter->algo_data; + int old_udelay = algo_data->udelay; + algo_data->udelay = UDELAY; + + buffer[0] = addr; + buffer[1] = val; + + if(2 != i2c_master_send(client,buffer,2)) + { + printk(KERN_WARNING "PIC16C54: I/O error, trying (write %d 0x%x)\n",addr, val); + ret=-1; + }else + { + ret = 0; + } + + algo_data->udelay = old_udelay; + + return ret; +} + +static int pic16c54_read(struct i2c_client *client, unsigned char* buffer, int length) +{ + int ret=0; + struct i2c_algo_bit_data* algo_data = client->adapter->algo_data; + int old_udelay = algo_data->udelay; + algo_data->udelay = UDELAY; + + if(length != i2c_master_recv(client,buffer,length)) + { + printk(KERN_WARNING "PIC16C54: I/O error, trying (read %d bytes)\n",length); + ret=-1; + }else + { + ret = length; + } + + algo_data->udelay = old_udelay; + + return ret; +} + + + +/* ****************** * + * IR file operations * + * ****************** */ +static int ir_poll_thread(void *data) +{ + struct i2c_client *client = data; + struct IR *ir = client->data; + unsigned char b; + + +#ifdef __SMP__ + lock_kernel(); +#endif + + exit_mm(current); + current->session = 1; + current->pgrp = 1; + sigfillset(¤t->blocked); + current->fs->umask = 0; + strcpy(current->comm,"PIC16C54 - IR poll"); + + ir->thread = current; + +#ifdef __SMP__ + unlock_kernel(); +#endif + + printk("ir: thread started\n"); + if(ir->notify != NULL) + up(ir->notify); + + for (;;) + { + if (ir->rmmod) + break; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/10); + if (signal_pending(current)) + break; + + /* poll IR chip */ + if (1 != pic16c54_read(client,&b,1)) + { + dprintk("ir: read error\n"); + continue; + } + + /* ignore 0xaa */ + if(b == 0xaa) + continue; + + /* look what we have */ + dprintk("ir: key 0x%02x)\n", b); + + /* put into fifo */ + down(&ir->lock); + if (((ir->tail+1)%BUFLEN) == ir->head) + { + up(&ir->lock); + dprintk("ir: input buffer overflow\n"); + } else + { + ir->buffer[ir->tail++]=b; + ir->tail%=BUFLEN; + up(&ir->lock); + wake_up_interruptible(&ir->wait_poll); + } + } + + dprintk("ir: thread done\n"); + ir->thread = NULL; + + if(ir->notify != NULL) + up(ir->notify); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +static unsigned int lirc_poll(struct file *file, struct poll_table_struct * wait) +{ + struct i2c_client *client = file->private_data; + struct IR *ir = client->data; + + poll_wait(file, &ir->wait_poll, wait); + + if(ir->head != ir->tail) + return POLLIN | POLLRDNORM; + + return 0; +} + + +static int lirc_ioctl(struct inode *ino, struct file *fp,unsigned int cmd, unsigned long arg) +{ + int result = 0; + unsigned long features = LIRC_CAN_REC_LIRCCODE; + unsigned long mode; + + switch (cmd) + { + case LIRC_GET_FEATURES: + result = put_user(features,(unsigned long *) arg); + break; + case LIRC_SET_REC_MODE: + result = get_user(mode, (unsigned long *) arg); + if (result) + { + break; + } + if ((LIRC_MODE2REC(mode) & features) == 0) + { + result = -EINVAL; + } + break; + case LIRC_GET_LENGTH: + result = put_user(CODE_LENGTH, (unsigned long *)arg); + break; + default: + result = -ENOIOCTLCMD; + } + return result; +} + +/* This function is called whenever a process attempts to open the device + * file */ +static int lirc_open(struct inode *inode, struct file *file) +{ + struct i2c_client *client = irs[0]; + struct IR *ir; + + if (NULL == client) + return -ENODEV; + file->private_data = client; + ir = client->data; + + down(&ir->lock); + if (0 == ir->open) + { + ir->head = 0; + ir->tail = 0; + } + ir->open++; + up(&ir->lock); + + /* lock bttv in memory while /dev/lirc is in use */ + if (client->adapter->inc_use) + client->adapter->inc_use(client->adapter); + + MOD_INC_USE_COUNT; + return SUCCESS; +} + + +/* This function is called when a process closes the device file. */ +static int lirc_close(struct inode *inode, struct file *file) +{ + struct i2c_client *client = file->private_data; + struct IR *ir = client->data; + + ir->open--; + + if (client->adapter->dec_use) + client->adapter->dec_use(client->adapter); + + MOD_DEC_USE_COUNT; + return SUCCESS; +} + +static ssize_t lirc_read(struct file * file, char * buffer,size_t length, loff_t *ppos) +{ + struct i2c_client *client = file->private_data; + struct IR *ir = client->data; + unsigned char data; + + down(&ir->lock); + + /* we are only prepared to send 1 byte */ + if(length != 1) + { + up(&ir->lock); + return -EIO; + } + + /* while input buffer is empty, wait for input */ + while (ir->head==ir->tail) + { + up(&ir->lock); + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + if (signal_pending(current)) + return -ERESTARTSYS; + interruptible_sleep_on(&ir->wait_poll); + current->state = TASK_RUNNING; + down(&ir->lock); + } + + data = ir->buffer[ir->head++]; + + ir->head%=BUFLEN; + up(&ir->lock); + + if (copy_to_user(buffer,&data,1)) + return -EFAULT; + return length; + +} + + + +static ssize_t lirc_write(struct file * file, const char * buffer,size_t length, loff_t *ppos) +{ + return -EINVAL; +} + + +static struct file_operations lirc_pv951_fops = +{ + NULL, // seek + lirc_read, // read + lirc_write, // write + NULL, // readdir + lirc_poll, // poll + lirc_ioctl, // ioctl + NULL, // mmap + lirc_open, // open + NULL, // flush + lirc_close // release +}; + + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int pic16c54_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + + if(cmd == AUDC_SET_INPUT) /* Interpret messages from bttv */ + { + __u16* sarg = arg; + + switch(*sarg) + { + case AUDIO_EXTERN: + pic16c54_write(client, REG_MISC, MISC_SND_NOTMUTE); + pic16c54_write(client, REG_MISC, MISC_SWITCH_LINE_IN); + break; + + case AUDIO_TUNER: + case AUDIO_RADIO: + pic16c54_write(client, REG_MISC, MISC_SND_NOTMUTE); + pic16c54_write(client, REG_MISC, MISC_SWITCH_TUNER); + break; + + case AUDIO_OFF: + pic16c54_write(client, REG_MISC, MISC_SND_MUTE); + break; + + case AUDIO_ON: + default: + pic16c54_write(client, REG_MISC, MISC_SND_NOTMUTE); + break; + } + } + + return SUCCESS; +} + + +static int pic16c54_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind) +{ + + int i; + DECLARE_MUTEX_LOCKED(sem); + struct IR *ir; + struct i2c_client *client; + + client_template.adapter = adap; + client_template.addr = addr; + + dprintk(KERN_DEBUG "pic16c54_attach\n"); + + + if (NULL == (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) + return -ENOMEM; + + memcpy(client,&client_template,sizeof(struct i2c_client)); + + if (NULL == (ir = kmalloc(sizeof(struct IR),GFP_KERNEL))) { + kfree(client); + return -ENOMEM; + } + + memset(ir,0,sizeof(struct IR)); + init_MUTEX(&ir->lock); + init_waitqueue_head(&ir->wait_poll); + client->data = ir; + + /* startup thread */ + ir->notify = &sem; + kernel_thread(ir_poll_thread, (void *)client, 0); + down(&sem); + ir->notify = NULL; + + /* register device */ + register_chrdev(LIRC_MAJOR, DEVICE_NAME, &lirc_pv951_fops); + + /* update our own array */ + for (i = 0; i < IR_MAX; i++) + { + if (NULL == irs[i]) + { + irs[i] = client; + break; + } + } + + pic16c54_write(client, REG_MISC, MISC_SWITCH_TUNER); /* Switch audio to tuner*/ + pic16c54_write(client, REG_MISC, MISC_SND_MUTE); + + + MOD_INC_USE_COUNT; + + strcpy(client->name,"PIC16C54"); + printk(KERN_INFO "pic16c54: initialized\n"); + + i2c_attach_client(client); + return SUCCESS; +} + +static int pic16c54_probe(struct i2c_adapter *adap) +{ + int ret = 1; + struct i2c_algo_bit_data* algo_data = adap->algo_data; + int old_udelay = algo_data->udelay; + algo_data->udelay = UDELAY; + + dprintk(KERN_DEBUG "pic16c54_probe \n"); + + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + { + ret = i2c_probe(adap, &addr_data, pic16c54_attach); + if(ret!=0) + { + dprintk(KERN_WARNING "PIC16C54: I/O Error : probing faild!"); + } + } + + algo_data->udelay = old_udelay; + + return ret; +} + +static int pic16c54_detach(struct i2c_client *client) +{ + DECLARE_MUTEX_LOCKED(sem); + struct IR *ir = (struct IR*)client->data; + int i; + + dprintk(KERN_DEBUG "pic16c54_detach\n"); + + /* unregister device */ + unregister_chrdev(LIRC_MAJOR, DEVICE_NAME); + + /* shutdown control thread */ + if (ir->thread) + { + ir->notify = &sem; + ir->rmmod = 1; + down(&sem); + ir->notify = NULL; + } + + /* update our own array */ + for (i = 0; i < IR_MAX; i++) + { + if (client == irs[i]) + { + irs[i] = NULL; + break; + } + } + + pic16c54_write(client, REG_MISC, MISC_SWITCH_TUNER); /* Switch audio to tuner*/ + pic16c54_write(client, REG_MISC, MISC_SND_MUTE); + + i2c_detach_client(client); + + kfree(ir); + kfree(client); + + MOD_DEC_USE_COUNT; + return SUCCESS; +} + + + +static struct i2c_driver driver = +{ + "i2c pic16c54 driver", + I2C_DRIVERID_TEA6300, /* Should be changed!*/ + I2C_DF_NOTIFY, + pic16c54_probe, + pic16c54_detach, + pic16c54_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +int init_module(void) +{ + i2c_add_driver(&driver); + return SUCCESS; +} + +void cleanup_module(void) +{ + i2c_del_driver(&driver); +} + + diff -urN linux-2.2.16.SuSE/drivers/char/pic16c54.h linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/pic16c54.h --- linux-2.2.16.SuSE/drivers/char/pic16c54.h Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/pic16c54.h Sat Jun 10 19:45:37 2000 @@ -0,0 +1,37 @@ + +/********************************************************************** +* Filename : 16c54.H * +* History : * +* 1. Created By Johnson on 04-29-98 * +* * +* Common : * +* To define the registers and ID of 16C54 * +**********************************************************************/ + +#ifndef _16C54_H_ +#define _16C54_H_ + +// define ID of 16C54 +#define ID_16C54 0x96 + + +// the registers of 16C54, I2C sub address. +#define REG_KEY_CODE 0x01 //Not use. +#define REG_MISC 0x02 + + +// bit definition of the RESET register, I2C data. +#define MISC_RESET_REMOTE_CTL 0x01 //bit 0, Reset to receive the key code + // of remote controller +#define MISC_MTS_MAIN 0x02 //bit 1 +#define MISC_MTS_SAP 0x04 //bit 2 +#define MISC_MTS_BOTH 0x08 //bit 3 +#define MISC_SND_MUTE 0x10 //bit 4 , Mute Audio(Line-in and Tuner) +#define MISC_SND_NOTMUTE 0x20 //bit 5 +#define MISC_LINE_MUTE 0x40 //bit 6 , Switch to Line-in +#define MISC_LINE_NOTMUTE 0x80 //bit 7 , Switch to Tuner + +#define MISC_SWITCH_TUNER 0x40 //bit 6 , Switch to Line-in +#define MISC_SWITCH_LINE_IN 0x80 //bit 7 , Switch to Tuner + +#endif diff -urN linux-2.2.16.SuSE/drivers/char/tda7432.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tda7432.c --- linux-2.2.16.SuSE/drivers/char/tda7432.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tda7432.c Fri Apr 28 21:40:17 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 linux-2.2.16.SuSE/drivers/char/tda8425.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tda8425.c --- linux-2.2.16.SuSE/drivers/char/tda8425.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tda8425.c Fri Apr 28 21:40:23 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 linux-2.2.16.SuSE/drivers/char/tda985x.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tda985x.c --- linux-2.2.16.SuSE/drivers/char/tda985x.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tda985x.c Sat Apr 8 14:57:53 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 linux-2.2.16.SuSE/drivers/char/tda9875.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tda9875.c --- linux-2.2.16.SuSE/drivers/char/tda9875.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tda9875.c Fri Apr 28 21:38:27 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 linux-2.2.16.SuSE/drivers/char/tea6300.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tea6300.c --- linux-2.2.16.SuSE/drivers/char/tea6300.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tea6300.c Sun Jun 18 18:01:26 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 linux-2.2.16.SuSE/drivers/char/tea6420.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tea6420.c --- linux-2.2.16.SuSE/drivers/char/tea6420.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tea6420.c Wed Mar 22 21:20:24 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 linux-2.2.16.SuSE/drivers/char/tuner.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tuner.c --- linux-2.2.16.SuSE/drivers/char/tuner.c Sun Jun 25 17:30:22 2000 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tuner.c Mon Jun 5 23:30:58 2000 @@ -6,42 +6,58 @@ #include #include #include -#include - -#include +#include +#include +#include #include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +# include "kcompat24.h" +#endif #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 +MODULE_PARM(addr,"i"); -#if LINUX_VERSION_CODE < 0x02017f -void schedule_timeout(int j) +struct tuner { - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies + j; - schedule(); -} -#endif - -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 +72,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 +86,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 +174,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,138 +237,214 @@ 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_detach(struct i2c_device *device) +static int tuner_probe(struct i2c_adapter *adap) { - struct tuner *t = (struct tuner*)device->data; + 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_client *client) +{ + 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; -#ifdef MODULE -int init_module(void) -#else -int i2c_tuner_init(void) -#endif +int tuner_init_module(void) { - i2c_register_driver(&i2c_driver_tuner); + i2c_add_driver(&driver); return 0; } -#ifdef MODULE -void cleanup_module(void) +void tuner_cleanup_module(void) { - i2c_unregister_driver(&i2c_driver_tuner); + i2c_del_driver(&driver); } -#endif + +module_init(tuner_init_module); +module_exit(tuner_cleanup_module); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff -urN linux-2.2.16.SuSE/drivers/char/tuner.h linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tuner.h --- linux-2.2.16.SuSE/drivers/char/tuner.h Sun Jun 25 17:30:13 2000 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tuner.h Fri Dec 17 20:12:49 1999 @@ -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 linux-2.2.16.SuSE/drivers/char/tvaudio.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tvaudio.c --- linux-2.2.16.SuSE/drivers/char/tvaudio.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tvaudio.c Mon Jun 12 17:57:10 2000 @@ -0,0 +1,865 @@ +/* + * experimental driver for simple i2c audio chips. + * + * Copyright (c) 2000 Gerd Knorr + * based on code by: + * Eric Sandeen (eric_sandeen@bigfoot.com) + * Steve VanDeBogart (vandebo@uclink.berkeley.edu) + * Greg Alexander (galexand@acm.org) + * + * This code is placed under the terms of the GNU General Public License + * + * OPTIONS: + * debug - set to 1 if you'd like to see debug messages + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bttv.h" +#include "audiochip.h" + + +/* ---------------------------------------------------------------------- */ +/* insmod args */ + +MODULE_PARM(debug,"i"); +static int debug = 0; /* insmod parameter */ + +#define dprintk if (debug) printk + + +/* ---------------------------------------------------------------------- */ +/* our structs */ + +#define MAXREGS 64 + +struct CHIPSTATE; +typedef int (*getvalue)(int); +typedef int (*getmode)(struct CHIPSTATE*); +typedef void (*setmode)(struct CHIPSTATE*, int mode); + +/* i2c command */ +typedef struct AUDIOCMD { + int count; /* # of bytes to send */ + unsigned char bytes[MAXREGS+1]; /* addr, data, data, ... */ +} audiocmd; + +/* chip description */ +struct CHIPDESC { + char *name; /* chip name */ + int addr_lo, addr_hi; /* i2c address range */ + int registers; /* # of registers */ + + int *insmodopt; + int flags; +#define CHIP_HAS_VOLUME 1 +#define CHIP_HAS_BASSTREBLE 2 +#define CHIP_HAS_INPUTSEL 4 + + /* various i2c command sequences */ + audiocmd init; + + /* which register has which value */ + int leftreg,rightreg,treblereg,bassreg; + + /* initialize with (defaults to 65535/65535/32768/32768 */ + int leftinit,rightinit,trebleinit,bassinit; + + /* functions to convert the values (v4l -> chip) */ + getvalue volfunc,treblefunc,bassfunc; + + /* get/set mode */ + getmode getmode; + setmode setmode; + + /* input switch register + values for v4l inputs */ + int inputreg; + int inputmap[8]; + int inputmute; +}; + +/* current state of the chip */ +struct CHIPSTATE { + struct i2c_client c; + + /* index into CHIPDESC array */ + int type; + + /* shadow register set */ + audiocmd shadow; + + /* current settings */ + __u16 left,right,treble,bass; +}; + + +/* ---------------------------------------------------------------------- */ +/* i2c adresses */ + +/* Addresses to scan */ +#define I2C_PIC16C54 0x96 /* PV951 */ +#define I2C_TEA6420 0x98 +#define I2C_TDA985x_L 0xb4 /* also used by 9873 */ +#define I2C_TDA985x_H 0xb6 + +static unsigned short normal_i2c[] = { + I2C_TDA8425 >> 1, + I2C_TEA6300 >> 1, + I2C_TEA6420 >> 1, + I2C_TDA985x_L >> 1, + I2C_TDA985x_H >> 1, + I2C_PIC16C54 >> 1, + I2C_CLIENT_END }; +static unsigned short normal_i2c_range[2] = { I2C_CLIENT_END, 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 struct i2c_driver driver; +static struct i2c_client client_template; + + +/* ---------------------------------------------------------------------- */ +/* i2c I/O functions */ + +static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) +{ + unsigned char buffer[2]; + + dprintk("%s: chip_write: reg%d=0x%x\n", chip->c.name,subaddr, val); + + /* update our shadow register set */ + chip->shadow.bytes[subaddr+1] = val; + + /* send data to the chip */ + buffer[0] = subaddr; + buffer[1] = val; + if (2 != i2c_master_send(&chip->c,buffer,2)) { + printk(KERN_WARNING "%s: I/O error (write reg%d=0x%x)\n", + chip->c.name, subaddr, val); + return -1; + } + return 0; +} + +static int chip_read(struct CHIPSTATE *chip) +{ + unsigned char buffer; + + if (1 != i2c_master_recv(&chip->c,&buffer,1)) { + printk(KERN_WARNING "%s: I/O error (read)\n", + chip->c.name); + return -1; + } + dprintk("%s: chip_read: 0x%x\n",chip->c.name,buffer); + return buffer; +} + +static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) +{ + int i; + + if (0 == cmd->count) + return 0; + + /* update our shadow register set; print bytes if (debug > 0) */ + dprintk("%s: chip_cmd(%s): reg=%d, data:", + chip->c.name,name,cmd->bytes[0]); + for (i = 1; i < cmd->count; i++) { + dprintk(" 0x%x",cmd->bytes[i]); + chip->shadow.bytes[i+cmd->bytes[0]] = cmd->bytes[i]; + } + dprintk("\n"); + + /* send data to the chip */ + if (cmd->count != i2c_master_send(&chip->c,cmd->bytes,cmd->count)) { + printk(KERN_WARNING "%s: I/O error (%s)\n", chip->c.name, name); + return -1; + } + return 0; +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda985x */ + +/* 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 */ + +int tda9855_volume(int val) { return val/0x2e8+0x27; } +int tda9855_bass(int val) { return val/0xccc+0x06; } +int tda9855_treble(int val) { return (val/0x1c71+0x3)<<1; } + +int tda985x_getmode(struct CHIPSTATE *chip) +{ + int mode; + + mode = ((TDA985x_STP | TDA985x_SAPP) & + chip_read(chip)) >> 4; + /* Add mono mode regardless of SAP and stereo */ + /* Allows forced mono */ + return mode | VIDEO_SOUND_MONO; +} + +void tda985x_setmode(struct CHIPSTATE *chip, int mode) +{ + int c6 = chip->shadow.bytes[TDA9850_C4+1] & 0x3f; + + switch (mode) { + case VIDEO_SOUND_MONO: + c6 |= TDA985x_MONO; + break; + case VIDEO_SOUND_STEREO: + c6 |= TDA985x_STEREO; + break; + case VIDEO_SOUND_LANG1: + c6 |= TDA985x_SAP; + break; + } + chip_write(chip,TDA985x_C6,c6); +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda9873h */ + +/* Subaddresses for TDA9873H */ + +#define TDA9873_SW 0x00 /* Switching */ +#define TDA9873_AD 0x01 /* Adjust */ +#define TDA9873_PT 0x02 /* Port */ + +/* Subaddress 0x00: Switching Data + * B7..B0: + * + * B1, B0: Input source selection + * 0, 0 internal + * 1, 0 external stereo + * 0, 1 external mono + */ +#define TDA9873_INTERNAL 0 +#define TDA9873_EXT_STEREO 2 +#define TDA9873_EXT_MONO 1 + +/* B3, B2: output signal select + * B4 : transmission mode + * 0, 0, 1 Mono + * 1, 0, 0 Stereo + * 1, 1, 1 Stereo (reversed channel) + * 0, 0, 0 Dual AB + * 0, 0, 1 Dual AA + * 0, 1, 0 Dual BB + * 0, 1, 1 Dual BA + */ + +#define TDA9873_TR_MONO 4 +#define TDA9873_TR_STEREO 1 << 4 +#define TDA9873_TR_REVERSE (1 << 3) & (1 << 2) +#define TDA9873_TR_DUALA 1 << 2 +#define TDA9873_TR_DUALB 1 << 3 + +/* output level controls + * B5: output level switch (0 = reduced gain, 1 = normal gain) + * B6: mute (1 = muted) + * B7: auto-mute (1 = auto-mute enabled) + */ + +#define TDA9873_GAIN_NORMAL 1 << 5 +#define TDA9873_MUTE 1 << 6 +#define TDA9873_AUTOMUTE 1 << 7 + +/* Subaddress 0x01: Adjust/standard */ + +/* Lower 4 bits (C3..C0) control stereo adjustment on R channel (-0.6 - +0.7 dB) + * Recommended value is +0 dB + */ + +#define TDA9873_STEREO_ADJ 0x06 /* 0dB gain */ + +/* Bits C6..C4 control FM stantard + * C6, C5, C4 + * 0, 0, 0 B/G (PAL FM) + * 0, 0, 1 M + * 0, 1, 0 D/K(1) + * 0, 1, 1 D/K(2) + * 1, 0, 0 D/K(3) + * 1, 0, 1 I + */ +#define TDA9873_BG 0 +#define TDA9873_M 1 +#define TDA9873_DK1 2 +#define TDA9873_DK2 3 +#define TDA9873_DK3 4 +#define TDA9873_I 5 + +/* C7 controls identification response time (1=fast/0=normal) + */ +#define TDA9873_IDR_NORM 0 +#define TDA9873_IDR_FAST 1 << 7 + + +/* Subaddress 0x02: Port data */ + +/* E1, E0 free programmable ports P1/P2 + 0, 0 both ports low + 0, 1 P1 high + 1, 0 P2 high + 1, 1 both ports high +*/ + +#define TDA9873_PORTS 3 + +/* E2: test port */ +#define TDA9873_TST_PORT 1 << 2 + +/* E5..E3 control mono output channel (together with transmission mode bit B4) + * + * E5 E4 E3 B4 OUTM + * 0 0 0 0 mono + * 0 0 1 0 DUAL B + * 0 1 0 1 mono (from stereo decoder) + */ +#define TDA9873_MOUT_MONO 0 +#define TDA9873_MOUT_FMONO 0 +#define TDA9873_MOUT_DUALA 0 +#define TDA9873_MOUT_DUALB 1 << 3 +#define TDA9873_MOUT_ST 1 << 4 +#define TDA9873_MOUT_EXTM (1 << 4 ) & (1 << 3) +#define TDA9873_MOUT_EXTL 1 << 5 +#define TDA9873_MOUT_EXTR (1 << 5 ) & (1 << 3) +#define TDA9873_MOUT_EXTLR (1 << 5 ) & (1 << 4) +#define TDA9873_MOUT_MUTE (1 << 5 ) & (1 << 4) & (1 << 3) + +/* Status bits: (chip read) */ +#define TDA9873_PONR 0 /* Power-on reset detected if = 1 */ +#define TDA9873_STEREO 2 /* Stereo sound is identified */ +#define TDA9873_DUAL 4 /* Dual sound is identified */ + +int tda9873_getmode(struct CHIPSTATE *chip) +{ + int mode; + + mode = chip_read(chip); + dprintk ("tda9873_getmode(): raw chip read: %d\n", mode); + mode = ((TDA9873_STEREO | TDA9873_DUAL) & mode) ; + dprintk ("tda9873_getmode(): now: %d, return : %d\n", mode,(mode | VIDEO_SOUND_MONO)); + + /* Add mono mode regardless of SAP and stereo */ + /* Allows forced mono */ + return mode | VIDEO_SOUND_MONO; +} + +void tda9873_setmode(struct CHIPSTATE *chip, int mode) +{ + int sw_data = chip->shadow.bytes[TDA9873_SW+1] & 0xe3; + /* int adj_data = chip->shadow.bytes[TDA9873_AD+1] ; */ + dprintk("tda9873_setmode(): chip->shadow.bytes[%d] = %d\n", TDA9873_SW+1, chip->shadow.bytes[TDA9873_SW+1]); + dprintk("tda9873_setmode(): sw_data = %d\n", sw_data); + + switch (mode) { + case VIDEO_SOUND_MONO: + sw_data |= TDA9873_TR_MONO; + break; + case VIDEO_SOUND_STEREO: + sw_data |= TDA9873_TR_STEREO; + break; + case VIDEO_SOUND_LANG1: + sw_data |= TDA9873_TR_DUALA; + break; + case VIDEO_SOUND_LANG2: + sw_data |= TDA9873_TR_DUALB; + break; + } + dprintk("tda9873_setmode(): req. mode %d; chip_write: %d\n", mode, sw_data); + chip_write(chip,TDA9873_SW,sw_data); +} + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tea6420 */ + +#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 */ + +#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 */ + +int tea6300_shift10(int val) { return val >> 10; } +int tea6300_shift12(int val) { return val >> 12; } + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for tda8425 */ + +#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 */ + +int tda8425_shift10(int val) { return val >> 10 | 0xc0; } +int tda8425_shift12(int val) { return val >> 12 | 0xf0; } + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - defines+functions for pic16c54 (PV951) */ + +/* the registers of 16C54, I2C sub address. */ +#define PIC16C54_REG_KEY_CODE 0x01 /* Not use. */ +#define PIC16C54_REG_MISC 0x02 + +/* bit definition of the RESET register, I2C data. */ +#define PIC16C54_MISC_RESET_REMOTE_CTL 0x01 /* bit 0, Reset to receive the key */ + /* code of remote controller */ +#define PIC16C54_MISC_MTS_MAIN 0x02 /* bit 1 */ +#define PIC16C54_MISC_MTS_SAP 0x04 /* bit 2 */ +#define PIC16C54_MISC_MTS_BOTH 0x08 /* bit 3 */ +#define PIC16C54_MISC_SND_MUTE 0x10 /* bit 4, Mute Audio(Line-in and Tuner) */ +#define PIC16C54_MISC_SND_NOTMUTE 0x20 /* bit 5 */ +#define PIC16C54_MISC_SWITCH_TUNER 0x40 /* bit 6 , Switch to Line-in */ +#define PIC16C54_MISC_SWITCH_LINE 0x80 /* bit 7 , Switch to Tuner */ + + +/* ---------------------------------------------------------------------- */ +/* audio chip descriptions - struct CHIPDESC */ + +/* insmod options to enable/disable individual audio chips */ +int tda8425 = 1; +int tda9850 = 1; +int tda9855 = 1; +int tda9873 = 1; +int tea6300 = 0; +int pic16c54 = 1; +MODULE_PARM(tda8425,"i"); +MODULE_PARM(tda9850,"i"); +MODULE_PARM(tda9855,"i"); +MODULE_PARM(tda9873,"i"); +MODULE_PARM(tea6300,"i"); +MODULE_PARM(pic16c54,"i"); + +struct CHIPDESC chiplist[] = { + { + name: "tda9850", + insmodopt: &tda9850, + addr_lo: I2C_TDA985x_L >> 1, + addr_hi: I2C_TDA985x_H >> 1, + registers: 11, + + getmode: tda985x_getmode, + setmode: tda985x_setmode, + + init: { 8, { TDA9850_C4, 0x08, 0x08, TDA985x_STEREO, 0x07, 0x10, 0x10, 0x03 } } + }, + { + name: "tda9855", + insmodopt: &tda9855, + addr_lo: I2C_TDA985x_L >> 1, + addr_hi: I2C_TDA985x_H >> 1, + registers: 11, + flags: CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, + + leftreg: TDA9855_VR, + rightreg: TDA9855_VL, + bassreg: TDA9855_BA, + treblereg: TDA9855_TR, + volfunc: tda9855_volume, + bassfunc: tda9855_bass, + treblefunc: tda9855_treble, + + getmode: tda985x_getmode, + setmode: tda985x_setmode, + + init: { 12, { 0, 0x6f, 0x6f, 0x0e, 0x07<<1, 0x8<<2, + TDA9855_MUTE | TDA9855_AVL | TDA9855_LOUD | TDA9855_INT, + TDA985x_STEREO | TDA9855_LINEAR | TDA9855_TZCM | TDA9855_VZCM, + 0x07, 0x10, 0x10, 0x03 }} + }, + { + name: "tda9873h", + insmodopt: &tda9873, + addr_lo: I2C_TDA985x_L >> 1, + addr_hi: I2C_TDA985x_H >> 1, + registers: 3, + + getmode: tda9873_getmode, + setmode: tda9873_setmode, + + init: { 4, { TDA9873_SW, 0xa0, 0x06, 0x03 } } + }, + { + name: "tea6300", + insmodopt: &tea6300, + addr_lo: I2C_TEA6300 >> 1, + addr_hi: I2C_TEA6300 >> 1, + registers: 6, + flags: CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, + + leftreg: TEA6300_VR, + rightreg: TEA6300_VL, + bassreg: TEA6300_BA, + treblereg: TEA6300_TR, + volfunc: tea6300_shift10, + bassfunc: tea6300_shift12, + treblefunc: tea6300_shift12, + + inputreg: TEA6300_S, + inputmap: { TEA6300_S_SA, TEA6300_S_SB, TEA6300_S_SC }, + inputmute: TEA6300_S_GMU, + }, + { + name: "tda8425", + insmodopt: &tda8425, + addr_lo: I2C_TDA8425 >> 1, + addr_hi: I2C_TDA8425 >> 1, + registers: 9, + flags: CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE | CHIP_HAS_INPUTSEL, + + leftreg: TDA8425_VR, + rightreg: TDA8425_VL, + bassreg: TDA8425_BA, + treblereg: TDA8425_TR, + volfunc: tda8425_shift10, + bassfunc: tda8425_shift12, + treblefunc: tda8425_shift12, + + inputreg: TDA8425_S1, + inputmap: { TDA8425_S1_ON, TDA8425_S1_ON, TDA8425_S1_ON }, + inputmute: TDA8425_S1_OFF, + }, + { + name: "pic16c54", + insmodopt: &pic16c54, + addr_lo: I2C_PIC16C54 >> 1, + addr_hi: I2C_PIC16C54>> 1, + registers: 2, + flags: CHIP_HAS_INPUTSEL, + + inputreg: PIC16C54_REG_MISC, + inputmap: {PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_TUNER, + PIC16C54_MISC_SND_NOTMUTE|PIC16C54_MISC_SWITCH_LINE}, + inputmute: PIC16C54_MISC_SND_MUTE, + }, + { name: NULL } /* EOF */ +}; + + +/* ---------------------------------------------------------------------- */ +/* i2c registration */ + +static int chip_attach(struct i2c_adapter *adap, int addr, + unsigned short flags, int kind) +{ + struct CHIPSTATE *chip; + struct CHIPDESC *desc; + + /* find description for the chip */ + dprintk("tvaudio: chip @ addr=0x%x\n",addr<<1); + for (desc = chiplist; desc->name != NULL; desc++) { + if (0 == *(desc->insmodopt)) + continue; + if (addr >= desc->addr_lo && + addr <= desc->addr_hi) + break; + } + if (desc->name == NULL) { + dprintk("tvaudio: no matching chip description found\n"); + return -EIO; + } + dprintk("tvaudio: %s matches:%s%s%s\n",desc->name, + (desc->flags & CHIP_HAS_VOLUME) ? " volume" : "", + (desc->flags & CHIP_HAS_BASSTREBLE) ? " bass/treble" : "", + (desc->flags & CHIP_HAS_INPUTSEL) ? " audiomux" : ""); + + /* fill required data structures */ + chip = kmalloc(sizeof(*chip),GFP_KERNEL); + if (!chip) + return -ENOMEM; + memcpy(&chip->c,&client_template,sizeof(struct i2c_client)); + chip->c.adapter = adap; + chip->c.addr = addr; + chip->c.data = chip; + strcpy(chip->c.name,desc->name); + + chip->type = desc-chiplist; + chip->shadow.count = desc->registers+1; + + /* register */ + MOD_INC_USE_COUNT; + i2c_attach_client(&chip->c); + + /* initialization */ + chip_cmd(chip,"init",&desc->init); + if (desc->flags & CHIP_HAS_VOLUME) { + chip->left = desc->leftinit ? desc->leftinit : 65536; + chip->right = desc->rightinit ? desc->rightinit : 65536; + chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); + chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); + } + if (desc->flags & CHIP_HAS_BASSTREBLE) { + chip->treble = desc->trebleinit ? desc->trebleinit : 32768; + chip->bass = desc->bassinit ? desc->bassinit : 32768; + chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); + chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); + } + return 0; +} + +static int chip_probe(struct i2c_adapter *adap) +{ + if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return i2c_probe(adap, &addr_data, chip_attach); + return 0; +} + +static int chip_detach(struct i2c_client *client) +{ + struct CHIPSTATE *chip = client->data; + + i2c_detach_client(&chip->c); + kfree(chip); + MOD_DEC_USE_COUNT; + return 0; +} + + +/* ---------------------------------------------------------------------- */ +/* video4linux interface */ + +static int chip_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + __u16 *sarg = arg; + struct CHIPSTATE *chip = client->data; + struct CHIPDESC *desc = chiplist + chip->type; + + dprintk("%s: chip_command 0x%x\n",chip->c.name,cmd); + + switch (cmd) { + case AUDC_SET_INPUT: + if (desc->flags & CHIP_HAS_INPUTSEL) { + if (*sarg &= AUDIO_MUTE) + chip_write(chip,desc->inputreg,desc->inputmute); + else + chip_write(chip,desc->inputreg,desc->inputmap[*sarg]); + } + break; + /* --- v4l ioctls --- */ + /* take care: bttv does userspace copying, we'll get a + kernel pointer here... */ + case VIDIOCGAUDIO: + { + struct video_audio *va = arg; + + if (desc->flags & CHIP_HAS_VOLUME) { + va->flags |= VIDEO_AUDIO_VOLUME; + va->volume = MAX(chip->left,chip->right); + va->balance = (32768*MIN(chip->left,chip->right))/ + (va->volume ? va->volume : 1); + } + if (desc->flags & CHIP_HAS_BASSTREBLE) { + va->flags |= VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; + va->bass = chip->bass; + va->treble = chip->treble; + } + if (desc->getmode) + va->mode = desc->getmode(chip); + else + va->mode = VIDEO_SOUND_MONO; + break; + } + + case VIDIOCSAUDIO: + { + struct video_audio *va = arg; + + if (desc->flags & CHIP_HAS_VOLUME) { + chip->left = (MIN(65536 - va->balance,32768) * + va->volume) / 32768; + chip->right = (MIN(va->balance,32768) * + va->volume) / 32768; + chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); + chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); + } + if (desc->flags & CHIP_HAS_BASSTREBLE) { + chip->bass = va->bass; + chip->treble = va->treble; + chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); + chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); + } + if (desc->setmode && va->mode) + desc->setmode(chip,va->mode); + break; + } + } + return 0; +} + + +static struct i2c_driver driver = { + "generic i2c audio driver", + I2C_DRIVERID_TDA9855, /* FIXME */ + I2C_DF_NOTIFY, + chip_probe, + chip_detach, + chip_command, +}; + +static struct i2c_client client_template = +{ + "(unset)", /* name */ + -1, + 0, + 0, + NULL, + &driver +}; + +int audiochip_init_module(void) +{ + i2c_add_driver(&driver); + return 0; +} + +void audiochip_cleanup_module(void) +{ + i2c_del_driver(&driver); +} + +module_init(audiochip_init_module); +module_exit(audiochip_cleanup_module); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -urN linux-2.2.16.SuSE/drivers/char/tvmixer.c linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tvmixer.c --- linux-2.2.16.SuSE/drivers/char/tvmixer.c Thu Jan 1 01:00:00 1970 +++ linux-2.2.16.SuSE.bttv-0.7.35/drivers/char/tvmixer.c Wed Jun 7 20:32:30 2000 @@ -0,0 +1,355 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +# include "kcompat24.h" +#endif +#include "audiochip.h" + +#define DEV_MAX 4 + +static int debug = 0; +static int devnr = -1; + +MODULE_PARM(debug,"i"); +MODULE_PARM(devnr,"i"); + +/* ----------------------------------------------------------------------- */ + +struct TVMIXER { + struct i2c_client *dev; + int minor; + int count; +}; + +static struct TVMIXER devices[DEV_MAX]; + +static int tvmixer_adapters(struct i2c_adapter *adap); +static int tvmixer_clients(struct i2c_client *client); + +static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); +static int tvmixer_open(struct inode *inode, struct file *file); +static int tvmixer_release(struct inode *inode, struct file *file); +static loff_t tvmixer_llseek(struct file *file, loff_t offset, int origin); + + +static struct i2c_driver driver = { + "tv card mixer driver", + 42 /* I2C_DRIVERID_FIXME */, + I2C_DF_DUMMY, + tvmixer_adapters, + tvmixer_clients, +}; + +static struct file_operations tvmixer_fops = { + llseek: tvmixer_llseek, + ioctl: tvmixer_ioctl, + open: tvmixer_open, + release: tvmixer_release, +}; + +/* ----------------------------------------------------------------------- */ + +static int mix_to_v4l(int i) +{ + int r; + + r = ((i & 0xff) * 65536 + 50) / 100; + if (r > 65535) r = 65535; + if (r < 0) r = 0; + return r; +} + +static int v4l_to_mix(int i) +{ + int r; + + r = (i * 100 + 32768) / 65536; + if (r > 100) r = 100; + if (r < 0) r = 0; + return r | (r << 8); +} + +static int v4l_to_mix2(int l, int r) +{ + r = (r * 100 + 32768) / 65536; + if (r > 100) r = 100; + if (r < 0) r = 0; + l = (l * 100 + 32768) / 65536; + if (l > 100) l = 100; + if (l < 0) l = 0; + return (r << 8) | l; +} + +static int tvmixer_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct video_audio va; + int left,right,ret,val = 0; + struct TVMIXER *mix = file->private_data; + struct i2c_client *client = mix->dev; + + if (NULL == client) + return -ENODEV; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "tv card", sizeof(info.id)); + strncpy(info.name, client->name, sizeof(info.name)); + info.modify_counter = 42 /* FIXME */; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "tv card", sizeof(info.id)); + strncpy(info.name, client->name, sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + + if (_SIOC_DIR(cmd) & _SIOC_WRITE) + if (get_user(val, (int *)arg)) + return -EFAULT; + + /* read state */ + memset(&va,0,sizeof(va)); + client->driver->command(client,VIDIOCGAUDIO,&va); + + switch (cmd) { + case MIXER_READ(SOUND_MIXER_RECMASK): + case MIXER_READ(SOUND_MIXER_CAPS): + case MIXER_READ(SOUND_MIXER_RECSRC): + case MIXER_WRITE(SOUND_MIXER_RECSRC): + ret = 0; + break; + + case MIXER_READ(SOUND_MIXER_STEREODEVS): + ret = SOUND_MASK_VOLUME; + break; + case MIXER_READ(SOUND_MIXER_DEVMASK): + ret = SOUND_MASK_VOLUME; + if (va.flags & VIDEO_AUDIO_BASS) + ret |= SOUND_MASK_BASS; + if (va.flags & VIDEO_AUDIO_TREBLE) + ret |= SOUND_MASK_TREBLE; + break; + + case MIXER_WRITE(SOUND_MIXER_VOLUME): + left = mix_to_v4l(val); + right = mix_to_v4l(val >> 8); + va.volume = MAX(left,right); + va.balance = (32768*MIN(left,right)) / (va.volume ? va.volume : 1); + va.balance = (leftdriver->command(client,VIDIOCSAUDIO,&va); + client->driver->command(client,VIDIOCGAUDIO,&va); + /* fall throuth */ + case MIXER_READ(SOUND_MIXER_VOLUME): + left = (MIN(65536 - va.balance,32768) * + va.volume) / 32768; + right = (MIN(va.balance,32768) * + va.volume) / 32768; + ret = v4l_to_mix2(left,right); + break; + + case MIXER_WRITE(SOUND_MIXER_BASS): + va.bass = mix_to_v4l(val); + client->driver->command(client,VIDIOCSAUDIO,&va); + client->driver->command(client,VIDIOCGAUDIO,&va); + /* fall throuth */ + case MIXER_READ(SOUND_MIXER_BASS): + ret = v4l_to_mix(va.bass); + break; + + case MIXER_WRITE(SOUND_MIXER_TREBLE): + va.treble = mix_to_v4l(val); + client->driver->command(client,VIDIOCSAUDIO,&va); + client->driver->command(client,VIDIOCGAUDIO,&va); + /* fall throuth */ + case MIXER_READ(SOUND_MIXER_TREBLE): + ret = v4l_to_mix(va.treble); + break; + + default: + return -EINVAL; + } + if (put_user(ret, (int *)arg)) + return -EFAULT; + return 0; +} + +static int tvmixer_open(struct inode *inode, struct file *file) +{ + int i, minor = MINOR(inode->i_rdev); + struct TVMIXER *mix = NULL; + struct i2c_client *client = NULL; + + for (i = 0; i < DEV_MAX; i++) { + if (devices[i].minor == minor) { + mix = devices+i; + client = mix->dev; + break; + } + } + + if (NULL == client) + return -ENODEV; + + /* lock bttv in memory while the mixer is in use */ + file->private_data = mix; + if (client->adapter->inc_use) + client->adapter->inc_use(client->adapter); + MOD_INC_USE_COUNT; + return 0; +} + +static int tvmixer_release(struct inode *inode, struct file *file) +{ + struct TVMIXER *mix = file->private_data; + struct i2c_client *client = mix->dev; + + if (NULL == client) + return -ENODEV; + + if (client->adapter->dec_use) + client->adapter->dec_use(client->adapter); + MOD_DEC_USE_COUNT; + return 0; +} + +static loff_t tvmixer_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* ----------------------------------------------------------------------- */ + +static int tvmixer_adapters(struct i2c_adapter *adap) +{ + return 0; +} + +static int tvmixer_clients(struct i2c_client *client) +{ + struct video_audio va; + int i,minor; + + /* TV card ??? */ + if (client->adapter->id != (I2C_ALGO_BIT | I2C_HW_B_BT848)) { + if (debug) + printk("tvmixer: %s is not a tv card\n", + client->adapter->name); + return -1; + } + printk("tvmixer: debug: %s\n",client->name); + + /* unregister ?? */ + for (i = 0; i < DEV_MAX; i++) { + if (devices[i].dev == client) { + /* unregister */ + unregister_sound_mixer(devices[i].minor); + devices[i].dev = NULL; + devices[i].minor = -1; + printk("tvmixer: %s unregistered (#1)\n",client->name); + return 0; + } + } + + /* look for a free slot */ + for (i = 0; i < DEV_MAX; i++) + if (NULL == devices[i].dev) + break; + if (i == DEV_MAX) { + printk(KERN_WARNING "tvmixer: DEV_MAX too small\n"); + return -1; + } + + /* audio chip with mixer ??? */ + if (NULL == client->driver->command) { + if (debug) + printk("tvmixer: %s: driver->command is NULL\n", + client->driver->name); + return -1; + } + memset(&va,0,sizeof(va)); + if (0 != client->driver->command(client,VIDIOCGAUDIO,&va)) { + if (debug) + printk("tvmixer: %s: VIDIOCGAUDIO failed\n", + client->name); + return -1; + } + if (0 == (va.flags & VIDEO_AUDIO_VOLUME)) { + if (debug) + printk("tvmixer: %s: has no volume control\n", + client->name); + return -1; + } + + /* everything is fine, register */ + if ((minor = register_sound_mixer(&tvmixer_fops,devnr)) < 0) { + printk(KERN_ERR "tvmixer: cannot allocate mixer device\n"); + return -1; + } + + devices[i].minor = minor; + devices[i].count = 0; + devices[i].dev = client; + printk("tvmixer: %s (%s) registered with minor %d\n", + client->name,client->adapter->name,minor); + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +int tvmixer_init_module(void) +{ + int i; + + for (i = 0; i < DEV_MAX; i++) + devices[i].minor = -1; + i2c_add_driver(&driver); + return 0; +} + +void tvmixer_cleanup_module(void) +{ + int i; + + i2c_del_driver(&driver); + for (i = 0; i < DEV_MAX; i++) { + if (devices[i].minor != -1) { + unregister_sound_mixer(devices[i].minor); + printk("tvmixer: %s unregistered (#2)\n", + devices[i].dev->name); + } + } +} + +module_init(tvmixer_init_module); +module_exit(tvmixer_cleanup_module); + +/* + * Overrides for Emacs so that we follow Linus's tabbing style. + * --------------------------------------------------------------------------- + * Local variables: + * c-basic-offset: 8 + * End: + */