From patchwork Wed Apr 1 07:35:10 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: David Wong X-Patchwork-Id: 15590 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 n317ZMj8007796 for ; Wed, 1 Apr 2009 07:35:22 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1759985AbZDAHfS (ORCPT ); Wed, 1 Apr 2009 03:35:18 -0400 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1759775AbZDAHfS (ORCPT ); Wed, 1 Apr 2009 03:35:18 -0400 Received: from rv-out-0506.google.com ([209.85.198.228]:41583 "EHLO rv-out-0506.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1759985AbZDAHfM (ORCPT ); Wed, 1 Apr 2009 03:35:12 -0400 Received: by rv-out-0506.google.com with SMTP id f9so3478902rvb.1 for ; Wed, 01 Apr 2009 00:35:10 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:received:in-reply-to:references :date:message-id:subject:from:to:cc:content-type; bh=85jEV6On9g5bHhXRYxF7y9k5QZ4SHoNDH8U64H5ZT1k=; b=BLrck6WsMg1Le8uZwOVn2Q637xsfWUsMvgngea8x4lzOhJZ6ZKOBij/PdsE4+1PWp9 pcSFJZAuI/V71ciAB0qnJExlaGivjeLL+1sV9c3gDCkkVhEeM9uTvo8rFr04Au76qXdH vbWkOAmTqnajDZsxxxGvqNINdvA1tUX97mNpk= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:in-reply-to:references:date:message-id:subject:from:to :cc:content-type; b=x+VKTYSKaI0kqgQXa6ErLV3eomom439w4d1MfGO/h7aelK3dF9dw7Ws2N+KRX6Z1ND hr1cST5WuHYxPQeywhoCQMzeRWObzqtGxte2hmRpuyVadDi6bFIJmbquFUHJMxNXlCUP M4HSmhrHWEAKv+XEDypbT9qa1BWWULbb8aAp8= MIME-Version: 1.0 Received: by 10.141.26.19 with SMTP id d19mr3897056rvj.137.1238571310742; Wed, 01 Apr 2009 00:35:10 -0700 (PDT) In-Reply-To: <20090327065726.5e4b4211@pedra.chehab.org> References: <15ed362e0903170855k2ec1e5afm613de692c237e34d@mail.gmail.com> <20090327065726.5e4b4211@pedra.chehab.org> Date: Wed, 1 Apr 2009 15:35:10 +0800 Message-ID: <15ed362e0904010035w7428c9b1saea12978d4ccc218@mail.gmail.com> Subject: Re: [PATCH] Support for Legend Silicon LGS8913/LGS8GL5/LGS8GXX China DMB-TH digital demodulator From: David Wong To: Mauro Carvalho Chehab Cc: linux-media@vger.kernel.org Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Resubmit after code clean up. This patch contains the unified driver for Legend Silicon LGS8913 and LGS8GL5. It should replace lgs8gl5.c in media/dvb/frontends in the future. Signed-off-by: David T.L. Wong On Fri, Mar 27, 2009 at 5:57 PM, Mauro Carvalho Chehab wrote: > On Tue, 17 Mar 2009 23:55:05 +0800 > David Wong wrote: > >> +#undef USE_FAKE_SIGNAL_STRENGTH > > Hmm... why do you need this upstream? Is the signal strength working? If so, > just remove this test code. > >> + >> +static void lgs8gxx_auto_lock(struct lgs8gxx_state *priv); > > I don't see why do you need to prototype this function. > >> + >> +static int debug = 0; > > Don't initialize static vars to zero. Kernel already does this, and static > initialization requires eats some space. > >> +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; > > Hmm... this is weird. > > That's said, maybe you may need some DVBS2 API additions for DMB. You should > propose some API additions and provide a patch for it. > >> +     /* 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; >> +     } > > Same here: if there's no exact match, we should first patch the core files to > improve the API, and then use the correct values. > >> +     fe_params->u.ofdm.code_rate_HP = >> +     fe_params->u.ofdm.code_rate_LP = translated_fec; > > The above seems weird. It would be better to do: > > +       fe_params->u.ofdm.code_rate_HP = translated_fec; > +       fe_params->u.ofdm.code_rate_LP = translated_fec; > > The gcc optimizer will produce the same code, but this way would be cleaner for > those who are reading the source code. > >> +static >> +int lgs8gxx_get_tune_settings(struct dvb_frontend *fe, >> +                           struct dvb_frontend_tune_settings *fesettings) >> +{ >> +     /* FIXME: copy from tda1004x.c */ > > It would be nice if you fix those FIXME's. > >> +     fesettings->min_delay_ms = 800; >> +     /* Drift compensation makes no sense for DVB-T */ > > DVB-T??? > >> +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; >> +} > > I dunno if you are following all those discussions about SNR. We're trying to > standardize the meaning for all those status reads (SNR, signal strength, etc. > > Nothing were decided yet, but while we don't take a decision, the better is if > you provide some comments at the source code specifying what's the unit for > each of those status (dB? 0.1 dB steps? dB * 256 ?). > >> +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 >> +     }, > > Also here we should reflect the proper DMB parameters, after the API additions. > > --- > > Before submitting patches, please check they with checkpatch.pl ( see > http://linuxtv.org/hg/v4l-dvb/raw-file/tip/README.patches for the submission > procedures). > > Please fix the CodingStyle errors detected by the tool: > > > ERROR: do not initialise statics to 0 or NULL > #91: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:43: > +static int debug = 0; > > WARNING: printk() should include KERN_ facility level > #145: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:97: > +               printk("%s: reg=0x%02X, data=0x%02X\n", __func__, reg, b1[0]); > > ERROR: do not use C99 // comments > #164: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:116: > +       if_conf = 0x10; // AGC output on; > > ERROR: spaces required around that ':' (ctx:VxV) > #167: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:119: > +               ((config->ext_adc) ? 0x80:0x00) | >                                         ^ > > ERROR: spaces required around that ':' (ctx:VxV) > #168: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:120: > +               ((config->if_neg_center) ? 0x04:0x00) | >                                               ^ > > ERROR: spaces required around that ':' (ctx:VxV) > #169: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:121: > +               ((config->if_freq == 0) ? 0x08:0x00) | /* Baseband */ >                                              ^ > > ERROR: spaces required around that ':' (ctx:VxV) > #170: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:122: > +               ((config->ext_adc && config->adc_signed) ? 0x02:0x00) | >                                                               ^ > > ERROR: spaces required around that ':' (ctx:VxV) > #171: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:123: > +               ((config->ext_adc && config->if_neg_edge) ? 0x01:0x00); >                                                                ^ > > WARNING: braces {} are not necessary for single statement blocks > #216: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:168: > +       if (priv->config->prod == LGS8GXX_PROD_LGS8913) { > +               lgs8gxx_write_reg(priv, 0xC6, 0x01); > +       } > > ERROR: do not use C99 // comments > #223: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:175: > +       // clear FEC self reset > > WARNING: braces {} are not necessary for single statement blocks > #244: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:196: > +       if (priv->config->prod == LGS8GXX_PROD_LGS8G52) { > +               lgs8gxx_write_reg(priv, 0xD9, 0x40); > +       } > > ERROR: trailing whitespace > #300: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:252: > +^Iint err; $ > > ERROR: space required after that ',' (ctx:VxV) > #327: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:279: > +       int i,j; >             ^ > > ERROR: spaces required around that '=' (ctx:WxV) > #338: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:290: > +               for (j =0 ; j < 2; j++) { >                       ^ > > ERROR: trailing statements should be on next line > #341: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:293: > +                       if (err) goto out; > +                       if (err) goto out; > ERROR: trailing statements should be on next line > #342: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:294: > +                       if (locked) goto locked; > +                       if (locked) goto locked; > ERROR: spaces required around that '=' (ctx:WxV) > #344: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:296: > +               for (j =0 ; j < 2; j++) { >                       ^ > > ERROR: trailing statements should be on next line > #347: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:299: > +                       if (err) goto out; > +                       if (err) goto out; > ERROR: trailing statements should be on next line > #348: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:300: > +                       if (locked) goto locked; > +                       if (locked) goto locked; > ERROR: trailing statements should be on next line > #352: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:304: > +               if (err) goto out; > +               if (err) goto out; > ERROR: trailing statements should be on next line > #353: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:305: > +               if (locked) goto locked; > +               if (locked) goto locked; > ERROR: do not use C99 // comments > #381: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:333: > +       //u8 ctrl_frame = 0, mode = 0, rate = 0; > > ERROR: trailing whitespace > #395: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:347: > +^I$ > > WARNING: braces {} are not necessary for single statement blocks > #404: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:356: > +       if (priv->config->prod == LGS8GXX_PROD_LGS8913) { > +               lgs8gxx_write_reg(priv, 0xC0, detected_param); > +       } > > ERROR: do not use C99 // comments > #407: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:359: > +       //lgs8gxx_soft_reset(priv); > > WARNING: suspect code indent for conditional statements (8, 8) > #412: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:364: > +       if (gi == 0x2) > +       switch(gi) { > > ERROR: space required before the open parenthesis '(' > #413: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:365: > +       switch(gi) { > > ERROR: trailing whitespace > #467: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:419: > +^Ilgs8gxx_write_reg(priv, 0x2C, 0); $ > > WARNING: line over 80 characters > #477: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:429: > +       struct lgs8gxx_state *priv = (struct lgs8gxx_state *)fe->demodulator_priv; > > WARNING: braces {} are not necessary for single statement blocks > #493: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:445: > +       if (config->prod == LGS8GXX_PROD_LGS8913) { > +               lgs8913_init(priv); > +       } > > WARNING: suspect code indent for conditional statements (8, 8) > #550: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:502: > +       if ((fe_params->u.ofdm.code_rate_HP == FEC_AUTO) || > [...] > +       } else { > > ERROR: space required before the open parenthesis '(' > #629: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:581: > +       switch(t & LGS_FEC_MASK) { > > ERROR: space required before the open parenthesis '(' > #646: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:598: > +       switch(t & SC_MASK) { > > WARNING: line over 80 characters > #707: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:659: > +                       *fe_status |= FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; > > ERROR: trailing whitespace > #724: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:676: > +^Idprintk("%s()\n", __func__);^I$ > > ERROR: space prohibited before that close parenthesis ')' > #734: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:686: > +       if (v < 0x100 ) > > ERROR: trailing whitespace > #748: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:700: > +^I^I$ > > ERROR: trailing whitespace > #818: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:770: > +^I$ > > ERROR: "foo* bar" should be "foo *bar" > #865: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:817: > +static int lgs8gxx_i2c_gate_ctrl(struct dvb_frontend* fe, int enable) > > WARNING: braces {} are not necessary for any arm of this statement > #871: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:823: > +       if (enable) { > [...] > +       } else { > [...] > > WARNING: line over 80 characters > #872: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:824: > +               return lgs8gxx_write_reg(priv, 0x01, 0x80 | priv->config->tuner_address); > > ERROR: do not use C99 // comments > #896: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:848: > +       //.sleep = lgs8gxx_sleep, > > ERROR: space required after that ',' (ctx:VxV) > #917: FILE: linux/drivers/media/dvb/frontends/lgs8gxx.c:869: > +       dprintk("%s()\n",__func__); >                        ^ > > ERROR: trailing whitespace > #1111: FILE: linux/drivers/media/dvb/frontends/lgs8gxx_priv.h:58: > +#define GI_595^I0x01^I$ > > > > Cheers, > Mauro > 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 Wed Apr 01 15:28:04 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 Wed Apr 01 15:28:04 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 Wed Apr 01 15:28:04 2009 +0800 @@ -0,0 +1,859 @@ +/* + * 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) + +static int debug; +static int fake_signal_str; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +module_param(fake_signal_str, int, 0644); +MODULE_PARM_DESC(fake_signal_str, "fake signal strength for LGS8913." +"Signal strength calculation is slow.(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(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n", + __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg, 1); + + if (ret != 1) + dprintk(KERN_DEBUG "%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(KERN_DEBUG "%s: error reg=0x%x, ret=%i\n", + __func__, reg, ret); + return -1; + } + + *p_data = b1[0]; + if (debug >= 2) + printk(KERN_DEBUG "%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 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); + + 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 */ + /* TODO 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); + } + + /* 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; +#if 0 + int translated_fec = FEC_1_2; +#endif + + 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); +#if 0 + /* 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 = translated_fec; + fe_params->u.ofdm.code_rate_LP = translated_fec; +#endif + fe_params->u.ofdm.code_rate_HP = FEC_AUTO; + fe_params->u.ofdm.code_rate_LP = FEC_AUTO; + + /* 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_AUTO; + + /* guard interval */ + fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; + + /* 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; + 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; + s16 max_strength = 0; + u8 str; + u16 i, gi = priv->curr_gi; + + dprintk("%s\n", __func__); + + ret = lgs8gxx_read_reg(priv, 0x4B, &t); + if (ret != 0) + return -EIO; + + if (fake_signal_str) { + if ((t & 0xC0) == 0xC0) { + dprintk("Fake signal strength as 50\n"); + *signal = 0x32; + } else + *signal = 0; + return 0; + } + + 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); + + 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) { + u8 v = 0x80 | priv->config->tuner_address; + return lgs8gxx_write_reg(priv, 0x01, v); + } + 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_AUTO | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = lgs8gxx_release, + + .init = lgs8gxx_init, +#if 0 + .sleep = lgs8gxx_sleep, +#endif + .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) { + 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 Wed Apr 01 15:28:04 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 Wed Apr 01 15:28:04 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