[linux-dvb] [PATCH] Add support for LGDT3303 VSB/QAM Frontend ( LG 5th Gen Chipset)
Mac Michaels
wmichaels1 at earthlink.net
Sun Jul 10 16:40:17 CEST 2005
I glanced at the 3303 part of the driver and I think is
reasonable to combine them. I have not examined the card
level stuff yet. Give me a couple of days and I'll post a
patch everyone can try.
-- Mac
On Sunday 10 July 2005 01:16 am, Michael Krufky wrote:
> Tylor and Mac-
>
> Check out this web page:
>
> http://www.fortetw.com/en/Product_vsb.htm
>
> ... It has the following:
>
> <<
> The VSB/QAM Receiver is a highly upgraded single chip
> that supports both ATSC terrestrial and digital cable
> broadcast environment. The LGDT3302 and LGDT3303 enable
> to demodulate, equalize, and correct signals of ATSC
> compliant 8/16 VSB and ITU-T J.83 Annex B,C compliant
> 64/256 QAM
>
> [ LGDT3302 ]
> - ATSC compliant 8/16 VSB and MMDS 2/4/6/8/16 VSB
> receiver - ITU-T J.83 Annex B compliant 64/256 Qam
> receiver - Integrated 10-bit A/D converter
> - Parallel/serial MPEG-2 transport interface
> - Support I2C bus interface
> - Embedded 64KB SRAM for VSB and 64/256 QAM
> - Supports 44MHz IF input as well as 6MHz IF input
> - 100 TQFP
> [ LGDT3303 ]
> - 5th Generation VSB/QAM receiver
> - Enhanced Multi-Ghost cancellation
> - Pin-to-pin compatable with LGDT3302
>
>
> This last line makes me think that these two drivers can
> be combined into a single LGDT330X module.... What do you
> guys think?
>
> I also saw the following in Google cache, (website
> deleted), if anyone reading the list is interested:
>
> <<
> The VSB/QAM receiver (LGDT3302) is a highly upgraded
> single chip that supports both north American digital
> terrestrial broadcast television and digital cable
> television standards. This LGDT3302 enables to
> demodulate, equalize and correct signals of ATSC
> compliant 8/16 VSB and ITU-T J.83 Annex B compliant
> 64/256 QAM.
>
> The 10-bit ADC is integrated in this chip in order to
> simplify system design and achieve better performance.
> The LGDT3302 accepts a VSB or QAM modulated analog signal
> of 44MHz IF or 6MHz low IF, which is sampled by 25MHz
> clock, and delivers a transport stream by parallel or
> serial.
>
> The 64 KB SRAM is embedded to support all modes of
> de-interleaving in 64/256 QAM. There is no need to use
> external memory for de-interleaver.
>
> The thermal characteristics of the former product,
> Advanced VSB receiver (LGDT3301), has been confirmed
> already in several products and become an unique feature
> of our chips. This attribute is materialized with this
> new chip too.
>
> ATSC compliant 8/16 VSB and MMDS 2/4/8/16 VSB receiver
>
> ITU-T J.83 Annex B compliant 64/256 QAM receiver
>
> Integrated 10-bit A/D converter
>
> (supports for optional external A/D converter)
>
> All digital demodulation
>
> Parallel/serial MPEG-2 transport interface
>
> Supports I2C bus interface
>
> Boundary Scan test circuit complies to the IEEE
> Std.1149.1
>
> Highly integrated solution for the lowest system cost
>
> Embedded 64KB SRAM for VSB and 64/256QAM
> (No need to use external de-interleaver SRAM)
>
> Supports 44MHz IF input as well as low IF input(6MHz)
> 100 pin TQFP
>
> Operating voltage(I/O, Core): 3.3V, 1.8V
>
> Taylor Jacob wrote:
> >This patch adds support for the LGDT3303 VSB/QAM
> > chipset. I used the
>
> same SNR
>
> >code as the LGDT3302 chipset that was recently added to
> > cvs. Any
>
> questions
>
> >please feel free to ask. This will add support for the
> > BBTI Air2PC
>
> card due to
>
> >ship very soon that utilizes this chipset.
> >
> >Taylor Jacob
> >
> >
> >
> >/*
> > Support for BBTI Technisat Air2PC - ATSC - 3rd Gen
> >
> > Copyright (C) 2005 Taylor Jacob
> > <rtjacob at earthlink.net>
> >
> > This program is free software; you can redistribute
> > it and/or modify it under the terms of the GNU General
> > Public License as published by the Free Software
> > Foundation; either version 2 of the License, or (at
> > your option) any later version.
> >
> > This program is distributed in the hope that it
> > will be useful, but WITHOUT ANY WARRANTY; without even
> > the implied warranty of MERCHANTABILITY or FITNESS FOR
> > A PARTICULAR PURPOSE. See the GNU General Public
> > License for more details.
> >
> > You should have received a copy of the GNU General
> > Public License along with this program; if not, write
> > to the Free Software Foundation, Inc., 675 Mass Ave,
> > Cambridge, MA 02139, USA.
> >
> >*/
> >
> >#include <linux/init.h>
> >#include <linux/module.h>
> >#include <linux/moduleparam.h>
> >#include <linux/device.h>
> >#include <linux/kernel.h>
> >#include <asm/div64.h>
> >
> >#include "dvb_frontend.h"
> >#include "lgdt3303.h"
> >
> >struct lgdt3303_state {
> >
> > struct i2c_adapter* i2c;
> > struct dvb_frontend_ops ops;
> > const struct lgdt3303_config* config;
> > struct dvb_frontend frontend;
> > u8 current_modulation;
> >
> > /* demodulator private data */
> > u8 initialised:1;
> >};
> >
> >static int debug;
> >#define dprintk(args...) \
> > do { \
> > if (debug) printk(KERN_DEBUG "lgdt3303: "
> > args); \ } while (0)
> >
> >static int i2c_writebytes (struct lgdt3303_state*
> > state, u8 reg, u8
>
> *buf, u8 len)
>
> >{
> > /* probbably a much better way or doing this */
> > u8 buf2 [256],x;
> > int err;
> > struct i2c_msg msg = { .addr =
> > state->config->demod_address,
>
> .flags = 0, .buf = buf2, .len = len + 1 };
>
> > buf2[0] = reg;
> > for (x = 0 ; x < len ; x++)
> > buf2[x+1] = buf[x];
> >
> > if ((err = i2c_transfer (state->i2c, &msg, 1)) !=
> > 1) { printk ("%s: i2c write error (addr %02x, err ==
> > %i)\n", __FUNCTION__, state->config->demod_address,
> > err); return -EREMOTEIO;
> > }
> >
> > return 0;
> >}
> >
> >static int i2c_tunerwritebytes (struct lgdt3303_state*
> > state, u8 *buf,
>
> u8 len)
>
> >{
> > /* probbably a much better way or doing this */
> > int err;
> > struct i2c_msg msg = { .addr =
> > state->config->tuner_address,
>
> .flags = 0, .buf = buf, .len = len };
>
> > if ((err = i2c_transfer (state->i2c, &msg, 1)) !=
> > 1) { printk ("%s: i2c write error (addr %02x, err ==
> > %i)\n", __FUNCTION__, state->config->demod_address,
> > err); return -EREMOTEIO;
> > }
> >
> > return 0;
> >}
> >
> >static u8 i2c_readbytes (struct lgdt3303_state* state,
> > u8 reg, u8*
>
> buf, u8 len)
>
> >{
> > u8 reg2 [] = { reg };
> >
> > struct i2c_msg msg [] = { { .addr =
> > state->config->demod_address,
>
> .flags = 0, .buf = reg2, .len = 1 },
>
> > { .addr = state->config->demod_address,
> > .flags = I2C_M_RD,
>
> .buf = buf, .len = len } };
>
> > int err;
> >
> > if ((err = i2c_transfer (state->i2c, msg, 2)) != 2)
> > { printk ("%s: i2c read error (addr %02x, err ==
> > %i)\n", __FUNCTION__, state->config->demod_address,
> > err); return -EREMOTEIO;
> > }
> >
> > return 0;
> >}
> >
> >static void lgdt3303_soft_reset(struct lgdt3303_state*
> > state) {
> > u8 buf;
> > dprintk("%s\n", __FUNCTION__);
> >
> > buf = 0x00;
> > i2c_writebytes(state,0x02,&buf,1);
> >
> > buf = 0x01;
> > i2c_writebytes(state,0x02,&buf,1);
> >
> > return;
> >}
> >
> >static int lgdt3303_setup_frontend_parameters (struct
> > dvb_frontend* fe, struct dvb_frontend_parameters *p) {
> > struct lgdt3303_state* state =
> > fe->demodulator_priv; u32 freq = 0;
> > u16 tunerfreq = 0;
> > u8 buf[6];
> >
> > freq = 44000000 + p->frequency;
> >
> >
> > tunerfreq = freq / 62500;
> >
> > buf[0] = (tunerfreq >> 8) & 0x7F;
> > buf[1] = (tunerfreq & 0xFF);
> > buf[2] = 0x86;
> >
> > if (p->frequency < 160000000) {
> > buf[3] = 0x01;
> > } else if (p->frequency > 445000000) {
> > buf[3] = 0x04;
> > } else {
> > buf[3] = 0x02;
> > }
> >
> > /* Cut and paste from BBTI Windows driver
> > Important? */ buf[4] = buf[2] | 0x18;
> > buf[5] = 0x50;
> >
> > i2c_tunerwritebytes(state,buf,4);
> >
> > i2c_tunerwritebytes(state,&buf[4],2);
> >
> > /* invert clock */
> > buf[0] = 0xF3;
> > i2c_writebytes(state,0x87,buf,1);
> >
> > state->current_modulation = p->u.vsb.modulation;
> >
> > switch (p->u.vsb.modulation)
> > {
> > case VSB_8:
> > buf[0] = 0x03;
> > i2c_writebytes(state,0x00,buf,1);
> > buf[0] = 0x40;
> > i2c_writebytes(state,0x0d,buf,1);
> > buf[0] = 0x87;
> > i2c_writebytes(state,0x0e,buf,1);
> > buf[0] = 0x8e;
> > i2c_writebytes(state,0x0f,buf,1);
> > buf[0] = 0x01;
> > i2c_writebytes(state,0x10,buf,1);
> > buf[0] = 0x88;
> > i2c_writebytes(state,0x47,buf,1);
> > buf[0] = 0x14;
> > i2c_writebytes(state,0x4c,buf,1);
> > break;
> >
> > case QAM_64:
> > buf[0] = 0x00;
> > i2c_writebytes(state,0x00,buf,1);
> > i2c_writebytes(state,0x0d,buf,1);
> > i2c_writebytes(state,0x0e,buf,1);
> > i2c_writebytes(state,0x0f,buf,1);
> > i2c_writebytes(state,0x10,buf,1);
> > buf[0] = 0x63;
> > i2c_writebytes(state,0x51,buf,1);
> > buf[0] = 0x66;
> > i2c_writebytes(state,0x47,buf,1);
> > buf[0] = 0x66;
> > i2c_writebytes(state,0x48,buf,1);
> > buf[0] = 0x1a;
> > i2c_writebytes(state,0x4d,buf,1);
> > buf[0] = 0x14;
> > i2c_writebytes(state,0x4c,buf,1);
> > buf[0] = 0x08;
> > i2c_writebytes(state,0x49,buf,1);
> > buf[0] = 0x9b;
> > i2c_writebytes(state,0x4a,buf,1);
> > break;
> >
> > case QAM_256:
> > buf[0] = 0x01;
> > i2c_writebytes(state,0x00,buf,1);
> > buf[0] = 0x00;
> > i2c_writebytes(state,0x0d,buf,1);
> > i2c_writebytes(state,0x0e,buf,1);
> > i2c_writebytes(state,0x0f,buf,1);
> > i2c_writebytes(state,0x10,buf,1);
> > buf[0] = 0x63;
> > i2c_writebytes(state,0x51,buf,1);
> > buf[0] = 0x66;
> > i2c_writebytes(state,0x47,buf,1);
> > buf[0] = 0x66;
> > i2c_writebytes(state,0x48,buf,1);
> > buf[0] = 0x1a;
> > i2c_writebytes(state,0x4d,buf,1);
> > buf[0] = 0x14;
> > i2c_writebytes(state,0x4c,buf,1);
> > buf[0] = 0x08;
> > i2c_writebytes(state,0x49,buf,1);
> > buf[0] = 0x9b;
> > i2c_writebytes(state,0x4a,buf,1);
> > break;
> > default:
> > break;
> > }
> >
> > lgdt3303_soft_reset(state);
> >
> > return 0;
> >}
> >
> >static int lgdt3303_read_status(struct dvb_frontend*
> > fe, fe_status_t*
>
> status)
>
> >{
> > struct lgdt3303_state* state =
> > fe->demodulator_priv; u8 lock;
> > *status = 0;
> >
> > i2c_readbytes(state,0x1C,&lock,1);
> > if (lock & 0x80) {
> > *status |= FE_HAS_SIGNAL;
> > *status |= FE_HAS_CARRIER;
> > }
> >
> > i2c_readbytes(state,0x58,&lock,1);
> > if (lock & 0x01) {
> > *status |= FE_HAS_VITERBI;
> > }
> >
> > if (lock & 0x02) {
> > *status |= FE_HAS_SYNC;
> > *status |= FE_HAS_LOCK;
> > }
> > return 0;
> >}
> >
> >static int lgdt3303_read_ber(struct dvb_frontend* fe,
> > u32* ber) {
> >
> > /* Not implimented in frontend */
> > *ber = 0;
> >
> > return 0;
> >}
> >
> >static int lgdt3303_read_signal_strength(struct
> > dvb_frontend* fe, u16*
>
> strength)
>
> >{
> > struct lgdt3303_state* state =
> > fe->demodulator_priv; u8 b[] = {0,0,0};
> > u16 temp = 0;
> >
> > i2c_readbytes(state,0x52,&b[0],1);
> > i2c_readbytes(state,0x54,&b[1],1);
> >
> > /* This math was given from example from the
> > windows bbti driver */ temp = 1700 - ((b[0] & 0x07) <<
> > 8 | b[1]);
> >
> > *strength = temp * (0xFFFF/1700);
> >
> > return 0;
> >}
> >
> >static int lgdt3303_read_snr(struct dvb_frontend* fe,
> > u16* snr) {
> >
> > struct lgdt3303_state* state =
> > fe->demodulator_priv; u8 b[] = {0,0,0};
> > u32 noise = 0;
> >
> > /* This code cut and pasted from the LGDT3302.C
> > driver and ought * to be put in a common place at some
> > point */
> >
> > /*
> > * Spec sheet shows formula for SNR_EQ = 10
> > log10(25 * 24**2 / noise) * and SNR_PH = 10 log10(25 *
> > 32**2 / noise) for equalizer and
>
> phase tracker
>
> > * respectively. The following tables are built on
> > these formulas. * The usual definition is SNR = 20
> > log10(signal/noise) * If the specification is wrong
> > the value retuned is 1/2 the
>
> actual SNR in db.
>
> > *
> > * This table is a an ordered list of noise values
> > computed by the * formula from the spec sheet such
> > that the index into the table * starting at 43 or 45
> > is the SNR value in db. There are
>
> duplicate noise
>
> > * value entries at the beginning because the SNR
> > varies more than * 1 db for a change of 1 digit in
> > noise at very small values of
>
> noise.
>
> > *
> > * Examples from SNR_EQ table:
> > * noise SNR
> > * 0 43
> > * 1 42
> > * 2 39
> > * 3 37
> > * 4 36
> > * 5 35
> > * 6 34
> > * 7 33
> > * 8 33
> > * 9 32
> > * 10 32
> > * 11 31
> > * 12 31
> > * 13 30
> > */
> >
> > static const u32 SNR_EQ[] =
> > { 1, 2, 2, 2, 3, 3, 4,
> > 4,
>
> 5, 7,
>
> > 9, 11, 13, 17, 21, 26,
> > 33, 41,
>
> 52, 65,
>
> > 81, 102, 129, 162, 204, 257,
> > 323, 406,
>
> 511, 644,
>
> > 810, 1020, 1284, 1616, 2035, 2561,
> > 3224, 4059,
>
> 5110, 6433,
>
> > 8098, 10195, 12835, 16158, 20341, 25608,
> > 32238, 40585,
>
> 51094, 64323,
>
> > 80978, 101945, 128341, 161571, 203406,
> > 256073, 0x40000 };
> >
> > static const u32 SNR_PH[] =
> > { 1, 2, 2, 2, 3, 3,
> > 4, 5,
>
> 6, 8,
>
> > 10, 12, 15, 19, 23, 29,
> > 37, 46,
>
> 58, 73,
>
> > 91, 115, 144, 182, 229, 288,
> > 362, 456,
>
> 574, 722,
>
> > 909, 1144, 1440, 1813, 2282, 2873,
> > 3617, 4553,
>
> 5732, 7216,
>
> > 9084, 11436, 14396, 18124, 22817, 28724,
> > 36161, 45524,
>
> 57312, 72151,
>
> > 90833, 114351, 143960, 181235, 228161,
> > 0x040000 };
> >
> > static u32 snr_db; /* index into SNR_EQ[] */
> >
> > if (state->current_modulation == VSB_8) {
> >
> > /* Equalizer Mean-Square Error Register for VSB
> > */
> >
> > i2c_readbytes(state,0x6E,&b[0],1);
> > i2c_readbytes(state,0x71,&b[1],1);
> > i2c_readbytes(state,0x72,&b[2],1);
> >
> > noise = ((b[0] & 0x07) << 16) | (b[1] << 8) |
> > b[2];
> >
> > /*
> > * Look up noise value in table.
> > * A better search algorithm could be used...
> > * watch out there are duplicate entries.
> > */
> > for (snr_db = 0; snr_db < sizeof(SNR_EQ);
> > snr_db++) { if (noise < SNR_EQ[snr_db]) {
> > *snr = (43 - snr_db) * (0xFFFF/43);
> >
> > break;
> > }
> > }
> > } else {
> > /* Phase Tracker Mean-Square Error Register for
> > QAM */
> >
> > i2c_readbytes(state,0x6F,&b[0],1);
> > i2c_readbytes(state,0x70,&b[1],1);
> > i2c_readbytes(state,0x71,&b[2],1);
> > noise = ((b[0] & 7<<3) << 13) | (b[1] << 8) |
> > b[2];
> >
> > /* Look up noise value in table. */
> > for (snr_db = 0; snr_db < sizeof(SNR_PH);
> > snr_db++) { if (noise < SNR_PH[snr_db]) {
> > *snr = (45 - snr_db) * (0xFFFF/45);
> > break;
> > }
> > }
> > }
> >
> > return 0;
> >}
> >
> >static int lgdt3303_read_ucblocks(struct dvb_frontend*
> > fe, u32* ucblocks) {
> > struct lgdt3303_state* state =
> > fe->demodulator_priv; u8 b[] = {0,0,0};
> >
> > i2c_readbytes(state,0x8B,&b[0],1);
> > i2c_readbytes(state,0x8C,&b[1],1);
> >
> > *ucblocks = (b[0] << 8) | b[1];
> >
> > return 0;
> >}
> >
> >static int lgdt3303_sleep(struct dvb_frontend* fe)
> >{
> > return 0;
> >}
> >
> >static int lgdt3303_init(struct dvb_frontend* fe)
> >{
> > return 0;
> >}
> >
> >static int lgdt3303_get_tune_settings(struct
> > dvb_frontend* fe, struct
>
> dvb_frontend_tune_settings* fesettings)
>
> >{
> > fesettings->min_delay_ms = 500;
> > fesettings->step_size = 0;
> > fesettings->max_drift = 0;
> > return 0;
> >}
> >
> >static void lgdt3303_release(struct dvb_frontend* fe)
> >{
> > struct lgdt3303_state* state =
> > fe->demodulator_priv; kfree(state);
> >}
> >
> >static struct dvb_frontend_ops lgdt3303_ops;
> >
> >struct dvb_frontend* lgdt3303_attach(const struct
> > lgdt3303_config* config, struct i2c_adapter* i2c)
> >{
> > struct lgdt3303_state* state = NULL;
> > u8 buf = 0;
> >
> > /* allocate memory for the internal state */
> > state = kmalloc(sizeof(struct lgdt3303_state),
> > GFP_KERNEL); if (state == NULL) goto error;
> >
> > /* setup the state */
> > state->config = config;
> > state->i2c = i2c;
> > state->current_modulation = VSB_8;
> > memcpy(&state->ops, &lgdt3303_ops, sizeof(struct
> > dvb_frontend_ops));
> >
> > i2c_readbytes(state, 0x85, &buf, 1);
> > if (buf != 0x20) goto error;
> >
> > i2c_readbytes(state, 0x86, &buf, 1);
> > if (buf != 0x40) goto error;
> >
> > /* create dvb_frontend */
> > state->frontend.ops = &state->ops;
> > state->frontend.demodulator_priv = state;
> > return &state->frontend;
> >
> >error:
> > kfree(state);
> > return NULL;
> >}
> >
> >static struct dvb_frontend_ops lgdt3303_ops = {
> >
> > .info = {
> > .name = "LGDT3303 VSB/QAM frontend",
> > .type = FE_ATSC,
> > .frequency_min = 54000000,
> > .frequency_max = 860000000,
> > /* stepsize is just a guess */
> > .frequency_stepsize = 166666,
> > .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_8VSB | FE_CAN_QAM_64 |
> > FE_CAN_QAM_256 },
> >
> > .release = lgdt3303_release,
> >
> > .init = lgdt3303_init,
> > .sleep = lgdt3303_sleep,
> >
> > .set_frontend = lgdt3303_setup_frontend_parameters,
> > .get_tune_settings = lgdt3303_get_tune_settings,
> >
> > .read_status = lgdt3303_read_status,
> > .read_ber = lgdt3303_read_ber,
> > .read_signal_strength =
> > lgdt3303_read_signal_strength, .read_snr =
> > lgdt3303_read_snr,
> > .read_ucblocks = lgdt3303_read_ucblocks,
> >
> >};
> >
> >module_param(debug, int, 0644);
> >MODULE_PARM_DESC(debug, "Turn on/off frontend debugging
> > (default:off).");
> >
> >MODULE_DESCRIPTION("LGDT3303 ATSC (8VSB & ITU J83
> > AnnexB FEC
>
> QAM64/256) demodulator driver");
>
> >MODULE_AUTHOR("Taylor Jacob");
> >MODULE_LICENSE("GPL");
> >
> >EXPORT_SYMBOL(lgdt3303_attach);
> >
> >
> >
> >/*
> > Driver for the LGDT3303 demodulator
> >*/
> >
> >#ifndef LGDT3303_H
> >#define LGDT3303_H
> >
> >#include <linux/dvb/frontend.h>
> >
> >struct lgdt3303_config
> >{
> > /* the demodulator's i2c address */
> > u8 demod_address;
> > u8 tuner_address;
> >
> >};
> >
> >extern struct dvb_frontend* lgdt3303_attach(const
> > struct
>
> lgdt3303_config* config,
>
> > struct i2c_adapter* i2c);
> >
> >#endif // LGDT3303_H
More information about the linux-dvb
mailing list