📄 saa7115.c
字号:
0x02, 0x84, /* input tuner -> input 4, amplifier active */ 0x09, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */ 0x80, 0x20, /* enable task B */ 0x88, 0xd0, 0x88, 0xf0, 0x00, 0x00};/* ============== SAA7715 AUDIO settings ============= *//* 48.0 kHz */static const unsigned char saa7115_cfg_48_audio[] = { 0x34, 0xce, 0x35, 0xfb, 0x36, 0x30, 0x00, 0x00};/* 44.1 kHz */static const unsigned char saa7115_cfg_441_audio[] = { 0x34, 0xf2, 0x35, 0x00, 0x36, 0x2d, 0x00, 0x00};/* 32.0 kHz */static const unsigned char saa7115_cfg_32_audio[] = { 0x34, 0xdf, 0x35, 0xa7, 0x36, 0x20, 0x00, 0x00};/* 48.0 kHz 60hz */static const unsigned char saa7115_cfg_60hz_48_audio[] = { 0x30, 0xcd, 0x31, 0x20, 0x32, 0x03, 0x00, 0x00};/* 48.0 kHz 50hz */static const unsigned char saa7115_cfg_50hz_48_audio[] = { 0x30, 0x00, 0x31, 0xc0, 0x32, 0x03, 0x00, 0x00};/* 44.1 kHz 60hz */static const unsigned char saa7115_cfg_60hz_441_audio[] = { 0x30, 0xbc, 0x31, 0xdf, 0x32, 0x02, 0x00, 0x00};/* 44.1 kHz 50hz */static const unsigned char saa7115_cfg_50hz_441_audio[] = { 0x30, 0x00, 0x31, 0x72, 0x32, 0x03, 0x00, 0x00};/* 32.0 kHz 60hz */static const unsigned char saa7115_cfg_60hz_32_audio[] = { 0x30, 0xde, 0x31, 0x15, 0x32, 0x02, 0x00, 0x00};/* 32.0 kHz 50hz */static const unsigned char saa7115_cfg_50hz_32_audio[] = { 0x30, 0x00, 0x31, 0x80, 0x32, 0x02, 0x00, 0x00};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, enum v4l2_audio_clock_freq freq){ struct saa7115_state *state = i2c_get_clientdata(client); saa7115_dbg("set audio clock freq: %d\n", freq); switch (freq) { case V4L2_AUDCLK_32_KHZ: saa7115_writeregs(client, saa7115_cfg_32_audio); if (state->std & V4L2_STD_525_60) { saa7115_writeregs(client, saa7115_cfg_60hz_32_audio); } else { saa7115_writeregs(client, saa7115_cfg_50hz_32_audio); } break; case V4L2_AUDCLK_441_KHZ: saa7115_writeregs(client, saa7115_cfg_441_audio); if (state->std & V4L2_STD_525_60) { saa7115_writeregs(client, saa7115_cfg_60hz_441_audio); } else { saa7115_writeregs(client, saa7115_cfg_50hz_441_audio); } break; case V4L2_AUDCLK_48_KHZ: saa7115_writeregs(client, saa7115_cfg_48_audio); if (state->std & V4L2_STD_525_60) { saa7115_writeregs(client, saa7115_cfg_60hz_48_audio); } else { saa7115_writeregs(client, saa7115_cfg_50hz_48_audio); } break; default: saa7115_dbg("invalid audio setting %d\n", freq); return -EINVAL; } 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) { saa7115_err("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) { saa7115_err("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) { saa7115_err("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) { saa7115_err("invalid hue setting %d\n", ctrl->value); return -ERANGE; } state->hue = ctrl->value; saa7115_write(client, 0x0d, state->hue); break; } 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; // This works for NTSC-M, SECAM-L and the 50Hz PAL variants. if (std & V4L2_STD_525_60) { saa7115_dbg("decoder set standard 60 Hz\n"); saa7115_writeregs(client, saa7115_cfg_60hz_video); } else { saa7115_dbg("decoder set standard 50 Hz\n"); saa7115_writeregs(client, saa7115_cfg_50hz_video); } state->std = std; /* restart task B if needed */ if (taskb && state->ident == V4L2_IDENT_SAA7114) { 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); char *audfreq = "undefined"; int reg1e, reg1f; int signalOk; int vcr; switch (state->audclk_freq) { case V4L2_AUDCLK_32_KHZ: audfreq = "32 kHz"; break; case V4L2_AUDCLK_441_KHZ: audfreq = "44.1 kHz"; break; case V4L2_AUDCLK_48_KHZ: audfreq = "48 kHz"; break; } saa7115_info("Audio frequency: %s\n", audfreq); if (client->name[6] == '4') { /* status for the saa7114 */ reg1f = saa7115_read(client, 0x1f); signalOk = (reg1f & 0xc1) == 0x81; saa7115_info("Video signal: %s\n", signalOk ? "ok" : "bad"); saa7115_info("Frequency: %s\n", (reg1f & 0x20) ? "60Hz" : "50Hz"); 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); saa7115_info("Video signal: %s\n", signalOk ? (vcr ? "VCR" : "broadcast/DVD") : "bad"); saa7115_info("Frequency: %s\n", (reg1f & 0x20) ? "60Hz" : "50Hz"); switch (reg1e & 0x03) { case 1: saa7115_info("Detected format: NTSC\n"); break; case 2: saa7115_info("Detected format: PAL\n"); break; case 3: saa7115_info("Detected format: SECAM\n"); break; default: saa7115_info("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; /* saa7114 doesn't yet support VBI */ if (state->ident == V4L2_IDENT_SAA7114) 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];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -