📄 msp3400.c
字号:
break; default: /* doing nothing: a safe, sane default */ audmode = 0; return; } msp->audmode = audmode; msp34xxg_set_source(client, source);}/* ----------------------------------------------------------------------- */static int msp_attach(struct i2c_adapter *adap, int addr, 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 int msp_suspend(struct device * dev, u32 state, u32 level);static int msp_resume(struct device * dev, u32 level);static void msp_wake_thread(struct i2c_client *client);static struct i2c_driver driver = { .owner = THIS_MODULE, .name = "i2c msp3400 driver", .id = I2C_DRIVERID_MSP3400, .flags = I2C_DF_NOTIFY, .attach_adapter = msp_probe, .detach_client = msp_detach, .command = msp_command, .driver = { .suspend = msp_suspend, .resume = msp_resume, },};static struct i2c_client client_template ={ I2C_DEVNAME("(unset)"), .flags = I2C_CLIENT_ALLOW_USE, .driver = &driver,};static int msp_attach(struct i2c_adapter *adap, int addr, int kind){ struct msp3400c *msp; struct i2c_client *c; int (*thread_func)(void *data) = NULL; 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->volume = 58880; /* 0db gain */ msp->balance = 32768; msp->bass = 32768; msp->treble = 32768; msp->input = -1; msp->muted = 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->volume, msp->balance); 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); msp->opmode = opmode; if (OPMODE_AUTO == msp->opmode) {#if 0 /* seems to work for ivtv only, disable by default for now ... */ if (HAVE_SIMPLER(msp)) msp->opmode = OPMODE_SIMPLER; else#endif if (HAVE_SIMPLE(msp)) msp->opmode = OPMODE_SIMPLE; else msp->opmode = OPMODE_MANUAL; } /* 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_SIMPLER(msp)) printk(" +simpler"); if (HAVE_RADIO(msp)) printk(" +radio"); /* version-specific initialization */ switch (msp->opmode) { case OPMODE_MANUAL: printk(" mode=manual"); thread_func = msp3400c_thread; break; case OPMODE_SIMPLE: printk(" mode=simple"); thread_func = msp3410d_thread; break; case OPMODE_SIMPLER: printk(" mode=simpler"); thread_func = msp34xxg_thread; break; } printk("\n"); /* startup control thread if needed */ if (thread_func) { msp->kthread = kthread_run(thread_func, c, "msp34xx"); if (NULL == msp->kthread) printk(KERN_WARNING "msp34xx: kernel_thread() failed\n"); msp_wake_thread(c); } /* 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 */ if (msp->kthread >= 0) { msp->restart = 1; kthread_stop(msp->kthread); } msp3400c_reset(client); i2c_detach_client(client); kfree(msp); kfree(client); return 0;}static int msp_probe(struct i2c_adapter *adap){ if (adap->class & I2C_CLASS_TV_ANALOG) return i2c_probe(adap, &addr_data, msp_attach); return 0;}static void msp_wake_thread(struct i2c_client *client){ struct msp3400c *msp = i2c_get_clientdata(client); if (NULL == msp->kthread) return; msp3400c_setvolume(client,msp->muted,0,0); msp->watch_stereo = 0; msp->restart = 1; wake_up_interruptible(&msp->wq);}/* ----------------------------------------------------------------------- */static int mode_v4l2_to_v4l1(int rxsubchans){ int mode = 0; if (rxsubchans & V4L2_TUNER_SUB_STEREO) mode |= VIDEO_SOUND_STEREO; if (rxsubchans & V4L2_TUNER_SUB_LANG2) mode |= VIDEO_SOUND_LANG2; if (rxsubchans & V4L2_TUNER_SUB_LANG1) mode |= VIDEO_SOUND_LANG1; if (0 == mode) mode |= VIDEO_SOUND_MONO; return mode;}static int mode_v4l1_to_v4l2(int mode){ if (mode & VIDEO_SOUND_STEREO) return V4L2_TUNER_MODE_STEREO; if (mode & VIDEO_SOUND_LANG2) return V4L2_TUNER_MODE_LANG2; if (mode & VIDEO_SOUND_LANG1) return V4L2_TUNER_MODE_LANG1; return V4L2_TUNER_MODE_MONO;}static void msp_any_detect_stereo(struct i2c_client *client){ struct msp3400c *msp = i2c_get_clientdata(client); switch (msp->opmode) { case OPMODE_MANUAL: case OPMODE_SIMPLE: autodetect_stereo(client); break; case OPMODE_SIMPLER: msp34xxg_detect_stereo(client); break; }}static void msp_any_set_audmode(struct i2c_client *client, int audmode){ struct msp3400c *msp = i2c_get_clientdata(client); switch (msp->opmode) { case OPMODE_MANUAL: case OPMODE_SIMPLE: msp->watch_stereo = 0; msp3400c_set_audmode(client, audmode); break; case OPMODE_SIMPLER: msp34xxg_set_audmode(client, audmode); break; }}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; break; default: if (*sarg & AUDIO_MUTE) msp3400c_set_scart(client,SCART_MUTE,0); break; } if (scart) { msp->rxsubchans = V4L2_TUNER_SUB_STEREO; msp->audmode = V4L2_TUNER_MODE_STEREO; msp3400c_set_scart(client,scart,0); msp3400c_write(client,I2C_MSP3400C_DFP,0x000d,0x1900); if (msp->opmode != OPMODE_SIMPLER) msp3400c_set_audmode(client, msp->audmode); } msp_wake_thread(client); break; case AUDC_SET_RADIO: dprintk(KERN_DEBUG "msp34xx: AUDC_SET_RADIO\n"); msp->norm = VIDEO_MODE_RADIO; dprintk(KERN_DEBUG "msp34xx: switching to radio mode\n"); msp->watch_stereo = 0; switch (msp->opmode) { case OPMODE_MANUAL: /* 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->volume, msp->balance); break; case OPMODE_SIMPLE: case OPMODE_SIMPLER: /* the thread will do for us */ msp_wake_thread(client); break; } break; /* --- 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 = msp->volume; va->balance = (va->volume) ? msp->balance : 32768; va->bass = msp->bass; va->treble = msp->treble; msp_any_detect_stereo(client); va->mode = mode_v4l2_to_v4l1(msp->rxsubchans); break; } case VIDIOCSAUDIO: { struct video_audio *va = arg; dprintk(KERN_DEBUG "msp34xx: VIDIOCSAUDIO\n"); msp->muted = (va->flags & VIDEO_AUDIO_MUTE); msp->volume = va->volume; msp->balance = va->balance; msp->bass = va->bass; msp->treble = va->treble; msp3400c_setvolume(client, msp->muted, msp->volume, msp->balance); msp3400c_setbass(client,msp->bass); msp3400c_settreble(client,msp->treble); if (va->mode != 0 && msp->norm != VIDEO_MODE_RADIO) msp_any_set_audmode(client,mode_v4l1_to_v4l2(va->mode)); break; } case VIDIOCSCHAN: { struct video_channel *vc = arg; dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN (norm=%d)\n",vc->norm); msp->norm = vc->norm; msp_wake_thread(client); break; } case VIDIOCSFREQ: case VIDIOC_S_FREQUENCY: { /* new channel -- kick audio carrier scan */ dprintk(KERN_DEBUG "msp34xx: VIDIOCSFREQ\n"); msp_wake_thread(client); break; } /* --- v4l2 ioctls --- */ case VIDIOC_G_TUNER: { struct v4l2_tuner *vt = arg; msp_any_detect_stereo(client); vt->audmode = msp->audmode; vt->rxsubchans = msp->rxsubchans; vt->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1| V4L2_TUNER_CAP_LANG2; break; } case VIDIOC_S_TUNER: { struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; /* only set audmode */ if (vt->audmode != -1 && vt->audmode != 0) msp_any_set_audmode(client, vt->audmode); break; } /* msp34xx specific */ case MSP_SET_MATRIX: { struct msp_matrix *mspm = arg; dprintk(KERN_DEBUG "msp34xx: MSP_SET_MATRIX\n"); msp3400c_set_scart(client, mspm->input, mspm->output); break; } default: /* nothing */ break; } return 0;}static int msp_suspend(struct device * dev, u32 state, u32 level){ struct i2c_client *c = container_of(dev, struct i2c_client, dev); dprintk("msp34xx: suspend\n"); msp3400c_reset(c); return 0;}static int msp_resume(struct device * dev, u32 level){ struct i2c_client *c = container_of(dev, struct i2c_client, dev); dprintk("msp34xx: resume\n"); msp_wake_thread(c); return 0;}/* ----------------------------------------------------------------------- */static int __init msp3400_init_module(void){ return i2c_add_driver(&driver);}static void __exit 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 + -