📄 msp3400.c
字号:
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, client, "msp34xx"); if (NULL == msp->kthread) msp3400_warn("kernel_thread() failed\n"); msp_wake_thread(client); } /* done */ i2c_attach_client(client); /* update our own array */ for (i = 0; i < MSP3400_MAX; i++) { if (NULL == msps[i]) { msps[i] = client; break; } } return 0;}static int msp_detach(struct i2c_client *client){ struct msp3400c *msp = i2c_get_clientdata(client); int i; /* shutdown control thread */ if (msp->kthread) { msp->restart = 1; kthread_stop(msp->kthread); } 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); 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_setstereo(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: msp3400_dbg("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_setstereo(client, msp->audmode); } msp_wake_thread(client); break; case AUDC_SET_RADIO: msp3400_dbg("AUDC_SET_RADIO\n"); msp->norm = VIDEO_MODE_RADIO; msp3400_dbg("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->left, msp->right); break; case OPMODE_SIMPLE: case OPMODE_SIMPLER: /* the thread will do for us */ msp_wake_thread(client); break; } break; /* 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; } /* --- v4l ioctls --- */ /* take care: bttv does userspace copying, we'll get a kernel pointer here... */ case VIDIOCGAUDIO: { struct video_audio *va = arg; msp3400_dbg("VIDIOCGAUDIO\n"); va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE | VIDEO_AUDIO_MUTABLE; if (msp->muted) va->flags |= VIDEO_AUDIO_MUTE; 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; msp_any_detect_stereo(client); va->mode = mode_v4l2_to_v4l1(msp->rxsubchans); break; } case VIDIOCSAUDIO: { struct video_audio *va = arg; msp3400_dbg("VIDIOCSAUDIO\n"); 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; msp3400_dbg("VIDIOCSAUDIO setting va->volume to %d\n", va->volume); msp3400_dbg("VIDIOCSAUDIO setting va->balance to %d\n", va->balance); msp3400_dbg("VIDIOCSAUDIO setting va->flags to %d\n", va->flags); msp3400_dbg("VIDIOCSAUDIO setting msp->left to %d\n", msp->left); msp3400_dbg("VIDIOCSAUDIO setting msp->right to %d\n", msp->right); msp3400_dbg("VIDIOCSAUDIO setting msp->bass to %d\n", msp->bass); msp3400_dbg("VIDIOCSAUDIO setting msp->treble to %d\n", msp->treble); msp3400_dbg("VIDIOCSAUDIO setting msp->mode to %d\n", msp->mode); 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_any_set_audmode(client,mode_v4l1_to_v4l2(va->mode)); break; } case VIDIOCSCHAN: { struct video_channel *vc = arg; msp3400_dbg("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 */ msp3400_dbg("VIDIOCSFREQ\n"); msp_wake_thread(client); break; } /* msp34xx specific */ case MSP_SET_MATRIX: { struct msp_matrix *mspm = arg; msp3400_dbg("MSP_SET_MATRIX\n"); msp3400c_set_scart(client, mspm->input, mspm->output); break; } /* --- v4l2 ioctls --- */ case VIDIOC_S_STD: { v4l2_std_id *id = arg; /*FIXME: use V4L2 mode flags on msp3400 instead of V4L1*/ if (*id & V4L2_STD_PAL) { msp->norm=VIDEO_MODE_PAL; } else if (*id & V4L2_STD_SECAM) { msp->norm=VIDEO_MODE_SECAM; } else { msp->norm=VIDEO_MODE_NTSC; } msp_wake_thread(client); return 0; } case VIDIOC_ENUMINPUT: { struct v4l2_input *i = arg; if (i->index != 0) return -EINVAL; i->type = V4L2_INPUT_TYPE_TUNER; switch (i->index) { case AUDIO_RADIO: strcpy(i->name,"Radio"); break; case AUDIO_EXTERN_1: strcpy(i->name,"Extern 1"); break; case AUDIO_EXTERN_2: strcpy(i->name,"Extern 2"); break; case AUDIO_TUNER: strcpy(i->name,"Television"); break; default: return -EINVAL; } return 0; } case VIDIOC_G_AUDIO: { struct v4l2_audio *a = arg; memset(a,0,sizeof(*a)); switch (a->index) { case AUDIO_RADIO: strcpy(a->name,"Radio"); break; case AUDIO_EXTERN_1: strcpy(a->name,"Extern 1"); break; case AUDIO_EXTERN_2: strcpy(a->name,"Extern 2"); break; case AUDIO_TUNER: strcpy(a->name,"Television"); break; default: return -EINVAL; } msp_any_detect_stereo(client); if (msp->audmode == V4L2_TUNER_MODE_STEREO) { a->capability=V4L2_AUDCAP_STEREO; } break; } case VIDIOC_S_AUDIO: { struct v4l2_audio *sarg = arg; switch (sarg->index) { 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; } 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 (sarg->capability==V4L2_AUDCAP_STEREO) { msp->audmode = V4L2_TUNER_MODE_STEREO; } else { msp->audmode &= ~V4L2_TUNER_MODE_STEREO; } msp_any_set_audmode(client, msp->audmode); msp_wake_thread(client); break; } 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; } case VIDIOC_G_AUDOUT: { struct v4l2_audioout *a=(struct v4l2_audioout *)arg; int idx=a->index; memset(a,0,sizeof(*a)); switch (idx) { case 0: strcpy(a->name,"Scart1 Out"); break; case 1: strcpy(a->name,"Scart2 Out"); break; case 2: strcpy(a->name,"I2S Out"); break; default: return -EINVAL; } break; } case VIDIOC_S_AUDOUT: { struct v4l2_audioout *a=(struct v4l2_audioout *)arg; if (a->index<0||a->index>2) return -EINVAL; if (a->index==2) { if (a->mode == V4L2_AUDMODE_32BITS) msp->i2s_mode=1; else msp->i2s_mode=0; } msp3400_dbg("Setting audio out on msp34xx to input %i, mode %i\n", a->index,msp->i2s_mode); msp3400c_set_scart(client,msp->in_scart,a->index+1); break; } default: /* nothing */ break; } return 0;}static int msp_suspend(struct device * dev, pm_message_t state){ struct i2c_client *client = container_of(dev, struct i2c_client, dev); msp3400_dbg("msp34xx: suspend\n"); msp3400c_reset(client); return 0;}static int msp_resume(struct device * dev){ struct i2c_client *client = container_of(dev, struct i2c_client, dev); msp3400_dbg("msp34xx: resume\n"); msp_wake_thread(client); 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 + -