📄 saa7115.c
字号:
static int saa7115_odd_parity(u8 c){ c ^= (c >> 4); c ^= (c >> 2); c ^= (c >> 1); return c & 1;}static int saa7115_decode_vps(u8 * dst, u8 * p){ static const u8 biphase_tbl[] = { 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, 0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87, 0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3, 0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85, 0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1, 0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5, 0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1, 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, 0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86, 0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2, 0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84, 0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0, 0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4, 0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0, 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, 0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96, 0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2, 0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94, 0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0, 0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4, 0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0, }; int i; u8 c, err = 0; for (i = 0; i < 2 * 13; i += 2) { err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]]; c = (biphase_tbl[p[i + 1]] & 0xf) | ((biphase_tbl[p[i]] & 0xf) << 4); dst[i / 2] = c; } return err & 0xf0;}static int saa7115_decode_wss(u8 * p){ static const int wss_bits[8] = { 0, 0, 0, 1, 0, 1, 1, 1 }; unsigned char parity; int wss = 0; int i; for (i = 0; i < 16; i++) { int b1 = wss_bits[p[i] & 7]; int b2 = wss_bits[(p[i] >> 3) & 7]; if (b1 == b2) return -1; wss |= b2 << i; } parity = wss & 15; parity ^= parity >> 2; parity ^= parity >> 1; if (!(parity & 1)) return -1; return wss;}static int saa7115_set_audio_clock_freq(struct i2c_client *client, u32 freq){ struct saa7115_state *state = i2c_get_clientdata(client); u32 acpf; u32 acni; u32 hz; u64 f; u8 acc = 0; /* reg 0x3a, audio clock control */ v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq); /* sanity check */ if (freq < 32000 || freq > 48000) return -EINVAL; /* The saa7113 has no audio clock */ if (state->ident == V4L2_IDENT_SAA7113) return 0; /* hz is the refresh rate times 100 */ hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000; /* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */ acpf = (25600 * freq) / hz; /* acni = (256 * freq * 2^23) / crystal_frequency = (freq * 2^(8+23)) / crystal_frequency = (freq << 31) / crystal_frequency */ f = freq; f = f << 31; do_div(f, state->crystal_freq); acni = f; if (state->ucgc) { acpf = acpf * state->cgcdiv / 16; acni = acni * state->cgcdiv / 16; acc = 0x80; if (state->cgcdiv == 3) acc |= 0x40; } if (state->apll) acc |= 0x08; saa7115_write(client, 0x38, 0x03); saa7115_write(client, 0x39, 0x10); saa7115_write(client, 0x3a, acc); saa7115_write(client, 0x30, acpf & 0xff); saa7115_write(client, 0x31, (acpf >> 8) & 0xff); saa7115_write(client, 0x32, (acpf >> 16) & 0x03); saa7115_write(client, 0x34, acni & 0xff); saa7115_write(client, 0x35, (acni >> 8) & 0xff); saa7115_write(client, 0x36, (acni >> 16) & 0x3f); state->audclk_freq = freq; return 0;}static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl){ struct saa7115_state *state = i2c_get_clientdata(client); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (ctrl->value < 0 || ctrl->value > 255) { v4l_err(client, "invalid brightness setting %d\n", ctrl->value); return -ERANGE; } state->bright = ctrl->value; saa7115_write(client, 0x0a, state->bright); break; case V4L2_CID_CONTRAST: if (ctrl->value < 0 || ctrl->value > 127) { v4l_err(client, "invalid contrast setting %d\n", ctrl->value); return -ERANGE; } state->contrast = ctrl->value; saa7115_write(client, 0x0b, state->contrast); break; case V4L2_CID_SATURATION: if (ctrl->value < 0 || ctrl->value > 127) { v4l_err(client, "invalid saturation setting %d\n", ctrl->value); return -ERANGE; } state->sat = ctrl->value; saa7115_write(client, 0x0c, state->sat); break; case V4L2_CID_HUE: if (ctrl->value < -127 || ctrl->value > 127) { v4l_err(client, "invalid hue setting %d\n", ctrl->value); return -ERANGE; } state->hue = ctrl->value; saa7115_write(client, 0x0d, state->hue); break; default: return -EINVAL; } return 0;}static int saa7115_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl){ struct saa7115_state *state = i2c_get_clientdata(client); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: ctrl->value = state->bright; break; case V4L2_CID_CONTRAST: ctrl->value = state->contrast; break; case V4L2_CID_SATURATION: ctrl->value = state->sat; break; case V4L2_CID_HUE: ctrl->value = state->hue; break; default: return -EINVAL; } return 0;}static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std){ struct saa7115_state *state = i2c_get_clientdata(client); int taskb = saa7115_read(client, 0x80) & 0x10; /* Prevent unnecessary standard changes. During a standard change the I-Port is temporarily disabled. Any devices reading from that port can get confused. Note that VIDIOC_S_STD is also used to switch from radio to TV mode, so if a VIDIOC_S_STD is broadcast to all I2C devices then you do not want to have an unwanted side-effect here. */ if (std == state->std) return; // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. if (std & V4L2_STD_525_60) { v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n"); saa7115_writeregs(client, saa7115_cfg_60hz_video); } else { v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n"); saa7115_writeregs(client, saa7115_cfg_50hz_video); } /* Register 0E - Bits D6-D4 on NO-AUTO mode (SAA7113 doesn't have auto mode) 50 Hz / 625 lines 60 Hz / 525 lines 000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz) 001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz) 010 Combination-PAL N (3.58MHz) NTSC 4.43 (60 Hz) 011 NTSC N (3.58MHz) PAL M (3.58MHz) 100 reserved NTSC-Japan (3.58MHz) */ if (state->ident == V4L2_IDENT_SAA7113) { u8 reg = saa7115_read(client, 0x0e) & 0x8f; if (std == V4L2_STD_PAL_M) { reg |= 0x30; } else if (std == V4L2_STD_PAL_N) { reg |= 0x20; } else if (std == V4L2_STD_PAL_60) { reg |= 0x10; } else if (std == V4L2_STD_NTSC_M_JP) { reg |= 0x40; } saa7115_write(client, 0x0e, reg); } state->std = std; /* restart task B if needed */ if (taskb && state->ident != V4L2_IDENT_SAA7115) { saa7115_writeregs(client, saa7115_cfg_vbi_on); } /* switch audio mode too! */ saa7115_set_audio_clock_freq(client, state->audclk_freq);}static v4l2_std_id saa7115_get_v4lstd(struct i2c_client *client){ struct saa7115_state *state = i2c_get_clientdata(client); return state->std;}static void saa7115_log_status(struct i2c_client *client){ struct saa7115_state *state = i2c_get_clientdata(client); int reg1e, reg1f; int signalOk; int vcr; v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq); if (state->ident != V4L2_IDENT_SAA7115) { /* status for the saa7114 */ reg1f = saa7115_read(client, 0x1f); signalOk = (reg1f & 0xc1) == 0x81; v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad"); v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); return; } /* status for the saa7115 */ reg1e = saa7115_read(client, 0x1e); reg1f = saa7115_read(client, 0x1f); signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80; vcr = !(reg1f & 0x10); if (state->input >= 6) { v4l_info(client, "Input: S-Video %d\n", state->input - 6); } else { v4l_info(client, "Input: Composite %d\n", state->input); } v4l_info(client, "Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz"); switch (reg1e & 0x03) { case 1: v4l_info(client, "Detected format: NTSC\n"); break; case 2: v4l_info(client, "Detected format: PAL\n"); break; case 3: v4l_info(client, "Detected format: SECAM\n"); break; default: v4l_info(client, "Detected format: BW/No color\n"); break; }}/* setup the sliced VBI lcr registers according to the sliced VBI format */static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt){ struct saa7115_state *state = i2c_get_clientdata(client); int is_50hz = (state->std & V4L2_STD_625_50); u8 lcr[24]; int i, x; /* saa7113/7114 doesn't yet support VBI */ if (state->ident != V4L2_IDENT_SAA7115) return; for (i = 0; i <= 23; i++) lcr[i] = 0xff; if (fmt->service_set == 0) { /* raw VBI */ if (is_50hz) for (i = 6; i <= 23; i++) lcr[i] = 0xdd; else for (i = 10; i <= 21; i++) lcr[i] = 0xdd; } else { /* sliced VBI */ /* first clear lines that cannot be captured */ if (is_50hz) { for (i = 0; i <= 5; i++) fmt->service_lines[0][i] = fmt->service_lines[1][i] = 0; } else { for (i = 0; i <= 9; i++) fmt->service_lines[0][i] = fmt->service_lines[1][i] = 0; for (i = 22; i <= 23; i++) fmt->service_lines[0][i] = fmt->service_lines[1][i] = 0; } /* Now set the lcr values according to the specified service */ for (i = 6; i <= 23; i++) { lcr[i] = 0; for (x = 0; x <= 1; x++) { switch (fmt->service_lines[1-x][i]) { case 0: lcr[i] |= 0xf << (4 * x); break; case V4L2_SLICED_TELETEXT_B: lcr[i] |= 1 << (4 * x); break; case V4L2_SLICED_CAPTION_525: lcr[i] |= 4 << (4 * x); break; case V4L2_SLICED_WSS_625: lcr[i] |= 5 << (4 * x); break; case V4L2_SLICED_VPS: lcr[i] |= 7 << (4 * x); break; } } } } /* write the lcr registers */ for (i = 2; i <= 23; i++) { saa7115_write(client, i - 2 + 0x41, lcr[i]); } /* enable/disable raw VBI capturing */ saa7115_writeregs(client, fmt->service_set == 0 ? saa7115_cfg_vbi_on : saa7115_cfg_vbi_off);}static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt){ static u16 lcr2vbi[] = { 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ 0, V4L2_SLICED_CAPTION_525, /* 4 */ V4L2_SLICED_WSS_625, 0, /* 5 */ V4L2_SLICED_VPS, 0, 0, 0, 0, /* 7 */ 0, 0, 0, 0 }; struct v4l2_sliced_vbi_format *sliced = &fmt->fmt.sliced; int i; if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) return -EINVAL; memset(sliced, 0, sizeof(*sliced)); /* done if using raw VBI */ if (saa7115_read(client, 0x80) & 0x10) return 0; for (i = 2; i <= 23; i++) { u8 v = saa7115_read(client, i - 2 + 0x41); sliced->service_lines[0][i] = lcr2vbi[v >> 4]; sliced->service_lines[1][i] = lcr2vbi[v & 0xf]; sliced->service_set |= sliced->service_lines[0][i] | sliced->service_lines[1][i]; } return 0;}static int saa7115_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt){ struct saa7115_state *state = i2c_get_clientdata(client); struct v4l2_pix_format *pix; int HPSC, HFSC; int VSCY, Vsrc; int is_50hz = state->std & V4L2_STD_625_50; if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { saa7115_set_lcr(client, &fmt->fmt.sliced); return 0; } if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; pix = &(fmt->fmt.pix); v4l_dbg(1, debug, client, "decoder set size\n"); /* FIXME need better bounds checking here */ if ((pix->width < 1) || (pix->width > 1440)) return -EINVAL; if ((pix->height < 1) || (pix->height > 960)) return -EINVAL; /* probably have a valid size, let's set it */ /* Set output width/height */ /* width */ saa7115_write(client, 0xcc, (u8) (pix->width & 0xff)); saa7115_write(client, 0xcd, (u8) ((pix->width >> 8) & 0xff)); /* height */ saa7115_write(client, 0xce, (u8) (pix->height & 0xff)); saa7115_write(client, 0xcf, (u8) ((pix->height >> 8) & 0xff)); /* Scaling settings */ /* Hprescaler is floor(inres/outres) */ /* FIXME hardcoding input res */ if (pix->width != 720) { HPSC = (int)(720 / pix->width); /* 0 is not allowed (div. by zero) */ HPSC = HPSC ? HPSC : 1; HFSC = (int)((1024 * 720) / (HPSC * pix->width)); v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC); /* FIXME hardcodes to "Task B" * write H prescaler integer */ saa7115_write(client, 0xd0, (u8) (HPSC & 0x3f)); /* write H fine-scaling (luminance) */ saa7115_write(client, 0xd8, (u8) (HFSC & 0xff)); saa7115_write(client, 0xd9, (u8) ((HFSC >> 8) & 0xff)); /* write H fine-scaling (chrominance) * must be lum/2, so i'll just bitshift :) */ saa7115_write(client, 0xDC, (u8) ((HFSC >> 1) & 0xff)); saa7115_write(client, 0xDD, (u8) ((HFSC >> 9) & 0xff)); } else { if (is_50hz) { v4l_dbg(1, debug, client, "Setting full 50hz width\n"); saa7115_writeregs(client, saa7115_cfg_50hz_fullres_x); } else { v4l_dbg(1, debug, client, "Setting full 60hz width\n"); saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x); } } Vsrc = is_50hz ? 576 : 480; if (pix->height != Vsrc) { VSCY = (int)((1024 * Vsrc) / pix->height); v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); /* Correct Contrast and Luminance */ saa7115_write(client, 0xd5, (u8) (64 * 1024 / VSCY)); saa7115_write(client, 0xd6, (u8) (64 * 1024 / VSCY));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -