📄 msp3400.c
字号:
goto done; restart: if (msp->mode == MSP_MODE_EXTERN) continue; msp->restart = 0; del_timer(&msp->wake_stereo); msp->watch_stereo = 0; /* put into sane state (and mute) */ msp3400c_reset(client); /* start autodetect */ switch (msp->norm) { case VIDEO_MODE_PAL: mode = 0x1003; std = 1; break; case VIDEO_MODE_NTSC: /* BTSC */ mode = 0x2003; std = 0x0020; break; case VIDEO_MODE_SECAM: mode = 0x0003; std = 1; break; case VIDEO_MODE_RADIO: mode = 0x0003; std = 0x0040; break; default: mode = 0x0003; std = 1; break; } msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, std); if (debug) { int i; for (i = 0; modelist[i].name != NULL; i++) if (modelist[i].retval == std) break; printk("msp3410: setting mode: %s (0x%04x)\n", modelist[i].name ? modelist[i].name : "unknown",std); } if (std != 1) { /* programmed some specific mode */ val = std; } else { /* triggered autodetect */ for (;;) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ/10); if (signal_pending(current)) goto done; if (msp->restart) goto restart; /* check results */ val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); if (val < 0x07ff) break; dprintk("msp3410: detection still in progress\n"); } } for (i = 0; modelist[i].name != NULL; i++) if (modelist[i].retval == val) break; dprintk("msp3410: current mode: %s (0x%04x)\n", modelist[i].name ? modelist[i].name : "unknown", val); msp->main = modelist[i].main; msp->second = modelist[i].second; if (amsound && (msp->norm == VIDEO_MODE_SECAM) && (val != 0x0009)) { /* autodetection has failed, let backup */ dprintk("msp3410: autodetection failed, switching to backup mode: %s (0x%04x)\n", modelist[8].name ? modelist[8].name : "unknown",val); val = 0x0009; msp3400c_write(client, I2C_MSP3400C_DEM, 0x20, val); } /* set various prescales */ msp3400c_write(client, I2C_MSP3400C_DFP, 0x0d, 0x1900); /* scart */ msp3400c_write(client, I2C_MSP3400C_DFP, 0x0e, 0x2403); /* FM */ msp3400c_write(client, I2C_MSP3400C_DFP, 0x10, 0x5a00); /* nicam */ /* set stereo */ switch (val) { case 0x0008: /* B/G NICAM */ case 0x000a: /* I NICAM */ if (val == 0x0008) msp->mode = MSP_MODE_FM_NICAM1; else msp->mode = MSP_MODE_FM_NICAM2; /* just turn on stereo */ msp->stereo = VIDEO_SOUND_STEREO; msp->nicam_on = 1; msp->watch_stereo = 1; msp3400c_setstereo(client,VIDEO_SOUND_STEREO); break; case 0x0009: msp->mode = MSP_MODE_AM_NICAM; msp->stereo = VIDEO_SOUND_MONO; msp->nicam_on = 1; msp3400c_setstereo(client,VIDEO_SOUND_MONO); msp->watch_stereo = 1; break; case 0x0020: /* BTSC */ /* just turn on stereo */ msp->mode = MSP_MODE_BTSC; msp->stereo = VIDEO_SOUND_STEREO; msp->nicam_on = 0; msp->watch_stereo = 1; msp3400c_setstereo(client,VIDEO_SOUND_STEREO); break; case 0x0040: /* FM radio */ msp->mode = MSP_MODE_FM_RADIO; msp->stereo = VIDEO_SOUND_STEREO; msp->nicam_on = 0; msp->watch_stereo = 0; /* scart routing */ msp3400c_set_scart(client,SCART_IN2,0); msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0220); msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0220); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0220); break; case 0x0003: msp->mode = MSP_MODE_FM_TERRA; msp->stereo = VIDEO_SOUND_MONO; msp->nicam_on = 0; msp->watch_stereo = 1; break; } /* unmute + restore dfp registers */ msp3400c_setbass(client, msp->bass); msp3400c_settreble(client, msp->treble); msp3400c_setvolume(client, msp->muted, msp->left, msp->right); msp3400c_restore_dfp(client); if (msp->watch_stereo) mod_timer(&msp->wake_stereo, jiffies+HZ); msp->active = 0; }done: dprintk("msp3410: thread: exit\n"); msp->active = 0; msp->thread = NULL; if(msp->notify != NULL) up(msp->notify); return 0;}/* ----------------------------------------------------------------------- */static int msp_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind);static int msp_detach(struct i2c_client *client);static int msp_probe(struct i2c_adapter *adap);static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg);static struct i2c_driver driver = { name: "i2c msp3400 driver", id: I2C_DRIVERID_MSP3400, flags: I2C_DF_NOTIFY, attach_adapter: msp_probe, detach_client: msp_detach, command: msp_command,};static struct i2c_client client_template = { name: "(unset)", driver: &driver,};static int msp_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind){ DECLARE_MUTEX_LOCKED(sem); struct msp3400c *msp; struct i2c_client *c; int rev1,rev2,i; client_template.adapter = adap; client_template.addr = addr; if (-1 == msp3400c_reset(&client_template)) { dprintk("msp3400: no chip found\n"); return -1; } if (NULL == (c = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) return -ENOMEM; memcpy(c,&client_template,sizeof(struct i2c_client)); if (NULL == (msp = kmalloc(sizeof(struct msp3400c),GFP_KERNEL))) { kfree(c); return -ENOMEM; } memset(msp,0,sizeof(struct msp3400c)); msp->left = 65535; msp->right = 65535; msp->bass = 32768; msp->treble = 32768; for (i = 0; i < DFP_COUNT; i++) msp->dfp_regs[i] = -1; c->data = msp; init_waitqueue_head(&msp->wq); if (-1 == msp3400c_reset(c)) { kfree(msp); dprintk("msp3400: no chip found\n"); return -1; } rev1 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1e); if (-1 != rev1) rev2 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1f); if ((-1 == rev1) || (0 == rev1 && 0 == rev2)) { kfree(msp); printk("msp3400: error while reading chip version\n"); return -1; }#if 0 /* this will turn on a 1kHz beep - might be useful for debugging... */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x0014, 0x1040);#endif sprintf(c->name,"MSP34%02d%c-%c%d", (rev2>>8)&0xff, (rev1&0xff)+'@', ((rev1>>8)&0xff)+'@', rev2&0x1f); msp->nicam = (((rev2>>8)&0xff) != 00) ? 1 : 0; if (simple == -1) { /* default mode */ /* msp->simple = (((rev2>>8)&0xff) == 0) ? 0 : 1; */ msp->simple = ((rev1&0xff)+'@' > 'C'); } else { /* use insmod option */ msp->simple = simple; } /* timer for stereo checking */ msp->wake_stereo.function = msp3400c_stereo_wake; msp->wake_stereo.data = (unsigned long)msp; /* hello world :-) */ printk(KERN_INFO "msp34xx: init: chip=%s",c->name); if (msp->nicam) printk(", has NICAM support"); printk("\n"); /* startup control thread */ MOD_INC_USE_COUNT; msp->notify = &sem; kernel_thread(msp->simple ? msp3410d_thread : msp3400c_thread, (void *)c, 0); down(&sem); msp->notify = NULL; wake_up_interruptible(&msp->wq); /* update our own array */ for (i = 0; i < MSP3400_MAX; i++) { if (NULL == msps[i]) { msps[i] = c; break; } } /* done */ i2c_attach_client(c); return 0;}static int msp_detach(struct i2c_client *client){ DECLARE_MUTEX_LOCKED(sem); struct msp3400c *msp = (struct msp3400c*)client->data; int i; /* shutdown control thread */ del_timer(&msp->wake_stereo); if (msp->thread) { msp->notify = &sem; msp->rmmod = 1; wake_up_interruptible(&msp->wq); down(&sem); msp->notify = NULL; } msp3400c_reset(client); /* update our own array */ for (i = 0; i < MSP3400_MAX; i++) { if (client == msps[i]) { msps[i] = NULL; break; } } i2c_detach_client(client); kfree(msp); kfree(client); MOD_DEC_USE_COUNT; return 0;}static int msp_probe(struct i2c_adapter *adap){ if (adap->id == (I2C_ALGO_BIT | I2C_HW_B_BT848)) return i2c_probe(adap, &addr_data, msp_attach); return 0;}static void msp_wake_thread(struct i2c_client *client){ struct msp3400c *msp = (struct msp3400c*)client->data; msp3400c_setvolume(client,msp->muted,0,0); msp->watch_stereo=0; del_timer(&msp->wake_stereo); if (msp->active) msp->restart = 1; wake_up_interruptible(&msp->wq);}static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg){ struct msp3400c *msp = (struct msp3400c*)client->data; __u16 *sarg = arg;#if 0 int *iarg = (int*)arg;#endif switch (cmd) { case AUDC_SET_INPUT: /* scart switching - IN1 is often used for external input - Hauppauge uses IN2 for the radio */ dprintk(KERN_DEBUG "msp34xx: AUDC_SET_INPUT(%d)\n",*sarg); switch (*sarg) { case AUDIO_RADIO: msp->mode = MSP_MODE_FM_RADIO; msp->stereo = VIDEO_SOUND_STEREO; msp3400c_set_scart(client,SCART_IN2,0); msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); msp3400c_setstereo(client,msp->stereo); break; case AUDIO_EXTERN: msp->mode = MSP_MODE_EXTERN; msp->stereo = VIDEO_SOUND_STEREO; msp3400c_set_scart(client,SCART_IN1,0); msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); msp3400c_setstereo(client,msp->stereo); break; case AUDIO_TUNER: msp->mode = -1; msp_wake_thread(client); break; default: if (*sarg & AUDIO_MUTE) msp3400c_set_scart(client,SCART_MUTE,0); break; } if (msp->active) msp->restart = 1; break; case AUDC_SET_RADIO: msp->norm = VIDEO_MODE_RADIO; msp->watch_stereo=0; del_timer(&msp->wake_stereo); dprintk("msp34xx: switching to radio mode\n"); if (msp->simple) { /* the thread will do for us */ msp_wake_thread(client); } else { /* set msp3400 to FM radio mode */ msp3400c_setmode(client,MSP_MODE_FM_RADIO); msp3400c_setcarrier(client, MSP_CARRIER(10.7),MSP_CARRIER(10.7)); msp3400c_setvolume(client,msp->muted,msp->left,msp->right); } if (msp->active) msp->restart = 1; break;#if 1 /* work-in-progress: hook to control the DFP registers */ case MSP_SET_DFPREG: { struct msp_dfpreg *r = arg; int i; if (r->reg < 0 || r->reg >= DFP_COUNT) return -EINVAL; for (i = 0; i < sizeof(bl_dfp)/sizeof(int); i++) if (r->reg == bl_dfp[i]) return -EINVAL; msp->dfp_regs[r->reg] = r->value; msp3400c_write(client,I2C_MSP3400C_DFP,r->reg,r->value); return 0; } case MSP_GET_DFPREG: { struct msp_dfpreg *r = arg; if (r->reg < 0 || r->reg >= DFP_COUNT) return -EINVAL; r->value = msp3400c_read(client,I2C_MSP3400C_DFP,r->reg); return 0; }#endif /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ case VIDIOCGAUDIO: { struct video_audio *va = arg; va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE | VIDEO_AUDIO_MUTABLE; if (msp->muted) va->flags |= VIDEO_AUDIO_MUTE; va->volume=MAX(msp->left,msp->right); va->balance=(32768*MIN(msp->left,msp->right))/ (va->volume ? va->volume : 1); va->balance=(msp->left<msp->right)? (65535-va->balance) : va->balance; if (0 == va->volume) va->balance = 32768; va->bass = msp->bass; va->treble = msp->treble; if (msp->norm != VIDEO_MODE_RADIO) { autodetect_stereo(client); va->mode = msp->stereo; } break; } case VIDIOCSAUDIO: { struct video_audio *va = arg; msp->muted = (va->flags & VIDEO_AUDIO_MUTE); msp->left = (MIN(65536 - va->balance,32768) * va->volume) / 32768; msp->right = (MIN(va->balance,32768) * va->volume) / 32768; msp->bass = va->bass; msp->treble = va->treble; msp3400c_setvolume(client,msp->muted,msp->left,msp->right); msp3400c_setbass(client,msp->bass); msp3400c_settreble(client,msp->treble); if (va->mode != 0) { msp->watch_stereo=0; del_timer(&msp->wake_stereo); msp->stereo = va->mode; msp3400c_setstereo(client,va->mode); } break; } case VIDIOCSCHAN: { struct video_channel *vc = arg; dprintk("msp34xx: switching to TV mode\n"); msp->norm = vc->norm; break; } case VIDIOCSFREQ: { /* new channel -- kick audio carrier scan */ msp_wake_thread(client); break; } default: /* nothing */ break; } return 0;}/* ----------------------------------------------------------------------- */int msp3400_init_module(void){ i2c_add_driver(&driver); return 0;}void msp3400_cleanup_module(void){ i2c_del_driver(&driver);}module_init(msp3400_init_module);module_exit(msp3400_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 + -