/*
 * 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.
 *
 * Copyright (C) 2002,2003 Florian Schirmer <jolt@tuxbox.org>
 *
 * Copyright (C) 2007,2008 NXP B.V.
 * All Rights Reserved.
 *
 * NXP based DVB adapter driver.
 * Based on the bt8xx driver by Florian Schirmer <jolt@tuxbox.org>
 *
*//*

Rev Date        Author        Comments
--------------------------------------------------------------------------------
  1             Ch. Grosse    First version
SOUsb225#15 20081210 batelaan Merged in cx24117 prototype version
SOUsb225#16 20081210 batelaan Merged in cx24116p
SOUsb225#17 20090127 batelaan Merged in GYX1D
SOUsb225#18 20090219 ocourson Merged in TDA10086/TDA8260
CFR_STB#17.3 20090302 cgrosse Reworked frontend_init function
CFR_STB#17.4 20090310 guo     Add Samsung ISDB-T FE driver (ie tdhl5 dvb4linux API compliant)
CFR_STB#17.5 20090313 guo     Add Thomson ISDB-T FE drivers (NuTune, dvb4linux API compliant)
CFR_STB#17.6 20090323 cgrosse Add NXP TDA18218 Silicon Tuner
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

#include "phStbDemux_common.h"
#include "phStbDemux_frontend.h"

#include <linux/ctype.h>

// For tuner configuration
#include <prjconfig.h>

#include "tda1004x.h"
#include "tda10048.h"
#include "tda1002x.h"
#include "tda18211.h"
#include "tda18218.h"
#include "td1316.h"
#include "tda665xtt.h"
#include "tc90507xbg.h"
#include "tua6039f.h"
#include "mb86a20s.h"
#include "max3541.h"
#include "gyx1d.h"
#include "tda10086.h"
#include "tda826x.h"
#if defined(CONFIG_CX24120) || defined(CONFIG_CX24117) || defined(CONFIG_CX24116P)
#include "cx24120b.h" /* CX24120 and CX24117 share the same driver called "cx24120.c" TBC*/
#include "cx24116p.h"
#endif

#define I2C_NAME(s) (s)->name

/* i2c addresses for demodulators / 8-bit addresses */
#define DEMOD_I2C_ADDRESS_TDA10046_1 0x10
#define DEMOD_I2C_ADDRESS_TDA10046_2 0x12
#define DEMOD_I2C_ADDRESS_TDA10046_3 0x14
#define DEMOD_I2C_ADDRESS_TDA10046_4 0x16

#define DEMOD_I2C_ADDRESS_TDA10048_1 0x10
#define DEMOD_I2C_ADDRESS_TDA10048_2 0x12

#define DEMOD_I2C_ADDRESS_TDA10086_1 0x1C
#define DEMOD_I2C_ADDRESS_TDA10086_2 0x1E

#define DEMOD_I2C_ADDRESS_TDA10021_1 0x1A
#define DEMOD_I2C_ADDRESS_TDA10021_2 0x18

/* CX24120 or CX24116 or CX24117 */
#define DEMOD_I2C_ADDRESS_CX241xx_1 0x0A
#define DEMOD_I2C_ADDRESS_CX241xx_2 0x2A
#define DEMOD_I2C_ADDRESS_CX241xx_3 0x8A
#define DEMOD_I2C_ADDRESS_CX241xx_4 0xAA

#define DEMOD_I2C_ADDRESS_TC90507XBG_1 0x10
#define DEMOD_I2C_ADDRESS_MB86A20S_1   0x10

/* i2c addresses for tuners / 7-bit addresses */
#define TUNER_I2C_ADDRESS_TDA8275A_1 0x60
#define TUNER_I2C_ADDRESS_TDA8275A_2 0x61

#define TUNER_I2C_ADDRESS_TU1216  0x60

#define TUNER_I2C_ADDRESS_TD1316_1 0x60
#define TUNER_I2C_ADDRESS_TD1316_2 0x61
#define TUNER_I2C_ADDRESS_TD1316_3 0x62
#define TUNER_I2C_ADDRESS_TD1316_4 0x63

#define TUNER_I2C_ADDRESS_TDA18211_1 0x60
#define TUNER_I2C_ADDRESS_TDA18211_2 0x63

#define TUNER_I2C_ADDRESS_TDA18218_1 0x60
#define TUNER_I2C_ADDRESS_TDA18218_2 0x63

#define TUNER_I2C_ADDRESS_MAX3541_1 0x60
#define TUNER_I2C_ADDRESS_MAX3541_2 0x61
#define TUNER_I2C_ADDRESS_MAX3541_3 0x62
#define TUNER_I2C_ADDRESS_MAX3541_4 0x63

#define TUNER_I2C_ADDRESS_GYX1D_1 0x60
#define TUNER_I2C_ADDRESS_GYX1D_2 0x61
#define TUNER_I2C_ADDRESS_GYX1D_3 0x62
#define TUNER_I2C_ADDRESS_GYX1D_4 0x63

#define TUNER_I2C_ADDRESS_TDA826X_1 0x60
#define TUNER_I2C_ADDRESS_TDA826X_2 0x61
#define TUNER_I2C_ADDRESS_TDA826X_3 0x62
#define TUNER_I2C_ADDRESS_TDA826X_4 0x63

#define TUNER_I2C_ADDRESS_TDA665XTT_1 0x10
#define TUNER_I2C_ADDRESS_TUA6039F_1  0x10

#define KHZ   1000

/******************************************************************
* LOCAL TYPEDEFS                                                  *
* recommendation only:                     [p]phStbDbg_<Word>+_t  *
*******************************************************************/
typedef enum {
  Demod_Model_TDA10046,
  Demod_Model_TDA10048,
  Demod_Model_TDA10086,
  Demod_Model_TDA10021,
  Demod_Model_CX24116P,
  Demod_Model_CX24120,
  Demod_Model_CX24117,
  Demod_Model_TDHL5_D,
  Demod_Model_TC90507XBG,
  Demod_Model_MB86A20S,
  Demod_Model_NONE,
  Demod_Model_COUNT
} Demod_Model_t;

typedef enum {
  Tuner_Model_TDA8275A,
  Tuner_Model_TU1216,
  Tuner_Model_TD1316,
  Tuner_Model_TDA18211,
  Tuner_Model_TDA18218,
  Tuner_Model_MAX3541,
  Tuner_Model_DTT8D1T,
  Tuner_Model_TDQG4,
  Tuner_Model_DCT4C2,
  Tuner_Model_GYX1D,
  Tuner_Model_TDA826X,
  Tuner_Model_CX24118A,
  Tuner_Model_CX24128,
  Tuner_Model_TDHL5_T,
  Tuner_Model_TDA665XTT,
  Tuner_Model_TUA6039F,
  Tuner_Model_ALPS_TDHE1,
  Tuner_Model_CU1216,
  Tuner_Model_NONE,
  Tuner_Model_COUNT
} Tuner_Model_t;

typedef struct Demod_Model_Desc {
    Demod_Model_t  demod_model;
    char *         demod_name;        // e.g. "TDA10048"
    unsigned short * normal_i2c;      // list of poss. I2C addresses (demod addresses)
} Demod_Model_Desc_t;

typedef struct Tuner_Model_Desc {
    Tuner_Model_t  tuner_model;
    char *         tuner_name;        // e.g. "TD1316"
} Tuner_Model_Desc_t;

/******************************************************************
* STATIC FUNCTION PROTOTYPES                  <Module>_<Word>+    *
*******************************************************************/

static int dvb_phStb_attach_adapter (struct i2c_adapter *adapter);
static int dvb_phStb_detach_client (struct i2c_client *client);

static void gate_tuner_bus(struct dvb_frontend* fe, int gate_on);

/******************************************************************
* STATIC DATA                  g[k|p|kp|pk|kpk]<Module>_<Word>+   *
*******************************************************************/
/* Note:
   - If we have only one frontend connected (on TS0 or on TS1) it will be attached to adapter0.
   - If we have two identical frontends, the first one detected will be attached to adapter0,
   the second one will be attached to adapter1. (in respect normal_i2c_xxx list)
   - If we have two different frontends, demod0/tuner0 will be adapter0, and demod1/tuner1 will
   be adapter1.
*/

int debug;   // module parameter
char * demod0 = "NONE"; // module parameter - default value
char * demod1 = "NONE"; // module parameter - default value
char * tuner0 = "NONE"; // module parameter - default value
char * tuner1 = "NONE"; // module parameter - default value

static char * NoneString = "NONE";

/* gDemod_Model[0] will be installed on /dev/dvb/adapter0 */
/* gDemod_Model[1] will be installed on /dev/dvb/adapter1 */
static Demod_Model_t gDemod_Model[2] = {Demod_Model_NONE, Demod_Model_NONE};
static Tuner_Model_t gTuner_Model[2] = {Tuner_Model_NONE, Tuner_Model_NONE};

static unsigned short normal_i2c_TDA10046[] = {DEMOD_I2C_ADDRESS_TDA10046_1 >> 1, DEMOD_I2C_ADDRESS_TDA10046_2 >> 1, DEMOD_I2C_ADDRESS_TDA10046_3 >> 1, DEMOD_I2C_ADDRESS_TDA10046_4 >> 1, I2C_CLIENT_END};
static unsigned short normal_i2c_TDA10048[] = {DEMOD_I2C_ADDRESS_TDA10048_1 >> 1, DEMOD_I2C_ADDRESS_TDA10048_2 >> 1, I2C_CLIENT_END};
static unsigned short normal_i2c_TDA10086[] = {DEMOD_I2C_ADDRESS_TDA10086_1 >> 1, DEMOD_I2C_ADDRESS_TDA10086_2 >> 1, I2C_CLIENT_END};
static unsigned short normal_i2c_TDA10021[] = {DEMOD_I2C_ADDRESS_TDA10021_1 >> 1, DEMOD_I2C_ADDRESS_TDA10021_2 >> 1, I2C_CLIENT_END};
static unsigned short normal_i2c_CX24116P[] = {DEMOD_I2C_ADDRESS_CX241xx_1  >> 1, DEMOD_I2C_ADDRESS_CX241xx_2  >> 1, I2C_CLIENT_END};
static unsigned short normal_i2c_CX24120[]  = {DEMOD_I2C_ADDRESS_CX241xx_4  >> 1, DEMOD_I2C_ADDRESS_CX241xx_1  >> 1, I2C_CLIENT_END};
static unsigned short normal_i2c_CX24117[]  = {DEMOD_I2C_ADDRESS_CX241xx_1  >> 1, DEMOD_I2C_ADDRESS_CX241xx_2  >> 1, I2C_CLIENT_END};
static unsigned short normal_i2c_TDHL5_D[]  = {I2C_CLIENT_END};
static unsigned short normal_i2c_TC90507XBG[]  = {DEMOD_I2C_ADDRESS_TC90507XBG_1, I2C_CLIENT_END};
static unsigned short normal_i2c_MB86A20S[]    = {DEMOD_I2C_ADDRESS_MB86A20S_1, I2C_CLIENT_END};
static unsigned short normal_i2c_NONE[]     = {I2C_CLIENT_END};

#define NORMAL_I2C_LIST_SIZE (12)
static unsigned short normal_i2c_List[NORMAL_I2C_LIST_SIZE]   = {I2C_CLIENT_END};

/* normal_i2c_List is dynamically filled in phStbFrontend_init() */
/* example : if only one demod tda10048 -> normal_i2c_List = normal_i2c_TDA10048 */
/*           if two demods tda10048 -> normal_i2c_List = normal_i2c_TDA10048 */
/*           if demod tda10048 + tda10086 -> normal_i2c_List = normal_i2c_TDA10048 + normal_i2c_TDA10086*/

/* Info: normal_i2c_List[] is a list of possible i2c address for the demodulator chip */
/* i2c_probe() will try every address in this list */

static const Demod_Model_Desc_t supported_demods [Demod_Model_COUNT] = {
     { Demod_Model_TDA10046,   "TDA10046"  , normal_i2c_TDA10046},
     { Demod_Model_TDA10048,   "TDA10048"  , normal_i2c_TDA10048},
     { Demod_Model_TDA10086,   "TDA10086"  , normal_i2c_TDA10086},
     { Demod_Model_TDA10021,   "TDA10021"  , normal_i2c_TDA10021},
     { Demod_Model_CX24116P,   "CX24116P"  , normal_i2c_CX24116P},
     { Demod_Model_CX24120,    "CX24120"   , normal_i2c_CX24120},
     { Demod_Model_CX24117,    "CX24117"   , normal_i2c_CX24117},
     { Demod_Model_TDHL5_D,    "TDHL5_D"   , normal_i2c_TDHL5_D},
     { Demod_Model_TC90507XBG, "ISDB_TC90507XBG", normal_i2c_TC90507XBG},
     { Demod_Model_MB86A20S,   "ISDB_MB86A20S"  , normal_i2c_MB86A20S},
     { Demod_Model_NONE,       "NONE"      , normal_i2c_NONE}
};

static const Tuner_Model_Desc_t supported_tuners [Tuner_Model_COUNT] = {
     { Tuner_Model_TD1316,         "TD1316"},
     { Tuner_Model_DTT8D1T,        "DTT8D1T"},
     { Tuner_Model_TDQG4,          "TDQG4"},
     { Tuner_Model_DCT4C2,         "DCT4C2"},
     { Tuner_Model_GYX1D,          "GYX1D"},
     { Tuner_Model_TDA18211,       "TDA18211"},
     { Tuner_Model_TDA18218,       "TDA18218"},
     { Tuner_Model_TDA826X,        "TDA826X"},
     { Tuner_Model_CX24118A,       "CX24118A"},
     { Tuner_Model_TDA8275A,       "TDA8275A"},
     { Tuner_Model_TU1216,         "TU1216"},
     { Tuner_Model_MAX3541,        "MAX3541"},
     { Tuner_Model_CX24128,        "CX24128"},
     { Tuner_Model_TDHL5_T,        "TDHL5_T"},
     { Tuner_Model_TDA665XTT,      "ISDB_TDA665XTT"},
     { Tuner_Model_TUA6039F,       "ISDB_TUA6039F"},
     { Tuner_Model_ALPS_TDHE1,     "ALPS_TDHE1"},
     { Tuner_Model_CU1216,         "CU1216"},
     { Tuner_Model_NONE,           "NONE"}
};

static const u8 tda8275aSlaveAddr[] = {TUNER_I2C_ADDRESS_TDA8275A_2, TUNER_I2C_ADDRESS_TDA8275A_1};
static const u8 tda18211SlaveAddr[] = {TUNER_I2C_ADDRESS_TDA18211_1, TUNER_I2C_ADDRESS_TDA18211_2};
static const u8 tda18218SlaveAddr[] = {TUNER_I2C_ADDRESS_TDA18218_1, TUNER_I2C_ADDRESS_TDA18218_1};
static const u8 td1316SlaveAddr[] = {TUNER_I2C_ADDRESS_TD1316_1, TUNER_I2C_ADDRESS_TD1316_2};
static const u8 max3541SlaveAddr[] = {TUNER_I2C_ADDRESS_MAX3541_1, TUNER_I2C_ADDRESS_MAX3541_2};
static const u8 gyx1dSlaveAddr[] = {TUNER_I2C_ADDRESS_GYX1D_1, TUNER_I2C_ADDRESS_GYX1D_2};
static const u8 tda826xSlaveAddr[] = {TUNER_I2C_ADDRESS_TDA826X_1, TUNER_I2C_ADDRESS_TDA826X_1};

/* These values override the min/max capability returned by the demod and/or tuner driver */
/* These values are ignored with a sat frontend */
long gMin_Freq = (174000 * KHZ); // TODO: shouldn't this be 30MHz (lowest VHF) !
long gMax_Freq = (862000 * KHZ); // TODO: shouldn't this be 3GHz  (Highest UHF) !

/* I2C detection variables */
static int dvb_phStb_frontend_count = 0;
static struct i2c_driver dvb_phStb_i2c_driver;
struct i2c_client *dvb_phStb_i2c_client[MAX_SUPPORTED_TUNER];

/*
 * Generic i2c probe
 * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
 */

static unsigned short probe[2]           = { I2C_CLIENT_END, I2C_CLIENT_END };
static unsigned short ignore[2]          = { I2C_CLIENT_END, I2C_CLIENT_END };

static struct i2c_client_address_data dvb_phStb_address_data = {
        .normal_i2c             = 0, /* dynamically assigned */
        .probe                  = probe,
        .ignore                 = ignore,
};

static struct i2c_driver dvb_phStb_i2c_driver = {
        .driver = {
            .name = "dvb_phStb",
         },
        .id = I2C_DRIVERID_DVB,
        .attach_adapter = dvb_phStb_attach_adapter,
        .detach_client = dvb_phStb_detach_client,
};

void ConcatAddressList(unsigned short* dest, size_t size_dest, unsigned short* src, size_t size_src)
{
  unsigned int dest_cpt = 0;

  /* Check size */
    if (size_src > size_dest)
    return;
  /* find end of destination list */
  while (*dest != I2C_CLIENT_END)
  {
    dest++;
    dest_cpt++;
  }

  /* Check available space in destination */
  if ( (size_dest/sizeof(unsigned short))-dest_cpt < size_src/sizeof(unsigned short))
    return;

  /* copy source list in destination list */
  while (*src != I2C_CLIENT_END)
  {
      *dest = *src;
    dest++;
    src++;
  }
  /* set the end of list marker */
  *dest = I2C_CLIENT_END;
}

Demod_Model_t DemodModelFromString(char * string, unsigned short* i2c_address_list, size_t size_of_list)
{
	 int i;
   
	 for (i=0; i< Demod_Model_COUNT; i++)
   {
        if (0 == strcmp(string, supported_demods[i].demod_name))
        {
            ConcatAddressList(i2c_address_list, size_of_list, supported_demods[i].normal_i2c, sizeof(supported_demods[i].normal_i2c));
            break;
        }
   }
   if (i == Demod_Model_COUNT)
      return Demod_Model_NONE;
   else
      return supported_demods[i].demod_model;
}

Tuner_Model_t TunerModelFromString(char * string)
{
	 int i;
   
	 for (i=0; i< Tuner_Model_COUNT; i++)
   {
        if (0 == strcmp(string, supported_tuners[i].tuner_name))
        {
            break;
        }
   }
   if (i == Tuner_Model_COUNT)
      return Tuner_Model_NONE;
   else
      return supported_tuners[i].tuner_model;
}

void phStbFrontend_init(void)
{
   int i,j;

   dvb_phStb_frontend_count = 0;
   dvb_phStb_i2c_client[0]  = NULL;
   dvb_phStb_i2c_client[1]  = NULL;

   for (i=0;i<NORMAL_I2C_LIST_SIZE;i++)
   {
       normal_i2c_List[i] = I2C_CLIENT_END;
   }

   /* demod0 string to uppercase */
   for (j=0; demod0[j]!=0; j++)
   {
       demod0[j] = toupper(demod0[j]);
   }

   /* demod1 string to uppercase */
   for (j=0; demod1[j]!=0; j++)
   {
       demod1[j] = toupper(demod1[j]);
   }

   /* usually gDemod_Model[0]=demod0 and gDemod_Model[1]=demod1
      except in one case : demod0="NONE" demod1!="NONE".
      in that case gDemod_Model[0]=demod1 and gDemod_Model[1]=demod0 */
      
   if ((0 == strcmp(demod0, NoneString)) && (0 != strcmp(demod1, NoneString)))
   {
      gDemod_Model[0] = DemodModelFromString(demod1, &normal_i2c_List, sizeof(normal_i2c_List));
      gTuner_Model[0] = TunerModelFromString(tuner1);
   }
   else
   {
      gDemod_Model[0] = DemodModelFromString(demod0, &normal_i2c_List, sizeof(normal_i2c_List));
      gTuner_Model[0] = TunerModelFromString(tuner0);
      
      gDemod_Model[1] = DemodModelFromString(demod1, &normal_i2c_List, sizeof(normal_i2c_List));
      gTuner_Model[1] = TunerModelFromString(tuner1);
   }
   
   dvb_phStb_address_data.normal_i2c = normal_i2c_List;
   
   dprintk("gFE_Model[0]=%d \n", gDemod_Model[0]);
   dprintk("gTuner_Model[0]=%d \n", gTuner_Model[0]);
   dprintk("gFE_Model[1]=%d \n", gDemod_Model[1]);
   dprintk("gTuner_Model[1]=%d \n", gTuner_Model[1]);

#if !defined(CONFIG_TDHL5_T) && !(defined(CONFIG_IC_EMULATION) || defined(TMFL_IC_EMULATION))
   i2c_add_driver(&dvb_phStb_i2c_driver);
#else
   printk("IC_EMULATION : phStbDemux Skipping i2c_add_driver\n");
#endif

   return;

}

void phStbFrontend_exit(void)
{

#if !defined(CONFIG_TDHL5_T) && !(defined(CONFIG_IC_EMULATION) || defined(TMFL_IC_EMULATION))
   i2c_del_driver(&dvb_phStb_i2c_driver);
#else
   printk("IC_EMULATION : phStbDemux Skipping i2c_del_driver\n");
#endif

   return;
}

/**
 Gate tuner I2c bus on/off - bus should be disabled when not actively tuning

 @param fe frontend device
 @param gate_on Enable or disable tuner bus - 0: disable bus; !0: enable bus
*/
static void gate_tuner_bus(struct dvb_frontend* fe, int gate_on)
{
   if (fe != NULL && fe->ops.i2c_gate_ctrl)
   {
      fe->ops.i2c_gate_ctrl(fe, gate_on);
   }
}

#if !(defined(CONFIG_IC_EMULATION) || defined(TMFL_IC_EMULATION))
static int tu1216_pll_init(struct dvb_frontend* fe)
{
   struct dvb_phStb_card * this_device = (struct dvb_phStb_card *) fe->dvb->priv;
   static u8 tu1216_init_agc[]  = { 0x0b, 0xf5, 0x84, 0xab };
   static u8 tu1216_init_divider[] = { 0x0b, 0xf5, 0xCA, 0xab };
   struct i2c_msg tuner_write_msg = {.addr = TUNER_I2C_ADDRESS_TU1216, .flags = 0};

   u8 b0[] = { TUNER_I2C_ADDRESS_TU1216 };
   u8 b1[] = { 0 };
   struct i2c_msg tuner_read_msg[] = {{ .flags = 0,        .buf = b0, .len = 1 },
                                      { .flags = I2C_M_RD,  .buf = b1, .len = 1 }};
   // Gate tuner I2c on
   ///@todo Rewrite so single 'return' with single gate-Off
   gate_tuner_bus(fe, 1);

   dprintk("tu1216_pll_init\n");
   // setup AGC configuration (for internal AGC)
   tuner_write_msg.buf = tu1216_init_agc;
   tuner_write_msg.len = sizeof(tu1216_init_agc);

   if (i2c_transfer(this_device->i2c_adapter, &tuner_write_msg, 1) != 1)
   {
      gate_tuner_bus(fe, 0);    // Gate tuner I2c off
      return -EIO;
   }
   msleep(1);

   // setup PLL configuration for 166KHz
   tuner_write_msg.buf = tu1216_init_divider;
   tuner_write_msg.len = sizeof(tu1216_init_divider);

   if (i2c_transfer(this_device->i2c_adapter, &tuner_write_msg, 1) != 1)
   {
      gate_tuner_bus(fe, 0);    // Gate tuner I2c off
      return -EIO;
   }
   msleep(1);

   if (debug)
   {
      int ret;

      // read status

      tuner_read_msg[0].addr = TUNER_I2C_ADDRESS_TU1216;
      tuner_read_msg[1].addr = TUNER_I2C_ADDRESS_TU1216;
      ret = i2c_transfer(this_device->i2c_adapter, tuner_read_msg, 2);

      msleep(1);

      dprintk("tu1216_pll_init: Tuner STATUS=0x%X\n", b1[0]);
      if (ret != 2)
      {
         gate_tuner_bus(fe, 0);    // Gate tuner I2c off
         return -EIO;
      }
   }

    gate_tuner_bus(fe, 0);    // Gate tuner I2c off
    return 0;
}

static int tu1216_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
{
   struct dvb_phStb_card *this_device = (struct dvb_phStb_card *) fe->dvb->priv;
   u8 tuner_buf[4];
   struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) };
   int tuner_frequency = 0;
   u8 band, cp, filter;

   u8 b0[] = { TUNER_I2C_ADDRESS_TU1216 };
   u8 b1[] = { 0 };
   int ret;
   struct i2c_msg tuner_read_msg[] = {{ .flags = 0,        .buf = b0, .len = 1 },
                                      { .flags = I2C_M_RD,  .buf = b1, .len = 1 }};

   // Gate tuner I2c on
   ///@todo Rewrite so single 'return' with single gate-Off
   gate_tuner_bus(fe, 1);

   dprintk("tu1216_pll_set\n");
   if (debug)
   {
      // read status

      tuner_read_msg[0].addr = TUNER_I2C_ADDRESS_TU1216;
      tuner_read_msg[1].addr = TUNER_I2C_ADDRESS_TU1216;
      ret = i2c_transfer(this_device->i2c_adapter, tuner_read_msg, 2);

      msleep(1);

      dprintk("tu1216_pll_set: Tuner STATUS=0x%X\n", b1[0]);
      if (ret != 2) {
         gate_tuner_bus(fe, 0);    // Gate tuner I2c off
         return -EIO;
      }
   }

   // determine charge pump
   tuner_frequency = params->frequency + 36166000;
   if (tuner_frequency < 87000000)
   {
      gate_tuner_bus(fe, 0);    // Gate tuner I2c off
      return -EINVAL;
   }
   else if (tuner_frequency < 130000000)
      cp = 3;
   else if (tuner_frequency < 160000000)
      cp = 5;
   else if (tuner_frequency < 200000000)
      cp = 6;
   else if (tuner_frequency < 290000000)
      cp = 3;
   else if (tuner_frequency < 420000000)
      cp = 5;
   else if (tuner_frequency < 480000000)
      cp = 6;
   else if (tuner_frequency < 620000000)
      cp = 3;
   else if (tuner_frequency < 830000000)
      cp = 5;
   else if (tuner_frequency < 895000000)
      cp = 7;
   else
   {
      gate_tuner_bus(fe, 0);    // Gate tuner I2c off
      return -EINVAL;
   }

   // determine band
   if (params->frequency < 49000000)
   {
      gate_tuner_bus(fe, 0);    // Gate tuner I2c off
      return -EINVAL;
   }
   else if (params->frequency < 161000000)
      band = 1;
   else if (params->frequency < 444000000)
      band = 2;
   else if (params->frequency < 861000000)
      band = 4;
   else
   {
      gate_tuner_bus(fe, 0);    // Gate tuner I2c off
      return -EINVAL;
   }

   // setup PLL filter
   switch (params->u.ofdm.bandwidth) {
      case BANDWIDTH_6_MHZ:
         filter = 0;
         break;

      case BANDWIDTH_7_MHZ:
         filter = 0;
         break;

      case BANDWIDTH_8_MHZ:
         filter = 1;
         break;

      default:
         gate_tuner_bus(fe, 0);    // Gate tuner I2c off
         return -EINVAL;
   }

   // calculate divisor
   // ((36166000+((1000000/6)/2)) + Finput)/(1000000/6)
   tuner_frequency = (((params->frequency / 1000) * 6) + 217496) / 1000;

   // setup tuner buffer
   tuner_buf[0] = (tuner_frequency >> 8) & 0x7f;
   tuner_buf[1] = tuner_frequency & 0xff;
   tuner_buf[2] = 0xca;
   tuner_buf[3] = (cp << 5) | (filter << 3) | band;

   if (i2c_transfer(this_device->i2c_adapter, &tuner_msg, 1) != 1)
   {
      gate_tuner_bus(fe, 0);    // Gate tuner I2c off
      return -EIO;
   }

   msleep(1);
   gate_tuner_bus(fe, 0);    // Gate tuner I2c off
   return 0;
}

static int tu1216_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
{
    struct dvb_phStb_card *this_device = (struct dvb_phStb_card *) fe->dvb->priv;

    return request_firmware(fw, name, &this_device->i2c_adapter->dev);
}


static struct tda1004x_config tda10046_config_0[MAX_SUPPORTED_TUNER] = {
   {
   // .demod_address is set in frontend_init
      .invert = 1,
      .invert_oclk = 1,
      .xtal_freq  = TDA10046_XTAL_4M,
      .if_freq    = TDA10046_FREQ_3617,
      .agc_config = TDA10046_AGC_DEFAULT,
      .request_firmware = tu1216_request_firmware
   },
   {
   // .demod_address is set in frontend_init
      .invert = 1,
      .invert_oclk = 1,
      .xtal_freq  = TDA10046_XTAL_4M,
      .if_freq    = TDA10046_FREQ_3617,
      .agc_config = TDA10046_AGC_DEFAULT,
      .request_firmware = tu1216_request_firmware
   }
};

static int tda8275a_pll_init(struct dvb_frontend* fe)
{
   struct dvb_phStb_card * this_device = (struct dvb_phStb_card *) fe->dvb->priv;
   u8 tda8275a_init_data[] = {0x1e, 0xea, 0x00, 0x10, 0x00, 0x5f, 0x0c, 0x06, 0x26, 0xff, 0x40, 0x00, 0x39};
   u8 tda8275a_init_addr[] = {0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0};
   struct i2c_msg tuner_write_msg;
   u8     buffer[2];
   int i = 0;
   int retcode; // success (0) or error (-ve) return code

   tuner_write_msg.addr  = this_device->tuner_addr;
   tuner_write_msg.flags = 0;
   tuner_write_msg.buf   = buffer;
   tuner_write_msg.len   = 2;

#ifdef CONFIG_NXP_STB220
   {
      /* nasty hack to turn on STB220 tuner power supply */
      unsigned long *mmio = (unsigned long *)0xb7e0f000;
      unsigned long tmp;
      tmp = *(mmio + 5);
      *(mmio + 5) = (tmp & 0x7ffdf);
      tmp = *(mmio + 2);
      *(mmio + 2) = (tmp | 0x00020);
      tmp = *(mmio + 1);
      *(mmio + 1) = (tmp | 0x00020);
   }
#endif

   // Gate tuner I2c on - 'returns' must hereafter go via exit: label
   gate_tuner_bus(fe, 1);

   retcode = 0;

   do
   {
      buffer[0] = tda8275a_init_addr[i];
      buffer[1] = tda8275a_init_data[i];
      if (i2c_transfer(this_device->i2c_adapter, &tuner_write_msg, 1) != 1)
      {
          retcode = -EIO;
          goto exit;
      }
   } while (++i < sizeof(tda8275a_init_data));

   msleep(1);

exit: // common exit once tuner bus is gated on ...

   // Gate tuner I2c off
   gate_tuner_bus(fe, 0);

   return retcode;
}

static int tda8275a_pll_set(struct dvb_frontend* fe, struct dvb_frontend_parameters* params)
{
   struct dvb_phStb_card *this_device = (struct dvb_phStb_card *) fe->dvb->priv;
   u8 tda8275a_tune_data[] = { 0x00, 0x00, 0x00, 0x16, 0x00, 0x4b, 0x0c, 0x06, 0x24, 0xff, 0x60, 0x00, 0x39, 0x3c, 0x40};
   u8 tda8275a_tune_addr[] = { 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0x60, 0xa0};
   struct i2c_msg tuner_write_msg;
   static const unsigned long freq[26][4] = {{820000000, 0x1c,  1, 1},
                                             {780000000, 0x1c,  1, 0},
                                             {700000000, 0x14,  1, 1},
                                             {650000000, 0x14,  1, 0},
                                             {620000000, 0x0c,  1, 1},
                                             {550000000, 0x0c,  1, 0},
                                             {538000000, 0x0b,  1, 0},
                                             {520000000, 0x03,  1, 1},
                                             {455000000, 0x03,  1, 0},
                                             {390000000, 0x3b,  2, 0},
                                             {325000000, 0x33,  2, 0},
                                             {290000000, 0x2b,  2, 0},
                                             {269000000, 0x2a,  2, 0},
                                             {227500000, 0x22,  2, 0},
                                             {195000000, 0x5a,  4, 0},
                                             {183000000, 0x52,  4, 0},
                                             {162500000, 0x51,  4, 0},
                                             {154000000, 0x49,  4, 0},
                                             {134500000, 0x49,  4, 0},
                                             {113750000, 0x41,  4, 0},
                                             { 97500000, 0x79,  8, 0},
                                             { 81250000, 0x70,  8, 0},
                                             { 67250000, 0x68,  8, 0},
                                             { 56875000, 0x60,  8, 0},
                                             { 49000000, 0x98, 16, 0},
                                             {        0,    0,  0, 0}};
   u8            buffer[2];
   unsigned long tunerFreq;
   unsigned char Dmask;
   unsigned long D;
   unsigned long N;
   unsigned long scr;
   int i = 0;
   int retcode; // success (0) or error (-ve) return code

   tuner_write_msg.addr  = this_device->tuner_addr;
   tuner_write_msg.flags = 0;
   tuner_write_msg.buf   = buffer;
   tuner_write_msg.len   = 2;

   switch (params->u.ofdm.bandwidth)
   {
      case BANDWIDTH_6_MHZ:
         tunerFreq = params->frequency + 4000000;
         break;

      case BANDWIDTH_7_MHZ:
         tunerFreq = params->frequency + 4500000;
         break;

      case BANDWIDTH_8_MHZ:
         tunerFreq = params->frequency + 5000000;
         break;

      default:
         return -EINVAL;
   }

   if (tunerFreq > 900000000)
   {
      return -EINVAL;
   }
   else
   {
      int i = 0;

      while(freq[i][0] > tunerFreq)
      {
         i++;
      }

      if (freq[i][0] == 0)
      {
         return -EINVAL;
      }

      Dmask = freq[i][1];
      D     = freq[i][2];
      scr   = freq[i][3];
   }

   N = (tunerFreq * D) / 250000;

   dprintk("tda8275a_pll_set freq = %u, D = %ld, N = %ld\n", params->frequency, D, N);

   tda8275a_tune_data[0] &= 0xc0;
   tda8275a_tune_data[0] |= ((N >> 6) & 0x3f);
   tda8275a_tune_data[1] &= 0x03;
   tda8275a_tune_data[1] |= ((N << 2) & 0xfc);
   tda8275a_tune_data[4]  = Dmask;
   tda8275a_tune_data[5] &= 0xcf;
   tda8275a_tune_data[5] |= ((tunerFreq < 550000000) ? 0x10 : 0x00);

   // Gate tuner I2c on - 'returns' must hereafter go via exit: label
   gate_tuner_bus(fe, 1);

   retcode = 0;

   do
   {
      buffer[0] = tda8275a_tune_addr[i];
      buffer[1] = tda8275a_tune_data[i];
      if (i2c_transfer(this_device->i2c_adapter, &tuner_write_msg, 1) != 1)
      {
          retcode = -EIO;
          goto exit;
      }
   } while (++i < sizeof(tda8275a_tune_data));

   msleep(2);

   tda8275a_tune_data[3] &= 0xf0;
   tda8275a_tune_data[3] |= scr;

   buffer[0] = tda8275a_tune_addr[3];
   buffer[1] = tda8275a_tune_data[3];
   if (i2c_transfer(this_device->i2c_adapter, &tuner_write_msg, 1) != 1)
   {
       retcode = -EIO;
       goto exit;
   }

   msleep(550);

   tda8275a_tune_data[5] &= 0xf0;
   tda8275a_tune_data[5] |= 0x0f;
   buffer[0] = tda8275a_tune_addr[5];
   buffer[1] = tda8275a_tune_data[5];
   if (i2c_transfer(this_device->i2c_adapter, &tuner_write_msg, 1) != 1)
   {
       retcode = -EIO;
       goto exit;
   }

exit: // common exit once tuner bus is gated on ...

   // Gate tuner I2c off
   gate_tuner_bus(fe, 0);

   return retcode;
}

static struct tda1004x_config tda10046_config_1[MAX_SUPPORTED_TUNER] = {
   {
   // .demod_address is set in frontend_init
      .invert = 1,
      .invert_oclk = 1,
      .xtal_freq  = TDA10046_XTAL_4M,
      .if_freq    = TDA10046_FREQ_045,
      .agc_config = TDA10046_AGC_TDA827X,
      .request_firmware = tu1216_request_firmware
   },
   {
   // .demod_address is set in frontend_init
      .invert = 1,
      .invert_oclk = 1,
      .xtal_freq  = TDA10046_XTAL_4M,
      .if_freq    = TDA10046_FREQ_045,
      .agc_config = TDA10046_AGC_TDA827X,
      .request_firmware = tu1216_request_firmware
   }
};

static struct tc90507xbg_config tc90507xbg_conf = {
	.if_freq = 44
};
static struct mb86a20s_config mb86a20s_conf = {
	.if_freq = 44
};

static struct tda10048_config tda10048_conf = {
   //  .demod_address is set in frontend_init
   //  .xtal_freq is set in frontend_init
   //  .if_freq is set in frontend_init
   //  .PLL_M_Factor is set in frontend_init
   //  .PLL_N_Factor is set in frontend_init
   //  .PLL_P_Factor is set in frontend_init
       .AGC_TunUsed = TDA10048_AGC_TUN_UNUSED,
       .AGC_PolaTun = TDA10048_AGC_POLA_TUN_INVERTED,
       .AGC_PolaIf =TDA10048_AGC_POLA_IF_INVERTED,
       .AGC_TunMin = TDA10048_AGC_TUN_MIN,
       .AGC_TunMax = TDA10048_AGC_TUN_MAX,
       .AGC_IfMin = TDA10048_AGC_IF_MIN,
       .AGC_IfMax = TDA10048_AGC_IF_MAX,
       .OUT_ParaSer = TDA10048_CFG_OUT_PARALLEL,
#if defined(CONFIG_COMMON_INTERFACE_PRESENT)
       .OUT_ModeAB = TDA10048_CFG_OUT_MODE_B,
#else
       .OUT_ModeAB = TDA10048_CFG_OUT_MODE_A,
#endif
       .OUT_ParaDiv = 7,
       .OUT_MsbLsb = TDA10048_CFG_OUT_MSB_FIRST,
       .OUT_Poclk = TDA10048_CFG_OUT_MODE_A_RISING_EDGE,
       .spectral_inv = 1,
       .tuning_algo = 1,
   };

#if defined(CONFIG_CX24120) || defined(CONFIG_CX24117)
struct cx24120b_config cx241xx_conf;  /* field  ".demod_address" is set in frontend_init */
#endif
#if defined(CONFIG_CX24116P)
struct cx24116p_config cx24116p_conf;
#endif

static struct tda10086_config tda10086_conf = {
   //  .demod_address is set in frontend_init
       .invert =  0,     /* TODO CHECK THIS PARAMETER */ /* does the "inversion" need inverted? */
};

#if defined(CONFIG_10021)
static int alps_tdhe1_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
{
 struct dvb_phStb_card *this_device = (struct dvb_phStb_card *) fe->dvb->priv;
 u8 buf[4];
 struct i2c_msg msg = {.addr = 0x61,.flags = 0,.buf = buf,.len = sizeof(buf) };

#define TUNER_MUL 62500
#define _IF_ 36125000
 u32 div;

 div = (params->frequency + _IF_ + (TUNER_MUL / 2)) / TUNER_MUL;
 buf[0] = (div >> 8) & 0x7f;
 buf[1] = div & 0xff;
 buf[2] = 0x85;
 buf[3] = (params->frequency < (153000000 + _IF_) ? 0x01 :
             params->frequency < (430000000 + _IF_) ? 0x02 :
             params->frequency < (822000000 + _IF_) ? 0x08 : 0x88);

 if (i2c_transfer(this_device->i2c_adapter, &msg, 1) != 1)
   {
  return -EIO;
   }
   msleep(1);

 return 0;
}

//static struct tda10021_config alps_tdhe1_config[MAX_SUPPORTED_TUNER];

static u8 read_pwm_tdhe1(struct dvb_phStb_card *card)
{
   u8 b = 0x34; /* 0xff Modified by ZHQ 11/07/2005 */
   u8 pwm;
   struct i2c_msg msg[] = { {.addr = 0x0c,.flags = 0,       .buf = &b,  .len = 1},
                            {.addr = 0x0c,.flags = I2C_M_RD,.buf = &pwm,.len = 1}
                          };

   if ((i2c_transfer(card->i2c_adapter, msg, 2) != 2) || (pwm == 0xff))
   {
  pwm = 0x48;
   }
   return pwm;
}

static int philips_cu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
{
 struct dvb_phStb_card *this_device = (struct dvb_phStb_card *) fe->dvb->priv;
 u8 buf[4];
 struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };

#define TUNER_MUL 62500

 u32 div = (params->frequency + 36125000 + TUNER_MUL / 2) / TUNER_MUL;

 buf[0] = (div >> 8) & 0x7f;
 buf[1] = div & 0xff;
 buf[2] = 0xCe;
 buf[3] = (params->frequency < 174500000 ? 0x01 :
    params->frequency < 454000000 ? 0x02 : 0x04);
 if (i2c_transfer(this_device->i2c_adapter, &msg, 1) != 1)
  return -EIO;
 return 0;
}


//static struct tda10021_config cu1216_config[MAX_SUPPORTED_TUNER];
static struct tda10021_config tda10021_conf[MAX_SUPPORTED_TUNER];

static u8 read_pwm_cu1216(struct dvb_phStb_card *card)
{
   u8 b = 0x34;
   u8 pwm;
   struct i2c_msg msg[] = { {.addr = 0x0c,.flags = 0,       .buf = &b,  .len = 1},
                            {.addr = 0x0c,.flags = I2C_M_RD,.buf = &pwm,.len = 1}
                          };

   if ((i2c_transfer(card->i2c_adapter, msg, 2) != 2) || (pwm == 0xff))
   {
  pwm = 0x48;
   }
   return pwm;
}

#endif /*CONFIG_10021*/


void frontend_init(struct dvb_phStb_card *card, int instance_count)
{
   enum td1316_type        td1316type;

   /* This function is always called twice, one time with instance_count = 0     */
   /* another time with instance_count = 1                                       */
   /* If i2c_probe detect only one channel decoder, we will have                 */
   /* dvb_phStb_i2c_client[0] != 0 and dvb_phStb_i2c_client[1] = 0               */
   /* and the following code will be executed once                               */
   /* If i2c_probe detect two channel decoders, we will have                     */
   /* dvb_phStb_i2c_client[0] != 0 and dvb_phStb_i2c_client[1] != 0              */
   /* and the following code will be executed twice                              */

   if ((card->i2c_adapter) && (dvb_phStb_i2c_client[instance_count]))
   {
      switch (gDemod_Model[instance_count])
      {
#if defined(CONFIG_10021)
      case Demod_Model_TDA10021:
          /* Set up the I2C address */
          tda10021_conf[instance_count].demod_address = dvb_phStb_i2c_client[instance_count]->addr;
          card->fe = dvb_attach(tda10021_attach, &tda10021_conf[instance_count], card->i2c_adapter, read_pwm_tdhe1(card));
          if (card->fe) {
              if (gTuner_Model[instance_count] == Tuner_Model_ALPS_TDHE1)
                  card->fe->ops.tuner_ops.set_params = alps_tdhe1_pll_set;
              if (gTuner_Model[instance_count] == Tuner_Model_CU1216)
                  card->fe->ops.tuner_ops.set_params = philips_cu1216_pll_set;
          }
          break;
#endif /*CONFIG_10021*/


        case Demod_Model_TDA10046:
            if (gTuner_Model[instance_count] == Tuner_Model_TU1216)
            {
                /* Set up the I2C address */
                tda10046_config_0[instance_count].demod_address = dvb_phStb_i2c_client[instance_count]->addr;
                card->fe = dvb_attach(tda10046_attach, &tda10046_config_0[instance_count], card->i2c_adapter);
                if (card->fe) {
                    card->fe->ops.tuner_ops.init = tu1216_pll_init;
                    card->fe->ops.tuner_ops.set_params = tu1216_pll_set;
                }
            }
            else /* silcon tuner TDA8275A */
            {
                /* Set up the I2C address */
                tda10046_config_1[instance_count].demod_address = dvb_phStb_i2c_client[instance_count]->addr;
                card->tuner_addr = tda8275aSlaveAddr[instance_count];
                card->fe = dvb_attach(tda10046_attach, &tda10046_config_1[instance_count], card->i2c_adapter);
                if (card->fe) {
                    card->fe->ops.tuner_ops.init       = tda8275a_pll_init;
                    card->fe->ops.tuner_ops.set_params = tda8275a_pll_set;
                }
            }
            break;


        case Demod_Model_TDA10048:
            tda10048_conf.demod_address = dvb_phStb_i2c_client[instance_count]->addr;
            switch (gTuner_Model[instance_count])
            {
                case Tuner_Model_TDA18211:
                  tda10048_conf.xtal_freq = TDA10048_XTAL_16M;
                  tda10048_conf.if_freq   = TDA10048_IF_4M;
                  tda10048_conf.PLL_M_Factor = 1;
                  tda10048_conf.PLL_N_Factor = 4;
                  tda10048_conf.PLL_P_Factor = 0;
                  card->tuner_addr = tda18211SlaveAddr[instance_count];
                  break;
                case Tuner_Model_TDA18218:
                  tda10048_conf.xtal_freq = TDA10048_XTAL_16M;
                  tda10048_conf.if_freq   = TDA10048_IF_4M;
                  tda10048_conf.PLL_M_Factor = 1;
                  tda10048_conf.PLL_N_Factor = 4;
                  tda10048_conf.PLL_P_Factor = 0;
                  card->tuner_addr = tda18218SlaveAddr[instance_count];
                  break;
                case Tuner_Model_TD1316:
                case Tuner_Model_DTT8D1T:
                case Tuner_Model_TDQG4:
                case Tuner_Model_DCT4C2:
                  tda10048_conf.xtal_freq = TDA10048_XTAL_4M;
                  tda10048_conf.if_freq   = TDA10048_IF_36M16;
                  tda10048_conf.PLL_M_Factor = 5;
                  tda10048_conf.PLL_N_Factor = 0;
                  tda10048_conf.PLL_P_Factor = 0;
                  card->tuner_addr = td1316SlaveAddr[instance_count];
                  break;
               case Tuner_Model_MAX3541:
                  tda10048_conf.xtal_freq = TDA10048_XTAL_4M;
                  tda10048_conf.if_freq   = TDA10048_IF_36M;
                  tda10048_conf.PLL_M_Factor = 5;
                  tda10048_conf.PLL_N_Factor = 0;
                  tda10048_conf.PLL_P_Factor = 0;
                  card->tuner_addr = max3541SlaveAddr[instance_count];
                  break;
#if defined(CONFIG_GYX1D)
              case Tuner_Model_GYX1D:
                  tda10048_conf.xtal_freq = TDA10048_XTAL_4M;
                  tda10048_conf.if_freq   = TDA10048_IF_36M16;
                  tda10048_conf.PLL_M_Factor = 5;
                  tda10048_conf.PLL_N_Factor = 0;
                  tda10048_conf.PLL_P_Factor = 0;
                  card->tuner_addr = gyx1dSlaveAddr[instance_count];
                  break;
#endif
            default:
                  /* should not happen */
                  break;
            } /* end switch (gTuner_Model[instance_count]) */
            /* attach tda10048 */
            card->fe = dvb_attach(tda10048_attach, &tda10048_conf, card->i2c_adapter);
            /* attach tuner */
            if (card->fe) {
                switch (gTuner_Model[instance_count])
                {
                    case Tuner_Model_TDA18211:
                      card->fe = dvb_attach(tda18211_attach, card->fe, card->tuner_addr, card->i2c_adapter, instance_count == 0); // set Master if instance_count zero
                    break;
                    case Tuner_Model_TDA18218:
                      card->fe = dvb_attach(tda18218_attach, card->fe, card->tuner_addr, card->i2c_adapter);
                    break;
                    case Tuner_Model_MAX3541:
                      card->fe = dvb_attach(max3541_attach, card->fe, card->tuner_addr, card->i2c_adapter);
                    break;
#if defined(CONFIG_GYX1D)
                    case Tuner_Model_GYX1D:
                      card->fe = dvb_attach(gyx1d_attach, card->fe, card->tuner_addr, card->i2c_adapter);
                    break;
#endif
                    case Tuner_Model_DTT8D1T:
                      td1316type = DTT8D1T;
                      /* no break! */
                    case Tuner_Model_TDQG4:
                      td1316type = TDQG4;
                      /* no break! */
                    case Tuner_Model_DCT4C2:
                      td1316type = DCT4C2;
                      /* no break! */
                    case Tuner_Model_TD1316:
                      td1316type = TD1316_mkx;
                      /* no break! */
                      card->fe = dvb_attach(td1316_attach, card->fe, card->tuner_addr, card->i2c_adapter, td1316type);
                    break;
                    default:
                      /* should not happen */
                    break;

                } /* end switch (gTuner_Model[instance_count]) */
            }
            break;


        case Demod_Model_TDA10086:
            tda10086_conf.demod_address = dvb_phStb_i2c_client[instance_count]->addr;
            /* no test on tuner - only tda826x is possible for the moment */
            card->tuner_addr = tda826xSlaveAddr[instance_count];
            card->fe = dvb_attach(tda10086_attach, &tda10086_conf, card->i2c_adapter);
            if (card->fe) {
                 card->fe = dvb_attach(tda826x_attach, card->fe, card->tuner_addr, card->i2c_adapter, 0 /* has_loopthrough - CHECK_THIS_PARAMETER */);
            }
            break;

#if defined(CONFIG_CX24120) || defined(CONFIG_CX24117)
        case Demod_Model_CX24120:
        case Demod_Model_CX24117:
            cx241xx_conf.demod_address = dvb_phStb_i2c_client[instance_count]->addr;
            card->fe = dvb_attach(cx24120b_attach, &cx241xx_conf, card->i2c_adapter);
            break;
#endif

        case Demod_Model_TC90507XBG:
            tc90507xbg_conf.demod_address = dvb_phStb_i2c_client[instance_count]->addr;
            card->tuner_addr = TUNER_I2C_ADDRESS_TDA665XTT_1;
            card->fe = dvb_attach(tc90507xbg_attach, &tc90507xbg_conf, card->i2c_adapter);
            if (card->fe) {
                 card->fe = dvb_attach(tda665xtt_attach, card->fe, card->tuner_addr, card->i2c_adapter);
            }
            break;

        case Demod_Model_MB86A20S:
            mb86a20s_conf.demod_address = dvb_phStb_i2c_client[instance_count]->addr;
            card->tuner_addr = TUNER_I2C_ADDRESS_TUA6039F_1;
            card->fe = dvb_attach(mb86a20s_attach, &mb86a20s_conf, card->i2c_adapter);
            if (card->fe) {
                 card->fe = dvb_attach(tua6039f_attach, card->fe, card->tuner_addr, card->i2c_adapter);
            }
            break;

#if defined(CONFIG_CX24116P)
        case Demod_Model_CX24116P:
            cx24116p_conf.demod_address = dvb_phStb_i2c_client[instance_count]->addr;
            dprintk ("FE_Model_CX24116_CX24128A, cx24116p_conf.demod_address=0x%x\n", cx24116p_conf.demod_address);
            card->fe = dvb_attach(cx24116p_attach, &cx24116p_conf, card->i2c_adapter);
            break;
#endif

        default:
            card->fe = NULL;
            break;
      }

      if (card->fe != NULL)
      {
#if !defined(CONFIG_CX24120) && !defined(CONFIG_CX24117) && !defined(CONFIG_CX24116P) && !defined(CONFIG_TDA10086)
         /* if not sat frontend */
         card->fe->ops.info.frequency_min = gMin_Freq;
         card->fe->ops.info.frequency_max = gMax_Freq;
#endif

         if (dvb_register_frontend(&card->dvb_adapter, card->fe))
         {
            dprintk("dvb-phStb: Frontend registration failed!\n");
            dvb_frontend_detach(card->fe);
            card->fe = NULL;
         }
      }
   }
}
#endif  // #if !(defined(CONFIG_IC_EMULATION) || defined(TMFL_IC_EMULATION))


/* -------------------------- I2C Detection Functions ------------------------- */
#if !(defined(CONFIG_IC_EMULATION) || defined(TMFL_IC_EMULATION))
static int dvb_phStb_detect_client (struct i2c_adapter *adapter, int address, int kind)
{
    int rv;
    int tuner_nr = dvb_phStb_frontend_count;

    dprintk("%s: detecting dvb phStb client on address 0x%x (adapter %d)\n",
            THIS_MODULE_DESCRIPTION, address << 1, adapter->nr);

    /* Check for the maximum number of frontends connected and that they */
    /* have been found on the currect buses */
    if (dvb_phStb_frontend_count == MAX_SUPPORTED_TUNER)
    {
        return 0;
    }

    /* Check if the adapter supports the needed features */
    if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
    {
        return 0;
    }

    /* Create the I2C client data structure */
    dvb_phStb_i2c_client[tuner_nr] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
    if (dvb_phStb_i2c_client[tuner_nr] == 0)
    {
        return -ENOMEM;
    }
    memset(dvb_phStb_i2c_client[tuner_nr], 0, sizeof(struct i2c_client));
    dvb_phStb_i2c_client[tuner_nr]->addr    = address; /* (address of the demod) */
    dvb_phStb_i2c_client[tuner_nr]->adapter = adapter;
    dvb_phStb_i2c_client[tuner_nr]->driver  = &dvb_phStb_i2c_driver;
    snprintf(I2C_NAME(dvb_phStb_i2c_client[tuner_nr]), sizeof(I2C_NAME(dvb_phStb_i2c_client[tuner_nr])) - 1, "dvb_phStb");

    /* Attach the Demux to the I2C adapter */
    rv = i2c_attach_client(dvb_phStb_i2c_client[tuner_nr]);
    if (rv) {
        dprintk("%s: Failed to attach to client on adapter %s (0x%x) - 0x%02X\n",
                THIS_MODULE_DESCRIPTION,
                I2C_NAME(adapter), adapter->id, rv);
        kfree(dvb_phStb_i2c_client[tuner_nr]);
        return rv;
    }
    dvb_phStb_frontend_count++;

    dprintk("%s: found device on adapter %s (0x%x) - 0x%02X\n",
            THIS_MODULE_DESCRIPTION,
            I2C_NAME(adapter), adapter->id, rv);

    return 0;
}

static int dvb_phStb_attach_adapter (struct i2c_adapter *adapter)
{
   return i2c_probe(adapter, &dvb_phStb_address_data, &dvb_phStb_detect_client);
}

static int dvb_phStb_detach_client (struct i2c_client *client)
{
   int err;

   err = i2c_detach_client(client);
   if (err)
   {
      return err;
   }

   kfree(client);

   return 0;
}
#endif

