📄 budget-av.c
字号:
};static struct stv0299_config typhoon_config = { .demod_address = 0x68, .inittab = typhoon_cinergy1200s_inittab, .mclk = 88000000UL, .invert = 0, .enhanced_tuning = 0, .skip_reinit = 0, .lock_output = STV0229_LOCKOUTPUT_1, .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, .pll_set = philips_su1278_ty_ci_pll_set,};static struct stv0299_config cinergy_1200s_config = { .demod_address = 0x68, .inittab = typhoon_cinergy1200s_inittab, .mclk = 88000000UL, .invert = 0, .enhanced_tuning = 0, .skip_reinit = 0, .lock_output = STV0229_LOCKOUTPUT_0, .volt13_op0_op1 = STV0299_VOLT13_OP0, .min_delay_ms = 100, .set_symbol_rate = philips_su1278_ty_ci_set_symbol_rate, .pll_set = philips_su1278_ty_ci_pll_set,};static int philips_cu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params){ struct budget *budget = (struct budget *) 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] = 0x86; buf[3] = (params->frequency < 150000000 ? 0x01 : params->frequency < 445000000 ? 0x02 : 0x04); if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1) return -EIO; return 0;}static struct tda10021_config philips_cu1216_config = { .demod_address = 0x0c, .pll_set = philips_cu1216_pll_set,};static int philips_tu1216_pll_init(struct dvb_frontend *fe){ struct budget *budget = (struct budget *) fe->dvb->priv; static u8 tu1216_init[] = { 0x0b, 0xf5, 0x85, 0xab }; struct i2c_msg tuner_msg = {.addr = 0x60,.flags = 0,.buf = tu1216_init,.len = sizeof(tu1216_init) }; // setup PLL configuration if (i2c_transfer(&budget->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); return 0;}static int philips_tu1216_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params){ struct budget *budget = (struct budget *) 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; // determine charge pump tuner_frequency = params->frequency + 36166000; if (tuner_frequency < 87000000) 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 return -EINVAL; // determine band if (params->frequency < 49000000) return -EINVAL; else if (params->frequency < 161000000) band = 1; else if (params->frequency < 444000000) band = 2; else if (params->frequency < 861000000) band = 4; else 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: 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(&budget->i2c_adap, &tuner_msg, 1) != 1) return -EIO; msleep(1); return 0;}static int philips_tu1216_request_firmware(struct dvb_frontend *fe, const struct firmware **fw, char *name){ struct budget *budget = (struct budget *) fe->dvb->priv; return request_firmware(fw, name, &budget->dev->pci->dev);}static struct tda1004x_config philips_tu1216_config = { .demod_address = 0x8, .invert = 1, .invert_oclk = 1, .xtal_freq = TDA10046_XTAL_4M, .agc_config = TDA10046_AGC_DEFAULT, .if_freq = TDA10046_FREQ_3617, .pll_init = philips_tu1216_pll_init, .pll_set = philips_tu1216_pll_set, .pll_sleep = NULL, .request_firmware = philips_tu1216_request_firmware,};static u8 read_pwm(struct budget_av *budget_av){ u8 b = 0xff; u8 pwm; struct i2c_msg msg[] = { {.addr = 0x50,.flags = 0,.buf = &b,.len = 1}, {.addr = 0x50,.flags = I2C_M_RD,.buf = &pwm,.len = 1} }; if ((i2c_transfer(&budget_av->budget.i2c_adap, msg, 2) != 2) || (pwm == 0xff)) pwm = 0x48; return pwm;}#define SUBID_DVBS_KNC1 0x0010#define SUBID_DVBS_KNC1_PLUS 0x0011#define SUBID_DVBS_TYPHOON 0x4f56#define SUBID_DVBS_CINERGY1200 0x1154#define SUBID_DVBC_KNC1 0x0020#define SUBID_DVBC_KNC1_PLUS 0x0021#define SUBID_DVBC_CINERGY1200 0x1156#define SUBID_DVBT_KNC1_PLUS 0x0031#define SUBID_DVBT_KNC1 0x0030#define SUBID_DVBT_CINERGY1200 0x1157static void frontend_init(struct budget_av *budget_av){ struct saa7146_dev * saa = budget_av->budget.dev; struct dvb_frontend * fe = NULL; switch (saa->pci->subsystem_device) { case SUBID_DVBS_KNC1_PLUS: case SUBID_DVBC_KNC1_PLUS: case SUBID_DVBT_KNC1_PLUS: // Enable / PowerON Frontend saa7146_setgpio(saa, 0, SAA7146_GPIO_OUTLO); saa7146_setgpio(saa, 3, SAA7146_GPIO_OUTHI); break; } switch (saa->pci->subsystem_device) { case SUBID_DVBS_KNC1: case SUBID_DVBS_KNC1_PLUS: case SUBID_DVBS_TYPHOON: fe = stv0299_attach(&typhoon_config, &budget_av->budget.i2c_adap); break; case SUBID_DVBS_CINERGY1200: fe = stv0299_attach(&cinergy_1200s_config, &budget_av->budget.i2c_adap); break; case SUBID_DVBC_KNC1: case SUBID_DVBC_KNC1_PLUS: fe = tda10021_attach(&philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); break; case SUBID_DVBT_KNC1: case SUBID_DVBT_KNC1_PLUS: fe = tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap); break; case SUBID_DVBC_CINERGY1200: fe = tda10021_attach(&philips_cu1216_config, &budget_av->budget.i2c_adap, read_pwm(budget_av)); break; case SUBID_DVBT_CINERGY1200: fe = tda10046_attach(&philips_tu1216_config, &budget_av->budget.i2c_adap); break; } if (fe == NULL) { printk(KERN_ERR "budget-av: A frontend driver was not found " "for device %04x/%04x subsystem %04x/%04x\n", saa->pci->vendor, saa->pci->device, saa->pci->subsystem_vendor, saa->pci->subsystem_device); return; } budget_av->budget.dvb_frontend = fe; if (dvb_register_frontend(&budget_av->budget.dvb_adapter, budget_av->budget.dvb_frontend)) { printk(KERN_ERR "budget-av: Frontend registration failed!\n"); if (budget_av->budget.dvb_frontend->ops->release) budget_av->budget.dvb_frontend->ops->release(budget_av->budget.dvb_frontend); budget_av->budget.dvb_frontend = NULL; }}static void budget_av_irq(struct saa7146_dev *dev, u32 * isr){ struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; dprintk(8, "dev: %p, budget_av: %p\n", dev, budget_av); if (*isr & MASK_10) ttpci_budget_irq10_handler(dev, isr);}static int budget_av_detach(struct saa7146_dev *dev){ struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; int err; dprintk(2, "dev: %p\n", dev); if (1 == budget_av->has_saa7113) { saa7146_setgpio(dev, 0, SAA7146_GPIO_OUTLO); msleep(200); saa7146_unregister_device(&budget_av->vd, dev); } if (budget_av->budget.ci_present) ciintf_deinit(budget_av); if (budget_av->budget.dvb_frontend != NULL) dvb_unregister_frontend(budget_av->budget.dvb_frontend); err = ttpci_budget_deinit(&budget_av->budget); kfree(budget_av); return err;}static struct saa7146_ext_vv vv_data;static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info){ struct budget_av *budget_av; u8 *mac; int err; dprintk(2, "dev: %p\n", dev); if (!(budget_av = kmalloc(sizeof(struct budget_av), GFP_KERNEL))) return -ENOMEM; memset(budget_av, 0, sizeof(struct budget_av)); budget_av->has_saa7113 = 0; budget_av->budget.ci_present = 0; dev->ext_priv = budget_av; if ((err = ttpci_budget_init(&budget_av->budget, dev, info, THIS_MODULE))) { kfree(budget_av); return err; } /* knc1 initialization */ saa7146_write(dev, DD1_STREAM_B, 0x04000000); saa7146_write(dev, DD1_INIT, 0x07000600); saa7146_write(dev, MC2, MASK_09 | MASK_25 | MASK_10 | MASK_26); if (saa7113_init(budget_av) == 0) { budget_av->has_saa7113 = 1; if (0 != saa7146_vv_init(dev, &vv_data)) { /* fixme: proper cleanup here */ ERR(("cannot init vv subsystem.\n")); return err; } if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { /* fixme: proper cleanup here */ ERR(("cannot register capture v4l2 device.\n")); return err; } /* beware: this modifies dev->vv ... */ saa7146_set_hps_source_and_sync(dev, SAA7146_HPS_SOURCE_PORT_A, SAA7146_HPS_SYNC_PORT_A); saa7113_setinput(budget_av, 0); } else { ciintf_init(budget_av); } /* fixme: find some sane values here... */ saa7146_write(dev, PCI_BT_V1, 0x1c00101f); mac = budget_av->budget.dvb_adapter.proposed_mac; if (i2c_readregs(&budget_av->budget.i2c_adap, 0xa0, 0x30, mac, 6)) { printk(KERN_ERR "KNC1-%d: Could not read MAC from KNC1 card\n", budget_av->budget.dvb_adapter.num); memset(mac, 0, 6); } else { printk(KERN_INFO "KNC1-%d: MAC addr = %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", budget_av->budget.dvb_adapter.num, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); } budget_av->budget.dvb_adapter.priv = budget_av; frontend_init(budget_av); return 0;}#define KNC1_INPUTS 2static struct v4l2_input knc1_inputs[KNC1_INPUTS] = { {0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0}, {1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},};static struct saa7146_extension_ioctls ioctls[] = { {VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE}, {VIDIOC_G_INPUT, SAA7146_EXCLUSIVE}, {VIDIOC_S_INPUT, SAA7146_EXCLUSIVE}, {0, 0}};static int av_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg){ struct saa7146_dev *dev = fh->dev; struct budget_av *budget_av = (struct budget_av *) dev->ext_priv; switch (cmd) { case VIDIOC_ENUMINPUT:{ struct v4l2_input *i = arg; dprintk(1, "VIDIOC_ENUMINPUT %d.\n", i->index); if (i->index < 0 || i->index >= KNC1_INPUTS) { return -EINVAL; } memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input)); return 0; } case VIDIOC_G_INPUT:{ int *input = (int *) arg; *input = budget_av->cur_input; dprintk(1, "VIDIOC_G_INPUT %d.\n", *input); return 0; } case VIDIOC_S_INPUT:{ int input = *(int *) arg; dprintk(1, "VIDIOC_S_INPUT %d.\n", input); return saa7113_setinput(budget_av, input); } default: return -ENOIOCTLCMD; } return 0;}static struct saa7146_standard standard[] = { {.name = "PAL",.id = V4L2_STD_PAL, .v_offset = 0x17,.v_field = 288, .h_offset = 0x14,.h_pixels = 680, .v_max_out = 576,.h_max_out = 768 }, {.name = "NTSC",.id = V4L2_STD_NTSC, .v_offset = 0x16,.v_field = 240, .h_offset = 0x06,.h_pixels = 708, .v_max_out = 480,.h_max_out = 640, },};static struct saa7146_ext_vv vv_data = { .inputs = 2, .capabilities = 0, // perhaps later: V4L2_CAP_VBI_CAPTURE, but that need tweaking with the saa7113 .flags = 0, .stds = &standard[0], .num_stds = sizeof(standard) / sizeof(struct saa7146_standard), .ioctls = &ioctls[0], .ioctl = av_ioctl,};static struct saa7146_extension budget_extension;MAKE_BUDGET_INFO(knc1s, "KNC1 DVB-S", BUDGET_KNC1S);MAKE_BUDGET_INFO(knc1c, "KNC1 DVB-C", BUDGET_KNC1C);MAKE_BUDGET_INFO(knc1t, "KNC1 DVB-T", BUDGET_KNC1T);MAKE_BUDGET_INFO(knc1sp, "KNC1 DVB-S Plus", BUDGET_KNC1SP);MAKE_BUDGET_INFO(knc1cp, "KNC1 DVB-C Plus", BUDGET_KNC1CP);MAKE_BUDGET_INFO(knc1tp, "KNC1 DVB-T Plus", BUDGET_KNC1TP);MAKE_BUDGET_INFO(cin1200s, "TerraTec Cinergy 1200 DVB-S", BUDGET_CIN1200S);MAKE_BUDGET_INFO(cin1200c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C);MAKE_BUDGET_INFO(cin1200t, "Terratec Cinergy 1200 DVB-T", BUDGET_CIN1200T);static struct pci_device_id pci_tbl[] = { MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x4f56), MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010), MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011), MAKE_EXTENSION_PCI(knc1c, 0x1894, 0x0020), MAKE_EXTENSION_PCI(knc1cp, 0x1894, 0x0021), MAKE_EXTENSION_PCI(knc1t, 0x1894, 0x0030), MAKE_EXTENSION_PCI(knc1tp, 0x1894, 0x0031), MAKE_EXTENSION_PCI(cin1200s, 0x153b, 0x1154), MAKE_EXTENSION_PCI(cin1200c, 0x153b, 0x1156), MAKE_EXTENSION_PCI(cin1200t, 0x153b, 0x1157), { .vendor = 0, }};MODULE_DEVICE_TABLE(pci, pci_tbl);static struct saa7146_extension budget_extension = { .name = "budget_av", .pci_tbl = pci_tbl, .module = THIS_MODULE, .attach = budget_av_attach, .detach = budget_av_detach, .irq_mask = MASK_10, .irq_func = budget_av_irq,};static int __init budget_av_init(void){ return saa7146_register_extension(&budget_extension);}static void __exit budget_av_exit(void){ saa7146_unregister_extension(&budget_extension);}module_init(budget_av_init);module_exit(budget_av_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");MODULE_DESCRIPTION("driver for the SAA7146 based so-called " "budget PCI DVB w/ analog input and CI-module (e.g. the KNC cards)");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -