Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
0004  * flexcop-fe-tuner.c - methods for frontend attachment and DiSEqC controlling
0005  * see flexcop.c for copyright information
0006  */
0007 #include <media/tuner.h>
0008 #include "flexcop.h"
0009 #include "mt312.h"
0010 #include "stv0299.h"
0011 #include "s5h1420.h"
0012 #include "itd1000.h"
0013 #include "cx24113.h"
0014 #include "cx24123.h"
0015 #include "isl6421.h"
0016 #include "cx24120.h"
0017 #include "mt352.h"
0018 #include "bcm3510.h"
0019 #include "nxt200x.h"
0020 #include "dvb-pll.h"
0021 #include "lgdt330x.h"
0022 #include "tuner-simple.h"
0023 #include "stv0297.h"
0024 
0025 
0026 /* Can we use the specified front-end?  Remember that if we are compiled
0027  * into the kernel we can't call code that's in modules.  */
0028 #define FE_SUPPORTED(fe) IS_REACHABLE(CONFIG_DVB_ ## fe)
0029 
0030 #if FE_SUPPORTED(BCM3510) || (FE_SUPPORTED(CX24120) && FE_SUPPORTED(ISL6421))
0031 static int flexcop_fe_request_firmware(struct dvb_frontend *fe,
0032     const struct firmware **fw, char *name)
0033 {
0034     struct flexcop_device *fc = fe->dvb->priv;
0035 
0036     return request_firmware(fw, name, fc->dev);
0037 }
0038 #endif
0039 
0040 /* lnb control */
0041 #if (FE_SUPPORTED(MT312) || FE_SUPPORTED(STV0299)) && FE_SUPPORTED(PLL)
0042 static int flexcop_set_voltage(struct dvb_frontend *fe,
0043                    enum fe_sec_voltage voltage)
0044 {
0045     struct flexcop_device *fc = fe->dvb->priv;
0046     flexcop_ibi_value v;
0047     deb_tuner("polarity/voltage = %u\n", voltage);
0048 
0049     v = fc->read_ibi_reg(fc, misc_204);
0050     switch (voltage) {
0051     case SEC_VOLTAGE_OFF:
0052         v.misc_204.ACPI1_sig = 1;
0053         break;
0054     case SEC_VOLTAGE_13:
0055         v.misc_204.ACPI1_sig = 0;
0056         v.misc_204.LNB_L_H_sig = 0;
0057         break;
0058     case SEC_VOLTAGE_18:
0059         v.misc_204.ACPI1_sig = 0;
0060         v.misc_204.LNB_L_H_sig = 1;
0061         break;
0062     default:
0063         err("unknown SEC_VOLTAGE value");
0064         return -EINVAL;
0065     }
0066     return fc->write_ibi_reg(fc, misc_204, v);
0067 }
0068 #endif
0069 
0070 #if FE_SUPPORTED(S5H1420) || FE_SUPPORTED(STV0299) || FE_SUPPORTED(MT312)
0071 static int __maybe_unused flexcop_sleep(struct dvb_frontend* fe)
0072 {
0073     struct flexcop_device *fc = fe->dvb->priv;
0074     if (fc->fe_sleep)
0075         return fc->fe_sleep(fe);
0076     return 0;
0077 }
0078 #endif
0079 
0080 /* SkyStar2 DVB-S rev 2.3 */
0081 #if FE_SUPPORTED(MT312) && FE_SUPPORTED(PLL)
0082 static int flexcop_set_tone(struct dvb_frontend *fe, enum fe_sec_tone_mode tone)
0083 {
0084 /* u16 wz_half_period_for_45_mhz[] = { 0x01ff, 0x0154, 0x00ff, 0x00cc }; */
0085     struct flexcop_device *fc = fe->dvb->priv;
0086     flexcop_ibi_value v;
0087     u16 ax;
0088     v.raw = 0;
0089     deb_tuner("tone = %u\n",tone);
0090 
0091     switch (tone) {
0092     case SEC_TONE_ON:
0093         ax = 0x01ff;
0094         break;
0095     case SEC_TONE_OFF:
0096         ax = 0;
0097         break;
0098     default:
0099         err("unknown SEC_TONE value");
0100         return -EINVAL;
0101     }
0102 
0103     v.lnb_switch_freq_200.LNB_CTLPrescaler_sig = 1; /* divide by 2 */
0104     v.lnb_switch_freq_200.LNB_CTLHighCount_sig = ax;
0105     v.lnb_switch_freq_200.LNB_CTLLowCount_sig  = ax == 0 ? 0x1ff : ax;
0106     return fc->write_ibi_reg(fc,lnb_switch_freq_200,v);
0107 }
0108 
0109 static void flexcop_diseqc_send_bit(struct dvb_frontend* fe, int data)
0110 {
0111     flexcop_set_tone(fe, SEC_TONE_ON);
0112     udelay(data ? 500 : 1000);
0113     flexcop_set_tone(fe, SEC_TONE_OFF);
0114     udelay(data ? 1000 : 500);
0115 }
0116 
0117 static void flexcop_diseqc_send_byte(struct dvb_frontend* fe, int data)
0118 {
0119     int i, par = 1, d;
0120     for (i = 7; i >= 0; i--) {
0121         d = (data >> i) & 1;
0122         par ^= d;
0123         flexcop_diseqc_send_bit(fe, d);
0124     }
0125     flexcop_diseqc_send_bit(fe, par);
0126 }
0127 
0128 static int flexcop_send_diseqc_msg(struct dvb_frontend *fe,
0129     int len, u8 *msg, unsigned long burst)
0130 {
0131     int i;
0132 
0133     flexcop_set_tone(fe, SEC_TONE_OFF);
0134     mdelay(16);
0135 
0136     for (i = 0; i < len; i++)
0137         flexcop_diseqc_send_byte(fe,msg[i]);
0138     mdelay(16);
0139 
0140     if (burst != -1) {
0141         if (burst)
0142             flexcop_diseqc_send_byte(fe, 0xff);
0143         else {
0144             flexcop_set_tone(fe, SEC_TONE_ON);
0145             mdelay(12);
0146             udelay(500);
0147             flexcop_set_tone(fe, SEC_TONE_OFF);
0148         }
0149         msleep(20);
0150     }
0151     return 0;
0152 }
0153 
0154 static int flexcop_diseqc_send_master_cmd(struct dvb_frontend *fe,
0155     struct dvb_diseqc_master_cmd *cmd)
0156 {
0157     return flexcop_send_diseqc_msg(fe, cmd->msg_len, cmd->msg, 0);
0158 }
0159 
0160 static int flexcop_diseqc_send_burst(struct dvb_frontend *fe,
0161                      enum fe_sec_mini_cmd minicmd)
0162 {
0163     return flexcop_send_diseqc_msg(fe, 0, NULL, minicmd);
0164 }
0165 
0166 static struct mt312_config skystar23_samsung_tbdu18132_config = {
0167     .demod_address = 0x0e,
0168 };
0169 
0170 static int skystar2_rev23_attach(struct flexcop_device *fc,
0171     struct i2c_adapter *i2c)
0172 {
0173     struct dvb_frontend_ops *ops;
0174 
0175     fc->fe = dvb_attach(mt312_attach, &skystar23_samsung_tbdu18132_config, i2c);
0176     if (!fc->fe)
0177         return 0;
0178 
0179     if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
0180             DVB_PLL_SAMSUNG_TBDU18132))
0181         return 0;
0182 
0183     ops = &fc->fe->ops;
0184     ops->diseqc_send_master_cmd = flexcop_diseqc_send_master_cmd;
0185     ops->diseqc_send_burst      = flexcop_diseqc_send_burst;
0186     ops->set_tone               = flexcop_set_tone;
0187     ops->set_voltage            = flexcop_set_voltage;
0188     fc->fe_sleep                = ops->sleep;
0189     ops->sleep                  = flexcop_sleep;
0190     return 1;
0191 }
0192 #else
0193 #define skystar2_rev23_attach NULL
0194 #endif
0195 
0196 /* SkyStar2 DVB-S rev 2.6 */
0197 #if FE_SUPPORTED(STV0299) && FE_SUPPORTED(PLL)
0198 static int samsung_tbmu24112_set_symbol_rate(struct dvb_frontend *fe,
0199     u32 srate, u32 ratio)
0200 {
0201     u8 aclk = 0;
0202     u8 bclk = 0;
0203 
0204     if (srate < 1500000) {
0205         aclk = 0xb7; bclk = 0x47;
0206     } else if (srate < 3000000) {
0207         aclk = 0xb7; bclk = 0x4b;
0208     } else if (srate < 7000000) {
0209         aclk = 0xb7; bclk = 0x4f;
0210     } else if (srate < 14000000) {
0211         aclk = 0xb7; bclk = 0x53;
0212     } else if (srate < 30000000) {
0213         aclk = 0xb6; bclk = 0x53;
0214     } else if (srate < 45000000) {
0215         aclk = 0xb4; bclk = 0x51;
0216     }
0217 
0218     stv0299_writereg(fe, 0x13, aclk);
0219     stv0299_writereg(fe, 0x14, bclk);
0220     stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
0221     stv0299_writereg(fe, 0x20, (ratio >>  8) & 0xff);
0222     stv0299_writereg(fe, 0x21,  ratio        & 0xf0);
0223     return 0;
0224 }
0225 
0226 static u8 samsung_tbmu24112_inittab[] = {
0227     0x01, 0x15,
0228     0x02, 0x30,
0229     0x03, 0x00,
0230     0x04, 0x7D,
0231     0x05, 0x35,
0232     0x06, 0x02,
0233     0x07, 0x00,
0234     0x08, 0xC3,
0235     0x0C, 0x00,
0236     0x0D, 0x81,
0237     0x0E, 0x23,
0238     0x0F, 0x12,
0239     0x10, 0x7E,
0240     0x11, 0x84,
0241     0x12, 0xB9,
0242     0x13, 0x88,
0243     0x14, 0x89,
0244     0x15, 0xC9,
0245     0x16, 0x00,
0246     0x17, 0x5C,
0247     0x18, 0x00,
0248     0x19, 0x00,
0249     0x1A, 0x00,
0250     0x1C, 0x00,
0251     0x1D, 0x00,
0252     0x1E, 0x00,
0253     0x1F, 0x3A,
0254     0x20, 0x2E,
0255     0x21, 0x80,
0256     0x22, 0xFF,
0257     0x23, 0xC1,
0258     0x28, 0x00,
0259     0x29, 0x1E,
0260     0x2A, 0x14,
0261     0x2B, 0x0F,
0262     0x2C, 0x09,
0263     0x2D, 0x05,
0264     0x31, 0x1F,
0265     0x32, 0x19,
0266     0x33, 0xFE,
0267     0x34, 0x93,
0268     0xff, 0xff,
0269 };
0270 
0271 static struct stv0299_config samsung_tbmu24112_config = {
0272     .demod_address = 0x68,
0273     .inittab = samsung_tbmu24112_inittab,
0274     .mclk = 88000000UL,
0275     .invert = 0,
0276     .skip_reinit = 0,
0277     .lock_output = STV0299_LOCKOUTPUT_LK,
0278     .volt13_op0_op1 = STV0299_VOLT13_OP1,
0279     .min_delay_ms = 100,
0280     .set_symbol_rate = samsung_tbmu24112_set_symbol_rate,
0281 };
0282 
0283 static int skystar2_rev26_attach(struct flexcop_device *fc,
0284     struct i2c_adapter *i2c)
0285 {
0286     fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, i2c);
0287     if (!fc->fe)
0288         return 0;
0289 
0290     if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61, i2c,
0291             DVB_PLL_SAMSUNG_TBMU24112))
0292         return 0;
0293 
0294     fc->fe->ops.set_voltage = flexcop_set_voltage;
0295     fc->fe_sleep = fc->fe->ops.sleep;
0296     fc->fe->ops.sleep = flexcop_sleep;
0297     return 1;
0298 
0299 }
0300 #else
0301 #define skystar2_rev26_attach NULL
0302 #endif
0303 
0304 /* SkyStar2 DVB-S rev 2.7 */
0305 #if FE_SUPPORTED(S5H1420) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_ITD1000)
0306 static struct s5h1420_config skystar2_rev2_7_s5h1420_config = {
0307     .demod_address = 0x53,
0308     .invert = 1,
0309     .repeated_start_workaround = 1,
0310     .serial_mpeg = 1,
0311 };
0312 
0313 static struct itd1000_config skystar2_rev2_7_itd1000_config = {
0314     .i2c_address = 0x61,
0315 };
0316 
0317 static int skystar2_rev27_attach(struct flexcop_device *fc,
0318     struct i2c_adapter *i2c)
0319 {
0320     flexcop_ibi_value r108;
0321     struct i2c_adapter *i2c_tuner;
0322 
0323     /* enable no_base_addr - no repeated start when reading */
0324     fc->fc_i2c_adap[0].no_base_addr = 1;
0325     fc->fe = dvb_attach(s5h1420_attach, &skystar2_rev2_7_s5h1420_config,
0326                 i2c);
0327     if (!fc->fe)
0328         goto fail;
0329 
0330     i2c_tuner = s5h1420_get_tuner_i2c_adapter(fc->fe);
0331     if (!i2c_tuner)
0332         goto fail;
0333 
0334     fc->fe_sleep = fc->fe->ops.sleep;
0335     fc->fe->ops.sleep = flexcop_sleep;
0336 
0337     /* enable no_base_addr - no repeated start when reading */
0338     fc->fc_i2c_adap[2].no_base_addr = 1;
0339     if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap,
0340             0x08, 1, 1, false)) {
0341         err("ISL6421 could NOT be attached");
0342         goto fail_isl;
0343     }
0344     info("ISL6421 successfully attached");
0345 
0346     /* the ITD1000 requires a lower i2c clock - is it a problem ? */
0347     r108.raw = 0x00000506;
0348     fc->write_ibi_reg(fc, tw_sm_c_108, r108);
0349     if (!dvb_attach(itd1000_attach, fc->fe, i2c_tuner,
0350             &skystar2_rev2_7_itd1000_config)) {
0351         err("ITD1000 could NOT be attached");
0352         /* Should i2c clock be restored? */
0353         goto fail_isl;
0354     }
0355     info("ITD1000 successfully attached");
0356 
0357     return 1;
0358 
0359 fail_isl:
0360     fc->fc_i2c_adap[2].no_base_addr = 0;
0361 fail:
0362     /* for the next devices we need it again */
0363     fc->fc_i2c_adap[0].no_base_addr = 0;
0364     return 0;
0365 }
0366 #else
0367 #define skystar2_rev27_attach NULL
0368 #endif
0369 
0370 /* SkyStar2 rev 2.8 */
0371 #if FE_SUPPORTED(CX24123) && FE_SUPPORTED(ISL6421) && FE_SUPPORTED(TUNER_CX24113)
0372 static struct cx24123_config skystar2_rev2_8_cx24123_config = {
0373     .demod_address = 0x55,
0374     .dont_use_pll = 1,
0375     .agc_callback = cx24113_agc_callback,
0376 };
0377 
0378 static const struct cx24113_config skystar2_rev2_8_cx24113_config = {
0379     .i2c_addr = 0x54,
0380     .xtal_khz = 10111,
0381 };
0382 
0383 static int skystar2_rev28_attach(struct flexcop_device *fc,
0384     struct i2c_adapter *i2c)
0385 {
0386     struct i2c_adapter *i2c_tuner;
0387 
0388     fc->fe = dvb_attach(cx24123_attach, &skystar2_rev2_8_cx24123_config,
0389                 i2c);
0390     if (!fc->fe)
0391         return 0;
0392 
0393     i2c_tuner = cx24123_get_tuner_i2c_adapter(fc->fe);
0394     if (!i2c_tuner)
0395         return 0;
0396 
0397     if (!dvb_attach(cx24113_attach, fc->fe, &skystar2_rev2_8_cx24113_config,
0398             i2c_tuner)) {
0399         err("CX24113 could NOT be attached");
0400         return 0;
0401     }
0402     info("CX24113 successfully attached");
0403 
0404     fc->fc_i2c_adap[2].no_base_addr = 1;
0405     if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap,
0406             0x08, 0, 0, false)) {
0407         err("ISL6421 could NOT be attached");
0408         fc->fc_i2c_adap[2].no_base_addr = 0;
0409         return 0;
0410     }
0411     info("ISL6421 successfully attached");
0412     /* TODO on i2c_adap[1] addr 0x11 (EEPROM) there seems to be an
0413      * IR-receiver (PIC16F818) - but the card has no input for that ??? */
0414     return 1;
0415 }
0416 #else
0417 #define skystar2_rev28_attach NULL
0418 #endif
0419 
0420 /* AirStar DVB-T */
0421 #if FE_SUPPORTED(MT352) && FE_SUPPORTED(PLL)
0422 static int samsung_tdtc9251dh0_demod_init(struct dvb_frontend *fe)
0423 {
0424     static u8 mt352_clock_config[] = { 0x89, 0x18, 0x2d };
0425     static u8 mt352_reset[] = { 0x50, 0x80 };
0426     static u8 mt352_adc_ctl_1_cfg[] = { 0x8E, 0x40 };
0427     static u8 mt352_agc_cfg[] = { 0x67, 0x28, 0xa1 };
0428     static u8 mt352_capt_range_cfg[] = { 0x75, 0x32 };
0429 
0430     mt352_write(fe, mt352_clock_config, sizeof(mt352_clock_config));
0431     udelay(2000);
0432     mt352_write(fe, mt352_reset, sizeof(mt352_reset));
0433     mt352_write(fe, mt352_adc_ctl_1_cfg, sizeof(mt352_adc_ctl_1_cfg));
0434     mt352_write(fe, mt352_agc_cfg, sizeof(mt352_agc_cfg));
0435     mt352_write(fe, mt352_capt_range_cfg, sizeof(mt352_capt_range_cfg));
0436     return 0;
0437 }
0438 
0439 static struct mt352_config samsung_tdtc9251dh0_config = {
0440     .demod_address = 0x0f,
0441     .demod_init    = samsung_tdtc9251dh0_demod_init,
0442 };
0443 
0444 static int airstar_dvbt_attach(struct flexcop_device *fc,
0445     struct i2c_adapter *i2c)
0446 {
0447     fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c);
0448     if (!fc->fe)
0449         return 0;
0450 
0451     return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
0452                 DVB_PLL_SAMSUNG_TDTC9251DH0);
0453 }
0454 #else
0455 #define airstar_dvbt_attach NULL
0456 #endif
0457 
0458 /* AirStar ATSC 1st generation */
0459 #if FE_SUPPORTED(BCM3510)
0460 static struct bcm3510_config air2pc_atsc_first_gen_config = {
0461     .demod_address    = 0x0f,
0462     .request_firmware = flexcop_fe_request_firmware,
0463 };
0464 
0465 static int airstar_atsc1_attach(struct flexcop_device *fc,
0466     struct i2c_adapter *i2c)
0467 {
0468     fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, i2c);
0469     return fc->fe != NULL;
0470 }
0471 #else
0472 #define airstar_atsc1_attach NULL
0473 #endif
0474 
0475 /* AirStar ATSC 2nd generation */
0476 #if FE_SUPPORTED(NXT200X) && FE_SUPPORTED(PLL)
0477 static const struct nxt200x_config samsung_tbmv_config = {
0478     .demod_address = 0x0a,
0479 };
0480 
0481 static int airstar_atsc2_attach(struct flexcop_device *fc,
0482     struct i2c_adapter *i2c)
0483 {
0484     fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, i2c);
0485     if (!fc->fe)
0486         return 0;
0487 
0488     return !!dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL,
0489                 DVB_PLL_SAMSUNG_TBMV);
0490 }
0491 #else
0492 #define airstar_atsc2_attach NULL
0493 #endif
0494 
0495 /* AirStar ATSC 3rd generation */
0496 #if FE_SUPPORTED(LGDT330X)
0497 static struct lgdt330x_config air2pc_atsc_hd5000_config = {
0498     .demod_chip          = LGDT3303,
0499     .serial_mpeg         = 0x04,
0500     .clock_polarity_flip = 1,
0501 };
0502 
0503 static int airstar_atsc3_attach(struct flexcop_device *fc,
0504     struct i2c_adapter *i2c)
0505 {
0506     fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config,
0507                 0x59, i2c);
0508     if (!fc->fe)
0509         return 0;
0510 
0511     return !!dvb_attach(simple_tuner_attach, fc->fe, i2c, 0x61,
0512                 TUNER_LG_TDVS_H06XF);
0513 }
0514 #else
0515 #define airstar_atsc3_attach NULL
0516 #endif
0517 
0518 /* CableStar2 DVB-C */
0519 #if FE_SUPPORTED(STV0297) && FE_SUPPORTED(PLL)
0520 static u8 alps_tdee4_stv0297_inittab[] = {
0521     0x80, 0x01,
0522     0x80, 0x00,
0523     0x81, 0x01,
0524     0x81, 0x00,
0525     0x00, 0x48,
0526     0x01, 0x58,
0527     0x03, 0x00,
0528     0x04, 0x00,
0529     0x07, 0x00,
0530     0x08, 0x00,
0531     0x30, 0xff,
0532     0x31, 0x9d,
0533     0x32, 0xff,
0534     0x33, 0x00,
0535     0x34, 0x29,
0536     0x35, 0x55,
0537     0x36, 0x80,
0538     0x37, 0x6e,
0539     0x38, 0x9c,
0540     0x40, 0x1a,
0541     0x41, 0xfe,
0542     0x42, 0x33,
0543     0x43, 0x00,
0544     0x44, 0xff,
0545     0x45, 0x00,
0546     0x46, 0x00,
0547     0x49, 0x04,
0548     0x4a, 0x51,
0549     0x4b, 0xf8,
0550     0x52, 0x30,
0551     0x53, 0x06,
0552     0x59, 0x06,
0553     0x5a, 0x5e,
0554     0x5b, 0x04,
0555     0x61, 0x49,
0556     0x62, 0x0a,
0557     0x70, 0xff,
0558     0x71, 0x04,
0559     0x72, 0x00,
0560     0x73, 0x00,
0561     0x74, 0x0c,
0562     0x80, 0x20,
0563     0x81, 0x00,
0564     0x82, 0x30,
0565     0x83, 0x00,
0566     0x84, 0x04,
0567     0x85, 0x22,
0568     0x86, 0x08,
0569     0x87, 0x1b,
0570     0x88, 0x00,
0571     0x89, 0x00,
0572     0x90, 0x00,
0573     0x91, 0x04,
0574     0xa0, 0x86,
0575     0xa1, 0x00,
0576     0xa2, 0x00,
0577     0xb0, 0x91,
0578     0xb1, 0x0b,
0579     0xc0, 0x5b,
0580     0xc1, 0x10,
0581     0xc2, 0x12,
0582     0xd0, 0x02,
0583     0xd1, 0x00,
0584     0xd2, 0x00,
0585     0xd3, 0x00,
0586     0xd4, 0x02,
0587     0xd5, 0x00,
0588     0xde, 0x00,
0589     0xdf, 0x01,
0590     0xff, 0xff,
0591 };
0592 
0593 static struct stv0297_config alps_tdee4_stv0297_config = {
0594     .demod_address = 0x1c,
0595     .inittab = alps_tdee4_stv0297_inittab,
0596 };
0597 
0598 static int cablestar2_attach(struct flexcop_device *fc,
0599     struct i2c_adapter *i2c)
0600 {
0601     fc->fc_i2c_adap[0].no_base_addr = 1;
0602     fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, i2c);
0603     if (!fc->fe)
0604         goto fail;
0605 
0606     /* This tuner doesn't use the stv0297's I2C gate, but instead the
0607      * tuner is connected to a different flexcop I2C adapter.  */
0608     if (fc->fe->ops.i2c_gate_ctrl)
0609         fc->fe->ops.i2c_gate_ctrl(fc->fe, 0);
0610     fc->fe->ops.i2c_gate_ctrl = NULL;
0611 
0612     if (!dvb_attach(dvb_pll_attach, fc->fe, 0x61,
0613             &fc->fc_i2c_adap[2].i2c_adap, DVB_PLL_TDEE4))
0614         goto fail;
0615 
0616     return 1;
0617 
0618 fail:
0619     /* Reset for next frontend to try */
0620     fc->fc_i2c_adap[0].no_base_addr = 0;
0621     return 0;
0622 }
0623 #else
0624 #define cablestar2_attach NULL
0625 #endif
0626 
0627 /* SkyStar S2 PCI DVB-S/S2 card based on Conexant cx24120/cx24118 */
0628 #if FE_SUPPORTED(CX24120) && FE_SUPPORTED(ISL6421)
0629 static const struct cx24120_config skystar2_rev3_3_cx24120_config = {
0630     .i2c_addr = 0x55,
0631     .xtal_khz = 10111,
0632     .initial_mpeg_config = { 0xa1, 0x76, 0x07 },
0633     .request_firmware = flexcop_fe_request_firmware,
0634     .i2c_wr_max = 4,
0635 };
0636 
0637 static int skystarS2_rev33_attach(struct flexcop_device *fc,
0638     struct i2c_adapter *i2c)
0639 {
0640     fc->fe = dvb_attach(cx24120_attach,
0641                 &skystar2_rev3_3_cx24120_config, i2c);
0642     if (!fc->fe)
0643         return 0;
0644 
0645     fc->dev_type = FC_SKYS2_REV33;
0646     fc->fc_i2c_adap[2].no_base_addr = 1;
0647     if (!dvb_attach(isl6421_attach, fc->fe, &fc->fc_i2c_adap[2].i2c_adap,
0648             0x08, 0, 0, false)) {
0649         err("ISL6421 could NOT be attached!");
0650         fc->fc_i2c_adap[2].no_base_addr = 0;
0651         return 0;
0652     }
0653     info("ISL6421 successfully attached.");
0654 
0655     if (fc->has_32_hw_pid_filter)
0656         fc->skip_6_hw_pid_filter = 1;
0657 
0658     return 1;
0659 }
0660 #else
0661 #define skystarS2_rev33_attach NULL
0662 #endif
0663 
0664 static struct {
0665     flexcop_device_type_t type;
0666     int (*attach)(struct flexcop_device *, struct i2c_adapter *);
0667 } flexcop_frontends[] = {
0668     { FC_SKY_REV27, skystar2_rev27_attach },
0669     { FC_SKY_REV28, skystar2_rev28_attach },
0670     { FC_SKY_REV26, skystar2_rev26_attach },
0671     { FC_AIR_DVBT, airstar_dvbt_attach },
0672     { FC_AIR_ATSC2, airstar_atsc2_attach },
0673     { FC_AIR_ATSC3, airstar_atsc3_attach },
0674     { FC_AIR_ATSC1, airstar_atsc1_attach },
0675     { FC_CABLE, cablestar2_attach },
0676     { FC_SKY_REV23, skystar2_rev23_attach },
0677     { FC_SKYS2_REV33, skystarS2_rev33_attach },
0678 };
0679 
0680 /* try to figure out the frontend */
0681 int flexcop_frontend_init(struct flexcop_device *fc)
0682 {
0683     int i;
0684     for (i = 0; i < ARRAY_SIZE(flexcop_frontends); i++) {
0685         if (!flexcop_frontends[i].attach)
0686             continue;
0687         /* type needs to be set before, because of some workarounds
0688          * done based on the probed card type */
0689         fc->dev_type = flexcop_frontends[i].type;
0690         if (flexcop_frontends[i].attach(fc, &fc->fc_i2c_adap[0].i2c_adap))
0691             goto fe_found;
0692         /* Clean up partially attached frontend */
0693         if (fc->fe) {
0694             dvb_frontend_detach(fc->fe);
0695             fc->fe = NULL;
0696         }
0697     }
0698     fc->dev_type = FC_UNK;
0699     err("no frontend driver found for this B2C2/FlexCop adapter");
0700     return -ENODEV;
0701 
0702 fe_found:
0703     info("found '%s' .", fc->fe->ops.info.name);
0704     if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) {
0705         err("frontend registration failed!");
0706         dvb_frontend_detach(fc->fe);
0707         fc->fe = NULL;
0708         return -EINVAL;
0709     }
0710     fc->init_state |= FC_STATE_FE_INIT;
0711     return 0;
0712 }
0713 
0714 void flexcop_frontend_exit(struct flexcop_device *fc)
0715 {
0716     if (fc->init_state & FC_STATE_FE_INIT) {
0717         dvb_unregister_frontend(fc->fe);
0718         dvb_frontend_detach(fc->fe);
0719     }
0720     fc->init_state &= ~FC_STATE_FE_INIT;
0721 }