📄 msp3400-driver.c
字号:
#endif } }#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,12)) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)) if (current->flags & PF_FREEZE) { refrigerator (PF_FREEZE); }#endif remove_wait_queue(&state->wq, &wait);#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,12) try_to_freeze();#endif return state->restart;}/* ------------------------------------------------------------------------ */static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode){ if (rxsubchans == V4L2_TUNER_SUB_MONO) return VIDEO_SOUND_MONO; if (rxsubchans == V4L2_TUNER_SUB_STEREO) return VIDEO_SOUND_STEREO; if (audmode == V4L2_TUNER_MODE_LANG2) return VIDEO_SOUND_LANG2; return VIDEO_SOUND_LANG1;}static int msp_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 struct v4l2_queryctrl msp_qctrl_std[] = { { .id = V4L2_CID_AUDIO_VOLUME, .name = "Volume", .minimum = 0, .maximum = 65535, .step = 65535/100, .default_value = 58880, .flags = 0, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_AUDIO_MUTE, .name = "Mute", .minimum = 0, .maximum = 1, .step = 1, .default_value = 1, .flags = 0, .type = V4L2_CTRL_TYPE_BOOLEAN, },};static struct v4l2_queryctrl msp_qctrl_sound_processing[] = { { .id = V4L2_CID_AUDIO_BALANCE, .name = "Balance", .minimum = 0, .maximum = 65535, .step = 65535/100, .default_value = 32768, .flags = 0, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_AUDIO_BASS, .name = "Bass", .minimum = 0, .maximum = 65535, .step = 65535/100, .default_value = 32768, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_AUDIO_TREBLE, .name = "Treble", .minimum = 0, .maximum = 65535, .step = 65535/100, .default_value = 32768, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_AUDIO_LOUDNESS, .name = "Loudness", .minimum = 0, .maximum = 1, .step = 1, .default_value = 1, .flags = 0, .type = V4L2_CTRL_TYPE_BOOLEAN, },};static int msp_get_ctrl(struct i2c_client *client, struct v4l2_control *ctrl){ struct msp_state *state = i2c_get_clientdata(client); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: ctrl->value = state->volume; break; case V4L2_CID_AUDIO_MUTE: ctrl->value = state->muted; break; case V4L2_CID_AUDIO_BALANCE: if (!state->has_sound_processing) return -EINVAL; ctrl->value = state->balance; break; case V4L2_CID_AUDIO_BASS: if (!state->has_sound_processing) return -EINVAL; ctrl->value = state->bass; break; case V4L2_CID_AUDIO_TREBLE: if (!state->has_sound_processing) return -EINVAL; ctrl->value = state->treble; break; case V4L2_CID_AUDIO_LOUDNESS: if (!state->has_sound_processing) return -EINVAL; ctrl->value = state->loudness; break; default: return -EINVAL; } return 0;}static int msp_set_ctrl(struct i2c_client *client, struct v4l2_control *ctrl){ struct msp_state *state = i2c_get_clientdata(client); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: state->volume = ctrl->value; if (state->volume == 0) state->balance = 32768; break; case V4L2_CID_AUDIO_MUTE: if (ctrl->value < 0 || ctrl->value >= 2) return -ERANGE; state->muted = ctrl->value; break; case V4L2_CID_AUDIO_BASS: if (!state->has_sound_processing) return -EINVAL; state->bass = ctrl->value; break; case V4L2_CID_AUDIO_TREBLE: if (!state->has_sound_processing) return -EINVAL; state->treble = ctrl->value; break; case V4L2_CID_AUDIO_LOUDNESS: if (!state->has_sound_processing) return -EINVAL; state->loudness = ctrl->value; break; case V4L2_CID_AUDIO_BALANCE: if (!state->has_sound_processing) return -EINVAL; state->balance = ctrl->value; break; default: return -EINVAL; } msp_set_audio(client); return 0;}static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg){ struct msp_state *state = i2c_get_clientdata(client); if (msp_debug >= 2) v4l_i2c_print_ioctl(client, cmd); switch (cmd) { case AUDC_SET_RADIO: if (state->radio) return 0; state->radio = 1; v4l_dbg(1, msp_debug, client, "switching to radio mode\n"); state->watch_stereo = 0; switch (state->opmode) { case OPMODE_MANUAL: /* set msp3400 to FM radio mode */ msp3400c_set_mode(client, MSP_MODE_FM_RADIO); msp3400c_set_carrier(client, MSP_CARRIER(10.7), MSP_CARRIER(10.7)); msp_set_audio(client); break; case OPMODE_AUTODETECT: case OPMODE_AUTOSELECT: /* 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; va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_MUTABLE; if (state->has_sound_processing) va->flags |= VIDEO_AUDIO_BALANCE | VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE; if (state->muted) va->flags |= VIDEO_AUDIO_MUTE; va->volume = state->volume; va->balance = state->volume ? state->balance : 32768; va->bass = state->bass; va->treble = state->treble; if (state->radio) break; if (state->opmode == OPMODE_AUTOSELECT) msp_detect_stereo(client); va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans, state->audmode); break; } case VIDIOCSAUDIO: { struct video_audio *va = arg; state->muted = (va->flags & VIDEO_AUDIO_MUTE); state->volume = va->volume; state->balance = va->balance; state->bass = va->bass; state->treble = va->treble; msp_set_audio(client); if (va->mode != 0 && state->radio == 0 && state->audmode != msp_mode_v4l1_to_v4l2(va->mode)) { state->audmode = msp_mode_v4l1_to_v4l2(va->mode); msp_set_audmode(client); } break; } case VIDIOCSCHAN: { struct video_channel *vc = arg; int update = 0; v4l2_std_id std; if (state->radio) update = 1; state->radio = 0; if (vc->norm == VIDEO_MODE_PAL) std = V4L2_STD_PAL; else if (vc->norm == VIDEO_MODE_SECAM) std = V4L2_STD_SECAM; else std = V4L2_STD_NTSC; if (std != state->v4l2_std) { state->v4l2_std = std; update = 1; } if (update) msp_wake_thread(client); break; } case VIDIOCSFREQ: case VIDIOC_S_FREQUENCY: { /* new channel -- kick audio carrier scan */ msp_wake_thread(client); break; } /* --- v4l2 ioctls --- */ case VIDIOC_S_STD: { v4l2_std_id *id = arg; int update = state->radio || state->v4l2_std != *id; state->v4l2_std = *id; state->radio = 0; if (update) msp_wake_thread(client); return 0; } case VIDIOC_INT_G_AUDIO_ROUTING: { struct v4l2_routing *rt = arg; *rt = state->routing; break; } case VIDIOC_INT_S_AUDIO_ROUTING: { struct v4l2_routing *rt = arg; int tuner = (rt->input >> 3) & 1; int sc_in = rt->input & 0x7; int sc1_out = rt->output & 0xf; int sc2_out = (rt->output >> 4) & 0xf; u16 val, reg; int i; int extern_input = 1; if (state->routing.input == rt->input && state->routing.output == rt->output) break; state->routing = *rt; /* check if the tuner input is used */ for (i = 0; i < 5; i++) { if (((rt->input >> (4 + i * 4)) & 0xf) == 0) extern_input = 0; } if (extern_input) state->mode = MSP_MODE_EXTERN; else state->mode = MSP_MODE_AM_DETECT; msp_set_scart(client, sc_in, 0); msp_set_scart(client, sc1_out, 1); msp_set_scart(client, sc2_out, 2); msp_set_audmode(client); reg = (state->opmode == OPMODE_AUTOSELECT) ? 0x30 : 0xbb; val = msp_read_dem(client, reg); msp_write_dem(client, reg, (val & ~0x100) | (tuner << 8)); /* wake thread when a new input is chosen */ msp_wake_thread(client); break; } case VIDIOC_G_TUNER: { struct v4l2_tuner *vt = arg; if (state->radio) break; if (state->opmode == OPMODE_AUTOSELECT) msp_detect_stereo(client); vt->audmode = state->audmode; vt->rxsubchans = state->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; if (state->radio) /* TODO: add mono/stereo support for radio */ break; if (state->audmode == vt->audmode) break; state->audmode = vt->audmode; /* only set audmode */ msp_set_audmode(client); break; } case VIDIOC_INT_I2S_CLOCK_FREQ: { u32 *a = (u32 *)arg; v4l_dbg(1, msp_debug, client, "Setting I2S speed to %d\n", *a); switch (*a) { case 1024000: state->i2s_mode = 0; break; case 2048000: state->i2s_mode = 1; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -