📄 saa7115.c
字号:
} 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); saa7115_dbg("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)); saa7115_dbg("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) { saa7115_dbg("Setting full 50hz width\n"); saa7115_writeregs(client, saa7115_cfg_50hz_fullres_x); } else { saa7115_dbg("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); saa7115_dbg("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)); /* write V fine-scaling (luminance) */ saa7115_write(client, 0xe0, (u8) (VSCY & 0xff)); saa7115_write(client, 0xe1, (u8) ((VSCY >> 8) & 0xff)); /* write V fine-scaling (chrominance) */ saa7115_write(client, 0xe2, (u8) (VSCY & 0xff)); saa7115_write(client, 0xe3, (u8) ((VSCY >> 8) & 0xff)); } else { if (is_50hz) { saa7115_dbg("Setting full 50Hz height\n"); saa7115_writeregs(client, saa7115_cfg_50hz_fullres_y); } else { saa7115_dbg("Setting full 60hz height\n"); saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y); } } saa7115_writeregs(client, saa7115_cfg_reset_scaler); return 0;}/* Decode the sliced VBI data stream as created by the saa7115. The format is described in the saa7115 datasheet in Tables 25 and 26 and in Figure 33. The current implementation uses SAV/EAV codes and not the ancillary data headers. The vbi->p pointer points to the SDID byte right after the SAV code. */static void saa7115_decode_vbi_line(struct i2c_client *client, struct v4l2_decode_vbi_line *vbi){ static const char vbi_no_data_pattern[] = { 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0 }; struct saa7115_state *state = i2c_get_clientdata(client); u8 *p = vbi->p; u32 wss; int id1, id2; /* the ID1 and ID2 bytes from the internal header */ vbi->type = 0; /* mark result as a failure */ id1 = p[2]; id2 = p[3]; /* Note: the field bit is inverted for 60 Hz video */ if (state->std & V4L2_STD_525_60) id1 ^= 0x40; /* Skip internal header, p now points to the start of the payload */ p += 4; vbi->p = p; /* calculate field and line number of the VBI packet (1-23) */ vbi->is_second_field = ((id1 & 0x40) != 0); vbi->line = (id1 & 0x3f) << 3; vbi->line |= (id2 & 0x70) >> 4; /* Obtain data type */ id2 &= 0xf; /* If the VBI slicer does not detect any signal it will fill up the payload buffer with 0xa0 bytes. */ if (!memcmp(p, vbi_no_data_pattern, sizeof(vbi_no_data_pattern))) return; /* decode payloads */ switch (id2) { case 1: vbi->type = V4L2_SLICED_TELETEXT_B; break; case 4: if (!saa7115_odd_parity(p[0]) || !saa7115_odd_parity(p[1])) return; vbi->type = V4L2_SLICED_CAPTION_525; break; case 5: wss = saa7115_decode_wss(p); if (wss == -1) return; p[0] = wss & 0xff; p[1] = wss >> 8; vbi->type = V4L2_SLICED_WSS_625; break; case 7: if (saa7115_decode_vps(p, p) != 0) return; vbi->type = V4L2_SLICED_VPS; break; default: return; }}/* ============ SAA7115 AUDIO settings (end) ============= */static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg){ struct saa7115_state *state = i2c_get_clientdata(client); int *iarg = arg; /* ioctls to allow direct access to the saa7115 registers for testing */ switch (cmd) { case VIDIOC_S_FMT: return saa7115_set_v4lfmt(client, (struct v4l2_format *)arg); case VIDIOC_G_FMT: return saa7115_get_v4lfmt(client, (struct v4l2_format *)arg); case VIDIOC_INT_AUDIO_CLOCK_FREQ: return saa7115_set_audio_clock_freq(client, *(enum v4l2_audio_clock_freq *)arg); case VIDIOC_G_TUNER: { struct v4l2_tuner *vt = arg; int status; status = saa7115_read(client, 0x1f); saa7115_dbg("status: 0x%02x\n", status); vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0; break; } case VIDIOC_LOG_STATUS: saa7115_log_status(client); break; case VIDIOC_G_CTRL: return saa7115_get_v4lctrl(client, (struct v4l2_control *)arg); case VIDIOC_S_CTRL: return saa7115_set_v4lctrl(client, (struct v4l2_control *)arg); case VIDIOC_G_STD: *(v4l2_std_id *)arg = saa7115_get_v4lstd(client); break; case VIDIOC_S_STD: saa7115_set_v4lstd(client, *(v4l2_std_id *)arg); break; case VIDIOC_G_INPUT: *(int *)arg = state->input; break; case VIDIOC_S_INPUT: saa7115_dbg("decoder set input %d\n", *iarg); /* inputs from 0-9 are available */ if (*iarg < 0 || *iarg > 9) { return -EINVAL; } if (state->input == *iarg) break; saa7115_dbg("now setting %s input\n", *iarg >= 6 ? "S-Video" : "Composite"); state->input = *iarg; /* select mode */ saa7115_write(client, 0x02, (saa7115_read(client, 0x02) & 0xf0) | state->input); /* bypass chrominance trap for modes 6..9 */ saa7115_write(client, 0x09, (saa7115_read(client, 0x09) & 0x7f) | (state->input < 6 ? 0x0 : 0x80)); break; case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: saa7115_dbg("%s output\n", (cmd == VIDIOC_STREAMON) ? "enable" : "disable"); if (state->enable != (cmd == VIDIOC_STREAMON)) { state->enable = (cmd == VIDIOC_STREAMON); saa7115_write(client, 0x87, state->enable); } break; case VIDIOC_INT_DECODE_VBI_LINE: saa7115_decode_vbi_line(client, arg); break; case VIDIOC_INT_RESET: saa7115_dbg("decoder RESET\n"); saa7115_writeregs(client, saa7115_cfg_reset_scaler); break; case VIDIOC_INT_G_VBI_DATA: { struct v4l2_sliced_vbi_data *data = arg; switch (data->id) { case V4L2_SLICED_WSS_625: if (saa7115_read(client, 0x6b) & 0xc0) return -EIO; data->data[0] = saa7115_read(client, 0x6c); data->data[1] = saa7115_read(client, 0x6d); return 0; case V4L2_SLICED_CAPTION_525: if (data->field == 0) { /* CC */ if (saa7115_read(client, 0x66) & 0xc0) return -EIO; data->data[0] = saa7115_read(client, 0x67); data->data[1] = saa7115_read(client, 0x68); return 0; } /* XDS */ if (saa7115_read(client, 0x66) & 0x30) return -EIO; data->data[0] = saa7115_read(client, 0x69); data->data[1] = saa7115_read(client, 0x6a); return 0; default: return -EINVAL; } break; }#ifdef CONFIG_VIDEO_ADV_DEBUG case VIDIOC_INT_G_REGISTER: { struct v4l2_register *reg = arg; if (reg->i2c_id != I2C_DRIVERID_SAA711X) return -EINVAL; reg->val = saa7115_read(client, reg->reg & 0xff); break; } case VIDIOC_INT_S_REGISTER: { struct v4l2_register *reg = arg; if (reg->i2c_id != I2C_DRIVERID_SAA711X) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; saa7115_write(client, reg->reg & 0xff, reg->val & 0xff); break; }#endif case VIDIOC_INT_G_CHIP_IDENT: *iarg = state->ident; break; default: return -EINVAL; } return 0;}/* ----------------------------------------------------------------------- */static struct i2c_driver i2c_driver_saa7115;static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind){ struct i2c_client *client; struct saa7115_state *state; u8 chip_id; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return 0; client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); if (client == 0) return -ENOMEM; memset(client, 0, sizeof(struct i2c_client)); client->addr = address; client->adapter = adapter; client->driver = &i2c_driver_saa7115; client->flags = I2C_CLIENT_ALLOW_USE; snprintf(client->name, sizeof(client->name) - 1, "saa7115"); saa7115_dbg("detecting saa7115 client on address 0x%x\n", address << 1); saa7115_write(client, 0, 5); chip_id = saa7115_read(client, 0) & 0x0f; if (chip_id != 4 && chip_id != 5) { saa7115_dbg("saa7115 not found\n"); kfree(client); return 0; } if (chip_id == 4) { snprintf(client->name, sizeof(client->name) - 1, "saa7114"); } saa7115_info("saa711%d found @ 0x%x (%s)\n", chip_id, address << 1, adapter->name); state = kmalloc(sizeof(struct saa7115_state), GFP_KERNEL); i2c_set_clientdata(client, state); if (state == NULL) { kfree(client); return -ENOMEM; } memset(state, 0, sizeof(struct saa7115_state)); state->std = V4L2_STD_NTSC; state->input = -1; state->enable = 1; state->bright = 128; state->contrast = 64; state->hue = 0; state->sat = 64; state->ident = (chip_id == 4) ? V4L2_IDENT_SAA7114 : V4L2_IDENT_SAA7115; state->audclk_freq = V4L2_AUDCLK_48_KHZ; saa7115_dbg("writing init values\n"); /* init to 60hz/48khz */ saa7115_writeregs(client, saa7115_init_auto_input); saa7115_writeregs(client, saa7115_init_misc); saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x); saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y); saa7115_writeregs(client, saa7115_cfg_60hz_video); saa7115_writeregs(client, saa7115_cfg_48_audio); saa7115_writeregs(client, saa7115_cfg_60hz_48_audio); saa7115_writeregs(client, saa7115_cfg_reset_scaler); i2c_attach_client(client); saa7115_dbg("status: (1E) 0x%02x, (1F) 0x%02x\n", saa7115_read(client, 0x1e), saa7115_read(client, 0x1f)); return 0;}static int saa7115_probe(struct i2c_adapter *adapter){#ifdef I2C_CLASS_TV_ANALOG if (adapter->class & I2C_CLASS_TV_ANALOG)#else if (adapter->id == I2C_HW_B_BT848)#endif return i2c_probe(adapter, &addr_data, &saa7115_attach); return 0;}static int saa7115_detach(struct i2c_client *client){ struct saa7115_state *state = i2c_get_clientdata(client); int err; err = i2c_detach_client(client); if (err) { return err; } kfree(state); kfree(client); return 0;}/* ----------------------------------------------------------------------- *//* i2c implementation */static struct i2c_driver i2c_driver_saa7115 = { .name = "saa7115", .id = I2C_DRIVERID_SAA711X, .flags = I2C_DF_NOTIFY, .attach_adapter = saa7115_probe, .detach_client = saa7115_detach, .command = saa7115_command, .owner = THIS_MODULE,};static int __init saa7115_init_module(void){ return i2c_add_driver(&i2c_driver_saa7115);}static void __exit saa7115_cleanup_module(void){ i2c_del_driver(&i2c_driver_saa7115);}module_init(saa7115_init_module);module_exit(saa7115_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -