📄 tuner-core.c
字号:
/* * * i2c tv tuner chip device driver * core core, i.e. kernel interfaces, registering and so on */#include <linux/module.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/timer.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/slab.h>#include <linux/poll.h>#include <linux/i2c.h>#include <linux/types.h>#include <linux/init.h>#include "compat.h"#include <linux/videodev.h>#include <media/tuner.h>#include <media/tuner-types.h>#include <media/v4l2-common.h>#include <media/v4l2-ioctl.h>#include <media/v4l2-i2c-drv-legacy.h>#include "mt20xx.h"#include "tda8290.h"#include "tea5761.h"#include "tea5767.h"#include "tuner-xc2028.h"#include "tuner-simple.h"#include "tda9887.h"#include "xc5000.h"#define UNSET (-1U)#define PREFIX t->i2c->driver->driver.name/** This macro allows us to probe dynamically, avoiding static links */#ifdef CONFIG_MEDIA_ATTACH#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \ int __r = -EINVAL; \ typeof(&FUNCTION) __a = symbol_request(FUNCTION); \ if (__a) { \ __r = (int) __a(ARGS); \ symbol_put(FUNCTION); \ } else { \ printk(KERN_ERR "TUNER: Unable to find " \ "symbol "#FUNCTION"()\n"); \ } \ __r; \})static void tuner_detach(struct dvb_frontend *fe){ if (fe->ops.tuner_ops.release) { fe->ops.tuner_ops.release(fe); symbol_put_addr(fe->ops.tuner_ops.release); } if (fe->ops.analog_ops.release) { fe->ops.analog_ops.release(fe); symbol_put_addr(fe->ops.analog_ops.release); }}#else#define tuner_symbol_probe(FUNCTION, ARGS...) ({ \ FUNCTION(ARGS); \})static void tuner_detach(struct dvb_frontend *fe){ if (fe->ops.tuner_ops.release) fe->ops.tuner_ops.release(fe); if (fe->ops.analog_ops.release) fe->ops.analog_ops.release(fe);}#endifstruct tuner { /* device */ struct dvb_frontend fe; struct i2c_client *i2c; struct list_head list; unsigned int using_v4l2:1; /* keep track of the current settings */ v4l2_std_id std; unsigned int tv_freq; unsigned int radio_freq; unsigned int audmode; unsigned int mode; unsigned int mode_mask; /* Combination of allowable modes */ unsigned int type; /* chip type id */ unsigned int config; const char *name;};/* standard i2c insmod options */static unsigned short normal_i2c[] = {#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE)) 0x10,#endif 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, I2C_CLIENT_END};I2C_CLIENT_INSMOD;/* insmod options used at init time => read/only */static unsigned int addr;static unsigned int no_autodetect;static unsigned int show_i2c;/* insmod options used at runtime => read/write */static int tuner_debug;#define tuner_warn(fmt, arg...) do { \ printk(KERN_WARNING "%s %d-%04x: " fmt, PREFIX, \ i2c_adapter_id(t->i2c->adapter), \ t->i2c->addr, ##arg); \ } while (0)#define tuner_info(fmt, arg...) do { \ printk(KERN_INFO "%s %d-%04x: " fmt, PREFIX, \ i2c_adapter_id(t->i2c->adapter), \ t->i2c->addr, ##arg); \ } while (0)#define tuner_err(fmt, arg...) do { \ printk(KERN_ERR "%s %d-%04x: " fmt, PREFIX, \ i2c_adapter_id(t->i2c->adapter), \ t->i2c->addr, ##arg); \ } while (0)#define tuner_dbg(fmt, arg...) do { \ if (tuner_debug) \ printk(KERN_DEBUG "%s %d-%04x: " fmt, PREFIX, \ i2c_adapter_id(t->i2c->adapter), \ t->i2c->addr, ##arg); \ } while (0)/* ------------------------------------------------------------------------ */static unsigned int tv_range[2] = { 44, 958 };static unsigned int radio_range[2] = { 65, 108 };static char pal[] = "--";static char secam[] = "--";static char ntsc[] = "-";module_param(addr, int, 0444);module_param(no_autodetect, int, 0444);module_param(show_i2c, int, 0444);module_param_named(debug,tuner_debug, int, 0644);module_param_string(pal, pal, sizeof(pal), 0644);module_param_string(secam, secam, sizeof(secam), 0644);module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);module_param_array(tv_range, int, NULL, 0644);module_param_array(radio_range, int, NULL, 0644);MODULE_DESCRIPTION("device driver for various TV and TV+FM radio tuners");MODULE_AUTHOR("Ralph Metzler, Gerd Knorr, Gunther Mayer");MODULE_LICENSE("GPL");/* ---------------------------------------------------------------------- */static void fe_set_params(struct dvb_frontend *fe, struct analog_parameters *params){ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; struct tuner *t = fe->analog_demod_priv; if (NULL == fe_tuner_ops->set_analog_params) { tuner_warn("Tuner frontend module has no way to set freq\n"); return; } fe_tuner_ops->set_analog_params(fe, params);}static void fe_standby(struct dvb_frontend *fe){ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; if (fe_tuner_ops->sleep) fe_tuner_ops->sleep(fe);}static int fe_has_signal(struct dvb_frontend *fe){ u16 strength = 0; if (fe->ops.tuner_ops.get_rf_strength) fe->ops.tuner_ops.get_rf_strength(fe, &strength); return strength;}static int fe_set_config(struct dvb_frontend *fe, void *priv_cfg){ struct dvb_tuner_ops *fe_tuner_ops = &fe->ops.tuner_ops; struct tuner *t = fe->analog_demod_priv; if (fe_tuner_ops->set_config) return fe_tuner_ops->set_config(fe, priv_cfg); tuner_warn("Tuner frontend module has no way to set config\n"); return 0;}static void tuner_status(struct dvb_frontend *fe);static struct analog_demod_ops tuner_core_ops = { .set_params = fe_set_params, .standby = fe_standby, .has_signal = fe_has_signal, .set_config = fe_set_config, .tuner_status = tuner_status};/* Set tuner frequency, freq in Units of 62.5kHz = 1/16MHz */static void set_tv_freq(struct i2c_client *c, unsigned int freq){ struct tuner *t = i2c_get_clientdata(c); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct analog_parameters params = { .mode = t->mode, .audmode = t->audmode, .std = t->std }; if (t->type == UNSET) { tuner_warn ("tuner type not set\n"); return; } if (NULL == analog_ops->set_params) { tuner_warn ("Tuner has no way to set tv freq\n"); return; } if (freq < tv_range[0] * 16 || freq > tv_range[1] * 16) { tuner_dbg ("TV freq (%d.%02d) out of range (%d-%d)\n", freq / 16, freq % 16 * 100 / 16, tv_range[0], tv_range[1]); /* V4L2 spec: if the freq is not possible then the closest possible value should be selected */ if (freq < tv_range[0] * 16) freq = tv_range[0] * 16; else freq = tv_range[1] * 16; } params.frequency = freq; analog_ops->set_params(&t->fe, ¶ms);}static void set_radio_freq(struct i2c_client *c, unsigned int freq){ struct tuner *t = i2c_get_clientdata(c); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct analog_parameters params = { .mode = t->mode, .audmode = t->audmode, .std = t->std }; if (t->type == UNSET) { tuner_warn ("tuner type not set\n"); return; } if (NULL == analog_ops->set_params) { tuner_warn ("tuner has no way to set radio frequency\n"); return; } if (freq < radio_range[0] * 16000 || freq > radio_range[1] * 16000) { tuner_dbg ("radio freq (%d.%02d) out of range (%d-%d)\n", freq / 16000, freq % 16000 * 100 / 16000, radio_range[0], radio_range[1]); /* V4L2 spec: if the freq is not possible then the closest possible value should be selected */ if (freq < radio_range[0] * 16000) freq = radio_range[0] * 16000; else freq = radio_range[1] * 16000; } params.frequency = freq; analog_ops->set_params(&t->fe, ¶ms);}static void set_freq(struct i2c_client *c, unsigned long freq){ struct tuner *t = i2c_get_clientdata(c); switch (t->mode) { case V4L2_TUNER_RADIO: tuner_dbg("radio freq set to %lu.%02lu\n", freq / 16000, freq % 16000 * 100 / 16000); set_radio_freq(c, freq); t->radio_freq = freq; break; case V4L2_TUNER_ANALOG_TV: case V4L2_TUNER_DIGITAL_TV: tuner_dbg("tv freq set to %lu.%02lu\n", freq / 16, freq % 16 * 100 / 16); set_tv_freq(c, freq); t->tv_freq = freq; break; default: tuner_dbg("freq set: unknown mode: 0x%04x!\n",t->mode); }}static void tuner_i2c_address_check(struct tuner *t){ if ((t->type == UNSET || t->type == TUNER_ABSENT) || ((t->i2c->addr < 0x64) || (t->i2c->addr > 0x6f))) return; /* We already know that the XC5000 can only be located at * i2c address 0x61, 0x62, 0x63 or 0x64 */ if ((t->type == TUNER_XC5000) && ((t->i2c->addr <= 0x64)) && (t->i2c->addr >= 0x61)) return; tuner_warn("====================== WARNING! ======================\n"); tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n"); tuner_warn("will soon be dropped. This message indicates that your\n"); tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n", t->name, t->i2c->addr); tuner_warn("To ensure continued support for your device, please\n"); tuner_warn("send a copy of this message, along with full dmesg\n"); tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n"); tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n"); tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n", t->i2c->adapter->name, t->i2c->addr, t->type, t->name); tuner_warn("====================== WARNING! ======================\n");}static struct xc5000_config xc5000_cfg;static void set_type(struct i2c_client *c, unsigned int type, unsigned int new_mode_mask, unsigned int new_config, int (*tuner_callback) (void *dev, int component, int cmd, int arg)){ struct tuner *t = i2c_get_clientdata(c); struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; unsigned char buffer[4]; if (type == UNSET || type == TUNER_ABSENT) { tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr); return; } t->type = type; t->config = new_config; if (tuner_callback != NULL) { tuner_dbg("defining GPIO callback\n"); t->fe.callback = tuner_callback; } if (t->mode == T_UNINITIALIZED) { tuner_dbg ("tuner 0x%02x: called during i2c_client register by adapter's attach_inform\n", c->addr); return; } /* discard private data, in case set_type() was previously called */ tuner_detach(&t->fe); t->fe.analog_demod_priv = NULL; switch (t->type) { case TUNER_MT2032: if (!dvb_attach(microtune_attach, &t->fe, t->i2c->adapter, t->i2c->addr)) goto attach_failed; break; case TUNER_PHILIPS_TDA8290: { struct tda829x_config cfg = { .lna_cfg = t->config, }; if (!dvb_attach(tda829x_attach, &t->fe, t->i2c->adapter, t->i2c->addr, &cfg)) goto attach_failed; break; } case TUNER_TEA5767: if (!dvb_attach(tea5767_attach, &t->fe, t->i2c->adapter, t->i2c->addr)) goto attach_failed; t->mode_mask = T_RADIO; break; case TUNER_TEA5761: if (!dvb_attach(tea5761_attach, &t->fe, t->i2c->adapter, t->i2c->addr)) goto attach_failed; t->mode_mask = T_RADIO; break; case TUNER_PHILIPS_FMD1216ME_MK3: buffer[0] = 0x0b; buffer[1] = 0xdc; buffer[2] = 0x9c; buffer[3] = 0x60; i2c_master_send(c, buffer, 4); mdelay(1); buffer[2] = 0x86; buffer[3] = 0x54; i2c_master_send(c, buffer, 4); if (!dvb_attach(simple_tuner_attach, &t->fe, t->i2c->adapter, t->i2c->addr, t->type)) goto attach_failed; break; case TUNER_PHILIPS_TD1316: buffer[0] = 0x0b; buffer[1] = 0xdc; buffer[2] = 0x86; buffer[3] = 0xa4; i2c_master_send(c, buffer, 4); if (!dvb_attach(simple_tuner_attach, &t->fe, t->i2c->adapter, t->i2c->addr, t->type)) goto attach_failed; break; case TUNER_XC2028: { struct xc2028_config cfg = { .i2c_adap = t->i2c->adapter, .i2c_addr = t->i2c->addr, }; if (!dvb_attach(xc2028_attach, &t->fe, &cfg)) goto attach_failed; break; } case TUNER_TDA9887:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -