📄 saa7127.c
字号:
(enable << 7) | (state->cc_enable << 6) | 0x11); state->xds_enable = enable; } if (!enable) return 0; saa7127_dbg_highvol("XDS data: %04x\n", xds); saa7127_write(client, SAA7127_REG_LINE_21_EVEN_0, xds & 0xff); saa7127_write(client, SAA7127_REG_LINE_21_EVEN_1, xds >> 8); state->xds_data = xds; return 0;}/* ----------------------------------------------------------------------- */static int saa7127_set_wss(struct i2c_client *client, struct v4l2_sliced_vbi_data *data){ struct saa7127_state *state = i2c_get_clientdata(client); int enable = (data->line != 0); if (enable && (data->field != 0 || data->line != 23)) return -EINVAL; if (state->wss_enable != enable) { saa7127_dbg("Turn WSS %s\n", enable ? "on" : "off"); saa7127_write(client, 0x27, enable << 7); state->wss_enable = enable; } if (!enable) return 0; saa7127_write(client, 0x26, data->data[0]); saa7127_write(client, 0x27, 0x80 | (data->data[1] & 0x3f)); saa7127_dbg("WSS mode: %s\n", wss_strs[data->data[0] & 0xf]); state->wss_mode = (data->data[1] & 0x3f) << 8 | data->data[0]; return 0;}/* ----------------------------------------------------------------------- */static int saa7127_set_video_enable(struct i2c_client *client, int enable){ struct saa7127_state *state = i2c_get_clientdata(client); if (enable) { saa7127_dbg("Enable Video Output\n"); saa7127_write(client, 0x2d, state->reg_2d); saa7127_write(client, 0x61, state->reg_61); } else { saa7127_dbg("Disable Video Output\n"); saa7127_write(client, 0x2d, (state->reg_2d & 0xf0)); saa7127_write(client, 0x61, (state->reg_61 | 0xc0)); } state->video_enable = enable; return 0;}/* ----------------------------------------------------------------------- */static int saa7127_set_std(struct i2c_client *client, v4l2_std_id std){ struct saa7127_state *state = i2c_get_clientdata(client); const struct i2c_reg_value *inittab; if (std & V4L2_STD_525_60) { saa7127_dbg("Selecting 60 Hz video Standard\n"); inittab = saa7127_init_config_60hz; state->reg_61 = SAA7127_60HZ_DAC_CONTROL; } else { saa7127_dbg("Selecting 50 Hz video Standard\n"); inittab = saa7127_init_config_50hz; state->reg_61 = SAA7127_50HZ_DAC_CONTROL; } /* Write Table */ saa7127_write_inittab(client, inittab); state->std = std; return 0;}/* ----------------------------------------------------------------------- */static int saa7127_set_output_type(struct i2c_client *client, int output){ struct saa7127_state *state = i2c_get_clientdata(client); switch (output) { case SAA7127_OUTPUT_TYPE_RGB: state->reg_2d = 0x0f; /* RGB + CVBS (for sync) */ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ break; case SAA7127_OUTPUT_TYPE_COMPOSITE: state->reg_2d = 0x08; /* 00001000 CVBS only, RGB DAC's off (high impedance mode) */ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ break; case SAA7127_OUTPUT_TYPE_SVIDEO: state->reg_2d = 0xff; /* 11111111 croma -> R, luma -> CVBS + G + B */ state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ break; case SAA7127_OUTPUT_TYPE_YUV_V: state->reg_2d = 0x4f; /* reg 2D = 01001111, all DAC's on, RGB + VBS */ state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ break; case SAA7127_OUTPUT_TYPE_YUV_C: state->reg_2d = 0x0f; /* reg 2D = 00001111, all DAC's on, RGB + CVBS */ state->reg_3a = 0x0b; /* reg 3A = 00001011, bypass RGB-matrix */ break; case SAA7127_OUTPUT_TYPE_BOTH: state->reg_2d = 0xbf; state->reg_3a = 0x13; /* by default switch YUV to RGB-matrix on */ break; default: return -EINVAL; } saa7127_dbg("Selecting %s output type\n", output_strs[output]); /* Configure Encoder */ saa7127_write(client, 0x2d, state->reg_2d); saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb); state->output_type = output; return 0;}/* ----------------------------------------------------------------------- */static int saa7127_set_input_type(struct i2c_client *client, int input){ struct saa7127_state *state = i2c_get_clientdata(client); switch (input) { case SAA7127_INPUT_TYPE_NORMAL: /* avia */ saa7127_dbg("Selecting Normal Encoder Input\n"); state->reg_3a_cb = 0; break; case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ saa7127_dbg("Selecting Color Bar generator\n"); state->reg_3a_cb = 0x80; break; default: return -EINVAL; } saa7127_write(client, 0x3a, state->reg_3a | state->reg_3a_cb); state->input_type = input; return 0;}/* ----------------------------------------------------------------------- */static int saa7127_command(struct i2c_client *client, unsigned int cmd, void *arg){ struct saa7127_state *state = i2c_get_clientdata(client); struct v4l2_format *fmt = arg; int *iarg = arg; switch (cmd) { case VIDIOC_S_STD: if (state->std == *(v4l2_std_id *)arg) break; return saa7127_set_std(client, *(v4l2_std_id *)arg); case VIDIOC_G_STD: *(v4l2_std_id *)arg = state->std; break; case VIDIOC_S_INPUT: if (state->input_type == *iarg) break; return saa7127_set_input_type(client, *iarg); case VIDIOC_S_OUTPUT: if (state->output_type == *iarg) break; return saa7127_set_output_type(client, *iarg); case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: if (state->video_enable == (cmd == VIDIOC_STREAMON)) break; return saa7127_set_video_enable(client, cmd == VIDIOC_STREAMON); case VIDIOC_G_FMT: if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) return -EINVAL; memset(&fmt->fmt.sliced, 0, sizeof(fmt->fmt.sliced)); if (state->vps_enable) fmt->fmt.sliced.service_lines[0][16] = V4L2_SLICED_VPS; if (state->wss_enable) fmt->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625; if (state->cc_enable) { fmt->fmt.sliced.service_lines[0][21] = V4L2_SLICED_CAPTION_525; fmt->fmt.sliced.service_lines[1][21] = V4L2_SLICED_CAPTION_525; } fmt->fmt.sliced.service_set = (state->vps_enable ? V4L2_SLICED_VPS : 0) | (state->wss_enable ? V4L2_SLICED_WSS_625 : 0) | (state->cc_enable ? V4L2_SLICED_CAPTION_525 : 0); break; case VIDIOC_LOG_STATUS: saa7127_info("Standard: %s\n", (state->std & V4L2_STD_525_60) ? "60 Hz" : "50 Hz"); saa7127_info("Input: %s\n", state->input_type ? "color bars" : "normal"); saa7127_info("Output: %s\n", state->video_enable ? output_strs[state->output_type] : "disabled"); saa7127_info("WSS: %s\n", state->wss_enable ? wss_strs[state->wss_mode] : "disabled"); saa7127_info("VPS: %s\n", state->vps_enable ? "enabled" : "disabled"); saa7127_info("CC: %s\n", state->cc_enable ? "enabled" : "disabled"); break;#ifdef CONFIG_VIDEO_ADV_DEBUG case VIDIOC_INT_G_REGISTER: { struct v4l2_register *reg = arg; if (reg->i2c_id != I2C_DRIVERID_SAA7127) return -EINVAL; reg->val = saa7127_read(client, reg->reg & 0xff); break; } case VIDIOC_INT_S_REGISTER: { struct v4l2_register *reg = arg; if (reg->i2c_id != I2C_DRIVERID_SAA7127) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; saa7127_write(client, reg->reg & 0xff, reg->val & 0xff); break; }#endif case VIDIOC_INT_S_VBI_DATA: { struct v4l2_sliced_vbi_data *data = arg; switch (data->id) { case V4L2_SLICED_WSS_625: return saa7127_set_wss(client, data); case V4L2_SLICED_VPS: return saa7127_set_vps(client, data); case V4L2_SLICED_CAPTION_525: if (data->field == 0) return saa7127_set_cc(client, data); return saa7127_set_xds(client, data); default: return -EINVAL; } break; } case VIDIOC_INT_G_CHIP_IDENT: *(enum v4l2_chip_ident *)arg = state->ident; break; default: return -EINVAL; } return 0;}/* ----------------------------------------------------------------------- */static struct i2c_driver i2c_driver_saa7127;/* ----------------------------------------------------------------------- */static int saa7127_attach(struct i2c_adapter *adapter, int address, int kind){ struct i2c_client *client; struct saa7127_state *state; struct v4l2_sliced_vbi_data vbi = { 0, 0, 0, 0 }; /* set to disabled */ int read_result = 0; /* 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_saa7127; client->flags = I2C_CLIENT_ALLOW_USE; snprintf(client->name, sizeof(client->name) - 1, "saa7127"); saa7127_dbg("detecting saa7127 client on address 0x%x\n", address << 1); /* First test register 0: Bits 5-7 are a version ID (should be 0), and bit 2 should also be 0. This is rather general, so the second test is more specific and looks at the 'ending point of burst in clock cycles' which is 0x1d after a reset and not expected to ever change. */ if ((saa7127_read(client, 0) & 0xe4) != 0 || (saa7127_read(client, 0x29) & 0x3f) != 0x1d) { saa7127_dbg("saa7127 not found\n"); kfree(client); return 0; } state = kmalloc(sizeof(struct saa7127_state), GFP_KERNEL); if (state == NULL) { kfree(client); return (-ENOMEM); } i2c_set_clientdata(client, state); memset(state, 0, sizeof(struct saa7127_state)); /* Configure Encoder */ saa7127_dbg("Configuring encoder\n"); saa7127_write_inittab(client, saa7127_init_config_common); saa7127_set_std(client, V4L2_STD_NTSC); saa7127_set_output_type(client, SAA7127_OUTPUT_TYPE_BOTH); saa7127_set_vps(client, &vbi); saa7127_set_wss(client, &vbi); saa7127_set_cc(client, &vbi); saa7127_set_xds(client, &vbi); if (test_image == 1) { /* The Encoder has an internal Colorbar generator */ /* This can be used for debugging */ saa7127_set_input_type(client, SAA7127_INPUT_TYPE_TEST_IMAGE); } else { saa7127_set_input_type(client, SAA7127_INPUT_TYPE_NORMAL); } saa7127_set_video_enable(client, 1); /* Detect if it's an saa7129 */ read_result = saa7127_read(client, SAA7129_REG_FADE_KEY_COL2); saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, 0xaa); if (saa7127_read(client, SAA7129_REG_FADE_KEY_COL2) == 0xaa) { saa7127_info("saa7129 found @ 0x%x (%s)\n", address << 1, adapter->name); saa7127_write(client, SAA7129_REG_FADE_KEY_COL2, read_result); saa7127_write_inittab(client, saa7129_init_config_extra); state->ident = V4L2_IDENT_SAA7129; } else { saa7127_info("saa7127 found @ 0x%x (%s)\n", address << 1, adapter->name); state->ident = V4L2_IDENT_SAA7127; } i2c_attach_client(client); return 0;}/* ----------------------------------------------------------------------- */static int saa7127_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, saa7127_attach); return 0;}/* ----------------------------------------------------------------------- */static int saa7127_detach(struct i2c_client *client){ struct saa7127_state *state = i2c_get_clientdata(client); int err; /* Turn off TV output */ saa7127_set_video_enable(client, 0); err = i2c_detach_client(client); if (err) { return err; } kfree(state); kfree(client); return 0;}/* ----------------------------------------------------------------------- */static struct i2c_driver i2c_driver_saa7127 = { .name = "saa7127", .id = I2C_DRIVERID_SAA7127, .flags = I2C_DF_NOTIFY, .attach_adapter = saa7127_probe, .detach_client = saa7127_detach, .command = saa7127_command, .owner = THIS_MODULE,};/* ----------------------------------------------------------------------- */static int __init saa7127_init_module(void){ return i2c_add_driver(&i2c_driver_saa7127);}/* ----------------------------------------------------------------------- */static void __exit saa7127_cleanup_module(void){ i2c_del_driver(&i2c_driver_saa7127);}/* ----------------------------------------------------------------------- */module_init(saa7127_init_module);module_exit(saa7127_cleanup_module);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -