📄 cx25840-core.c
字号:
/* Sets Luma and UV Low pass filters */ cx25840_write(client, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30)); /* Enables comb filters */ cx25840_write(client, 0x47b, comb); /* Sets SC Step*/ cx25840_write(client, 0x47c, sc); cx25840_write(client, 0x47d, 0xff & sc >> 8); cx25840_write(client, 0x47e, 0xff & sc >> 16); /* Sets VBI parameters */ if (std & V4L2_STD_625_50) { cx25840_write(client, 0x47f, 0x01); state->vbi_line_offset = 5; } else { cx25840_write(client, 0x47f, 0x00); state->vbi_line_offset = 8; }}/* ----------------------------------------------------------------------- */static void input_change(struct i2c_client *client){ struct cx25840_state *state = i2c_get_clientdata(client); v4l2_std_id std = state->std; /* Follow step 8c and 8d of section 3.16 in the cx25840 datasheet */ if (std & V4L2_STD_SECAM) { cx25840_write(client, 0x402, 0); } else { cx25840_write(client, 0x402, 0x04); cx25840_write(client, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11); } cx25840_and_or(client, 0x401, ~0x60, 0); cx25840_and_or(client, 0x401, ~0x60, 0x60); cx25840_and_or(client, 0x810, ~0x01, 1); if (state->radio) { cx25840_write(client, 0x808, 0xf9); cx25840_write(client, 0x80b, 0x00); } else if (std & V4L2_STD_525_60) { /* Certain Hauppauge PVR150 models have a hardware bug that causes audio to drop out. For these models the audio standard must be set explicitly. To be precise: it affects cards with tuner models 85, 99 and 112 (model numbers from tveeprom). */ int hw_fix = state->pvr150_workaround; if (std == V4L2_STD_NTSC_M_JP) { /* Japan uses EIAJ audio standard */ cx25840_write(client, 0x808, hw_fix ? 0x2f : 0xf7); } else if (std == V4L2_STD_NTSC_M_KR) { /* South Korea uses A2 audio standard */ cx25840_write(client, 0x808, hw_fix ? 0x3f : 0xf8); } else { /* Others use the BTSC audio standard */ cx25840_write(client, 0x808, hw_fix ? 0x1f : 0xf6); } cx25840_write(client, 0x80b, 0x00); } else if (std & V4L2_STD_PAL) { /* Follow tuner change procedure for PAL */ cx25840_write(client, 0x808, 0xff); cx25840_write(client, 0x80b, 0x10); } else if (std & V4L2_STD_SECAM) { /* Select autodetect for SECAM */ cx25840_write(client, 0x808, 0xff); cx25840_write(client, 0x80b, 0x10); } cx25840_and_or(client, 0x810, ~0x01, 0);}static int set_input(struct i2c_client *client, enum cx25840_video_input vid_input, enum cx25840_audio_input aud_input){ struct cx25840_state *state = i2c_get_clientdata(client); u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && vid_input <= CX25840_COMPOSITE8); u8 reg; v4l_dbg(1, cx25840_debug, client, "decoder set video input %d, audio input %d\n", vid_input, aud_input); if (vid_input >= CX25840_VIN1_CH1) { v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n", vid_input); reg = vid_input & 0xff; if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON) is_composite = 0; else is_composite = 1; v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", reg, is_composite); } else if (is_composite) { reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); } else { int luma = vid_input & 0xf0; int chroma = vid_input & 0xf00; if ((vid_input & ~0xff0) || luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { v4l_err(client, "0x%04x is not a valid video input!\n", vid_input); return -EINVAL; } reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4); if (chroma >= CX25840_SVIDEO_CHROMA7) { reg &= 0x3f; reg |= (chroma - CX25840_SVIDEO_CHROMA7) >> 2; } else { reg &= 0xcf; reg |= (chroma - CX25840_SVIDEO_CHROMA4) >> 4; } } /* The caller has previously prepared the correct routing * configuration in reg (for the cx23885) so we have no * need to attempt to flip bits for earlier av decoders. */ if (!state->is_cx23885) { switch (aud_input) { case CX25840_AUDIO_SERIAL: /* do nothing, use serial audio input */ break; case CX25840_AUDIO4: reg &= ~0x30; break; case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break; case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break; case CX25840_AUDIO7: reg &= ~0xc0; break; case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; default: v4l_err(client, "0x%04x is not a valid audio input!\n", aud_input); return -EINVAL; } } cx25840_write(client, 0x103, reg); /* Set INPUT_MODE to Composite (0) or S-Video (1) */ cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); if (!state->is_cx23885) { /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) cx25840_and_or(client, 0x102, ~0x4, 4); else cx25840_and_or(client, 0x102, ~0x4, 0); } else { if (is_composite) /* ADC2 input select channel 2 */ cx25840_and_or(client, 0x102, ~0x2, 0); else /* ADC2 input select channel 3 */ cx25840_and_or(client, 0x102, ~0x2, 2); } state->vid_input = vid_input; state->aud_input = aud_input; if (!state->is_cx25836) { cx25840_audio_set_path(client); input_change(client); } if (state->is_cx23885) { /* Audio channel 1 src : Parallel 1 */ cx25840_write(client, 0x124, 0x03); /* Select AFE clock pad output source */ cx25840_write(client, 0x144, 0x05); /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ cx25840_write(client, 0x914, 0xa0); /* I2S_OUT_CTL: * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 * I2S_OUT_MASTER_MODE = Master */ cx25840_write(client, 0x918, 0xa0); cx25840_write(client, 0x919, 0x01); } return 0;}/* ----------------------------------------------------------------------- */static int set_v4lstd(struct i2c_client *client){ struct cx25840_state *state = i2c_get_clientdata(client); u8 fmt = 0; /* zero is autodetect */ u8 pal_m = 0; /* First tests should be against specific std */ if (state->std == V4L2_STD_NTSC_M_JP) { fmt = 0x2; } else if (state->std == V4L2_STD_NTSC_443) { fmt = 0x3; } else if (state->std == V4L2_STD_PAL_M) { pal_m = 1; fmt = 0x5; } else if (state->std == V4L2_STD_PAL_N) { fmt = 0x6; } else if (state->std == V4L2_STD_PAL_Nc) { fmt = 0x7; } else if (state->std == V4L2_STD_PAL_60) { fmt = 0x8; } else { /* Then, test against generic ones */ if (state->std & V4L2_STD_NTSC) fmt = 0x1; else if (state->std & V4L2_STD_PAL) fmt = 0x4; else if (state->std & V4L2_STD_SECAM) fmt = 0xc; } v4l_dbg(1, cx25840_debug, client, "changing video std to fmt %i\n",fmt); /* Follow step 9 of section 3.16 in the cx25840 datasheet. Without this PAL may display a vertical ghosting effect. This happens for example with the Yuan MPC622. */ if (fmt >= 4 && fmt < 8) { /* Set format to NTSC-M */ cx25840_and_or(client, 0x400, ~0xf, 1); /* Turn off LCOMB */ cx25840_and_or(client, 0x47b, ~6, 0); } cx25840_and_or(client, 0x400, ~0xf, fmt); cx25840_and_or(client, 0x403, ~0x3, pal_m); cx25840_std_setup(client); if (!state->is_cx25836) input_change(client); return 0;}/* ----------------------------------------------------------------------- */static int set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl){ struct cx25840_state *state = i2c_get_clientdata(client); switch (ctrl->id) { case CX25840_CID_ENABLE_PVR150_WORKAROUND: state->pvr150_workaround = ctrl->value; set_input(client, state->vid_input, state->aud_input); break; case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { v4l_err(client, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } cx25840_write(client, 0x414, ctrl->value - 128); break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 127) { v4l_err(client, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } cx25840_write(client, 0x415, ctrl->value << 1); break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 127) { v4l_err(client, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } cx25840_write(client, 0x420, ctrl->value << 1); cx25840_write(client, 0x421, ctrl->value << 1); break; case V4L2_CID_HUE: if (ctrl->value < -127 || ctrl->value > 127) { v4l_err(client, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } cx25840_write(client, 0x422, ctrl->value); break; case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: if (state->is_cx25836) return -EINVAL; return cx25840_audio(client, VIDIOC_S_CTRL, ctrl); default: return -EINVAL; } return 0;}static int get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl){ struct cx25840_state *state = i2c_get_clientdata(client); switch (ctrl->id) { case CX25840_CID_ENABLE_PVR150_WORKAROUND: ctrl->value = state->pvr150_workaround; break; case V4L2_CID_BRIGHTNESS: ctrl->value = (s8)cx25840_read(client, 0x414) + 128; break; case V4L2_CID_CONTRAST: ctrl->value = cx25840_read(client, 0x415) >> 1; break; case V4L2_CID_SATURATION: ctrl->value = cx25840_read(client, 0x420) >> 1; break; case V4L2_CID_HUE: ctrl->value = (s8)cx25840_read(client, 0x422); break; case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: if (state->is_cx25836) return -EINVAL; return cx25840_audio(client, VIDIOC_G_CTRL, ctrl); default: return -EINVAL; } return 0;}/* ----------------------------------------------------------------------- */static int get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt){ switch (fmt->type) { case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: return cx25840_vbi(client, VIDIOC_G_FMT, fmt); default: return -EINVAL; } return 0;}static int set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt){ struct cx25840_state *state = i2c_get_clientdata(client); struct v4l2_pix_format *pix; int HSC, VSC, Vsrc, Hsrc, filter, Vlines; int is_50Hz = !(state->std & V4L2_STD_525_60); switch (fmt->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: pix = &(fmt->fmt.pix); Vsrc = (cx25840_read(client, 0x476) & 0x3f) << 4; Vsrc |= (cx25840_read(client, 0x475) & 0xf0) >> 4; Hsrc = (cx25840_read(client, 0x472) & 0x3f) << 4; Hsrc |= (cx25840_read(client, 0x471) & 0xf0) >> 4; Vlines = pix->height + (is_50Hz ? 4 : 7); if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) || (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) { v4l_err(client, "%dx%d is not a valid size!\n", pix->width, pix->height); return -ERANGE; } HSC = (Hsrc * (1 << 20)) / pix->width - (1 << 20); VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9)); VSC &= 0x1fff; if (pix->width >= 385) filter = 0; else if (pix->width > 192) filter = 1; else if (pix->width > 96) filter = 2; else filter = 3; v4l_dbg(1, cx25840_debug, client, "decoder set size %dx%d -> scale %ux%u\n", pix->width, pix->height, HSC, VSC); /* HSCALE=HSC */ cx25840_write(client, 0x418, HSC & 0xff); cx25840_write(client, 0x419, (HSC >> 8) & 0xff); cx25840_write(client, 0x41a, HSC >> 16); /* VSCALE=VSC */ cx25840_write(client, 0x41c, VSC & 0xff); cx25840_write(client, 0x41d, VSC >> 8); /* VS_INTRLACE=1 VFILT=filter */ cx25840_write(client, 0x41e, 0x8 | filter); break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: return cx25840_vbi(client, VIDIOC_S_FMT, fmt); case V4L2_BUF_TYPE_VBI_CAPTURE: return cx25840_vbi(client, VIDIOC_S_FMT, fmt); default: return -EINVAL; } return 0;}/* ----------------------------------------------------------------------- */static void log_video_status(struct i2c_client *client){ static const char *const fmt_strs[] = { "0x0", "NTSC-M", "NTSC-J", "NTSC-4.43", "PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60", "0x9", "0xA", "0xB", "SECAM", "0xD", "0xE", "0xF" }; struct cx25840_state *state = i2c_get_clientdata(client); u8 vidfmt_sel = cx25840_read(client, 0x400) & 0xf; u8 gen_stat1 = cx25840_read(client, 0x40d); u8 gen_stat2 = cx25840_read(client, 0x40e); int vid_input = state->vid_input; v4l_info(client, "Video signal: %spresent\n", (gen_stat2 & 0x20) ? "" : "not "); v4l_info(client, "Detected format: %s\n", fmt_strs[gen_stat1 & 0xf]); v4l_info(client, "Specified standard: %s\n", vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection"); if (vid_input >= CX25840_COMPOSITE1 && vid_input <= CX25840_COMPOSITE8) { v4l_info(client, "Specified video input: Composite %d\n", vid_input - CX25840_COMPOSITE1 + 1); } else { v4l_info(client, "Specified video input: S-Video (Luma In%d, Chroma In%d)\n", (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8); } v4l_info(client, "Specified audioclock freq: %d Hz\n", state->audclk_freq);}/* ----------------------------------------------------------------------- */static void log_audio_status(struct i2c_client *client){ struct cx25840_state *state = i2c_get_clientdata(client); u8 download_ctl = cx25840_read(client, 0x803); u8 mod_det_stat0 = cx25840_read(client, 0x804); u8 mod_det_stat1 = cx25840_read(client, 0x805); u8 audio_config = cx25840_read(client, 0x808); u8 pref_mode = cx25840_read(client, 0x809); u8 afc0 = cx25840_read(client, 0x80b); u8 mute_ctl = cx25840_read(client, 0x8d3); int aud_input = state->aud_input; char *p; switch (mod_det_stat0) { case 0x00: p = "mono"; break; case 0x01: p = "stereo"; break; case 0x02: p = "dual"; break; case 0x04: p = "tri"; break; case 0x10: p = "mono with SAP"; break; case 0x11: p = "stereo with SAP"; break; case 0x12: p = "dual with SAP"; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -