2005-04-17 02:20:36 +04:00
/*
* tveeprom - eeprom decoder for tvcard configuration eeproms
*
* Data and decoding routines shamelessly borrowed from bttv - cards . c
* eeprom access routine shamelessly borrowed from bttv - if . c
* which are :
Copyright ( C ) 1996 , 97 , 98 Ralph Metzler ( rjkm @ thp . uni - koeln . de )
& Marcus Metzler ( mocm @ thp . uni - koeln . de )
( c ) 1999 - 2001 Gerd Knorr < kraxel @ goldbach . in - berlin . de >
* Adjustments to fit a more general model and all bugs :
Copyright ( C ) 2003 John Klar < linpvr at projectplasma . com >
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 0213 9 , USA .
*/
# include <linux/module.h>
# include <linux/moduleparam.h>
# include <linux/errno.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/videodev.h>
# include <linux/i2c.h>
# include <media/tuner.h>
# include <media/tveeprom.h>
2005-11-09 08:37:16 +03:00
# include <media/audiochip.h>
2005-04-17 02:20:36 +04:00
MODULE_DESCRIPTION ( " i2c Hauppauge eeprom decoder driver " ) ;
MODULE_AUTHOR ( " John Klar " ) ;
MODULE_LICENSE ( " GPL " ) ;
static int debug = 0 ;
module_param ( debug , int , 0644 ) ;
2005-09-10 00:04:05 +04:00
MODULE_PARM_DESC ( debug , " Debug level (0-1) " ) ;
2005-04-17 02:20:36 +04:00
# define STRM(array,i) (i < sizeof(array) / sizeof(char*) ? array[i] : "unknown")
2005-09-10 00:04:05 +04:00
# define tveeprom_info(fmt, arg...) do {\
printk ( KERN_INFO " tveeprom %d-%04x: " fmt , \
c - > adapter - > nr , c - > addr , # # arg ) ; } while ( 0 )
# define tveeprom_warn(fmt, arg...) do {\
printk ( KERN_WARNING " tveeprom %d-%04x: " fmt , \
c - > adapter - > nr , c - > addr , # # arg ) ; } while ( 0 )
# define tveeprom_dbg(fmt, arg...) do {\
if ( debug ) \
printk ( KERN_INFO " tveeprom %d-%04x: " fmt , \
c - > adapter - > nr , c - > addr , # # arg ) ; } while ( 0 )
2005-04-17 02:20:36 +04:00
/* ----------------------------------------------------------------------- */
/* some hauppauge specific stuff */
static struct HAUPPAUGE_TUNER_FMT
{
int id ;
char * name ;
}
hauppauge_tuner_fmt [ ] =
{
2005-09-10 00:04:05 +04:00
{ 0x00000000 , " unknown1 " } ,
{ 0x00000000 , " unknown2 " } ,
{ 0x00000007 , " PAL(B/G) " } ,
{ 0x00001000 , " NTSC(M) " } ,
{ 0x00000010 , " PAL(I) " } ,
{ 0x00400000 , " SECAM(L/L') " } ,
{ 0x00000e00 , " PAL(D/K) " } ,
{ 0x03000000 , " ATSC Digital " } ,
2005-04-17 02:20:36 +04:00
} ;
/* This is the full list of possible tuners. Many thanks to Hauppauge for
supplying this information . Note that many tuners where only used for
testing and never made it to the outside world . So you will only see
a subset in actual produced cards . */
static struct HAUPPAUGE_TUNER
{
int id ;
char * name ;
}
hauppauge_tuner [ ] =
{
/* 0-9 */
{ TUNER_ABSENT , " None " } ,
{ TUNER_ABSENT , " External " } ,
{ TUNER_ABSENT , " Unspecified " } ,
{ TUNER_PHILIPS_PAL , " Philips FI1216 " } ,
{ TUNER_PHILIPS_SECAM , " Philips FI1216MF " } ,
{ TUNER_PHILIPS_NTSC , " Philips FI1236 " } ,
{ TUNER_PHILIPS_PAL_I , " Philips FI1246 " } ,
{ TUNER_PHILIPS_PAL_DK , " Philips FI1256 " } ,
{ TUNER_PHILIPS_PAL , " Philips FI1216 MK2 " } ,
{ TUNER_PHILIPS_SECAM , " Philips FI1216MF MK2 " } ,
/* 10-19 */
{ TUNER_PHILIPS_NTSC , " Philips FI1236 MK2 " } ,
{ TUNER_PHILIPS_PAL_I , " Philips FI1246 MK2 " } ,
{ TUNER_PHILIPS_PAL_DK , " Philips FI1256 MK2 " } ,
{ TUNER_TEMIC_NTSC , " Temic 4032FY5 " } ,
{ TUNER_TEMIC_PAL , " Temic 4002FH5 " } ,
{ TUNER_TEMIC_PAL_I , " Temic 4062FY5 " } ,
{ TUNER_PHILIPS_PAL , " Philips FR1216 MK2 " } ,
{ TUNER_PHILIPS_SECAM , " Philips FR1216MF MK2 " } ,
{ TUNER_PHILIPS_NTSC , " Philips FR1236 MK2 " } ,
{ TUNER_PHILIPS_PAL_I , " Philips FR1246 MK2 " } ,
/* 20-29 */
{ TUNER_PHILIPS_PAL_DK , " Philips FR1256 MK2 " } ,
{ TUNER_PHILIPS_PAL , " Philips FM1216 " } ,
{ TUNER_PHILIPS_SECAM , " Philips FM1216MF " } ,
{ TUNER_PHILIPS_NTSC , " Philips FM1236 " } ,
{ TUNER_PHILIPS_PAL_I , " Philips FM1246 " } ,
{ TUNER_PHILIPS_PAL_DK , " Philips FM1256 " } ,
{ TUNER_TEMIC_4036FY5_NTSC , " Temic 4036FY5 " } ,
{ TUNER_ABSENT , " Samsung TCPN9082D " } ,
{ TUNER_ABSENT , " Samsung TCPM9092P " } ,
{ TUNER_TEMIC_4006FH5_PAL , " Temic 4006FH5 " } ,
/* 30-39 */
{ TUNER_ABSENT , " Samsung TCPN9085D " } ,
{ TUNER_ABSENT , " Samsung TCPB9085P " } ,
{ TUNER_ABSENT , " Samsung TCPL9091P " } ,
{ TUNER_TEMIC_4039FR5_NTSC , " Temic 4039FR5 " } ,
{ TUNER_PHILIPS_FQ1216ME , " Philips FQ1216 ME " } ,
{ TUNER_TEMIC_4066FY5_PAL_I , " Temic 4066FY5 " } ,
2005-11-09 08:36:56 +03:00
{ TUNER_PHILIPS_NTSC , " Philips TD1536 " } ,
{ TUNER_PHILIPS_NTSC , " Philips TD1536D " } ,
2005-04-17 02:20:36 +04:00
{ TUNER_PHILIPS_NTSC , " Philips FMR1236 " } , /* mono radio */
{ TUNER_ABSENT , " Philips FI1256MP " } ,
/* 40-49 */
{ TUNER_ABSENT , " Samsung TCPQ9091P " } ,
{ TUNER_TEMIC_4006FN5_MULTI_PAL , " Temic 4006FN5 " } ,
{ TUNER_TEMIC_4009FR5_PAL , " Temic 4009FR5 " } ,
{ TUNER_TEMIC_4046FM5 , " Temic 4046FM5 " } ,
{ TUNER_TEMIC_4009FN5_MULTI_PAL_FM , " Temic 4009FN5 " } ,
{ TUNER_ABSENT , " Philips TD1536D FH 44 " } ,
{ TUNER_LG_NTSC_FM , " LG TP18NSR01F " } ,
{ TUNER_LG_PAL_FM , " LG TP18PSB01D " } ,
{ TUNER_LG_PAL , " LG TP18PSB11D " } ,
{ TUNER_LG_PAL_I_FM , " LG TAPC-I001D " } ,
/* 50-59 */
{ TUNER_LG_PAL_I , " LG TAPC-I701D " } ,
{ TUNER_ABSENT , " Temic 4042FI5 " } ,
{ TUNER_MICROTUNE_4049FM5 , " Microtune 4049 FM5 " } ,
{ TUNER_ABSENT , " LG TPI8NSR11F " } ,
{ TUNER_ABSENT , " Microtune 4049 FM5 Alt I2C " } ,
2005-09-10 00:03:47 +04:00
{ TUNER_PHILIPS_FM1216ME_MK3 , " Philips FQ1216ME MK3 " } ,
2005-04-17 02:20:36 +04:00
{ TUNER_ABSENT , " Philips FI1236 MK3 " } ,
{ TUNER_PHILIPS_FM1216ME_MK3 , " Philips FM1216 ME MK3 " } ,
2005-09-10 00:03:37 +04:00
{ TUNER_PHILIPS_FM1236_MK3 , " Philips FM1236 MK3 " } ,
2005-04-17 02:20:36 +04:00
{ TUNER_ABSENT , " Philips FM1216MP MK3 " } ,
/* 60-69 */
2005-09-10 00:03:37 +04:00
{ TUNER_PHILIPS_FM1216ME_MK3 , " LG S001D MK3 " } ,
2005-04-17 02:20:36 +04:00
{ TUNER_ABSENT , " LG M001D MK3 " } ,
{ TUNER_ABSENT , " LG S701D MK3 " } ,
{ TUNER_ABSENT , " LG M701D MK3 " } ,
{ TUNER_ABSENT , " Temic 4146FM5 " } ,
{ TUNER_ABSENT , " Temic 4136FY5 " } ,
{ TUNER_ABSENT , " Temic 4106FH5 " } ,
{ TUNER_ABSENT , " Philips FQ1216LMP MK3 " } ,
{ TUNER_LG_NTSC_TAPE , " LG TAPE H001F MK3 " } ,
2005-09-10 00:03:47 +04:00
{ TUNER_LG_NTSC_TAPE , " LG TAPE H701F MK3 " } ,
2005-04-17 02:20:36 +04:00
/* 70-79 */
{ TUNER_ABSENT , " LG TALN H200T " } ,
{ TUNER_ABSENT , " LG TALN H250T " } ,
{ TUNER_ABSENT , " LG TALN M200T " } ,
{ TUNER_ABSENT , " LG TALN Z200T " } ,
{ TUNER_ABSENT , " LG TALN S200T " } ,
{ TUNER_ABSENT , " Thompson DTT7595 " } ,
{ TUNER_ABSENT , " Thompson DTT7592 " } ,
{ TUNER_ABSENT , " Silicon TDA8275C1 8290 " } ,
{ TUNER_ABSENT , " Silicon TDA8275C1 8290 FM " } ,
{ TUNER_ABSENT , " Thompson DTT757 " } ,
/* 80-89 */
{ TUNER_ABSENT , " Philips FQ1216LME MK3 " } ,
{ TUNER_ABSENT , " LG TAPC G701D " } ,
{ TUNER_LG_NTSC_NEW_TAPC , " LG TAPC H791F " } ,
2005-09-10 00:03:37 +04:00
{ TUNER_LG_PAL_NEW_TAPC , " TCL 2002MB 3 " } ,
{ TUNER_LG_PAL_NEW_TAPC , " TCL 2002MI 3 " } ,
2005-04-17 02:20:36 +04:00
{ TUNER_TCL_2002N , " TCL 2002N 6A " } ,
2005-11-09 08:36:56 +03:00
{ TUNER_PHILIPS_FM1236_MK3 , " Philips FQ1236 MK3 " } ,
2005-04-17 02:20:36 +04:00
{ TUNER_ABSENT , " Samsung TCPN 2121P30A " } ,
{ TUNER_ABSENT , " Samsung TCPE 4121P30A " } ,
2005-08-01 09:34:43 +04:00
{ TUNER_PHILIPS_FM1216ME_MK3 , " TCL MFPE05 2 " } ,
2005-04-17 02:20:36 +04:00
/* 90-99 */
{ TUNER_ABSENT , " LG TALN H202T " } ,
{ TUNER_PHILIPS_FQ1216AME_MK4 , " Philips FQ1216AME MK4 " } ,
{ TUNER_PHILIPS_FQ1236A_MK4 , " Philips FQ1236A MK4 " } ,
{ TUNER_ABSENT , " Philips FQ1286A MK4 " } ,
{ TUNER_ABSENT , " Philips FQ1216ME MK5 " } ,
{ TUNER_ABSENT , " Philips FQ1236 MK5 " } ,
2005-11-09 08:36:44 +03:00
{ TUNER_ABSENT , " Samsung TCPG_6121P30A " } ,
{ TUNER_TCL_2002MB , " TCL 2002MB_3H " } ,
2005-11-09 08:36:56 +03:00
{ TUNER_ABSENT , " TCL 2002MI_3H " } ,
{ TUNER_TCL_2002N , " TCL 2002N 5H " } ,
2005-11-09 08:36:44 +03:00
/* 100-109 */
{ TUNER_ABSENT , " Philips FMD1216ME " } ,
2005-11-09 08:36:56 +03:00
{ TUNER_TEA5767 , " Philips TEA5768HL FM Radio " } ,
{ TUNER_ABSENT , " Panasonic ENV57H12D5 " } ,
{ TUNER_ABSENT , " TCL MFNM05-4 " } ,
{ TUNER_ABSENT , " TCL MNM05-4 " } ,
{ TUNER_PHILIPS_FM1216ME_MK3 , " TCL MPE05-2 " } ,
{ TUNER_ABSENT , " TCL MQNM05-4 " } ,
{ TUNER_ABSENT , " LG TAPC-W701D " } ,
{ TUNER_ABSENT , " TCL 9886P-WM " } ,
{ TUNER_ABSENT , " TCL 1676NM-WM " } ,
2005-04-17 02:20:36 +04:00
} ;
2005-11-09 08:36:56 +03:00
static struct HAUPPAUGE_AUDIOIC
{
enum audiochip id ;
char * name ;
}
audioIC [ ] =
{
/* 0-4 */
{ AUDIO_CHIP_NONE , " None " } ,
{ AUDIO_CHIP_TEA6300 , " TEA6300 " } ,
{ AUDIO_CHIP_TEA6300 , " TEA6320 " } ,
{ AUDIO_CHIP_TDA985X , " TDA9850 " } ,
{ AUDIO_CHIP_MSP34XX , " MSP3400C " } ,
/* 5-9 */
{ AUDIO_CHIP_MSP34XX , " MSP3410D " } ,
{ AUDIO_CHIP_MSP34XX , " MSP3415 " } ,
{ AUDIO_CHIP_MSP34XX , " MSP3430 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP3438 " } ,
{ AUDIO_CHIP_UNKNOWN , " CS5331 " } ,
/* 10-14 */
{ AUDIO_CHIP_MSP34XX , " MSP3435 " } ,
{ AUDIO_CHIP_MSP34XX , " MSP3440 " } ,
{ AUDIO_CHIP_MSP34XX , " MSP3445 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP3411 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP3416 " } ,
/* 15-19 */
{ AUDIO_CHIP_MSP34XX , " MSP3425 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP3451 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP3418 " } ,
{ AUDIO_CHIP_UNKNOWN , " Type 0x12 " } ,
{ AUDIO_CHIP_UNKNOWN , " OKI7716 " } ,
/* 20-24 */
{ AUDIO_CHIP_UNKNOWN , " MSP4410 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP4420 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP4440 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP4450 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP4408 " } ,
/* 25-29 */
{ AUDIO_CHIP_UNKNOWN , " MSP4418 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP4428 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP4448 " } ,
{ AUDIO_CHIP_UNKNOWN , " MSP4458 " } ,
{ AUDIO_CHIP_UNKNOWN , " Type 0x1d " } ,
/* 30-34 */
{ AUDIO_CHIP_INTERNAL , " CX880 " } ,
{ AUDIO_CHIP_INTERNAL , " CX881 " } ,
{ AUDIO_CHIP_INTERNAL , " CX883 " } ,
{ AUDIO_CHIP_INTERNAL , " CX882 " } ,
{ AUDIO_CHIP_INTERNAL , " CX25840 " } ,
/* 35-38 */
{ AUDIO_CHIP_INTERNAL , " CX25841 " } ,
{ AUDIO_CHIP_INTERNAL , " CX25842 " } ,
{ AUDIO_CHIP_INTERNAL , " CX25843 " } ,
{ AUDIO_CHIP_INTERNAL , " CX23418 " } ,
2005-09-10 00:04:05 +04:00
} ;
2005-04-17 02:20:36 +04:00
2005-09-10 00:04:05 +04:00
/* This list is supplied by Hauppauge. Thanks! */
static const char * decoderIC [ ] = {
2005-11-09 08:36:56 +03:00
/* 0-4 */
" None " , " BT815 " , " BT817 " , " BT819 " , " BT815A " ,
/* 5-9 */
" BT817A " , " BT819A " , " BT827 " , " BT829 " , " BT848 " ,
/* 10-14 */
" BT848A " , " BT849A " , " BT829A " , " BT827A " , " BT878 " ,
/* 15-19 */
" BT879 " , " BT880 " , " VPX3226E " , " SAA7114 " , " SAA7115 " ,
/* 20-24 */
" CX880 " , " CX881 " , " CX883 " , " SAA7111 " , " SAA7113 " ,
/* 25-29 */
" CX882 " , " TVP5150A " , " CX25840 " , " CX25841 " , " CX25842 " ,
/* 30-31 */
" CX25843 " , " CX23418 " ,
2005-04-17 02:20:36 +04:00
} ;
static int hasRadioTuner ( int tunerType )
{
switch ( tunerType ) {
2005-11-09 08:36:56 +03:00
case 18 : //PNPEnv_TUNER_FR1236_MK2:
case 23 : //PNPEnv_TUNER_FM1236:
case 38 : //PNPEnv_TUNER_FMR1236:
case 16 : //PNPEnv_TUNER_FR1216_MK2:
case 19 : //PNPEnv_TUNER_FR1246_MK2:
case 21 : //PNPEnv_TUNER_FM1216:
case 24 : //PNPEnv_TUNER_FM1246:
case 17 : //PNPEnv_TUNER_FR1216MF_MK2:
case 22 : //PNPEnv_TUNER_FM1216MF:
case 20 : //PNPEnv_TUNER_FR1256_MK2:
case 25 : //PNPEnv_TUNER_FM1256:
case 33 : //PNPEnv_TUNER_4039FR5:
case 42 : //PNPEnv_TUNER_4009FR5:
case 52 : //PNPEnv_TUNER_4049FM5:
case 54 : //PNPEnv_TUNER_4049FM5_AltI2C:
case 44 : //PNPEnv_TUNER_4009FN5:
case 31 : //PNPEnv_TUNER_TCPB9085P:
case 30 : //PNPEnv_TUNER_TCPN9085D:
case 46 : //PNPEnv_TUNER_TP18NSR01F:
case 47 : //PNPEnv_TUNER_TP18PSB01D:
case 49 : //PNPEnv_TUNER_TAPC_I001D:
case 60 : //PNPEnv_TUNER_TAPE_S001D_MK3:
case 57 : //PNPEnv_TUNER_FM1216ME_MK3:
case 59 : //PNPEnv_TUNER_FM1216MP_MK3:
case 58 : //PNPEnv_TUNER_FM1236_MK3:
case 68 : //PNPEnv_TUNER_TAPE_H001F_MK3:
case 61 : //PNPEnv_TUNER_TAPE_M001D_MK3:
case 78 : //PNPEnv_TUNER_TDA8275C1_8290_FM:
case 89 : //PNPEnv_TUNER_TCL_MFPE05_2:
case 92 : //PNPEnv_TUNER_PHILIPS_FQ1236A_MK4:
return 1 ;
2005-04-17 02:20:36 +04:00
}
return 0 ;
}
2005-09-10 00:04:05 +04:00
void tveeprom_hauppauge_analog ( struct i2c_client * c , struct tveeprom * tvee ,
unsigned char * eeprom_data )
2005-04-17 02:20:36 +04:00
{
/* ----------------------------------------------
* * The hauppauge eeprom format is tagged
* *
* * if packet [ 0 ] = = 0x84 , then packet [ 0. .1 ] = = length
* * else length = packet [ 0 ] & 3f ;
* * if packet [ 0 ] & f8 = = f8 , then EOD and packet [ 1 ] = = checksum
* *
* * In our ( ivtv ) case we ' re interested in the following :
2005-09-10 00:04:05 +04:00
* * tuner type : tag [ 00 ] .05 or [ 0 a ] .01 ( index into hauppauge_tuner )
* * tuner fmts : tag [ 00 ] .04 or [ 0 a ] .00 ( bitmask index into hauppauge_tuner_fmt )
* * radio : tag [ 00 ] . { last } or [ 0 e ] .00 ( bitmask . bit2 = FM )
* * audio proc : tag [ 02 ] .01 or [ 05 ] .00 ( mask with 0x7f )
* * decoder proc : tag [ 09 ] .01 )
2005-04-17 02:20:36 +04:00
* * Fun info :
* * model : tag [ 00 ] .07 - 08 or [ 06 ] .00 - 01
* * revision : tag [ 00 ] .09 - 0 b or [ 06 ] .04 - 06
* * serial # : tag [ 01 ] .05 - 07 or [ 04 ] .04 - 06
* * # of inputs / outputs ? ? ?
*/
2005-11-09 08:36:56 +03:00
int i , j , len , done , beenhere , tag , start ;
2005-04-17 02:20:36 +04:00
2005-11-09 08:36:56 +03:00
int tuner1 = 0 , t_format1 = 0 , audioic = - 1 ;
2005-09-10 00:04:05 +04:00
char * t_name1 = NULL ;
2005-11-09 08:36:56 +03:00
const char * t_fmt_name1 [ 8 ] = { " none " , " " , " " , " " , " " , " " , " " , " " } ;
2005-04-17 02:20:36 +04:00
2005-11-09 08:36:56 +03:00
int tuner2 = 0 , t_format2 = 0 ;
2005-09-10 00:04:05 +04:00
char * t_name2 = NULL ;
2005-11-09 08:36:56 +03:00
const char * t_fmt_name2 [ 8 ] = { " none " , " " , " " , " " , " " , " " , " " , " " } ;
2005-09-10 00:04:05 +04:00
2005-11-09 08:36:56 +03:00
memset ( tvee , 0 , sizeof ( * tvee ) ) ;
2005-09-10 00:04:05 +04:00
done = len = beenhere = 0 ;
2005-11-09 08:36:56 +03:00
/* Hack for processing eeprom for em28xx */
if ( ( eeprom_data [ 0 ] = = 0x1a ) & & ( eeprom_data [ 1 ] = = 0xeb ) & &
( eeprom_data [ 2 ] = = 0x67 ) & & ( eeprom_data [ 3 ] = = 0x95 ) )
start = 0xa0 ;
else
start = 0 ;
for ( i = start ; ! done & & i < 256 ; i + = len ) {
2005-04-17 02:20:36 +04:00
if ( eeprom_data [ i ] = = 0x84 ) {
len = eeprom_data [ i + 1 ] + ( eeprom_data [ i + 2 ] < < 8 ) ;
2005-09-10 00:04:05 +04:00
i + = 3 ;
2005-04-17 02:20:36 +04:00
} else if ( ( eeprom_data [ i ] & 0xf0 ) = = 0x70 ) {
2005-09-10 00:04:05 +04:00
if ( eeprom_data [ i ] & 0x08 ) {
2005-04-17 02:20:36 +04:00
/* verify checksum! */
done = 1 ;
break ;
}
len = eeprom_data [ i ] & 0x07 ;
+ + i ;
} else {
2005-09-10 00:04:05 +04:00
tveeprom_warn ( " Encountered bad packet header [%02x]. "
2005-11-09 08:36:56 +03:00
" Corrupt or not a Hauppauge eeprom. \n " , eeprom_data [ i ] ) ;
2005-04-17 02:20:36 +04:00
return ;
}
2005-11-09 08:36:56 +03:00
if ( debug ) {
tveeprom_info ( " Tag [%02x] + %d bytes: " , eeprom_data [ i ] , len - 1 ) ;
for ( j = 1 ; j < len ; j + + ) {
printk ( " %02x " , eeprom_data [ i + j ] ) ;
}
printk ( " \n " ) ;
}
2005-04-17 02:20:36 +04:00
/* process by tag */
tag = eeprom_data [ i ] ;
switch ( tag ) {
case 0x00 :
2005-11-09 08:36:56 +03:00
/* tag: 'Comprehensive' */
2005-09-10 00:04:05 +04:00
tuner1 = eeprom_data [ i + 6 ] ;
t_format1 = eeprom_data [ i + 5 ] ;
2005-04-17 02:20:36 +04:00
tvee - > has_radio = eeprom_data [ i + len - 1 ] ;
2005-11-09 08:36:56 +03:00
/* old style tag, don't know how to detect
IR presence , mark as unknown . */
2005-09-10 00:04:05 +04:00
tvee - > has_ir = 2 ;
2005-04-17 02:20:36 +04:00
tvee - > model =
eeprom_data [ i + 8 ] +
( eeprom_data [ i + 9 ] < < 8 ) ;
tvee - > revision = eeprom_data [ i + 10 ] +
( eeprom_data [ i + 11 ] < < 8 ) +
( eeprom_data [ i + 12 ] < < 16 ) ;
break ;
2005-09-10 00:04:05 +04:00
2005-04-17 02:20:36 +04:00
case 0x01 :
2005-11-09 08:36:56 +03:00
/* tag: 'SerialID' */
2005-04-17 02:20:36 +04:00
tvee - > serial_number =
eeprom_data [ i + 6 ] +
( eeprom_data [ i + 7 ] < < 8 ) +
( eeprom_data [ i + 8 ] < < 16 ) ;
break ;
2005-09-10 00:04:05 +04:00
2005-04-17 02:20:36 +04:00
case 0x02 :
2005-11-09 08:36:56 +03:00
/* tag 'AudioInfo'
Note mask with 0x7F , high bit used on some older models
to indicate 4052 mux was removed in favor of using MSP
inputs directly . */
audioic = eeprom_data [ i + 2 ] & 0x7f ;
if ( audioic < sizeof ( audioIC ) / sizeof ( * audioIC ) )
tvee - > audio_processor = audioIC [ audioic ] . id ;
else
tvee - > audio_processor = AUDIO_CHIP_UNKNOWN ;
2005-04-17 02:20:36 +04:00
break ;
2005-09-10 00:04:05 +04:00
2005-11-09 08:36:56 +03:00
/* case 0x03: tag 'EEInfo' */
2005-09-10 00:04:05 +04:00
2005-04-17 02:20:36 +04:00
case 0x04 :
2005-11-09 08:36:56 +03:00
/* tag 'SerialID2' */
2005-04-17 02:20:36 +04:00
tvee - > serial_number =
eeprom_data [ i + 5 ] +
( eeprom_data [ i + 6 ] < < 8 ) +
( eeprom_data [ i + 7 ] < < 16 ) ;
break ;
2005-09-10 00:04:05 +04:00
2005-04-17 02:20:36 +04:00
case 0x05 :
2005-11-09 08:36:56 +03:00
/* tag 'Audio2'
Note mask with 0x7F , high bit used on some older models
to indicate 4052 mux was removed in favor of using MSP
inputs directly . */
audioic = eeprom_data [ i + 1 ] & 0x7f ;
if ( audioic < sizeof ( audioIC ) / sizeof ( * audioIC ) )
tvee - > audio_processor = audioIC [ audioic ] . id ;
else
tvee - > audio_processor = AUDIO_CHIP_UNKNOWN ;
2005-04-17 02:20:36 +04:00
break ;
2005-09-10 00:04:05 +04:00
2005-04-17 02:20:36 +04:00
case 0x06 :
2005-11-09 08:36:56 +03:00
/* tag 'ModelRev' */
2005-04-17 02:20:36 +04:00
tvee - > model =
eeprom_data [ i + 1 ] +
( eeprom_data [ i + 2 ] < < 8 ) ;
tvee - > revision = eeprom_data [ i + 5 ] +
( eeprom_data [ i + 6 ] < < 8 ) +
( eeprom_data [ i + 7 ] < < 16 ) ;
break ;
2005-09-10 00:04:05 +04:00
case 0x07 :
2005-11-09 08:36:56 +03:00
/* tag 'Details': according to Hauppauge not interesting
on any PCI - era or later boards . */
2005-09-10 00:04:05 +04:00
break ;
2005-11-09 08:36:56 +03:00
/* there is no tag 0x08 defined */
2005-09-10 00:04:05 +04:00
case 0x09 :
2005-11-09 08:36:56 +03:00
/* tag 'Video' */
2005-09-10 00:04:05 +04:00
tvee - > decoder_processor = eeprom_data [ i + 1 ] ;
break ;
2005-04-17 02:20:36 +04:00
case 0x0a :
2005-11-09 08:36:56 +03:00
/* tag 'Tuner' */
2005-09-10 00:04:05 +04:00
if ( beenhere = = 0 ) {
tuner1 = eeprom_data [ i + 2 ] ;
t_format1 = eeprom_data [ i + 1 ] ;
2005-04-17 02:20:36 +04:00
beenhere = 1 ;
} else {
2005-11-09 08:36:56 +03:00
/* a second (radio) tuner may be present */
2005-09-10 00:04:05 +04:00
tuner2 = eeprom_data [ i + 2 ] ;
t_format2 = eeprom_data [ i + 1 ] ;
2005-11-09 08:36:56 +03:00
if ( t_format2 = = 0 ) { /* not a TV tuner? */
tvee - > has_radio = 1 ; /* must be radio */
}
}
2005-09-10 00:04:05 +04:00
break ;
2005-11-09 08:36:56 +03:00
case 0x0b :
/* tag 'Inputs': according to Hauppauge this is specific
to each driver family , so no good assumptions can be
made . */
break ;
2005-09-10 00:04:05 +04:00
2005-11-09 08:36:56 +03:00
/* case 0x0c: tag 'Balun' */
/* case 0x0d: tag 'Teletext' */
2005-09-10 00:04:05 +04:00
2005-04-17 02:20:36 +04:00
case 0x0e :
2005-11-09 08:36:56 +03:00
/* tag: 'Radio' */
2005-04-17 02:20:36 +04:00
tvee - > has_radio = eeprom_data [ i + 1 ] ;
break ;
2005-09-10 00:04:05 +04:00
2005-11-09 08:36:56 +03:00
case 0x0f :
/* tag 'IRInfo' */
tvee - > has_ir = eeprom_data [ i + 1 ] ;
break ;
2005-09-10 00:04:05 +04:00
2005-11-09 08:36:56 +03:00
/* case 0x10: tag 'VBIInfo' */
/* case 0x11: tag 'QCInfo' */
/* case 0x12: tag 'InfoBits' */
2005-09-10 00:04:05 +04:00
2005-04-17 02:20:36 +04:00
default :
2005-09-10 00:04:05 +04:00
tveeprom_dbg ( " Not sure what to do with tag [%02x] \n " , tag ) ;
2005-04-17 02:20:36 +04:00
/* dump the rest of the packet? */
}
}
if ( ! done ) {
2005-09-10 00:04:05 +04:00
tveeprom_warn ( " Ran out of data! \n " ) ;
2005-04-17 02:20:36 +04:00
return ;
}
if ( tvee - > revision ! = 0 ) {
tvee - > rev_str [ 0 ] = 32 + ( ( tvee - > revision > > 18 ) & 0x3f ) ;
tvee - > rev_str [ 1 ] = 32 + ( ( tvee - > revision > > 12 ) & 0x3f ) ;
tvee - > rev_str [ 2 ] = 32 + ( ( tvee - > revision > > 6 ) & 0x3f ) ;
tvee - > rev_str [ 3 ] = 32 + ( tvee - > revision & 0x3f ) ;
tvee - > rev_str [ 4 ] = 0 ;
}
2005-11-09 08:36:56 +03:00
if ( hasRadioTuner ( tuner1 ) & & ! tvee - > has_radio ) {
tveeprom_info ( " The eeprom says no radio is present, but the tuner type \n " ) ;
tveeprom_info ( " indicates otherwise. I will assume that radio is present. \n " ) ;
tvee - > has_radio = 1 ;
}
2005-04-17 02:20:36 +04:00
2005-09-10 00:04:05 +04:00
if ( tuner1 < sizeof ( hauppauge_tuner ) / sizeof ( struct HAUPPAUGE_TUNER ) ) {
tvee - > tuner_type = hauppauge_tuner [ tuner1 ] . id ;
t_name1 = hauppauge_tuner [ tuner1 ] . name ;
2005-04-17 02:20:36 +04:00
} else {
2005-09-10 00:04:05 +04:00
t_name1 = " unknown " ;
}
if ( tuner2 < sizeof ( hauppauge_tuner ) / sizeof ( struct HAUPPAUGE_TUNER ) ) {
tvee - > tuner2_type = hauppauge_tuner [ tuner2 ] . id ;
t_name2 = hauppauge_tuner [ tuner2 ] . name ;
} else {
t_name2 = " unknown " ;
2005-04-17 02:20:36 +04:00
}
tvee - > tuner_formats = 0 ;
2005-09-10 00:04:05 +04:00
tvee - > tuner2_formats = 0 ;
for ( i = j = 0 ; i < 8 ; i + + ) {
if ( t_format1 & ( 1 < < i ) ) {
2005-04-17 02:20:36 +04:00
tvee - > tuner_formats | = hauppauge_tuner_fmt [ i ] . id ;
2005-09-10 00:04:05 +04:00
t_fmt_name1 [ j + + ] = hauppauge_tuner_fmt [ i ] . name ;
2005-04-17 02:20:36 +04:00
}
2005-11-09 08:36:56 +03:00
if ( t_format2 & ( 1 < < i ) ) {
tvee - > tuner2_formats | = hauppauge_tuner_fmt [ i ] . id ;
t_fmt_name2 [ j + + ] = hauppauge_tuner_fmt [ i ] . name ;
}
2005-04-17 02:20:36 +04:00
}
2005-09-10 00:04:05 +04:00
tveeprom_info ( " Hauppauge model %d, rev %s, serial# %d \n " ,
2005-11-09 08:36:56 +03:00
tvee - > model , tvee - > rev_str , tvee - > serial_number ) ;
2005-09-10 00:04:05 +04:00
tveeprom_info ( " tuner model is %s (idx %d, type %d) \n " ,
2005-11-09 08:36:56 +03:00
t_name1 , tuner1 , tvee - > tuner_type ) ;
2005-09-10 00:04:05 +04:00
tveeprom_info ( " TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x) \n " ,
2005-11-09 08:36:56 +03:00
t_fmt_name1 [ 0 ] , t_fmt_name1 [ 1 ] , t_fmt_name1 [ 2 ] , t_fmt_name1 [ 3 ] ,
t_fmt_name1 [ 4 ] , t_fmt_name1 [ 5 ] , t_fmt_name1 [ 6 ] , t_fmt_name1 [ 7 ] ,
t_format1 ) ;
if ( tuner2 ) {
tveeprom_info ( " second tuner model is %s (idx %d, type %d) \n " ,
t_name2 , tuner2 , tvee - > tuner2_type ) ;
}
if ( t_format2 ) {
tveeprom_info ( " TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x) \n " ,
t_fmt_name2 [ 0 ] , t_fmt_name2 [ 1 ] , t_fmt_name2 [ 2 ] , t_fmt_name2 [ 3 ] ,
t_fmt_name2 [ 4 ] , t_fmt_name2 [ 5 ] , t_fmt_name2 [ 6 ] , t_fmt_name2 [ 7 ] ,
t_format2 ) ;
}
if ( audioic < 0 ) {
tveeprom_info ( " audio processor is unknown (no idx) \n " ) ;
tvee - > audio_processor = AUDIO_CHIP_UNKNOWN ;
} else {
if ( audioic < sizeof ( audioIC ) / sizeof ( * audioIC ) )
tveeprom_info ( " audio processor is %s (idx %d) \n " ,
audioIC [ audioic ] . name , audioic ) ;
else
tveeprom_info ( " audio processor is unknown (idx %d) \n " ,
audioic ) ;
}
if ( tvee - > decoder_processor ) {
tveeprom_info ( " decoder processor is %s (idx %d) \n " ,
STRM ( decoderIC , tvee - > decoder_processor ) ,
tvee - > decoder_processor ) ;
}
if ( tvee - > has_ir = = 2 )
tveeprom_info ( " has %sradio \n " ,
tvee - > has_radio ? " " : " no " ) ;
else
tveeprom_info ( " has %sradio, has %sIR remote \n " ,
tvee - > has_radio ? " " : " no " ,
tvee - > has_ir ? " " : " no " ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( tveeprom_hauppauge_analog ) ;
/* ----------------------------------------------------------------------- */
/* generic helper functions */
int tveeprom_read ( struct i2c_client * c , unsigned char * eedata , int len )
{
unsigned char buf ;
int err ;
buf = 0 ;
2005-09-10 00:04:05 +04:00
if ( 1 ! = ( err = i2c_master_send ( c , & buf , 1 ) ) ) {
tveeprom_info ( " Huh, no eeprom present (err=%d)? \n " , err ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2005-09-10 00:04:05 +04:00
if ( len ! = ( err = i2c_master_recv ( c , eedata , len ) ) ) {
tveeprom_warn ( " i2c eeprom read error (err=%d) \n " , err ) ;
2005-04-17 02:20:36 +04:00
return - 1 ;
}
2005-11-09 08:36:56 +03:00
if ( debug ) {
int i ;
tveeprom_info ( " full 256-byte eeprom dump: \n " ) ;
for ( i = 0 ; i < len ; i + + ) {
if ( 0 = = ( i % 16 ) )
tveeprom_info ( " %02x: " , i ) ;
printk ( " %02x " , eedata [ i ] ) ;
if ( 15 = = ( i % 16 ) )
printk ( " \n " ) ;
}
}
2005-04-17 02:20:36 +04:00
return 0 ;
}
EXPORT_SYMBOL ( tveeprom_read ) ;
/* ----------------------------------------------------------------------- */
/* needed for ivtv.sf.net at the moment. Should go away in the long */
/* run, just call the exported tveeprom_* directly, there is no point in */
/* using the indirect way via i2c_driver->command() */
# ifndef I2C_DRIVERID_TVEEPROM
# define I2C_DRIVERID_TVEEPROM I2C_DRIVERID_EXP2
# endif
static unsigned short normal_i2c [ ] = {
0xa0 > > 1 ,
I2C_CLIENT_END ,
} ;
2005-07-13 00:59:07 +04:00
2005-04-17 02:20:36 +04:00
I2C_CLIENT_INSMOD ;
2005-07-27 22:45:51 +04:00
static struct i2c_driver i2c_driver_tveeprom ;
2005-04-17 02:20:36 +04:00
static int
tveeprom_command ( struct i2c_client * client ,
unsigned int cmd ,
void * arg )
{
struct tveeprom eeprom ;
u32 * eeprom_props = arg ;
u8 * buf ;
switch ( cmd ) {
case 0 :
buf = kmalloc ( 256 , GFP_KERNEL ) ;
memset ( buf , 0 , 256 ) ;
tveeprom_read ( client , buf , 256 ) ;
2005-09-10 00:04:05 +04:00
tveeprom_hauppauge_analog ( client , & eeprom , buf ) ;
2005-04-17 02:20:36 +04:00
kfree ( buf ) ;
eeprom_props [ 0 ] = eeprom . tuner_type ;
eeprom_props [ 1 ] = eeprom . tuner_formats ;
eeprom_props [ 2 ] = eeprom . model ;
eeprom_props [ 3 ] = eeprom . revision ;
2005-09-10 00:03:47 +04:00
eeprom_props [ 4 ] = eeprom . has_radio ;
2005-04-17 02:20:36 +04:00
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static int
tveeprom_detect_client ( struct i2c_adapter * adapter ,
int address ,
int kind )
{
struct i2c_client * client ;
client = kmalloc ( sizeof ( struct i2c_client ) , GFP_KERNEL ) ;
if ( NULL = = client )
return - ENOMEM ;
memset ( client , 0 , sizeof ( struct i2c_client ) ) ;
client - > addr = address ;
client - > adapter = adapter ;
client - > driver = & i2c_driver_tveeprom ;
client - > flags = I2C_CLIENT_ALLOW_USE ;
snprintf ( client - > name , sizeof ( client - > name ) , " tveeprom " ) ;
i2c_attach_client ( client ) ;
return 0 ;
}
static int
tveeprom_attach_adapter ( struct i2c_adapter * adapter )
{
2005-08-12 01:41:56 +04:00
if ( adapter - > id ! = I2C_HW_B_BT848 )
2005-04-17 02:20:36 +04:00
return 0 ;
return i2c_probe ( adapter , & addr_data , tveeprom_detect_client ) ;
}
static int
tveeprom_detach_client ( struct i2c_client * client )
{
int err ;
err = i2c_detach_client ( client ) ;
if ( err < 0 )
return err ;
kfree ( client ) ;
return 0 ;
}
2005-07-27 22:45:51 +04:00
static struct i2c_driver i2c_driver_tveeprom = {
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. name = " tveeprom " ,
. id = I2C_DRIVERID_TVEEPROM ,
. flags = I2C_DF_NOTIFY ,
. attach_adapter = tveeprom_attach_adapter ,
. detach_client = tveeprom_detach_client ,
. command = tveeprom_command ,
} ;
static int __init tveeprom_init ( void )
{
return i2c_add_driver ( & i2c_driver_tveeprom ) ;
}
static void __exit tveeprom_exit ( void )
{
i2c_del_driver ( & i2c_driver_tveeprom ) ;
}
module_init ( tveeprom_init ) ;
module_exit ( tveeprom_exit ) ;
/*
* Local variables :
* c - basic - offset : 8
* End :
*/