tuner-xc2028.c
来自「trident tm5600的linux驱动」· C语言 代码 · 共 1,287 行 · 第 1/2 页
C
1,287 行
} else { /* 16 SCODE entries per file; each SCODE entry is 12 bytes and * has a 2-byte size header in the firmware format. */ if (priv->firm[pos].size != 14 * 16 || scode >= 16 || le16_to_cpu(*(__u16 *)(p + 14 * scode)) != 12) return -EINVAL; p += 14 * scode + 2; } tuner_info("Loading SCODE for type="); dump_firm_type_and_int_freq(priv->firm[pos].type, priv->firm[pos].int_freq); printk("(%x), id %016llx.\n", priv->firm[pos].type, (unsigned long long)*id); if (priv->firm_version < 0x0202) rc = send_seq(priv, {0x20, 0x00, 0x00, 0x00}); else rc = send_seq(priv, {0xa0, 0x00, 0x00, 0x00}); if (rc < 0) return -EIO; rc = i2c_send(priv, p, 12); if (rc < 0) return -EIO; rc = send_seq(priv, {0x00, 0x8c}); if (rc < 0) return -EIO; return 0;}static int check_firmware(struct dvb_frontend *fe, unsigned int type, v4l2_std_id std, __u16 int_freq){ struct xc2028_data *priv = fe->tuner_priv; struct firmware_properties new_fw; int rc = 0, is_retry = 0; u16 version, hwmodel; v4l2_std_id std0; tuner_dbg("%s called\n", __func__); if (!priv->firm) { if (!priv->ctrl.fname) { tuner_info("xc2028/3028 firmware name not set!\n"); return -EINVAL; } rc = load_all_firmwares(fe); if (rc < 0) return rc; } if (priv->ctrl.mts && !(type & FM)) type |= MTS;retry: new_fw.type = type; new_fw.id = std; new_fw.std_req = std; new_fw.scode_table = SCODE | priv->ctrl.scode_table; new_fw.scode_nr = 0; new_fw.int_freq = int_freq; tuner_dbg("checking firmware, user requested type="); if (debug) { dump_firm_type(new_fw.type); printk("(%x), id %016llx, ", new_fw.type, (unsigned long long)new_fw.std_req); if (!int_freq) { printk("scode_tbl "); dump_firm_type(priv->ctrl.scode_table); printk("(%x), ", priv->ctrl.scode_table); } else printk("int_freq %d, ", new_fw.int_freq); printk("scode_nr %d\n", new_fw.scode_nr); } /* No need to reload base firmware if it matches */ if (((BASE | new_fw.type) & BASE_TYPES) == (priv->cur_fw.type & BASE_TYPES)) { tuner_dbg("BASE firmware not changed.\n"); goto skip_base; } /* Updating BASE - forget about all currently loaded firmware */ memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); /* Reset is needed before loading firmware */ rc = do_tuner_callback(fe, XC2028_TUNER_RESET, 0); if (rc < 0) goto fail; /* BASE firmwares are all std0 */ std0 = 0; rc = load_firmware(fe, BASE | new_fw.type, &std0); if (rc < 0) { tuner_err("Error %d while loading base firmware\n", rc); goto fail; } /* Load INIT1, if needed */ tuner_dbg("Load init1 firmware, if exists\n"); rc = load_firmware(fe, BASE | INIT1 | new_fw.type, &std0); if (rc == -ENOENT) rc = load_firmware(fe, (BASE | INIT1 | new_fw.type) & ~F8MHZ, &std0); if (rc < 0 && rc != -ENOENT) { tuner_err("Error %d while loading init1 firmware\n", rc); goto fail; }skip_base: /* * No need to reload standard specific firmware if base firmware * was not reloaded and requested video standards have not changed. */ if (priv->cur_fw.type == (BASE | new_fw.type) && priv->cur_fw.std_req == std) { tuner_dbg("Std-specific firmware already loaded.\n"); goto skip_std_specific; } /* Reloading std-specific firmware forces a SCODE update */ priv->cur_fw.scode_table = 0; rc = load_firmware(fe, new_fw.type, &new_fw.id); if (rc == -ENOENT) rc = load_firmware(fe, new_fw.type & ~F8MHZ, &new_fw.id); if (rc < 0) goto fail;skip_std_specific: if (priv->cur_fw.scode_table == new_fw.scode_table && priv->cur_fw.scode_nr == new_fw.scode_nr) { tuner_dbg("SCODE firmware already loaded.\n"); goto check_device; } if (new_fw.type & FM) goto check_device; /* Load SCODE firmware, if exists */ tuner_dbg("Trying to load scode %d\n", new_fw.scode_nr); rc = load_scode(fe, new_fw.type | new_fw.scode_table, &new_fw.id, new_fw.int_freq, new_fw.scode_nr);check_device: if (xc2028_get_reg(priv, 0x0004, &version) < 0 || xc2028_get_reg(priv, 0x0008, &hwmodel) < 0) { tuner_err("Unable to read tuner registers.\n"); goto fail; } tuner_dbg("Device is Xceive %d version %d.%d, " "firmware version %d.%d\n", hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, (version & 0xf0) >> 4, version & 0xf); /* Check firmware version against what we downloaded. */ if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) { if (!priv->ctrl.read_not_reliable) { tuner_err("Incorrect readback of firmware version.\n"); goto fail; } else { tuner_err("Returned an incorrect version. However, " "read is not reliable enough. Ignoring it.\n"); hwmodel = 3028; } } /* Check that the tuner hardware model remains consistent over time. */ if (priv->hwmodel == 0 && (hwmodel == 2028 || hwmodel == 3028)) { priv->hwmodel = hwmodel; priv->hwvers = version & 0xff00; } else if (priv->hwmodel == 0 || priv->hwmodel != hwmodel || priv->hwvers != (version & 0xff00)) { tuner_err("Read invalid device hardware information - tuner " "hung?\n"); goto fail; } memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); /* * By setting BASE in cur_fw.type only after successfully loading all * firmwares, we can: * 1. Identify that BASE firmware with type=0 has been loaded; * 2. Tell whether BASE firmware was just changed the next time through. */ priv->cur_fw.type |= BASE; return 0;fail: memset(&priv->cur_fw, 0, sizeof(priv->cur_fw)); if (!is_retry) { msleep(50); is_retry = 1; tuner_dbg("Retrying firmware load\n"); goto retry; } if (rc == -ENOENT) rc = -EINVAL; return rc;}static int xc2028_signal(struct dvb_frontend *fe, u16 *strength){ struct xc2028_data *priv = fe->tuner_priv; u16 frq_lock, signal = 0; int rc; tuner_dbg("%s called\n", __func__); mutex_lock(&priv->lock); /* Sync Lock Indicator */ rc = xc2028_get_reg(priv, 0x0002, &frq_lock); if (rc < 0) goto ret; /* Frequency is locked */ if (frq_lock == 1) signal = 32768; /* Get SNR of the video signal */ rc = xc2028_get_reg(priv, 0x0040, &signal); if (rc < 0) goto ret; /* Use both frq_lock and signal to generate the result */ signal = signal || ((signal & 0x07) << 12);ret: mutex_unlock(&priv->lock); *strength = signal; tuner_dbg("signal strength is %d\n", signal); return rc;}#define DIV 15625static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, enum tuner_mode new_mode, unsigned int type, v4l2_std_id std, u16 int_freq){ struct xc2028_data *priv = fe->tuner_priv; int rc = -EINVAL; unsigned char buf[4]; u32 div, offset = 0; tuner_dbg("%s called\n", __func__); mutex_lock(&priv->lock); tuner_dbg("should set frequency %d kHz\n", freq / 1000); if (check_firmware(fe, type, std, int_freq) < 0) goto ret; /* On some cases xc2028 can disable video output, if * very weak signals are received. By sending a soft * reset, this is re-enabled. So, it is better to always * send a soft reset before changing channels, to be sure * that xc2028 will be in a safe state. * Maybe this might also be needed for DTV. */ if (new_mode == T_ANALOG_TV) { rc = send_seq(priv, {0x00, 0x00}); } else if (priv->cur_fw.type & ATSC) { offset = 1750000; } else { offset = 2750000; /* * We must adjust the offset by 500kHz in two cases in order * to correctly center the IF output: * 1) When the ZARLINK456 or DIBCOM52 tables were explicitly * selected and a 7MHz channel is tuned; * 2) When tuning a VHF channel with DTV78 firmware. */ if (((priv->cur_fw.type & DTV7) && (priv->cur_fw.scode_table & (ZARLINK456 | DIBCOM52))) || ((priv->cur_fw.type & DTV78) && freq < 470000000)) offset -= 500000; } div = (freq - offset + DIV / 2) / DIV; /* CMD= Set frequency */ if (priv->firm_version < 0x0202) rc = send_seq(priv, {0x00, 0x02, 0x00, 0x00}); else rc = send_seq(priv, {0x80, 0x02, 0x00, 0x00}); if (rc < 0) goto ret; /* Return code shouldn't be checked. The reset CLK is needed only with tm6000. Driver should work fine even if this fails. */ msleep(priv->ctrl.msleep); do_tuner_callback(fe, XC2028_RESET_CLK, 1); msleep(10); buf[0] = 0xff & (div >> 24); buf[1] = 0xff & (div >> 16); buf[2] = 0xff & (div >> 8); buf[3] = 0xff & (div); rc = i2c_send(priv, buf, sizeof(buf)); if (rc < 0) goto ret; msleep(100); priv->frequency = freq; tuner_dbg("divisor= %02x %02x %02x %02x (freq=%d.%03d)\n", buf[0], buf[1], buf[2], buf[3], freq / 1000000, (freq % 1000000) / 1000); rc = 0;ret: mutex_unlock(&priv->lock); return rc;}static int xc2028_set_analog_freq(struct dvb_frontend *fe, struct analog_parameters *p){ struct xc2028_data *priv = fe->tuner_priv; unsigned int type=0; tuner_dbg("%s called\n", __func__); if (p->mode == V4L2_TUNER_RADIO) { type |= FM; if (priv->ctrl.input1) type |= INPUT1; return generic_set_freq(fe, (625l * p->frequency) / 10, T_ANALOG_TV, type, 0, 0); } /* if std is not defined, choose one */ if (!p->std) p->std = V4L2_STD_MN; /* PAL/M, PAL/N, PAL/Nc and NTSC variants should use 6MHz firmware */ if (!(p->std & V4L2_STD_MN)) type |= F8MHZ; /* Add audio hack to std mask */ p->std |= parse_audio_std_option(); return generic_set_freq(fe, 62500l * p->frequency, T_ANALOG_TV, type, p->std, 0);}static int xc2028_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *p){ struct xc2028_data *priv = fe->tuner_priv; unsigned int type=0; fe_bandwidth_t bw = BANDWIDTH_8_MHZ; u16 demod = 0; tuner_dbg("%s called\n", __func__); switch(fe->ops.info.type) { case FE_OFDM: bw = p->u.ofdm.bandwidth; break; case FE_QAM: tuner_info("WARN: There are some reports that " "QAM 6 MHz doesn't work.\n" "If this works for you, please report by " "e-mail to: v4l-dvb-maintainer@linuxtv.org\n"); bw = BANDWIDTH_6_MHZ; type |= QAM; break; case FE_ATSC: bw = BANDWIDTH_6_MHZ; /* The only ATSC firmware (at least on v2.7) is D2633 */ type |= ATSC | D2633; break; /* DVB-S is not supported */ default: return -EINVAL; } switch (bw) { case BANDWIDTH_8_MHZ: if (p->frequency < 470000000) priv->ctrl.vhfbw7 = 0; else priv->ctrl.uhfbw8 = 1; type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV8; type |= F8MHZ; break; case BANDWIDTH_7_MHZ: if (p->frequency < 470000000) priv->ctrl.vhfbw7 = 1; else priv->ctrl.uhfbw8 = 0; type |= (priv->ctrl.vhfbw7 && priv->ctrl.uhfbw8) ? DTV78 : DTV7; type |= F8MHZ; break; case BANDWIDTH_6_MHZ: type |= DTV6; priv->ctrl.vhfbw7 = 0; priv->ctrl.uhfbw8 = 0; break; default: tuner_err("error: bandwidth not supported.\n"); }; /* Selects between D2633 or D2620 firmware. It doesn't make sense for ATSC, since it should be D2633 on all cases */ if (fe->ops.info.type != FE_ATSC) { switch (priv->ctrl.type) { case XC2028_D2633: type |= D2633; break; case XC2028_D2620: type |= D2620; break; case XC2028_AUTO: default: /* Zarlink seems to need D2633 */ if (priv->ctrl.demod == XC3028_FE_ZARLINK456) type |= D2633; else type |= D2620; } } /* All S-code tables need a 200kHz shift */ if (priv->ctrl.demod) demod = priv->ctrl.demod + 200; return generic_set_freq(fe, p->frequency, T_DIGITAL_TV, type, 0, demod);}#if 0/* This is needed at sleep (S1/S3), but not at fe_standby. Otherwise, firmware will be loaded on every open() */static int xc2028_sleep(struct dvb_frontend *fe){ struct xc2028_data *priv = fe->tuner_priv; int rc = 0; tuner_dbg("%s called\n", __func__); mutex_lock(&priv->lock); if (priv->firm_version < 0x0202) rc = send_seq(priv, {0x00, 0x08, 0x00, 0x00}); else rc = send_seq(priv, {0x80, 0x08, 0x00, 0x00}); priv->cur_fw.type = 0; /* need firmware reload */ mutex_unlock(&priv->lock); return rc;}#endifstatic int xc2028_dvb_release(struct dvb_frontend *fe){ struct xc2028_data *priv = fe->tuner_priv; tuner_dbg("%s called\n", __func__); mutex_lock(&xc2028_list_mutex); /* only perform final cleanup if this is the last instance */ if (hybrid_tuner_report_instance_count(priv) == 1) { kfree(priv->ctrl.fname); free_firmware(priv); } if (priv) hybrid_tuner_release_state(priv); mutex_unlock(&xc2028_list_mutex); fe->tuner_priv = NULL; return 0;}static int xc2028_get_frequency(struct dvb_frontend *fe, u32 *frequency){ struct xc2028_data *priv = fe->tuner_priv; tuner_dbg("%s called\n", __func__); *frequency = priv->frequency; return 0;}static int xc2028_set_config(struct dvb_frontend *fe, void *priv_cfg){ struct xc2028_data *priv = fe->tuner_priv; struct xc2028_ctrl *p = priv_cfg; int rc = 0; tuner_dbg("%s called\n", __func__); mutex_lock(&priv->lock); memcpy(&priv->ctrl, p, sizeof(priv->ctrl)); if (priv->ctrl.max_len < 9) priv->ctrl.max_len = 13; if (p->fname) { if (priv->ctrl.fname && strcmp(p->fname, priv->ctrl.fname)) { kfree(priv->ctrl.fname); free_firmware(priv); } priv->ctrl.fname = kstrdup(p->fname, GFP_KERNEL); if (priv->ctrl.fname == NULL) rc = -ENOMEM; } mutex_unlock(&priv->lock); return rc;}static const struct dvb_tuner_ops xc2028_dvb_tuner_ops = { .info = { .name = "Xceive XC3028", .frequency_min = 42000000, .frequency_max = 864000000, .frequency_step = 50000, }, .set_config = xc2028_set_config, .set_analog_params = xc2028_set_analog_freq, .release = xc2028_dvb_release, .get_frequency = xc2028_get_frequency, .get_rf_strength = xc2028_signal, .set_params = xc2028_set_params,#if 0 .sleep = xc2028_sleep,#endif#if 0 int (*get_bandwidth)(struct dvb_frontend *fe, u32 *bandwidth); int (*get_status)(struct dvb_frontend *fe, u32 *status);#endif};struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe, struct xc2028_config *cfg){ struct xc2028_data *priv; int instance; if (debug) printk(KERN_DEBUG "xc2028: Xcv2028/3028 init called!\n"); if (NULL == cfg) return NULL; if (!fe) { printk(KERN_ERR "xc2028: No frontend!\n"); return NULL; } mutex_lock(&xc2028_list_mutex); instance = hybrid_tuner_request_state(struct xc2028_data, priv, hybrid_tuner_instance_list, cfg->i2c_adap, cfg->i2c_addr, "xc2028"); switch (instance) { case 0: /* memory allocation failure */ goto fail; break; case 1: /* new tuner instance */ priv->ctrl.max_len = 13; mutex_init(&priv->lock); fe->tuner_priv = priv; break; case 2: /* existing tuner instance */ fe->tuner_priv = priv; break; } memcpy(&fe->ops.tuner_ops, &xc2028_dvb_tuner_ops, sizeof(xc2028_dvb_tuner_ops)); tuner_info("type set to %s\n", "XCeive xc2028/xc3028 tuner"); if (cfg->ctrl) xc2028_set_config(fe, cfg->ctrl); mutex_unlock(&xc2028_list_mutex); return fe;fail: mutex_unlock(&xc2028_list_mutex); xc2028_dvb_release(fe); return NULL;}EXPORT_SYMBOL(xc2028_attach);MODULE_DESCRIPTION("Xceive xc2028/xc3028 tuner driver");MODULE_AUTHOR("Michel Ludwig <michel.ludwig@gmail.com>");MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?