From patchwork Fri Mar 27 04:01:12 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Wong X-Patchwork-Id: 14662 Received: from vger.kernel.org (vger.kernel.org [209.132.176.167]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n2R41H4x026349 for ; Fri, 27 Mar 2009 04:01:17 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1750850AbZC0EBR (ORCPT ); Fri, 27 Mar 2009 00:01:17 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1750853AbZC0EBR (ORCPT ); Fri, 27 Mar 2009 00:01:17 -0400 Received: from rv-out-0506.google.com ([209.85.198.231]:8272 "EHLO rv-out-0506.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750840AbZC0EBO (ORCPT ); Fri, 27 Mar 2009 00:01:14 -0400 Received: by rv-out-0506.google.com with SMTP id f9so1022903rvb.1 for ; Thu, 26 Mar 2009 21:01:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:date:message-id:subject :from:to:content-type; bh=onThXBBwjp9EhFTLxJRhdYRbtUueF2UVV5aa/RWt/xM=; b=GENz4TuseQ8Ac9+W2qaiGuxPKeXkbTCfy6w9faDCrmN8HD4bfYcg4D/0g5wHbzhJ+9 +r8ragZ0Dwp2nqB2zYV71Eud5RU5GWMlw5bMbHl+JsjJMQAuKEsw8wEFv7u2vXePgcZv BBopoMTg3vjUptlCWurHuo1GPGG72E2Wot0CM= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:date:message-id:subject:from:to:content-type; b=PHNA336X8DlK8ZCIObsfv/M8PoNvpUpvBv2dSqeK6kvkPOf7LXnD3hm7e4oyTf+ApD ejVUWaYHtDCovC1U9O+y6TZOx1tR9ad0SAYwquzfukTFL+jHKou1THJAO6ZYeSV2yXuP EQV/jugLA+3pstql9eWKVv6I00DrrMtnQj3ac= MIME-Version: 1.0 Received: by 10.141.211.5 with SMTP id n5mr855795rvq.106.1238126472358; Thu, 26 Mar 2009 21:01:12 -0700 (PDT) Date: Fri, 27 Mar 2009 12:01:12 +0800 Message-ID: <15ed362e0903262101iadbfd97iad20a38ca5c4f083@mail.gmail.com> Subject: [PATCH] Support for ASUS U3100 Mini DMB-TH USB Dongle From: David Wong To: linux-media@vger.kernel.org Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org This patch adds support for ASUS U3100 Mini DMB-TH USB Dongle, using unified LGS8GXX driver and partial Analog Device ADMTV102 support Signed-off-by: David T.L. Wong diff -r 6f0889fda317 linux/drivers/media/common/tuners/Kconfig --- a/linux/drivers/media/common/tuners/Kconfig Mon Mar 02 10:40:52 2009 +0100 +++ b/linux/drivers/media/common/tuners/Kconfig Fri Mar 20 11:50:24 2009 +0800 @@ -172,4 +172,11 @@ help Say Y here to support the Freescale MC44S803 based tuners +config MEDIA_TUNER_ADMTV102 + tristate "Analog Device MTV 102 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMIZE + help + Say Y here to support the Analog Device MTV 102 silicon tuner + endif # MEDIA_TUNER_CUSTOMIZE diff -r 6f0889fda317 linux/drivers/media/common/tuners/Makefile --- a/linux/drivers/media/common/tuners/Makefile Mon Mar 02 10:40:52 2009 +0100 +++ b/linux/drivers/media/common/tuners/Makefile Fri Mar 20 11:50:24 2009 +0800 @@ -23,6 +23,7 @@ obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o +obj-$(CONFIG_MEDIA_TUNER_ADMTV102) += admtv102.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff -r 6f0889fda317 linux/drivers/media/common/tuners/admtv102.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/common/tuners/admtv102.c Fri Mar 20 11:50:24 2009 +0800 @@ -0,0 +1,970 @@ +#include +#include +#include +#include "dvb_frontend.h" + +#include "admtv102.h" +#include "admtv102_priv.h" + +/* define the register address and data of Tuner that need to be initialized when power on */ +u8 AddrDataMemADMTV102_UHF[100]= +{ +//Addr Data + 0x10,0x6A, // LNA current, 0x4C will degrade 1 dB performance but get better power consumption. + 0x11,0xD8, // Mixer current + 0x12,0xC0, // Mixer gain + 0x15,0x3D, // TOP and ADJ to optimize SNR and ACI + 0x17,0x97, // TOP and ADJ to optimize SNR and ACI //*9A->97,Changed TOP and ADJ to optimize SNR and ACI + 0x18,0x03, // Changed power detector saturation voltage point and warning voltage point //* new insert + 0x1F,0x17, // VCOSEL,PLLF RFPGA amp current + 0x20,0xFF, // RFPGA amp current + 0x21,0xA4, // RFPGA amp current + 0x22,0xA4, // RFPGA amp current + 0x23,0xDF, // Mixer Bias control + 0x26,0xFA, // PLL BUFFER CURRENT + 0x27,0x00, // CONVCOL/H for VCO current control + 0x28,0xFF, // CONVCOBUFL/H for VCO buffer amplifier current control + 0x29,0xFF, // CONDIV1/2 for first and second divider current control + 0x2A,0xFF, // CONDIV3/4 for third and last divider current control + 0x2B,0xE7, // CONDIV5 for third and last divider current control + 0X2C,0xFF, // CONBUF0/1 for L-Band Buffer amp and first Buffer amp current control + 0x2E,0xFB, // CONBUF4 for forth Buffer amp current control + 0x30,0x80, // LFSW(Internal Loop Filter) to improve phase noise //* F8->80 + 0x32,0xc2, // LOOP filter boundary + 0x33,0x80, // DC offset control + 0x34,0xEC, // DC offset control + 0x39,0x96, // AGCHM AGC compensation value when LNA changes + 0x3A,0xA0, // AGCHM AGC compensation value when LNA changes + 0x3B,0x05, // AGCHM AGC compensation value when LNA changes + 0x3C,0xD0, // AGCHM AGC compensation value when LNA changes + 0x44,0xDF, // BBPGA needs to stay on for current silicon revision + 0x48,0x23, // current for output buffer amp //*23->21 + 0x49,0x08, // gain mode for output buffer amp + 0x4A,0xA0, // trip point for BBVGA + 0x4B,0x9D, // trip point for RFPGA + 0x4C,0x9D, // ADJRSSI warning point + 0x4D,0xC3, // PLL current for stability PLL lock + 0xff,0xff +}; + +u8 AddrDataMemADMTV102_VHF[100]= +{ +//Addr Data + 0x10,0x08, // LNA current + 0x11,0xc2, // Mixer current + 0x12,0xC0, // Mixer gain + 0x17,0x98, // TOP and ADJ to optimize SNR and ACI //* 9A->98 + 0x1F,0x17, // VCOSEL,PLLF RFPGA amp current + 0x20,0x9b, // RFPGA amp current + 0x21,0xA4, // RFPGA amp current + 0x22,0xA4, // RFPGA amp current + 0x23,0x9F, // Mixer Bias control + 0x26,0xF9, // PLL BUFFER CURRENT + 0x27,0x11, // CONVCOL/H for VCO current control + 0x28,0x92, // CONVCOBUFL/H for VCO buffer amplifier current control + 0x29,0xBC, // CONDIV1/2 for first and second divider current control + 0x2B,0xE7, // CONDIV5 for third and last divider current control + 0x2D,0x9C, + 0x2E,0xCE, // CONBUF4 for forth Buffer amp current control + 0x2F,0x1F, + 0x30,0x80, // LFSW(Internal Loop Filter) to improve phase noise + 0x32,0xc2, // LOOP filter boundary + 0x33,0x80, // DC offset control + 0x34,0xEC, // DC offset control + 0x48,0x29, // current for output buffer amp + 0x49,0x08, // gain mode for output buffer amp + 0x4A,0xA0, // trip point for BBVGA + 0x4B,0x9D, // trip point for RFPGA + 0x4C,0x9D, // ADJRSSI warning point + 0x4D,0xC3, // PLL current for stability PLL lock + 0xff,0xff +}; + + +u8 PLLRegSetTable [10][3]= +{ // 0x24,0x31,0x38 + {0x0F,0x04,0x50}, //13MHz //0x24: 0xnB-> 0xnF + {0x1F,0x04,0x50}, //16.384MHz + {0x2F,0x04,0x50}, //19.2MHz + {0x3F,0x04,0x50}, //20.48MHz + {0x4F,0x15,0x51}, //24.576MHz + {0x5F,0x15,0x51}, //26MHz + {0x5A,0x04,0x51}, //30.4MHz //* special set for 30.4MHz case + {0x6F,0x15,0x51}, //36MHz + {0x7F,0x15,0x51}, //38.4MHz + {0x3F,0x04,0x50}, //20MHz +}; + + +static int debug = 0; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "admtv102: " args); printk("\n"); }} while (0) + +// Reads a single register +static int admtv102_readreg(struct admtv102_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->cfg->i2c_address, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + printk(KERN_WARNING "admtv102 I2C read failed\n"); + return -EREMOTEIO; + } + if (debug >= 2) + printk("%s(reg=0x%02X) : 0x%02X\n", __func__, reg, *val); + return 0; +} + +// Writes a single register +static int admtv102_writereg(struct admtv102_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2 + }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "admtv102 I2C write failed\n"); + return -EREMOTEIO; + } + if (debug >= 2) + printk("%s(reg=0x%02X, val=0x%02X)\n", __func__, reg, val); + return 0; +} + +// Writes a set of consecutive registers +static int admtv102_writeregs(struct admtv102_priv *priv,u8 *buf, u8 len) +{ + struct i2c_msg msg = { + .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len + }; + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len); + return -EREMOTEIO; + } + return 0; +} + +/* ========================================================================= */ +/** check all fo these!!!! */ + +//u8 g_icp, +//g_convco,g_curTempState,g_CTUNE_CLKOFS; +//int g_VHFSet=UHFSupport; +//int g_TunerPLLType=REFCLK16384; +//ChannelInfo FindChannel[50]; +//MPEG_PROGINFO myMpegInfo[MaxProgNum]; +//int FindProgNum; // indicate how many program is found in one certain channel + +/* +The card should not do this as it prevents multiple cards from working!!!!! +struct admtv102_priv *state=NULL; +void myAdiInit(struct admtv102_priv *state) +{ + state = state; + /// func calls with struct admtv102_priv *state, +} +*/ + +//***************************************************************************** +// Function: Configure Tuner chip registers +// Input: target -- Device I2C address +// AddrData -- Register address and Config data array +// Output: None +//***************************************************************************** +void ConfigTuner(struct admtv102_priv *state, u8 *AddrData) +{ + int i=0; + u8 addr, data; + + while (AddrData[i]!=0xFF) + { + addr = AddrData[i++]; + data = AddrData[i++]; + admtv102_writereg(state, addr, data); + } +} + +//***************************************************************************** +// Function: Set Tuner LPF configuration +// Input: target -- Tuner I2C device Address +// refClkType -- Tuner PLL Type +// lpfBW -- Band width , in MHz unit +// Return: None +//***************************************************************************** +void SetLPF(struct admtv102_priv *state, u32 refClkType, u8 lpfBW) +{ + u8 tuneval; + u8 t; + int ret; + + admtv102_writereg(state, 0x15, (u8)( 0x38| ((lpfBW-3)& 0x07) )); + admtv102_writereg(state, 0x25 , (_EXTUNEOFF << 2) | (_TUNEEN<<1) ); + msleep(10); //change from 1 to 10 + + tuneval=0x10; //default value + ret = admtv102_readreg(state, 0x0F, &t); + if(ret == 0) + tuneval = t; + + admtv102_writereg(state, 0x25 , (_EXTUNEON << 2) | (_TUNEEN << 1) ); //change Tuning mode : auto-tune => manual tune(hold mode). + + if (refClkType==ADMTV102_REFCLK30400) + admtv102_writereg(state, 0x25, (u8)(((tuneval+state->CTUNE_CLKOFS)<<3) | (_EXTUNEON << 2) | (_TUNEEN << 1)) ); //Write CTUNE val. in order to store tuned value. + else + admtv102_writereg(state, 0x25, (u8)((tuneval<<3) | (_EXTUNEON << 2) | (_TUNEEN << 1)) ); //Write CTUNE val. in order to store tuned value. + + return; +} + +//****************************************************************************** +// Function: Tuner PLL Register Setting +// Input: target -- Tuner I2C device Address +// RegDat -- Reg set value table +// Return: success flag +//****************************************************************************** +int TunerPLLRegSet(struct admtv102_priv *state, u8* RegDat, int TunerPLLType) +{ + int i=0; + u8 addr, data; + u8 splitid; + u8 t; + + if(ADMTV102_REFCLK30400==TunerPLLType) + { + addr=0x24; + admtv102_readreg(state, 0x00, &splitid); + + if (0x0E==splitid) + data=REFCLK30400_CLKSEL_REG_SPLITID0E; // 0x5A + else if(0x0F==splitid) + data=REFCLK30400_CLKSEL_REG_SPLITID0F; // 0x6A, for mass product + else + data=RegDat[i]; + + admtv102_writereg(state, addr, data); + i++; + } + else { + addr=0x24; + data=RegDat[i++]; + admtv102_writereg(state, addr, data); + } + + addr=0x31; + data=RegDat[i++]; + admtv102_writereg(state, addr, data); + + addr=0x38; + data=RegDat[i++]; + admtv102_writereg(state, addr, data); + + return 1; +} +//***************************************************************************** +// Function: Distinguish Tuner Chip type by read SplidID +// Input: target -- Device I2C address +// +// Return: Tuner Type, MTV102 or ADMTV102 +//***************************************************************************** +int GetTunerType(struct admtv102_priv *state) +{ + u8 splitid,RetTunerType; + + admtv102_readreg (state, 0x00, &splitid); + + state->CTUNE_CLKOFS=CTUNE_CLKOFS_SPLIT0E; + switch(splitid) + { + case 0x0E: + state->CTUNE_CLKOFS=CTUNE_CLKOFS_SPLIT0E; + RetTunerType=Tuner_ADMTV102; + break; + case 0x0F: // for mass product version + state->CTUNE_CLKOFS=CTUNE_CLKOFS_SPLIT0F; + RetTunerType=Tuner_ADMTV102; + break; + case 0x08: + case 0x0A: + RetTunerType=Tuner_MTV102; + break; + default: + RetTunerType=Tuner_NewMTV102; + } + + return(RetTunerType); +} + +//***************************************************************************** +// Function: Main Function for Tuner Initialization +// only support ADMTV102 type, do not support MTV102 any more +// Input: None +// Return: None +//***************************************************************************** +void TunerInit(struct admtv102_priv *state) +{ + int TunerPLLType = state->cfg->ref_clk_type; + + GetTunerType(state); + state->icp=0; + state->convco=0; + state->curTempState=HIGH_TEMP; + if(TunerPLLType<0 || TunerPLLType>9 ) TunerPLLType=1; + if(state->VHFSet== VHFSupport) + ConfigTuner (state, &AddrDataMemADMTV102_VHF[0]); + else + ConfigTuner (state, &AddrDataMemADMTV102_UHF[0]); + + TunerPLLRegSet(state, &PLLRegSetTable[TunerPLLType][0],TunerPLLType); + SetLPF(state, TunerPLLType, 8); +} + +//***************************************************************************** +// Function: frequency setting for ADMTV102 +// Input: +// target : I2C address of tuner +// frequency : Tuner center frequency in MHz +// lpfBW : Channel Bandwidth in MHz (default: 8- 8MHZ) +// refClkType: Tuner PLL reference clock type +// frequency setting formula +// LOfrequency=(Clockfrequency/PLLR*(PLLN+PLLF/2^20))/PLLS; +// Return: None +//***************************************************************************** +void SetTunerFreq(struct admtv102_priv *state, u32 frequency, u32 lpfBW) +{ + u32 MTV10x_REFCLK; + u32 PLLFREQ,Freq; + u32 DIVSEL=0,VCOSEL=0; + u8 PC4=0,PC8_16=0,DATA47,_temper; + u32 Seg_num; + u8 PLLR; + u32 lofreq; + u32 PLLN, PLLF,tmp; + u32 MultiFactor,sub_exp,div_1,div_2; + + printk("%s(freq=%d, bw=%d)\n", __func__, frequency, lpfBW); + + state->frequency = frequency; + // judge if the VFHset has conflict with frequency value or not + if( frequency >400 && VHFSupport==state->VHFSet) // VHF is 174MHz ~ 245MHz + { + state->VHFSet=UHFSupport; + TunerInit(state); + } + else if(frequency <400 && UHFSupport==state->VHFSet) + { + state->VHFSet=VHFSupport; + TunerInit(state); + } + + PLLR=1; + lofreq = frequency*1000; + DIVSEL = LO2PLL_Freq(lofreq); + Seg_num = (0x01 << DIVSEL); + + if(Seg_num >=1 && Seg_num <=16) + { PLLFREQ = lofreq* (16/Seg_num); + Freq=frequency* (16/Seg_num); + } + else + { PLLFREQ = lofreq*2; + Freq=frequency*2; + } + + switch(state->cfg->ref_clk_type) + { + case ADMTV102_REFCLK13000: + MTV10x_REFCLK = 130; // 13*MultiFactor + MultiFactor=10; + sub_exp=1; + div_1=5; + div_2=13; // 130=13*5*(2^1) + break; + case ADMTV102_REFCLK16384: { + MTV10x_REFCLK = 16384; + MultiFactor=1000; + sub_exp=14; + div_1=1; + div_2=1; // 16384== 2^14 + break; } + case ADMTV102_REFCLK19200: { + MTV10x_REFCLK = 192; + MultiFactor=10; + sub_exp=6; + div_1=1; + div_2=3; // 192 = 2^6 *3 + break;} + case ADMTV102_REFCLK20480: { + MTV10x_REFCLK = 2048; + MultiFactor=100; + sub_exp=11; + div_1=1; + div_2=1; // 2048 = 2^11 + break;} + case ADMTV102_REFCLK24576: { + MTV10x_REFCLK = 24576; + MultiFactor=1000; + sub_exp=13; + div_1=1; + div_2=3; // 24576 = 2^13*3 + break; } + case ADMTV102_REFCLK26000: { + MTV10x_REFCLK = 260; + MultiFactor=10; + sub_exp=2; + div_1=5; + div_2=13; // 260=13*5*(2^2) + break;} + case ADMTV102_REFCLK30400: { + MTV10x_REFCLK = 304; + MultiFactor=10; + PLLR = 2 ; + SetLPF(state, ADMTV102_REFCLK30400,(u8)lpfBW); + admtv102_writereg(state, 0x19, PLLR); //Ref. Clock Divider PLL0 register , bit[7:4] is reserved, bit[3:0] is PLLR + sub_exp=4; + div_1=1; + div_2=19; // 304 = 16*19 + break; + } + case ADMTV102_REFCLK36000: { + MTV10x_REFCLK = 360; + MultiFactor=10; + sub_exp=3; + div_1=5; + div_2=9; // 360=2^3*9*5 + break;} + case ADMTV102_REFCLK38400:{ + MTV10x_REFCLK = 384; + MultiFactor=10; + sub_exp=7; + div_1=1; + div_2=3; // 384 = 2^7 *3 + break;} + case ADMTV102_REFCLK20000:{ + MTV10x_REFCLK = 200; + MultiFactor=10; + sub_exp=3; + div_1=5; + div_2=5; // 200=8*5*5 + break;} + default: { + MTV10x_REFCLK = 16384; + MultiFactor=1000; + sub_exp=14; + div_1=1; + div_2=1; // 16384== 2^14 + break;} + } + + PLLN = (Freq*MultiFactor* PLLR/MTV10x_REFCLK ); //new formula, liu dong, 2007/12/11 + tmp = ((PLLR*Freq*MultiFactor/div_1) << (20-sub_exp) )/div_2; + PLLF = tmp - (PLLN<<20); + + DATA47 = 0x10; //default Value + admtv102_readreg(state, 0x2f, &DATA47); + + if (PLLN<=66) + { + //4-prescaler + PC4 = 1; + PC8_16 = 0; + DATA47 = (u8)(DATA47 | (PC4<<6) | (PC8_16<<5)); + } + else + { + //8-prescaler + PC4 = 0; + PC8_16 = 0; + DATA47 = (u8) (DATA47 | (PC4<<6) | (PC8_16<<5)); + } + + // do a reset operation + admtv102_writereg(state, 0x27, 0x00 ); //added : v1.6.3 at PM 20:39 2007-07-26 + //PLL Reset Enable + admtv102_writereg(state, 0x2f, DATA47 ); //Reset Seq. 0 => 1 : //updated : v1.6.3 at PM 20:39 2007-07-26 + admtv102_writereg(state, 0x2f, (u8) (DATA47 | 0x80)); + + if ((PLLFREQ*2)<2592000) // 648*1000*2*2 according to data sheet + VCOSEL = 0; + else VCOSEL = 1; + + // read 0x09 register bit [5:0] + _temper=0x0C; + admtv102_readreg(state, 0x09, &_temper) ; //To get Temperature sensor value + _temper &= 0x3f; // get low 6 bits + + dpPhaseTuning(state, lofreq,_temper); //these value may changes: g_icp=0, state->convco=0, state->curTempState=HIGH_TEMP; + + if (lofreq>400000) //if iRF=UHF + { + admtv102_writereg(state, 0x1a, (u8)((state->icp<<2)| ((PLLN&0x300)>>8))); // change 1C to 7C. 2007/07/09 + } + else + { + admtv102_writereg(state, 0x1a, (u8)( 0xFC | ( ( PLLN&0x300 ) >> 8 )) ); + } + + //PLL Setting + admtv102_writereg(state, 0x1b, (u8)(PLLN & 0xFF)); + admtv102_writereg(state, 0x1c, (u8)( (DIVSEL<<4) | (VCOSEL<<7) | ((PLLF&0xF0000)>>16) ) ); + admtv102_writereg(state, 0x1d, (u8)( (PLLF&0x0FF00)>>8 ) ); + admtv102_writereg(state, 0x1e, (u8)( PLLF&0xFF )); + admtv102_writereg(state, 0x15, (u8)( 0x38| ((lpfBW-3)& 0x07) )); + + //PLL Reset + admtv102_writereg(state, 0x2f, DATA47); //Reset Seq. 0 => 1 => 0 : //updated : v1.5 at PM 20:39 2007-06-8 + admtv102_writereg(state, 0x2f, (u8)( DATA47 | 0x80 )); + admtv102_writereg(state, 0x2f, DATA47); + + admtv102_writereg(state, 0x27, state->convco ); + if (lofreq>400000) admtv102_writereg(state, 0x29, 0xBF ); + return; +} +//***************************************************************************** +// Function: calculate DIVSEL according to lofreq +// Input: lofreq -- Frequency point value in kHz unit +// Return: m_DIVSEL -- DIVSEL in register 0x1C +//***************************************************************************** +int LO2PLL_Freq(u32 lofreq) +{ + u32 fdefLoBoundFreq=940000; + + int Seg_num, m_DIVSEL=0; + + Seg_num=(int)(lofreq/(fdefLoBoundFreq/16)); + + while(Seg_num>0x01) + { + Seg_num=(Seg_num>>1); + m_DIVSEL++; + } + return(m_DIVSEL) ; +} + +//****************************************************************************** +// Function: Change CONVCO value according to Temperature Sensor(0x27[0:4]) +// Input: lofreq -- frequency in kHz unit +// temper -- Tuner 0x09 register content +// Return: None + +// ?? is this messing around with globals!!?!?!? + +//****************************************************************************** +void dpPhaseTuning(struct admtv102_priv *state, u32 lofreq, u8 temper) +{ + if (fDegVcoApply) + { + if(state->VHFSet== VHFSupport) { + if (temper<=vlowDegBoundary) //low boundary + { + state->convco=rglowDegCONVCO_VHF; + state->curTempState=LOW_TEMP; + } + else + if (temper>=vhighDegBoundary) //high boundary + { + state->convco=rgHighDegCONVCO; + state->curTempState=HIGH_TEMP; + } + } + else { + if (temper<=vlowDegBoundary) //low boundary + { + state->convco=rglowDegCONDIV; + state->icp=0x3F; + state->curTempState=LOW_TEMP; + } + else + if (temper>=vhighDegBoundary) //high boundary + { + state->convco=rgHighDegCONDIV; + if (( lofreq > 610000 ) && ( lofreq < 648000 )) //610MHz ~ 648MHz + state->icp=0x1F; + else state->icp=0x3F; + state->curTempState=HIGH_TEMP; + } + else + { if (state->curTempState) + { + state->convco=rglowDegCONDIV; + state->icp=0x3F; + } + else + { state->convco=rgHighDegCONDIV; + if (( lofreq > 610000 ) && ( lofreq < 648000 )) //610MHz ~ 648MHz + state->icp=0x1F; + else state->icp=0x3F; + } + } + } + } + return; +} +//****************************************************************************** +// Function: Do tuner temperature compensate, it can be called by processor +// for every 5~10 seconds. This may improve the tuner performance. +// +// Input: lofreq -- frequency in kHz unit +// Return: None +//****************************************************************************** +void TunerTemperatureComp(struct admtv102_priv *state, long lofreq) +{ + u8 Ori_Reg_0x1A,Reg_0x1A,_temper; + + Ori_Reg_0x1A=0xFC; + _temper=0x0C; + admtv102_readreg(state, 0x09, &_temper) ; //To get Temperature sensor value + _temper &= 0x3F; // get low 6 bits + + dpPhaseTuning(state, lofreq, _temper); + + admtv102_readreg(state, 0x1A, &Ori_Reg_0x1A) ; //To get Temperature sensor value + + // reserve bit 7, bit1 and bit 0 + Reg_0x1A= (u8)((state->icp <<2) | (Ori_Reg_0x1A & 0x83)); + // write state->icp into 0x1A register bit[6:2] + admtv102_writereg(state, 0x1A, Reg_0x1A); + admtv102_writereg(state, 0x27, state->convco); + + +} + +/* **************************************************************************** + * Function: calculate signal power using Tuner related registers + * Input: None + * Return: signal power in dBm unit + * Note: The precise is about 10dB + * ****************************************************************************/ +#define COMP497 497 +#define COMP530 -530 + +#define FREQ474 474 +#define FREQ858 858 + + +#if 0 +int TunerRSSICalc(struct admtv102_priv *state, int freq) +{ + int PowerDBValue=0; // in dBm unit + unsigned char RF_AGC_LowByte =0,RF_AGC_HighByte=0,LNA_Gain=0,GVBB=0; + int RF_AGC; + int CaseNumber; + int Coef[5]; + //int CompTab[2]={ 497, -530}; + //int FreqTab[2]={ 474, 858 }; + int Freq_Comp,BBAGC_Part; + unsigned char Val_0x3D,Val_0x3e,Val_0x3f; + + + + Freq_Comp= -( + (COMP530 - COMP497) * + (freq - FREQ474) / + (FREQ858 - FREQ474) + + COMP497 ); //0.01dB + + GVBB=0; + admtv102_readreg (state, 0x05, &RF_AGC_LowByte); + admtv102_readreg (state, 0x06, &RF_AGC_HighByte); + + RF_AGC= (RF_AGC_HighByte & 0x01); + RF_AGC= (RF_AGC<<8) + RF_AGC_LowByte; + + admtv102_readreg (state, 0x0d, &LNA_Gain); + LNA_Gain = ( (LNA_Gain & 0x60 )>> 5 ); + + admtv102_readreg (state, 0x04, &GVBB); + GVBB=(GVBB&0xf0)>>4; + + Val_0x3D=0; + Val_0x3e=0; + Val_0x3f=0; + +#if 0 + admtv102_readreg (state, lowDemodI2CAdr, 0x3d, &Val_0x3D); + admtv102_readreg (state, lowDemodI2CAdr, 0x3e, &Val_0x3e); + admtv102_readreg (state, lowDemodI2CAdr, 0x3f, &Val_0x3f); + if (Val_0x3f & 0x10) + Val_0x3f= (Val_0x3f & 0x0f);// two's complement to offset binary + else + Val_0x3f= (Val_0x3f|0x1f); // +#endif + + BBAGC_Part= Val_0x3f; + BBAGC_Part = ( BBAGC_Part <<8) + Val_0x3e; + + CaseNumber=0; // default algorithm + if (LNA_Gain==0 && GVBB==4) CaseNumber=1; //[0,-31dBm], case 1 + if (LNA_Gain==3 && GVBB==4 && RF_AGC<383) CaseNumber=2; //[-34dBm,-59dBm], case 2 + if (LNA_Gain==3 && GVBB>4 && RF_AGC>=383) CaseNumber=3; //[-61Bm,-93dBm], case 3 + if ( (LNA_Gain==0 && GVBB>4) || (LNA_Gain==3 && GVBB<4) ) CaseNumber=4; //[-2,-52dBm], case 4 + if (LNA_Gain==3 && GVBB>4 && RF_AGC<383) CaseNumber=5; //[-53,-78dBm], case 5 + if (GetTunerType(state,lowTunerI2CAdr)==Tuner_MTV102) CaseNumber=0; //use default formula + + //printk("RSSI Calc use formula %d\n", CaseNumber); + // basically all the tests I've found have ended up with cast 5!!!1 + + + // if BBAGC > 0.97, case number is forced to 0, liu dong, 2008/04/22 + if(Val_0x3f >= 0x10) CaseNumber=0; + + switch(CaseNumber) + { case 1: { + Coef[0]=-25; Coef[1]=-1000; Coef[2]=-287; Coef[3]=-11400 ; Coef[4]=-310; + BBAGC_Part=((Coef[3]*BBAGC_Part)/8192) + 5016; // 5016 = Coef[3]*0.44 + PowerDBValue=Coef[0]*RF_AGC+Coef[1]*LNA_Gain+Coef[2]*(GVBB-5)+BBAGC_Part+Coef[4]+Freq_Comp; + break; + } + case 2: { + Coef[0]=-13; Coef[1]=-752; Coef[2]=214; Coef[3]=1000 ; Coef[4]=886; + BBAGC_Part=((Coef[3]*BBAGC_Part)/8192)- 440; // 440 = Coef[3]*0.44 + PowerDBValue=Coef[0]*RF_AGC+Coef[1]*LNA_Gain+Coef[2]*(GVBB-5)+BBAGC_Part+Coef[4]+Freq_Comp; + break; + } + case 3: { + Coef[0]=-12; Coef[1]=-952; Coef[2]=-347; Coef[3]=-11400 ; Coef[4]=120; + BBAGC_Part=((Coef[3]*BBAGC_Part)/8192) + 5016; // 502 = Coef[3]*0.44 + PowerDBValue=Coef[0]*RF_AGC+Coef[1]*LNA_Gain+Coef[2]*(GVBB-5)+BBAGC_Part+Coef[4]+Freq_Comp; + break; + } + case 4: { + Coef[0]=-12; Coef[1]=-1000; Coef[2]=-330; Coef[3]=-11400 ; Coef[4]=70; + BBAGC_Part=((Coef[3]*BBAGC_Part)/8192) + 5016; // 5016 = Coef[3]*0.44 + PowerDBValue=Coef[0]*RF_AGC+Coef[1]*LNA_Gain+Coef[2]*(GVBB-5)+BBAGC_Part+Coef[4]+Freq_Comp; + break; + } + case 5: { + Coef[0]=-12; + Coef[1]=-952; + Coef[2]=-280; + Coef[3]=-11400 ; + Coef[4]=120; + + BBAGC_Part=((Coef[3]*BBAGC_Part)/8192) + 5016; // 502 = Coef[3]*0.44 + // LNA Gail == alway s3 ... (due to case) + // @ 562 => Good:AGC < 290 , (GVBB-5) < 5 + // @ 602 => Good:AGC < 290 , (GVBB-5) < 4 + // @ 746 ?? => Good:AGC < 290 , (GVBB-5) < 4 * at this freq, + + + //printk("(freq=%d) RF_AGC=%d, LNA_Gain=%d, GVBB=%d, BBAGC_Part=%d, Freq_Comp=%d\n", + // freq, RF_AGC, LNA_Gain, GVBB-5, BBAGC_Part, Freq_Comp); + + PowerDBValue= Coef[0] * RF_AGC + + Coef[1] * LNA_Gain + + Coef[2] * (GVBB-5) + + BBAGC_Part + + Coef[4] + + Freq_Comp; + break; + } + default: { + Coef[0]=-12; Coef[1]=-830; Coef[2]=800; + PowerDBValue=RF_AGC*Coef[0]+LNA_Gain*Coef[1] +Coef[2]; + BBAGC_Part= -1000*BBAGC_Part/8192; + PowerDBValue=PowerDBValue-300*(GVBB-5)+BBAGC_Part + Freq_Comp; // for 8934 case + break; + } + } + + return(PowerDBValue); +} + + + +int TunerRSSICalcAvg(struct admtv102_priv *state, int freq) +{ + + int i,RSSI_val_total=0; + for(i=0;i TunerADOutMin ) && ( TunerADout < TunerADOutMax ) ) && Tuner0x06Lock ) //pll lock cross check + Tuner_pll_lock=1; + else Tuner_pll_lock=0; + return Tuner_pll_lock; +} + +/* ========================================================================= */ + + +static int admtv102_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) +{ + struct admtv102_priv *priv; + u32 freq; + int i; + int ret = 0; + u8 bw = 8; + priv = fe->tuner_priv; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + freq = params->frequency / 1000 / 1000; // Hz -> MHz + + priv->bandwidth = BANDWIDTH_8_MHZ; + if (fe->ops.info.type == FE_OFDM) { + priv->bandwidth = params->u.ofdm.bandwidth; + printk("params->u.ofdm.bandwidth = %d\n", params->u.ofdm.bandwidth); + + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + bw = 6; + break; + case BANDWIDTH_7_MHZ: + bw = 7; + break; + case BANDWIDTH_8_MHZ: + bw = 8; + break; + } + } + + SetTunerFreq(priv, freq, bw); + + //Waits for pll lock or timeout + i = 0; + do { + if (TunerPllLockCheck(priv)) { + printk("Tuner PLL Locked\n"); + break; + } + msleep(4); + i++; + } while (i<10); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return ret; +} + +static int admtv102_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct admtv102_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int admtv102_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct admtv102_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +static int admtv102_init(struct dvb_frontend *fe) +{ + struct admtv102_priv *state = fe->tuner_priv; + + TunerInit(state); // init Tuner + SetTunerFreq(state, 746, 8); + return 0; +} + +static int admtv102_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +static int admtv102_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static const struct dvb_tuner_ops admtv102_tuner_ops = { + .info = { + .name = "Analog Device ADMTV102", + .frequency_min = 48000000, + .frequency_max = 860000000, + .frequency_step = 50000, + }, + + .release = admtv102_release, + + .init = admtv102_init, + .sleep = admtv102_sleep, + + .set_params = admtv102_set_params, + .get_frequency = admtv102_get_frequency, + .get_bandwidth = admtv102_get_bandwidth +}; + +/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */ +struct dvb_frontend * admtv102_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct admtv102_config *cfg) +{ + struct admtv102_priv *priv = NULL; + u8 id = 0; + + priv = kzalloc(sizeof(struct admtv102_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */ + + if (admtv102_readreg(priv,0,&id) != 0) { + kfree(priv); + return NULL; + } + +#if 0 + if (id != PART_REV) { + kfree(priv); + return NULL; + } +#endif + //printk(KERN_INFO "ADMTV102: successfully identified (ID = %d)\n", id); + memcpy(&fe->ops.tuner_ops, &admtv102_tuner_ops, sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */ + + return fe; +} +EXPORT_SYMBOL(admtv102_attach); + +MODULE_AUTHOR("David T.L. Wong"); +MODULE_DESCRIPTION("Analog Device ADMTV102 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff -r 6f0889fda317 linux/drivers/media/common/tuners/admtv102.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/common/tuners/admtv102.h Fri Mar 20 11:50:24 2009 +0800 @@ -0,0 +1,55 @@ +/* + * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner" + * + * Copyright (c) 2006 Olivier DANET + * + * 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.= + */ + +#ifndef ADMTV102_H +#define ADMTV102_H + +#define ADMTV102_REFCLK13000 0 +#define ADMTV102_REFCLK16384 1 +#define ADMTV102_REFCLK19200 2 +#define ADMTV102_REFCLK20480 3 +#define ADMTV102_REFCLK24576 4 +#define ADMTV102_REFCLK26000 5 +#define ADMTV102_REFCLK30400 6 +#define ADMTV102_REFCLK36000 7 +#define ADMTV102_REFCLK38400 8 +#define ADMTV102_REFCLK20000 9 + + +struct dvb_frontend; +struct i2c_adapter; + +struct admtv102_config { + u8 i2c_address; + u8 ref_clk_type; +}; + +#if defined(CONFIG_MEDIA_TUNER_ADMTV102) || (defined(CONFIG_MEDIA_TUNER_ADMTV102_MODULE) && defined(MODULE)) +extern struct dvb_frontend * admtv102_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct admtv102_config *cfg); +#else +static inline struct dvb_frontend * admtv102_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct admtv102_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif // CONFIG_MEDIA_TUNER_MT2060 + +#endif \ No newline at end of file diff -r 6f0889fda317 linux/drivers/media/common/tuners/admtv102_priv.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/common/tuners/admtv102_priv.h Fri Mar 20 11:50:24 2009 +0800 @@ -0,0 +1,57 @@ +struct admtv102_priv { + struct admtv102_config *cfg; + struct i2c_adapter *i2c; + + u32 frequency; + u32 bandwidth; + + int VHFSet; + u8 icp; + u8 convco; + u8 curTempState; + u8 CTUNE_CLKOFS; +}; + +#define Tuner_MTV102 0x00 +#define Tuner_ADMTV102 0x01 +#define Tuner_NewMTV102 0x02 + +#define VHFSupport 1 // +#define UHFSupport 0 // + +#define vlowDegBoundary 7 +#define rglowDegCONDIV 0xEC +#define vhighDegBoundary 9 +#define rgHighDegCONDIV 0x00 +#define fDegVcoApply 1 +#define HIGH_TEMP 0 +#define LOW_TEMP 1 +#define rglowDegCONVCO_VHF 0x9D +#define rgHighDegCONVCO 0x00 + +#define _EXTUNEOFF 0 +#define _EXTUNEON 1 +#define _TUNEEN 1 +#define _TUNEDIS 0 +#define CTUNEOFS 0x01 //default = 0 ; + +#define CTUNE_CLKOFS_SPLIT0E 0x09 /*added at v1.6.8*/ +#define CTUNE_CLKOFS_SPLIT0F 0x00 // for mass product +#define REFCLK30400_CLKSEL_REG_SPLITID0E 0x5A //for split ID is 0x0e +#define REFCLK30400_CLKSEL_REG_SPLITID0F 0x6A //for split ID is 0x0f + +#define TunerADOutMin 2 +#define TunerADOutMax 12 + +void ConfigTuner(struct admtv102_priv *state, u8 *AddrData); +void TunerInit(struct admtv102_priv *state); +int GetTunerType(struct admtv102_priv *state); +void SetTunerFreq(struct admtv102_priv *state, u32 frequency, u32 lpfBW); +void SetLPF(struct admtv102_priv *state,u32 refClkType,u8 lpfBW); +int LO2PLL_Freq(u32 lofreq); +int TunerPLLRegSet(struct admtv102_priv *state, u8 *RegDat, int TunerPLLType); +int TunerRSSICalc(struct admtv102_priv *state,int freq); +int TunerRSSICalcAvg(struct admtv102_priv *state,int freq); +void dpPhaseTuning(struct admtv102_priv *state,u32 lofreq, u8 temper); +void TunerTemperatureComp(struct admtv102_priv *state,long lofreq); +int CheckTunerI2C(struct admtv102_priv *state); diff -r 6f0889fda317 linux/drivers/media/dvb/dvb-usb/Kconfig --- a/linux/drivers/media/dvb/dvb-usb/Kconfig Mon Mar 02 10:40:52 2009 +0100 +++ b/linux/drivers/media/dvb/dvb-usb/Kconfig Fri Mar 20 11:50:24 2009 +0800 @@ -63,6 +63,13 @@ For an up-to-date list of devices supported by this driver, have a look on the Linux-DVB Wiki at www.linuxtv.org. + Say Y if you own such a device and want to use it. You should build it as + a module. + +config DVB_USB_DIBUSB_U3100DMB + tristate "ASUS U3100 Mini DMB-TH USB Stick" + depends on DVB_USB + help Say Y if you own such a device and want to use it. You should build it as a module. diff -r 6f0889fda317 linux/drivers/media/dvb/dvb-usb/Makefile --- a/linux/drivers/media/dvb/dvb-usb/Makefile Mon Mar 02 10:40:52 2009 +0100 +++ b/linux/drivers/media/dvb/dvb-usb/Makefile Fri Mar 20 11:50:24 2009 +0800 @@ -23,6 +23,9 @@ dvb-usb-dibusb-mc-objs = dibusb-mc.o obj-$(CONFIG_DVB_USB_DIBUSB_MC) += dvb-usb-dibusb-common.o dvb-usb-dibusb-mc.o + +dvb-usb-dibusb-u3100dmb-objs = dibusb-u3100dmb.o +obj-$(CONFIG_DVB_USB_DIBUSB_U3100DMB) += dvb-usb-dibusb-common.o dvb-usb-dibusb-u3100dmb.o dvb-usb-nova-t-usb2-objs = nova-t-usb2.o obj-$(CONFIG_DVB_USB_NOVA_T_USB2) += dvb-usb-dibusb-common.o dvb-usb-nova-t-usb2.o diff -r 6f0889fda317 linux/drivers/media/dvb/dvb-usb/dibusb-u3100dmb.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/dvb/dvb-usb/dibusb-u3100dmb.c Fri Mar 20 11:50:24 2009 +0800 @@ -0,0 +1,326 @@ +/* ASUS U3100 Mini DMB-TH USB Digital TV stick + * + * based on GPL code from DiBcom, which has + * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "dibusb.h" +#include "admtv102.h" +#include "lgs8gxx.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +/* USB Driver stuff */ +static struct dvb_usb_device_properties u3100dmb_properties; + +static int u3100dmb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + printk("u3100dmb_probe");//JHA + return dvb_usb_device_init(intf,&u3100dmb_properties,THIS_MODULE,NULL,adapter_nr); +} + +// 20080513, chihming +static int asus_dmbth_identify_state (struct usb_device *udev, struct + dvb_usb_device_properties *props, struct dvb_usb_device_description **desc, + int *cold) +{ + + static unsigned short int pid=0; + //printk("%s: iManufacturer => %d, iProduct => 0x%0x\n",__FUNCTION__, udev->descriptor.iManufacturer, udev->descriptor.iProduct);//20080513, chihming + *cold = udev->descriptor.iManufacturer == 0 && udev->descriptor.iProduct == 0; + #if 0 + t = last_tick + 3*HZ; + if (time_before(jiffies, t)) + *cold = false; + else + *cold = true; + if (last_tick == 0) + *cold = true; + last_tick = jiffies; + printk("time_before => %s\n", (*cold) ? "true":"false"); + #endif + // 20071103, chihming + #if 1 + //printk("%s: pid => 0x%x, idProduct => 0x%x\n", __FUNCTION__, pid, le16_to_cpu(udev->descriptor.idProduct)) + if (pid == 0 && (0x1749 == le16_to_cpu(udev->descriptor.idProduct))) + { + int ret = usb_trylock_device(udev); + if (ret == 1) { + ret = usb_reset_device(udev); + usb_unlock_device(udev); + //printk("usb_reset_device1"); + } else { + ret = usb_reset_device(udev); + //printk("usb_reset_device2"); + } + msleep(100); + //printk("%s\n", __FUNCTION__); + } + pid = le16_to_cpu(udev->descriptor.idProduct); + //printk("%s: Now pid => 0x%x\n", __FUNCTION__, pid); + #endif + // + + + return 0; +} + +static int u3100dmb_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + #if 1 //20080515, chihming, dont remove + if (onoff) { + u8 b[3] = { DIBUSB_REQ_SET_IOCTL, DIBUSB_IOCTL_CMD_POWER_MODE, DIBUSB_IOCTL_POWER_WAKEUP }; + int ret = dvb_usb_generic_write(d,b,3); + printk("dibusb2_0_power_ctrl: ret of dvb_usb_generic_write ==> %d\n", ret); + return ret; + //return dvb_usb_generic_write(d,b,3); + } else + return 0; + #endif + + return 0; +} + +static struct lgs8gxx_config u3100dmb_lgs8gl5_cfg = { + .prod = LGS8GXX_PROD_LGS8GL5, + .demod_address = 0x19, + .serial_ts = 0, + .ts_clk_pol = 1, + .ts_clk_gated = 1, + .if_clk_freq = 30400, /* 30.4 MHz */ + .if_freq = 0, /* Baseband */ + .if_neg_center = 1, + .ext_adc = 0, + .adc_signed = 0, + .if_neg_edge = 0, + .tuner_address = 0x61, +}; + + +static int u3100dmb_frontend_attach(struct dvb_usb_adapter *adap) +{ +#if 0 + if ((adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000P_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL || + (adap->fe = dvb_attach(dib3000mc_attach, &adap->dev->i2c_adap, DEFAULT_DIB3000MC_I2C_ADDRESS, &mod3000p_dib3000p_config)) != NULL) { + if (adap->priv != NULL) { + struct dibusb_state *st = adap->priv; + st->ops.pid_parse = dib3000mc_pid_parse; + st->ops.pid_ctrl = dib3000mc_pid_control; + } + return 0; + } +#endif + if ((adap->fe = + dvb_attach(lgs8gxx_attach, + &u3100dmb_lgs8gl5_cfg, + &adap->dev->i2c_adap)) + != NULL) { + + return 0; + } + return -ENODEV; +} + +static struct admtv102_config u3100dmb_admtv102_cfg = { + .i2c_address = 0x61, + .ref_clk_type = ADMTV102_REFCLK16384 +}; + +static int u3100dmb_tuner_attach(struct dvb_usb_adapter *adap) +{ +// struct dibusb_state *st = adap->priv; +// struct i2c_adapter *tun_i2c; + + //tun_i2c = dib3000mc_get_tuner_i2c_master(adap->fe, 1); + //if (dvb_attach(adimtv102_attach, adap->fe, &adap->dev->i2c_adap, &stk3000p_adimtv102_config, if1) == NULL) { + /* not found - use panasonic pll parameters */ + // if (dvb_attach(dvb_pll_attach, adap->fe, 0x60, tun_i2c, DVB_PLL_ENV57H1XD5) == NULL) + // return -ENOMEM; + //} else { + //st->mt2060_present = 1; + /* set the correct parameters for the dib3000p */ + //dib3000mc_set_config(adap->fe, &stk3000p_dib3000p_config); JHA + //} + + if (dvb_attach(admtv102_attach, adap->fe, + &adap->dev->i2c_adap, &u3100dmb_admtv102_cfg) != NULL) { + return 0; + } + return -ENODEV; +} + +static int u3100dmb_i2c_msg(struct dvb_usb_device *d, u8 addr, + u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen) +{ + u8 sndbuf[wlen+4+1]; /* lead(1) devaddr,direction(1) addr(2) data(wlen) (len(2) (when reading)) */ + /* write only ? */ + int wo = (rbuf == NULL || rlen == 0), + len = 2 + wlen + (wo ? 0 : 2+1); + + sndbuf[0] = wo ? DIBUSB_REQ_I2C_WRITE : DIBUSB_REQ_I2C_READ; + sndbuf[1] = (addr << 1) | (wo ? 0 : 1); + + if (wo) + memcpy(&sndbuf[2],wbuf,wlen); + else + memcpy(&sndbuf[3],wbuf,wlen); + + if (!wo) { + sndbuf[wlen+2+1] = (rlen >> 8) & 0xff; + sndbuf[wlen+3+1] = rlen & 0xff; + } + //printk("%s: addr %x sb:%x %x %x %x %x %x slen %d \n",__func__, + // addr,sndbuf[0],sndbuf[1], sndbuf[2],sndbuf[3],sndbuf[4],sndbuf[5],len); + return dvb_usb_generic_rw(d,sndbuf,len,rbuf,rlen,0); +} + +/* + * I2C master xfer function + */ +static int u3100dmb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i; + + //printk("dibusb_i2c_xfer: num %x \n",num); //JHA + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + for (i = 0; i < num; i++) { + /* write/read request */ + if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { + if (u3100dmb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len, + msg[i+1].buf,msg[i+1].len) < 0) + break; + i++; + } else + if (u3100dmb_i2c_msg(d, msg[i].addr, msg[i].buf,msg[i].len,NULL,0) < 0) + break; + } + + mutex_unlock(&d->i2c_mutex); + return i; +} + +static u32 u3100dmb_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +struct i2c_algorithm u3100dmb_i2c_algo = { + .master_xfer = u3100dmb_i2c_xfer, + .functionality = u3100dmb_i2c_func, +}; + +/* do not change the order of the ID table */ +static struct usb_device_id dibusb_u3100dmb_table [] = { +/* 0 */ {USB_DEVICE (USB_VID_ASUS, USB_PID_U3100_8934_COLD) }, //JHA +/* 1 */ {USB_DEVICE (USB_VID_ASUS, USB_PID_U3100_8934_WARM) }, //JHA +/* 2 */ {USB_DEVICE (USB_VID_ASUS, USB_PID_U3100_8GL5_COLD) }, //JHA +/* 3 */ {USB_DEVICE (USB_VID_ASUS, USB_PID_U3100_8GL5_WARM) }, //JHA + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE (usb, dibusb_u3100dmb_table); + +static struct dvb_usb_device_properties u3100dmb_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + .firmware = "u3100dmbth.fw", + + .num_adapters = 1, + .adapter = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .streaming_ctrl = dibusb2_0_streaming_ctrl, + .pid_filter = dibusb_pid_filter, + .pid_filter_ctrl = dibusb_pid_filter_ctrl, + .frontend_attach = u3100dmb_frontend_attach, + .tuner_attach = u3100dmb_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 7, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + .size_of_priv = sizeof(struct dibusb_state), + } + }, + .identify_state = asus_dmbth_identify_state, // 20080513, chihming + .power_ctrl = u3100dmb_power_ctrl, + +#if 0 + .rc_interval = DEFAULT_RC_INTERVAL, + .rc_key_map = dibusb_rc_keys, + .rc_key_map_size = 111, /* FIXME */ + .rc_query = dibusb_rc_query, +#endif + + .i2c_algo = &u3100dmb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .num_device_descs = 7, + .devices = { + //{,JHA + { "ASUSTeK U3100 Mini DMB-TH ", + { &dibusb_u3100dmb_table[0], NULL }, + { &dibusb_u3100dmb_table[1], NULL }, + }, + { "ASUSTeK U3100 Mini DMB-TH", + { &dibusb_u3100dmb_table[2], NULL }, + { &dibusb_u3100dmb_table[3], NULL }, + }, + //},JHA + + + + { NULL }, + } +}; + +static struct usb_driver u3100dmb_driver = { + .name = "dvb_usb_u3100dmb", + .probe = u3100dmb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = dibusb_u3100dmb_table, +}; + +/* module stuff */ +static int __init u3100dmb_module_init(void) +{ + int result; + if ((result = usb_register(&u3100dmb_driver))) { + err("usb_register failed. Error number %d",result); + return result; + } + + return 0; +} + +static void __exit u3100dmb_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&u3100dmb_driver); +} + +module_init (u3100dmb_module_init); +module_exit (u3100dmb_module_exit); + +MODULE_AUTHOR("Patrick Boettcher "); +MODULE_DESCRIPTION("Driver for DiBcom USB2.0 DVB-T (DiB3000M-C/P based) devices"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff -r 6f0889fda317 linux/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c Mon Mar 02 10:40:52 2009 +0100 +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-firmware.c Fri Mar 20 11:50:24 2009 +0800 @@ -126,8 +126,9 @@ if ((*pos + hx->len + 4) >= fw->size) return -EINVAL; - - hx->addr = b[1] | (b[2] << 8); + // added??? to fix something - ak based on diff.. + hx->addr = le16_to_cpu( *((u16 *) &b[1]) ); + //hx->addr = b[1] | (b[2] << 8); hx->type = b[3]; if (hx->type == 0x04) { diff -r 6f0889fda317 linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h Mon Mar 02 10:40:52 2009 +0100 +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-ids.h Fri Mar 20 11:50:24 2009 +0800 @@ -234,6 +234,10 @@ #define USB_PID_ASUS_U3000 0x171f #define USB_PID_ASUS_U3000H 0x1736 #define USB_PID_ASUS_U3100 0x173f +#define USB_PID_U3100_8934_COLD 0x1748 +#define USB_PID_U3100_8934_WARM 0x1749 +#define USB_PID_U3100_8GL5_COLD 0x1721 +#define USB_PID_U3100_8GL5_WARM 0x1722 #define USB_PID_YUAN_EC372S 0x1edc #define USB_PID_YUAN_STK7700PH 0x1f08 #define USB_PID_YUAN_PD378S 0x2edc diff -r 6f0889fda317 linux/drivers/media/dvb/dvb-usb/dvb-usb-urb.c --- a/linux/drivers/media/dvb/dvb-usb/dvb-usb-urb.c Mon Mar 02 10:40:52 2009 +0100 +++ b/linux/drivers/media/dvb/dvb-usb/dvb-usb-urb.c Fri Mar 20 11:50:24 2009 +0800 @@ -7,6 +7,25 @@ * USB and URB stuff. */ #include "dvb-usb-common.h" + +int usb_bulk_msg2(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout) +{ + struct urb *urb; + struct usb_host_endpoint *ep; + + ep = (usb_pipein(pipe) ? usb_dev->ep_in : usb_dev->ep_out) + [usb_pipeendpoint(pipe)]; + + // modify the intervall. to match the old spec... + + + if (!ep || len < 0) + return -EINVAL; + ep->desc.bInterval = min(15, ep->desc.bInterval); + return usb_bulk_msg(usb_dev, pipe, data, len, actual_length, timeout); + +} int dvb_usb_generic_rw(struct dvb_usb_device *d, u8 *wbuf, u16 wlen, u8 *rbuf, u16 rlen, int delay_ms) @@ -38,12 +57,18 @@ /* an answer is expected, and no error before */ if (!ret && rbuf && rlen) { +#if 1 + ret = usb_bulk_msg2(d->udev,usb_rcvbulkpipe(d->udev, + d->props.generic_bulk_ctrl_endpoint),rbuf,rlen,&actlen, + 100); +#else if (delay_ms) msleep(delay_ms); ret = usb_bulk_msg(d->udev,usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint),rbuf,rlen,&actlen, 2000); +#endif if (ret) err("recv bulk message failed: %d",ret); diff -r 6f0889fda317 linux/drivers/media/dvb/frontends/Kconfig --- a/linux/drivers/media/dvb/frontends/Kconfig Mon Mar 02 10:40:52 2009 +0100 +++ b/linux/drivers/media/dvb/frontends/Kconfig Fri Mar 20 11:50:24 2009 +0800 @@ -513,6 +513,13 @@ help A DMB-TH tuner module. Say Y when you want to support this frontend. +config DVB_LGS8GXX + tristate "Legend Silicon LGS8913/LGS8GL5/LGS8GXX DMB-TH demodulator" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DMB-TH tuner module. Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff -r 6f0889fda317 linux/drivers/media/dvb/frontends/Makefile --- a/linux/drivers/media/dvb/frontends/Makefile Mon Mar 02 10:40:52 2009 +0100 +++ b/linux/drivers/media/dvb/frontends/Makefile Fri Mar 20 11:50:24 2009 +0800 @@ -60,6 +60,7 @@ obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o +obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o obj-$(CONFIG_DVB_AF9013) += af9013.o obj-$(CONFIG_DVB_CX24116) += cx24116.o diff -r 6f0889fda317 linux/drivers/media/dvb/frontends/lgs8gxx.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/dvb/frontends/lgs8gxx.c Fri Mar 20 11:50:24 2009 +0800 @@ -0,0 +1,907 @@ +/* + * Support for Legend Silicon DMB-TH demodulator + * LGS8913, LGS8GL5 + * experimental support LGS8G42, LGS8G52 + * + * Copyright (C) 2007,2008 David T.L. Wong + * Copyright (C) 2008 Sirius International (Hong Kong) Limited + * Timothy Lee (for initial work on LGS8GL5) + * + * 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 "dvb_frontend.h" + +#include "lgs8gxx.h" +#include "lgs8gxx_priv.h" + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "lgs8gxx: " args); \ + } while (0) + +#undef USE_FAKE_SIGNAL_STRENGTH + +static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv); + +static int debug = 0; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +/* LGS8GXX internal helper functions */ + +static int lgs8gxx_write_reg(struct lgs8gxx_state *priv, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; + + msg.addr = priv->config->demod_address; + if (reg >= 0xC0) + msg.addr += 0x02; + + if (debug >= 2) + printk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg, 1); + + if (ret != 1) + dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n", + __func__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} + +static int lgs8gxx_read_reg(struct lgs8gxx_state *priv, u8 reg, u8 *p_data) +{ + int ret; + u8 dev_addr; + + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .flags = 0, .buf = b0, .len = 1 }, + { .flags = I2C_M_RD, .buf = b1, .len = 1 }, + }; + + dev_addr = priv->config->demod_address; + if (reg >= 0xC0) + dev_addr += 0x02; + msg[1].addr = msg[0].addr = dev_addr; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret != 2) { + dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg, ret); + return -1; + } + + *p_data = b1[0]; + if (debug >= 2) + printk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]); + return 0; +} + +static int lgs8gxx_soft_reset(struct lgs8gxx_state *priv) +{ + lgs8gxx_write_reg(priv, 0x02, 0x00); + msleep(1); + lgs8gxx_write_reg(priv, 0x02, 0x01); + msleep(100); + + return 0; +} + +static int lgs8gxx_set_ad_mode(struct lgs8gxx_state *priv) +{ + const struct lgs8gxx_config *config = priv->config; + u8 if_conf; + + if_conf = 0x10; // AGC output on; + + if_conf |= + ((config->ext_adc) ? 0x80:0x00) | + ((config->if_neg_center) ? 0x04:0x00) | + ((config->if_freq == 0) ? 0x08:0x00) | /* Baseband */ + ((config->ext_adc && config->adc_signed) ? 0x02:0x00) | + ((config->ext_adc && config->if_neg_edge) ? 0x01:0x00); + + if (config->ext_adc && + (config->prod == LGS8GXX_PROD_LGS8G52)) { + lgs8gxx_write_reg(priv, 0xBA, 0x40); + } + + lgs8gxx_write_reg(priv, 0x07, if_conf); + + return 0; +} + +static int lgs8gxx_set_if_freq(struct lgs8gxx_state *priv, u32 freq /*in kHz*/) +{ + u64 val; + u32 v32; + u32 if_clk; + + if_clk = priv->config->if_clk_freq; + + val = freq; + if (freq != 0) { + val *= (u64)1 << 32; + if (if_clk != 0) + do_div(val, if_clk); + v32 = val & 0xFFFFFFFF; + dprintk("Set IF Freq to %dkHz\n", freq); + } else { + v32 = 0; + dprintk("Set IF Freq to baseband\n"); + } + dprintk("AFC_INIT_FREQ = 0x%08X\n", v32); + + lgs8gxx_write_reg(priv, 0x09, 0xFF & (v32)); + lgs8gxx_write_reg(priv, 0x0A, 0xFF & (v32 >> 8)); + lgs8gxx_write_reg(priv, 0x0B, 0xFF & (v32 >> 16)); + lgs8gxx_write_reg(priv, 0x0C, 0xFF & (v32 >> 24)); + + return 0; +} + +static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) +{ + u8 t; + + if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + lgs8gxx_write_reg(priv, 0xC6, 0x01); + } + + lgs8gxx_read_reg(priv, 0x7E, &t); + lgs8gxx_write_reg(priv, 0x7E, t | 0x01); + + // clear FEC self reset + lgs8gxx_read_reg(priv, 0xC5, &t); + lgs8gxx_write_reg(priv, 0xC5, t & 0xE0); + + if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + /* FEC auto detect */ + lgs8gxx_write_reg(priv, 0xC1, 0x03); + + lgs8gxx_read_reg(priv, 0x7C, &t); + t = (t & 0x8C) | 0x03; + lgs8gxx_write_reg(priv, 0x7C, t); + } + + + if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + /* BER test mode */ + lgs8gxx_read_reg(priv, 0xC3, &t); + t = (t & 0xEF) | 0x10; + lgs8gxx_write_reg(priv, 0xC3, t); + } + + if (priv->config->prod == LGS8GXX_PROD_LGS8G52) { + lgs8gxx_write_reg(priv, 0xD9, 0x40); + } + + return 0; +} + +static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) +{ + int ret = 0; + u8 t; + + /* turn off auto-detect; manual settings*/ + lgs8gxx_write_reg(priv, 0x7E, 0); + if (priv->config->prod == LGS8GXX_PROD_LGS8913) + lgs8gxx_write_reg(priv, 0xC1, 0); + + ret = lgs8gxx_read_reg(priv, 0xC5, &t); + t = (t & 0xE0) | 0x06; + lgs8gxx_write_reg(priv, 0xC5, t); + + lgs8gxx_soft_reset(priv); + + return 0; +} + +static int lgs8gxx_is_locked(struct lgs8gxx_state *priv, u8 *locked) +{ + int ret = 0; + u8 t; + + ret = lgs8gxx_read_reg(priv, 0x4B, &t); + if (ret != 0) + return ret; + + *locked = ((t & 0xC0) == 0xC0) ? 1 : 0; + return 0; +} + +static int lgs8gxx_is_autodetect_finished(struct lgs8gxx_state *priv, + u8 *finished) +{ + int ret = 0; + u8 t; + + ret = lgs8gxx_read_reg(priv, 0xA4, &t); + if (ret != 0) + return ret; + + *finished = ((t & 0x3) == 0x1) ? 1 : 0; + + return 0; +} + +static int lgs8gxx_autolock_gi(struct lgs8gxx_state *priv, u8 gi, u8 *locked) +{ + int err; + u8 ad_fini = 0; + + if (gi == GI_945) + dprintk("try GI 945\n"); + else if (gi == GI_595) + dprintk("try GI 595\n"); + else if (gi == GI_420) + dprintk("try GI 420\n"); + lgs8gxx_write_reg(priv, 0x04, gi); + lgs8gxx_soft_reset(priv); + msleep(50); + err = lgs8gxx_is_autodetect_finished(priv, &ad_fini); + if (err != 0) + return err; + if (ad_fini) { + err = lgs8gxx_is_locked(priv, locked); + if (err != 0) + return err; + } + + return 0; +} + +static int lgs8gxx_auto_detect(struct lgs8gxx_state *priv, + u8 *detected_param, u8 *gi) +{ + int i,j; + int err = 0; + u8 locked = 0, tmp_gi; + + dprintk("%s\n", __func__); + + lgs8gxx_set_mode_auto(priv); + /* Guard Interval */ + lgs8gxx_write_reg(priv, 0x03, 00); + + for (i = 0; i < 2; i++) { + for (j =0 ; j < 2; j++) { + tmp_gi = GI_945; + err = lgs8gxx_autolock_gi(priv, GI_945, &locked); + if (err) goto out; + if (locked) goto locked; + } + for (j =0 ; j < 2; j++) { + tmp_gi = GI_420; + err = lgs8gxx_autolock_gi(priv, GI_420, &locked); + if (err) goto out; + if (locked) goto locked; + } + tmp_gi = GI_595; + err = lgs8gxx_autolock_gi(priv, GI_595, &locked); + if (err) goto out; + if (locked) goto locked; + } + +locked: + if ((err == 0) && (locked == 1)) { + u8 t; + + lgs8gxx_read_reg(priv, 0xA2, &t); + *detected_param = t; + + if (tmp_gi == GI_945) + dprintk("GI 945 locked\n"); + else if (tmp_gi == GI_595) + dprintk("GI 595 locked\n"); + else if (tmp_gi == GI_420) + dprintk("GI 420 locked\n"); + *gi = tmp_gi; + } + if (!locked) + err = -1; + +out: + return err; +} + +static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv) +{ + s8 err; + //u8 ctrl_frame = 0, mode = 0, rate = 0; + u8 /*inter_leaver_len = 0,*/ gi = 0x2; + u8 detected_param = 0; + + err = lgs8gxx_auto_detect(priv, &detected_param, &gi); + + if (err != 0) { +#if 0 + /* Set auto guardinterval detection */ + lgs8gxx_write_reg(priv, 0x03, 0x01); +#endif + dprintk("lgs8gxx_auto_detect failed\n"); + } + + + /* Apply detected parameters */ + if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + u8 inter_leave_len = detected_param & TIM_MASK ; + inter_leave_len = (inter_leave_len == TIM_LONG) ? 0x60 : 0x40; + detected_param &= CF_MASK | SC_MASK | LGS_FEC_MASK; + detected_param |= inter_leave_len; + } + lgs8gxx_write_reg(priv, 0x7D, detected_param); + if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + lgs8gxx_write_reg(priv, 0xC0, detected_param); + } + //lgs8gxx_soft_reset(priv); + + /* Enter manual mode */ + lgs8gxx_set_mode_manual(priv); + + if (gi == 0x2) + switch(gi) { + case GI_945: + priv->curr_gi = 945; break; + case GI_595: + priv->curr_gi = 595; break; + case GI_420: + priv->curr_gi = 420; break; + default: + priv->curr_gi = 945; break; + } +} + +static int lgs8gxx_set_mpeg_mode(struct lgs8gxx_state *priv, + u8 serial, u8 clk_pol, u8 clk_gated) +{ + int ret = 0; + u8 t; + + ret = lgs8gxx_read_reg(priv, 0xC2, &t); + if (ret != 0) + return ret; + + t &= 0xF8; + t |= serial ? TS_SERIAL : TS_PARALLEL; + t |= clk_pol ? TS_CLK_INVERTED : TS_CLK_NORMAL; + t |= clk_gated ? TS_CLK_GATED : TS_CLK_FREERUN; + + ret = lgs8gxx_write_reg(priv, 0xC2, t); + if (ret != 0) + return ret; + + return 0; +} + + +/* LGS8913 demod frontend functions */ + +static int lgs8913_init(struct lgs8gxx_state *priv) +{ + u8 t; + + /* LGS8913 specific */ + lgs8gxx_write_reg(priv, 0xc1, 0x3); + + lgs8gxx_read_reg(priv, 0x7c, &t); + lgs8gxx_write_reg(priv, 0x7c, (t&0x8c) | 0x3); + + /* LGS8913 specific */ + lgs8gxx_read_reg(priv, 0xc3, &t); + lgs8gxx_write_reg(priv, 0xc3, t&0x10); + +#if 0 + /* set AGC ref */ + /* FIXME better set from configuration per hardware */ + lgs8gxx_write_reg(priv, 0x2C, 0); + lgs8gxx_write_reg(priv, 0x2D, 0x18); + lgs8gxx_write_reg(priv, 0x2E, 0xA2); +#endif + + return 0; +} + +static int lgs8gxx_init(struct dvb_frontend *fe) +{ + struct lgs8gxx_state *priv = (struct lgs8gxx_state *)fe->demodulator_priv; + const struct lgs8gxx_config *config = priv->config; + u8 data = 0; + s8 err; + dprintk("%s\n", __func__); + + lgs8gxx_read_reg(priv, 0, &data); + dprintk("reg 0 = 0x%02X\n", data); + + /* Setup MPEG output format */ + err = lgs8gxx_set_mpeg_mode(priv, config->serial_ts, + config->ts_clk_pol, + config->ts_clk_gated); + if (err != 0) + return -EIO; + + if (config->prod == LGS8GXX_PROD_LGS8913) { + lgs8913_init(priv); + } + lgs8gxx_set_if_freq(priv, priv->config->if_freq); + if (config->prod != LGS8GXX_PROD_LGS8913) + lgs8gxx_set_ad_mode(priv); + + return 0; +} + +static void lgs8gxx_release(struct dvb_frontend *fe) +{ + struct lgs8gxx_state *state = fe->demodulator_priv; + dprintk("%s\n", __func__); + + kfree(state); +} + +#if 0 +static int lgs8gxx_sleep(struct dvb_frontend *fe) +{ + dprintk("%s\n", __func__); + + return 0; +} +#endif + +static int lgs8gxx_write(struct dvb_frontend *fe, u8 *buf, int len) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + + if (len != 2) + return -EINVAL; + + return lgs8gxx_write_reg(priv, buf[0], buf[1]); +} + +static int lgs8gxx_set_fe(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fe_params) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + + dprintk("%s\n", __func__); + + /* set frequency */ + if (fe->ops.tuner_ops.set_params) { + fe->ops.tuner_ops.set_params(fe, fe_params); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* Hardcoded to use auto as much as possible */ + fe_params->u.ofdm.code_rate_HP = FEC_AUTO; + fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; + fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; + + /* Set standard params.. or put them to auto */ + if ((fe_params->u.ofdm.code_rate_HP == FEC_AUTO) || + (fe_params->u.ofdm.code_rate_LP == FEC_AUTO) || + (fe_params->u.ofdm.constellation == QAM_AUTO) || + (fe_params->u.ofdm.hierarchy_information == HIERARCHY_AUTO)) { + } else { + /* set constellation */ + switch (fe_params->u.ofdm.constellation) { + case QPSK: + case QAM_16: + case QAM_64: + break; + + default: + return -EINVAL; + } + + /* set hierarchy */ + switch (fe_params->u.ofdm.hierarchy_information) { + case HIERARCHY_NONE: + case HIERARCHY_1: + case HIERARCHY_2: + case HIERARCHY_4: + break; + + default: + return -EINVAL; + } + } + + /* set guard interval */ + switch (fe_params->u.ofdm.guard_interval) { + case GUARD_INTERVAL_1_32: + case GUARD_INTERVAL_1_16: + case GUARD_INTERVAL_1_8: + case GUARD_INTERVAL_1_4: + case GUARD_INTERVAL_AUTO: + break; + + default: + return -EINVAL; + } + + /* set transmission mode */ + switch (fe_params->u.ofdm.transmission_mode) { + case TRANSMISSION_MODE_2K: + case TRANSMISSION_MODE_8K: + case TRANSMISSION_MODE_AUTO: + break; + + default: + return -EINVAL; + } + + /* start auto lock */ + lgs8gxx_auto_lock(priv); + + msleep(10); + + return 0; +} + +static int lgs8gxx_get_fe(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fe_params) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + u8 t; + int translated_fec = FEC_1_2; + + dprintk("%s\n", __func__); + + /* TODO: get real readings from device */ + /* inversion status */ + fe_params->inversion = INVERSION_OFF; + + /* bandwidth */ + fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + + lgs8gxx_read_reg(priv, 0x7D, &t); + /* FEC. No exact match for DMB-TH, pick approx. value */ + switch(t & LGS_FEC_MASK) { + case LGS_FEC_0_4: /* FEC 0.4 */ + translated_fec = FEC_1_2; + break; + case LGS_FEC_0_6: /* FEC 0.6 */ + translated_fec = FEC_2_3; + break; + case LGS_FEC_0_8: /* FEC 0.8 */ + translated_fec = FEC_5_6; + break; + default: + translated_fec = FEC_1_2; + } + fe_params->u.ofdm.code_rate_HP = + fe_params->u.ofdm.code_rate_LP = translated_fec; + + /* constellation */ + switch(t & SC_MASK) { + case SC_QAM64: + fe_params->u.ofdm.constellation = QAM_64; + break; + case SC_QAM32: + fe_params->u.ofdm.constellation = QAM_32; + break; + case SC_QAM16: + fe_params->u.ofdm.constellation = QAM_16; + break; + case SC_QAM4: + case SC_QAM4NR: + fe_params->u.ofdm.constellation = QPSK; + break; + default: + fe_params->u.ofdm.constellation = QAM_64; + } + + /* transmission mode */ + fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; + + /* guard interval */ + fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; + + /* hierarchy */ + fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE; + + return 0; +} + +static +int lgs8gxx_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + /* FIXME: copy from tda1004x.c */ + fesettings->min_delay_ms = 800; + /* Drift compensation makes no sense for DVB-T */ + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static int lgs8gxx_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + s8 ret; + u8 t; + + dprintk("%s\n", __func__); + + ret = lgs8gxx_read_reg(priv, 0x4B, &t); + if (ret != 0) + return -EIO; + + dprintk("Reg 0x4B: 0x%02X\n", t); + + *fe_status = 0; + if (priv->config->prod == LGS8GXX_PROD_LGS8913) { + if ((t & 0x40) == 0x40) + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER; + if ((t & 0x80) == 0x80) + *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } else { + if ((t & 0x80) == 0x80) + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } + + /* success */ + dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); + return 0; +} + +static int lgs8gxx_read_signal_agc(struct lgs8gxx_state *priv, u16 *signal) +{ + u16 v; + u8 agc_lvl[2], cat; + + dprintk("%s()\n", __func__); + lgs8gxx_read_reg(priv, 0x3F, &agc_lvl[0]); + lgs8gxx_read_reg(priv, 0x3E, &agc_lvl[1]); + + v = agc_lvl[0]; + v <<= 8; + v |= agc_lvl[1]; + + dprintk("agc_lvl: 0x%04X\n", v); + + if (v < 0x100 ) + cat = 0; + else if (v < 0x190) + cat = 5; + else if (v < 0x2A8) + cat = 4; + else if (v < 0x381) + cat = 3; + else if (v < 0x400) + cat = 2; + else if (v == 0x400) + cat = 1; + else + cat = 0; + + *signal = cat; + + return 0; +} + +static int lgs8913_read_signal_strength(struct lgs8gxx_state *priv, u16 *signal) +{ + u8 t; s8 ret; +#ifndef USE_FAKE_SIGNAL_STRENGTH + s16 max_strength = 0; + u8 str; + u16 i, gi = priv->curr_gi; +#endif + + dprintk("%s\n", __func__); + + ret = lgs8gxx_read_reg(priv, 0x4B, &t); + if (ret != 0) + return -EIO; + +#ifdef USE_FAKE_SIGNAL_STRENGTH + if ((t & 0xC0) == 0xC0) { + dprintk("Fake signal strength as 50\n"); + *signal = 0x32; + } +#else + dprintk("gi = %d\n", gi); + for (i = 0; i < gi; i++) { + + if ((i & 0xFF) == 0) + lgs8gxx_write_reg(priv, 0x84, 0x03 & (i >> 8)); + lgs8gxx_write_reg(priv, 0x83, i & 0xFF); + + lgs8gxx_read_reg(priv, 0x94, &str); + if (max_strength < str) + max_strength = str; + } + + *signal = max_strength; + dprintk("%s: signal=0x%02X\n", __func__, *signal); +#endif + + lgs8gxx_read_reg(priv, 0x95, &t); + dprintk("%s: AVG Noise=0x%02X\n", __func__, t); + + return 0; +} + +static int lgs8gxx_read_signal_strength(struct dvb_frontend *fe, u16 *signal) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + + if (priv->config->prod == LGS8GXX_PROD_LGS8913) + return lgs8913_read_signal_strength(priv, signal); + else + return lgs8gxx_read_signal_agc(priv, signal); +} + +static int lgs8gxx_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + u8 t; + *snr = 0; + + lgs8gxx_read_reg(priv, 0x95, &t); + dprintk("AVG Noise=0x%02X\n", t); + *snr = 256 - t; + *snr <<= 8; + dprintk("snr=0x%x\n", *snr); + + return 0; +} + +static int lgs8gxx_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + dprintk("%s: ucblocks=0x%x\n", __func__, *ucblocks); + return 0; +} + +static int lgs8gxx_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + u8 r0, r1, r2, r3; + u32 total_cnt, err_cnt; + + dprintk("%s\n", __func__); + + lgs8gxx_write_reg(priv, 0xc6, 0x01); + lgs8gxx_write_reg(priv, 0xc6, 0x41); + lgs8gxx_write_reg(priv, 0xc6, 0x01); + + msleep(200); + + lgs8gxx_write_reg(priv, 0xc6, 0x81); + lgs8gxx_read_reg(priv, 0xd0, &r0); + lgs8gxx_read_reg(priv, 0xd1, &r1); + lgs8gxx_read_reg(priv, 0xd2, &r2); + lgs8gxx_read_reg(priv, 0xd3, &r3); + total_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0); + lgs8gxx_read_reg(priv, 0xd4, &r0); + lgs8gxx_read_reg(priv, 0xd5, &r1); + lgs8gxx_read_reg(priv, 0xd6, &r2); + lgs8gxx_read_reg(priv, 0xd7, &r3); + err_cnt = (r3 << 24) | (r2 << 16) | (r1 << 8) | (r0); + dprintk("error=%d total=%d\n", err_cnt, total_cnt); + + if (total_cnt == 0) + *ber = 0; + else + *ber = err_cnt * 100 / total_cnt; + + dprintk("%s: ber=0x%x\n", __func__, *ber); + return 0; +} + +static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) +{ + struct lgs8gxx_state *priv = fe->demodulator_priv; + + if (priv->config->tuner_address == 0) + return 0; + if (enable) { + return lgs8gxx_write_reg(priv, 0x01, 0x80 | priv->config->tuner_address); + } else { + return lgs8gxx_write_reg(priv, 0x01, 0); + } +} + +static struct dvb_frontend_ops lgs8gxx_ops = { + .info = { + .name = "Legend Silicon LGS8913/LGS8GXX DMB-TH", + .type = FE_OFDM, + .frequency_min = 474000000, + .frequency_max = 858000000, + .frequency_stepsize = 10000, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | + FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = lgs8gxx_release, + + .init = lgs8gxx_init, + //.sleep = lgs8gxx_sleep, + .write = lgs8gxx_write, + .i2c_gate_ctrl = lgs8gxx_i2c_gate_ctrl, + + .set_frontend = lgs8gxx_set_fe, + .get_frontend = lgs8gxx_get_fe, + .get_tune_settings = lgs8gxx_get_tune_settings, + + .read_status = lgs8gxx_read_status, + .read_ber = lgs8gxx_read_ber, + .read_signal_strength = lgs8gxx_read_signal_strength, + .read_snr = lgs8gxx_read_snr, + .read_ucblocks = lgs8gxx_read_ucblocks, +}; + +struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, + struct i2c_adapter *i2c) +{ + struct lgs8gxx_state *priv = NULL; + u8 data = 0; + + dprintk("%s()\n",__func__); + + if (config == NULL || i2c == NULL) + return NULL; + + priv = kzalloc(sizeof(struct lgs8gxx_state), GFP_KERNEL); + if (priv == NULL) + goto error_out; + + priv->config = config; + priv->i2c = i2c; + + /* check if the demod is there */ + if (lgs8gxx_read_reg(priv, 0, &data) != 0 /*|| + data != 0x0B*/) { + dprintk("%s lgs8gxx not found at i2c addr 0x%02X\n", + __func__, priv->config->demod_address); + goto error_out; + } + + lgs8gxx_read_reg(priv, 1, &data); + + memcpy(&priv->frontend.ops, &lgs8gxx_ops, + sizeof(struct dvb_frontend_ops)); + priv->frontend.demodulator_priv = priv; + + return &priv->frontend; + +error_out: + dprintk("%s() error_out\n", __func__); + kfree(priv); + return NULL; + +} +EXPORT_SYMBOL(lgs8gxx_attach); + +MODULE_DESCRIPTION("Legend Silicon LGS8913/LGS8GXX DMB-TH demodulator driver"); +MODULE_AUTHOR("David T. L. Wong "); +MODULE_LICENSE("GPL"); diff -r 6f0889fda317 linux/drivers/media/dvb/frontends/lgs8gxx.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/dvb/frontends/lgs8gxx.h Fri Mar 20 11:50:24 2009 +0800 @@ -0,0 +1,90 @@ +/* + * Support for Legend Silicon DMB-TH demodulator + * LGS8913, LGS8GL5 + * experimental support LGS8G42, LGS8G52 + * + * Copyright (C) 2007,2008 David T.L. Wong + * Copyright (C) 2008 Sirius International (Hong Kong) Limited + * Timothy Lee (for initial work on LGS8GL5) + * + * 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. + * + */ + +#ifndef __LGS8GXX_H__ +#define __LGS8GXX_H__ + +#include +#include + +#define LGS8GXX_PROD_LGS8913 0 +#define LGS8GXX_PROD_LGS8GL5 1 +#define LGS8GXX_PROD_LGS8G42 3 +#define LGS8GXX_PROD_LGS8G52 4 +#define LGS8GXX_PROD_LGS8G54 5 + +struct lgs8gxx_config { + + /* product type */ + u8 prod; + + /* the demodulator's i2c address */ + u8 demod_address; + + /* parallel or serial transport stream */ + u8 serial_ts; + + /* transport stream polarity*/ + u8 ts_clk_pol; + + /* transport stream clock gated by ts_valid */ + u8 ts_clk_gated; + + /* A/D Clock frequency */ + u32 if_clk_freq; /* in kHz */ + + /* IF frequency */ + u32 if_freq; /* in kHz */ + + /*Use External ADC*/ + u8 ext_adc; + + /*External ADC output two's complement*/ + u8 adc_signed; + + /*Sample IF data at falling edge of IF_CLK*/ + u8 if_neg_edge; + + /*IF use Negative center frequency*/ + u8 if_neg_center; + + /* slave address and configuration of the tuner */ + u8 tuner_address; +}; + +#if defined(CONFIG_DVB_LGS8GXX) || \ + (defined(CONFIG_DVB_LGS8GXX_MODULE) && defined(MODULE)) +extern struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *lgs8gxx_attach(const struct lgs8gxx_config *config, + struct i2c_adapter *i2c) { + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LGS8GXX */ + +#endif /* __LGS8GXX_H__ */ diff -r 6f0889fda317 linux/drivers/media/dvb/frontends/lgs8gxx_priv.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/linux/drivers/media/dvb/frontends/lgs8gxx_priv.h Fri Mar 20 11:50:24 2009 +0800 @@ -0,0 +1,70 @@ +/* + * Support for Legend Silicon DMB-TH demodulator + * LGS8913, LGS8GL5 + * experimental support LGS8G42, LGS8G52 + * + * Copyright (C) 2007,2008 David T.L. Wong + * Copyright (C) 2008 Sirius International (Hong Kong) Limited + * Timothy Lee (for initial work on LGS8GL5) + * + * 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. + * + */ + +#ifndef LGS8913_PRIV_H +#define LGS8913_PRIV_H + +struct lgs8gxx_state { + struct i2c_adapter *i2c; + /* configuration settings */ + const struct lgs8gxx_config *config; + struct dvb_frontend frontend; + u16 curr_gi; /* current guard interval */ +}; + +#define SC_MASK 0x1C /* Sub-Carrier Modulation Mask */ +#define SC_QAM64 0x10 /* 64QAM modulation */ +#define SC_QAM32 0x0C /* 32QAM modulation */ +#define SC_QAM16 0x08 /* 16QAM modulation */ +#define SC_QAM4NR 0x04 /* 4QAM modulation */ +#define SC_QAM4 0x00 /* 4QAM modulation */ + +#define LGS_FEC_MASK 0x03 /* FEC Rate Mask */ +#define LGS_FEC_0_4 0x00 /* FEC Rate 0.4 */ +#define LGS_FEC_0_6 0x01 /* FEC Rate 0.6 */ +#define LGS_FEC_0_8 0x02 /* FEC Rate 0.8 */ + +#define TIM_MASK 0x20 /* Time Interleave Length Mask */ +#define TIM_LONG 0x00 /* Time Interleave Length = 720 */ +#define TIM_MIDDLE 0x20 /* Time Interleave Length = 240 */ + +#define CF_MASK 0x80 /* Control Frame Mask */ +#define CF_EN 0x80 /* Control Frame On */ + +#define GI_MASK 0x03 /* Guard Interval Mask */ +#define GI_420 0x00 /* 1/9 Guard Interval */ +#define GI_595 0x01 +#define GI_945 0x02 /* 1/4 Guard Interval */ + + +#define TS_PARALLEL 0x00 /* Parallel TS Output a.k.a. SPI */ +#define TS_SERIAL 0x01 /* Serial TS Output a.k.a. SSI */ +#define TS_CLK_NORMAL 0x00 /* MPEG Clock Normal */ +#define TS_CLK_INVERTED 0x02 /* MPEG Clock Inverted */ +#define TS_CLK_GATED 0x00 /* MPEG clock gated */ +#define TS_CLK_FREERUN 0x04 /* MPEG clock free running*/ + + +#endif