📄 msp3400.c
字号:
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(KERN_DEBUG "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 (;;) { if (msp34xx_sleep(msp,HZ/10)) goto done; if (msp->restart) goto restart; /* check results */ val = msp3400c_read(client, I2C_MSP3400C_DEM, 0x7e); if (val < 0x07ff) break; dprintk(KERN_DEBUG "msp3410: detection still in progress\n"); } } for (i = 0; modelist[i].name != NULL; i++) if (modelist[i].retval == val) break; dprintk(KERN_DEBUG "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(KERN_DEBUG "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; /* not needed in theory if HAVE_RADIO(), but short programming enables carrier mute */ msp3400c_setmode(client,MSP_MODE_FM_RADIO); msp3400c_setcarrier(client, MSP_CARRIER(10.7), MSP_CARRIER(10.7)); /* scart routing */ msp3400c_set_scart(client,SCART_IN2,0);#if 0 /* radio from SCART_IN2 */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0220); msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0220); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0220);#else /* msp34xx does radio decoding */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x08, 0x0020); msp3400c_write(client,I2C_MSP3400C_DFP, 0x09, 0x0020); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0b, 0x0020);#endif break; case 0x0003: case 0x0004: case 0x0005: 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: msp->active = 0; dprintk(KERN_DEBUG "msp3410: thread: exit\n"); complete_and_exit(&msp->texit, 0); return 0;}/* ----------------------------------------------------------------------- */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)static int msp_attach(struct i2c_adapter *adap, int addr, int kind);#elsestatic int msp_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind);#endifstatic 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 = {#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) .owner = THIS_MODULE,#endif .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 = { I2C_DEVNAME("(unset)"), .flags = I2C_CLIENT_ALLOW_USE, .driver = &driver,};#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)static int msp_attach(struct i2c_adapter *adap, int addr, int kind)#elsestatic int msp_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind)#endif{ struct msp3400c *msp; struct i2c_client *c; int 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; msp->input = -1; msp->muted = 1; for (i = 0; i < DFP_COUNT; i++) msp->dfp_regs[i] = -1; i2c_set_clientdata(c, msp); init_waitqueue_head(&msp->wq); if (-1 == msp3400c_reset(c)) { kfree(msp); kfree(c); dprintk("msp3400: no chip found\n"); return -1; } msp->rev1 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1e); if (-1 != msp->rev1) msp->rev2 = msp3400c_read(c, I2C_MSP3400C_DFP, 0x1f); if ((-1 == msp->rev1) || (0 == msp->rev1 && 0 == msp->rev2)) { kfree(msp); kfree(c); 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(c,I2C_MSP3400C_DFP, 0x0014, 0x1040);#endif msp3400c_setvolume(c,msp->muted,msp->left,msp->right); snprintf(c->name, sizeof(c->name), "MSP34%02d%c-%c%d", (msp->rev2>>8)&0xff, (msp->rev1&0xff)+'@', ((msp->rev1>>8)&0xff)+'@', msp->rev2&0x1f); if (simple == -1) { /* default mode */ msp->simple = HAVE_SIMPLE(msp); } else { /* use insmod option */ msp->simple = simple; } /* timer for stereo checking */ init_timer(&msp->wake_stereo); msp->wake_stereo.function = msp3400c_stereo_wake; msp->wake_stereo.data = (unsigned long)msp; /* hello world :-) */ printk(KERN_INFO "msp34xx: init: chip=%s",i2c_clientname(c)); if (HAVE_NICAM(msp)) printk(" +nicam"); if (HAVE_SIMPLE(msp)) printk(" +simple"); if (HAVE_RADIO(msp)) printk(" +radio"); printk("\n"); /* startup control thread */#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) MOD_INC_USE_COUNT;#endif init_completion(&msp->texit); msp->tpid = kernel_thread(msp->simple ? msp3410d_thread : msp3400c_thread, (void *)c, 0); if (msp->tpid < 0) printk(KERN_WARNING "msp34xx: kernel_thread() failed\n"); wake_up_interruptible(&msp->wq); /* done */ i2c_attach_client(c); return 0;}static int msp_detach(struct i2c_client *client){ struct msp3400c *msp = i2c_get_clientdata(client); /* shutdown control thread */ del_timer_sync(&msp->wake_stereo); if (msp->tpid >= 0) { msp->rmmod = 1; wake_up_interruptible(&msp->wq); wait_for_completion(&msp->texit); } msp3400c_reset(client); i2c_detach_client(client); kfree(msp); kfree(client);#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) MOD_DEC_USE_COUNT;#endif return 0;}static int msp_probe(struct i2c_adapter *adap){#ifdef I2C_ADAP_CLASS_TV_ANALOG if (adap->class & I2C_ADAP_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, msp_attach);#else switch (adap->id) { case I2C_ALGO_BIT | I2C_HW_SMBUS_VOODOO3: case I2C_ALGO_BIT | I2C_HW_B_BT848: //case I2C_ALGO_SAA7134: return i2c_probe(adap, &addr_data, msp_attach); break; }#endif return 0;}static void msp_wake_thread(struct i2c_client *client){ struct msp3400c *msp = i2c_get_clientdata(client); 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 = i2c_get_clientdata(client); __u16 *sarg = arg; int scart = 0; switch (cmd) { case AUDC_SET_INPUT: dprintk(KERN_DEBUG "msp34xx: AUDC_SET_INPUT(%d)\n",*sarg); if (*sarg == msp->input) break; msp->input = *sarg; switch (*sarg) { case AUDIO_RADIO: /* Hauppauge uses IN2 for the radio */ msp->mode = MSP_MODE_FM_RADIO; scart = SCART_IN2; break; case AUDIO_EXTERN_1: /* IN1 is often used for external input ... */ msp->mode = MSP_MODE_EXTERN; scart = SCART_IN1; break; case AUDIO_EXTERN_2: /* ... sometimes it is IN2 through ;) */ msp->mode = MSP_MODE_EXTERN; scart = SCART_IN2; 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 (scart) { msp->stereo = VIDEO_SOUND_STEREO; msp3400c_set_scart(client,scart,0); msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); msp3400c_setstereo(client,msp->stereo); } if (msp->active) msp->restart = 1; break; case AUDC_SET_RADIO: dprintk(KERN_DEBUG "msp34xx: AUDC_SET_RADIO\n"); msp->norm = VIDEO_MODE_RADIO; msp->watch_stereo=0; del_timer(&msp->wake_stereo); dprintk(KERN_DEBUG "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; unsigned int i; if (r->reg < 0 || r->reg >= DFP_COUNT) return -EINVAL; for (i = 0; i < ARRAY_SIZE(bl_dfp); 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; dprintk(KERN_DEBUG "msp34xx: VIDIOCGAUDIO\n"); 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); if (0 == va->volume) { va->balance = 32768; } else { va->balance = (32768 * min(msp->left,msp->right)) / va->volume; va->balance = (msp->left<msp->right) ? (65535 - va->balance) : va->balance; } 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; dprintk(KERN_DEBUG "msp34xx: VIDIOCSAUDIO\n"); msp->muted = (va->flags & VIDEO_AUDIO_MUTE); msp->left = (min(65536 - va->balance,32768) * va->volume) / 32768; msp->right = (min(va->balance,(__u16)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->norm != VIDEO_MODE_RADIO) { msp->watch_stereo=0; del_timer(&msp->wake_stereo); msp->stereo = va->mode & 0x0f; msp3400c_setstereo(client,va->mode & 0x0f); } break; } case VIDIOCSCHAN: { struct video_channel *vc = arg; dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN (norm=%d)\n",vc->norm); msp->norm = vc->norm; break; } case VIDIOCSFREQ: { /* new channel -- kick audio carrier scan */ dprintk(KERN_DEBUG "msp34xx: VIDIOCSFREQ\n"); msp_wake_thread(client); break; } default: /* nothing */ break; } return 0;}/* ----------------------------------------------------------------------- */static int msp3400_init_module(void){ i2c_add_driver(&driver); return 0;}static 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 + -