📄 tuner.c
字号:
break;
case 'd':
case 'D':
config |= TEMIC_SET_PAL_DK;
break;
case 'l':
case 'L':
config |= TEMIC_SET_PAL_L;
break;
case 'b':
case 'B':
case 'g':
case 'G':
default:
config |= TEMIC_SET_PAL_BG;
break;
}
break;
case TUNER_PHILIPS_FQ1216ME:
config &= ~0x0f;
switch (pal[0]) {
case 'i':
case 'I':
config |= PHILIPS_SET_PAL_I;
break;
case 'l':
case 'L':
config |= PHILIPS_SET_PAL_L;
break;
case 'd':
case 'D':
case 'b':
case 'B':
case 'g':
case 'G':
config |= PHILIPS_SET_PAL_BGDK;
break;
}
break;
case TUNER_PHILIPS_ATSC:
/* 0x00 -> ATSC antenna input 1 */
/* 0x01 -> ATSC antenna input 2 */
/* 0x02 -> NTSC antenna input 1 */
/* 0x03 -> NTSC antenna input 2 */
config &= ~0x03;
#ifdef VIDEO_MODE_ATSC
if (VIDEO_MODE_ATSC != t->mode)
config |= 2;
#endif
/* FIXME: input */
break;
}
/*
* Philips FI1216MK2 remark from specification :
* for channel selection involving band switching, and to ensure
* smooth tuning to the desired channel without causing
* unnecessary charge pump action, it is recommended to consider
* the difference between wanted channel frequency and the
* current channel frequency. Unnecessary charge pump action
* will result in very low tuning voltage which may drive the
* oscillator to extreme conditions.
*
* Progfou: specification says to send config data before
* frequency in case (wanted frequency < current frequency).
*/
div=freq + tun->IFPCoff;
if (t->type == TUNER_PHILIPS_SECAM && freq < t->freq) {
buffer[0] = tun->config;
buffer[1] = config;
buffer[2] = (div>>8) & 0x7f;
buffer[3] = div & 0xff;
} else {
buffer[0] = (div>>8) & 0x7f;
buffer[1] = div & 0xff;
buffer[2] = tun->config;
buffer[3] = config;
}
dprintk("tuner: tv 0x%02x 0x%02x 0x%02x 0x%02x\n",
buffer[0],buffer[1],buffer[2],buffer[3]);
if (4 != (rc = i2c_master_send(c,buffer,4)))
printk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc);
}
static void default_set_radio_freq(struct i2c_client *c, unsigned int freq)
{
struct tunertype *tun;
struct tuner *t = i2c_get_clientdata(c);
unsigned char buffer[4];
unsigned div;
int rc;
tun=&tuners[t->type];
div = freq + (int)(16*10.7);
buffer[0] = (div>>8) & 0x7f;
buffer[1] = div & 0xff;
buffer[2] = tun->config;
switch (t->type) {
case TUNER_PHILIPS_FM1216ME_MK3:
buffer[3] = 0x19;
break;
default:
buffer[3] = 0xa4;
break;
}
dprintk("tuner: radio 0x%02x 0x%02x 0x%02x 0x%02x\n",
buffer[0],buffer[1],buffer[2],buffer[3]);
if (4 != (rc = i2c_master_send(c,buffer,4)))
printk("tuner: i2c i/o error: rc == %d (should be 4)\n",rc);
}
/* ---------------------------------------------------------------------- */
// 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);
if (t->type == UNSET) {
printk("tuner: tuner type not set\n");
return;
}
if (NULL == t->tv_freq) {
printk("tuner: Huh? tv_set is NULL?\n");
return;
}
if (freq < tv_range[0]*16 || freq > tv_range[1]*16) {
/* FIXME: better do that chip-specific, but
right now we don't have that in the config
struct and this way is still better than no
check at all */
printk("tuner: TV freq (%d.%02d) out of range (%d-%d)\n",
freq/16,freq%16*100/16,tv_range[0],tv_range[1]);
return;
}
t->tv_freq(c,freq);
}
static void set_radio_freq(struct i2c_client *c, unsigned int freq)
{
struct tuner *t = i2c_get_clientdata(c);
if (t->type == UNSET) {
printk("tuner: tuner type not set\n");
return;
}
if (NULL == t->radio_freq) {
printk("tuner: no radio tuning for this one, sorry.\n");
return;
}
if (freq < radio_range[0]*16 || freq > radio_range[1]*16) {
printk("tuner: radio freq (%d.%02d) out of range (%d-%d)\n",
freq/16,freq%16*100/16,
radio_range[0],radio_range[1]);
return;
}
t->radio_freq(c,freq);
}
static void set_type(struct i2c_client *c, unsigned int type)
{
struct tuner *t = i2c_get_clientdata(c);
if (t->type != UNSET) {
printk("tuner: type already set (%d)\n",t->type);
return;
}
if (type >= TUNERS)
return;
t->type = type;
printk("tuner: type set to %d (%s)\n", t->type,tuners[t->type].name);
strlcpy(c->name, tuners[t->type].name, sizeof(c->name));
switch (t->type) {
case TUNER_MT2032:
microtune_init(c);
break;
default:
t->tv_freq = default_set_tv_freq;
t->radio_freq = default_set_radio_freq;
break;
}
}
/* ---------------------------------------------------------------------- */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
#else
static int tuner_attach(struct i2c_adapter *adap, int addr,
unsigned short flags, int kind)
#endif
{
struct tuner *t;
struct i2c_client *client;
if (this_adap > 0)
return -1;
this_adap++;
client_template.adapter = adap;
client_template.addr = addr;
printk("tuner: chip found @ 0x%x\n", addr<<1);
if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL)))
return -ENOMEM;
memcpy(client,&client_template,sizeof(struct i2c_client));
t = kmalloc(sizeof(struct tuner),GFP_KERNEL);
if (NULL == t) {
kfree(client);
return -ENOMEM;
}
memset(t,0,sizeof(struct tuner));
i2c_set_clientdata(client, t);
t->type = UNSET;
t->radio_if2 = 10700*1000; // 10.7MHz - FM radio
i2c_attach_client(client);
if (type < TUNERS) {
printk("tuner: type forced to %d (%s) [insmod]\n",
t->type,tuners[t->type].name);
set_type(client,type);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_INC_USE_COUNT;
#endif
return 0;
}
static int tuner_probe(struct i2c_adapter *adap)
{
if (0 != addr) {
normal_i2c_range[0] = addr;
normal_i2c_range[1] = addr;
}
this_adap = 0;
#ifdef I2C_ADAP_CLASS_TV_ANALOG
if (adap->class & I2C_ADAP_CLASS_TV_ANALOG)
return i2c_probe(adap, &addr_data, tuner_attach);
#else
switch (adap->id) {
case I2C_ALGO_BIT | I2C_HW_B_BT848:
case I2C_ALGO_BIT | I2C_HW_B_RIVA:
case I2C_ALGO_SAA7134:
case I2C_ALGO_SAA7146:
return i2c_probe(adap, &addr_data, tuner_attach);
break;
}
#endif
return 0;
}
static int tuner_detach(struct i2c_client *client)
{
struct tuner *t = i2c_get_clientdata(client);
i2c_detach_client(client);
kfree(t);
kfree(client);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
MOD_DEC_USE_COUNT;
#endif
return 0;
}
static int
tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct tuner *t = i2c_get_clientdata(client);
unsigned int *iarg = (int*)arg;
switch (cmd) {
/* --- configuration --- */
case TUNER_SET_TYPE:
set_type(client,*iarg);
break;
case AUDC_SET_RADIO:
if (!t->radio) {
set_tv_freq(client,400 * 16);
t->radio = 1;
}
break;
case AUDC_CONFIG_PINNACLE:
switch (*iarg) {
case 2:
dprintk("tuner: pinnacle pal\n");
t->radio_if2 = 33300 * 1000;
break;
case 3:
dprintk("tuner: pinnacle ntsc\n");
t->radio_if2 = 41300 * 1000;
break;
}
break;
/* --- v4l ioctls --- */
/* take care: bttv does userspace copying, we'll get a
kernel pointer here... */
case VIDIOCSCHAN:
{
struct video_channel *vc = arg;
t->radio = 0;
t->mode = vc->norm;
if (t->freq)
set_tv_freq(client,t->freq);
return 0;
}
case VIDIOCSFREQ:
{
unsigned long *v = arg;
if (t->radio) {
dprintk("tuner: radio freq set to %lu.%02lu\n",
(*v)/16,(*v)%16*100/16);
set_radio_freq(client,*v);
} else {
dprintk("tuner: tv freq set to %lu.%02lu\n",
(*v)/16,(*v)%16*100/16);
set_tv_freq(client,*v);
}
t->freq = *v;
return 0;
}
case VIDIOCGTUNER:
{
struct video_tuner *vt = arg;
if (t->radio)
vt->signal = tuner_signal(client);
return 0;
}
case VIDIOCGAUDIO:
{
struct video_audio *va = arg;
if (t->radio)
va->mode = (tuner_stereo(client) ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO);
return 0;
}
default:
/* nothing */
break;
}
return 0;
}
/* ----------------------------------------------------------------------- */
static struct i2c_driver driver = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,54)
.owner = THIS_MODULE,
#endif
.name = "i2c TV tuner driver",
.id = I2C_DRIVERID_TUNER,
.flags = I2C_DF_NOTIFY,
.attach_adapter = tuner_probe,
.detach_client = tuner_detach,
.command = tuner_command,
};
static struct i2c_client client_template =
{
I2C_DEVNAME("(tuner unset)"),
.flags = I2C_CLIENT_ALLOW_USE,
.driver = &driver,
};
static int tuner_init_module(void)
{
i2c_add_driver(&driver);
return 0;
}
static void tuner_cleanup_module(void)
{
i2c_del_driver(&driver);
}
module_init(tuner_init_module);
module_exit(tuner_cleanup_module);
/*
* Overrides for Emacs so that we follow Linus's tabbing style.
* ---------------------------------------------------------------------------
* Local variables:
* c-basic-offset: 8
* End:
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -