Loading drivers/media/video/vpx3220.c +266 −231 Original line number Diff line number Diff line Loading @@ -24,11 +24,10 @@ #include <linux/types.h> #include <asm/uaccess.h> #include <linux/i2c.h> #include <media/v4l2-common.h> #include <media/v4l2-i2c-drv-legacy.h> #include <linux/videodev.h> #include <linux/videodev2.h> #include <linux/video_decoder.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); MODULE_AUTHOR("Laurent Pinchart"); Loading @@ -38,14 +37,20 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); static unsigned short normal_i2c[] = { 0x86 >> 1, 0x8e >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; #define VPX_TIMEOUT_COUNT 10 /* ----------------------------------------------------------------------- */ struct vpx3220 { struct v4l2_subdev sd; unsigned char reg[255]; v4l2_std_id norm; int ident; int input; int enable; int bright; Loading @@ -54,30 +59,38 @@ struct vpx3220 { int sat; }; static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) { return container_of(sd, struct vpx3220, sd); } static char *inputs[] = { "internal", "composite", "svideo" }; /* ----------------------------------------------------------------------- */ static inline int vpx3220_write(struct i2c_client *client, u8 reg, u8 value) static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct vpx3220 *decoder = i2c_get_clientdata(client); decoder->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } static inline int vpx3220_read(struct i2c_client *client, u8 reg) static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); return i2c_smbus_read_byte_data(client, reg); } static int vpx3220_fp_status(struct i2c_client *client) static int vpx3220_fp_status(struct v4l2_subdev *sd) { unsigned char status; unsigned int i; for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { status = vpx3220_read(client, 0x29); status = vpx3220_read(sd, 0x29); if (!(status & 4)) return 0; Loading @@ -91,57 +104,60 @@ static int vpx3220_fp_status(struct i2c_client *client) return -1; } static int vpx3220_fp_write(struct i2c_client *client, u8 fpaddr, u16 data) static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data) { struct i2c_client *client = v4l2_get_subdevdata(sd); /* Write the 16-bit address to the FPWR register */ if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) { v4l_dbg(1, debug, client, "%s: failed\n", __func__); v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } if (vpx3220_fp_status(client) < 0) if (vpx3220_fp_status(sd) < 0) return -1; /* Write the 16-bit data to the FPDAT register */ if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) { v4l_dbg(1, debug, client, "%s: failed\n", __func__); v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } return 0; } static u16 vpx3220_fp_read(struct i2c_client *client, u16 fpaddr) static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr) { struct i2c_client *client = v4l2_get_subdevdata(sd); s16 data; /* Write the 16-bit address to the FPRD register */ if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) { v4l_dbg(1, debug, client, "%s: failed\n", __func__); v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } if (vpx3220_fp_status(client) < 0) if (vpx3220_fp_status(sd) < 0) return -1; /* Read the 16-bit data from the FPDAT register */ data = i2c_smbus_read_word_data(client, 0x28); if (data == -1) { v4l_dbg(1, debug, client, "%s: failed\n", __func__); v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } return swab16(data); } static int vpx3220_write_block(struct i2c_client *client, const u8 *data, unsigned int len) static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) { u8 reg; int ret = -1; while (len >= 2) { reg = *data++; ret = vpx3220_write(client, reg, *data++); ret = vpx3220_write(sd, reg, *data++); if (ret < 0) break; len -= 2; Loading @@ -150,7 +166,7 @@ static int vpx3220_write_block(struct i2c_client *client, const u8 *data, unsign return ret; } static int vpx3220_write_fp_block(struct i2c_client *client, static int vpx3220_write_fp_block(struct v4l2_subdev *sd, const u16 *data, unsigned int len) { u8 reg; Loading @@ -158,7 +174,7 @@ static int vpx3220_write_fp_block(struct i2c_client *client, while (len > 1) { reg = *data++; ret |= vpx3220_fp_write(client, reg, *data++); ret |= vpx3220_fp_write(sd, reg, *data++); len -= 2; } Loading Loading @@ -261,44 +277,33 @@ static const unsigned short init_fp[] = { }; static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) { struct vpx3220 *decoder = i2c_get_clientdata(client); switch (cmd) { case VIDIOC_INT_INIT: static int vpx3220_init(struct v4l2_subdev *sd, u32 val) { vpx3220_write_block(client, init_common, sizeof(init_common)); vpx3220_write_fp_block(client, init_fp, sizeof(init_fp) >> 1); if (decoder->norm & V4L2_STD_NTSC) { vpx3220_write_fp_block(client, init_ntsc, sizeof(init_ntsc) >> 1); } else if (decoder->norm & V4L2_STD_PAL) { vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1); } else if (decoder->norm & V4L2_STD_SECAM) { vpx3220_write_fp_block(client, init_secam, sizeof(init_secam) >> 1); } else { vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1); } break; struct vpx3220 *decoder = to_vpx3220(sd); vpx3220_write_block(sd, init_common, sizeof(init_common)); vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); if (decoder->norm & V4L2_STD_NTSC) vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); else if (decoder->norm & V4L2_STD_PAL) vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); else if (decoder->norm & V4L2_STD_SECAM) vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); else vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); return 0; } case VIDIOC_QUERYSTD: case VIDIOC_INT_G_INPUT_STATUS: static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) { int res = V4L2_IN_ST_NO_SIGNAL, status; v4l2_std_id std = 0; v4l_dbg(1, debug, client, "VIDIOC_QUERYSTD/VIDIOC_INT_G_INPUT_STATUS\n"); v4l2_dbg(1, debug, sd, "VIDIOC_QUERYSTD/VIDIOC_INT_G_INPUT_STATUS\n"); status = vpx3220_fp_read(client, 0x0f3); status = vpx3220_fp_read(sd, 0x0f3); v4l_dbg(1, debug, client, "status: 0x%04x\n", status); v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status); if (status < 0) return status; Loading @@ -325,57 +330,62 @@ static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) break; } } if (pstd) *pstd = std; if (pstatus) *pstatus = status; return 0; } if (cmd == VIDIOC_QUERYSTD) *(v4l2_std_id *)arg = std; else *(int *)arg = res; break; static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { return vpx3220_status(sd, NULL, std); } case VIDIOC_S_STD: static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status) { v4l2_std_id *iarg = arg; return vpx3220_status(sd, status, NULL); } static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct vpx3220 *decoder = to_vpx3220(sd); int temp_input; /* Here we back up the input selection because it gets overwritten when we fill the registers with the choosen video norm */ temp_input = vpx3220_fp_read(client, 0xf2); v4l_dbg(1, debug, client, "VIDIOC_S_STD %llx\n", *iarg); if (*iarg & V4L2_STD_NTSC) { vpx3220_write_fp_block(client, init_ntsc, sizeof(init_ntsc) >> 1); v4l_dbg(1, debug, client, "norm switched to NTSC\n"); } else if (*iarg & V4L2_STD_PAL) { vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1); v4l_dbg(1, debug, client, "norm switched to PAL\n"); } else if (*iarg & V4L2_STD_SECAM) { vpx3220_write_fp_block(client, init_secam, sizeof(init_secam) >> 1); v4l_dbg(1, debug, client, "norm switched to SECAM\n"); temp_input = vpx3220_fp_read(sd, 0xf2); v4l2_dbg(1, debug, sd, "VIDIOC_S_STD %llx\n", std); if (std & V4L2_STD_NTSC) { vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); } else if (std & V4L2_STD_PAL) { vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); v4l2_dbg(1, debug, sd, "norm switched to PAL\n"); } else if (std & V4L2_STD_SECAM) { vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); v4l2_dbg(1, debug, sd, "norm switched to SECAM\n"); } else { return -EINVAL; } decoder->norm = *iarg; decoder->norm = std; /* And here we set the backed up video input again */ vpx3220_fp_write(client, 0xf2, temp_input | 0x0010); vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010); udelay(10); break; return 0; } case VIDIOC_INT_S_VIDEO_ROUTING: static int vpx3220_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { struct v4l2_routing *route = arg; int data; /* RJ: *iarg = 0: ST8 (PCTV) input *iarg = 1: COMPOSITE input *iarg = 2: SVHS input */ /* RJ: route->input = 0: ST8 (PCTV) input route->input = 1: COMPOSITE input route->input = 2: SVHS input */ const int input[3][2] = { {0x0c, 0}, Loading @@ -386,35 +396,31 @@ static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) if (route->input < 0 || route->input > 2) return -EINVAL; v4l_dbg(1, debug, client, "input switched to %s\n", inputs[route->input]); v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[route->input]); vpx3220_write(client, 0x33, input[route->input][0]); vpx3220_write(sd, 0x33, input[route->input][0]); data = vpx3220_fp_read(client, 0xf2) & ~(0x0020); data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020); if (data < 0) return data; /* 0x0010 is required to latch the setting */ vpx3220_fp_write(client, 0xf2, vpx3220_fp_write(sd, 0xf2, data | (input[route->input][1] << 5) | 0x0010); udelay(10); break; return 0; } case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) { int on = cmd == VIDIOC_STREAMON; v4l_dbg(1, debug, client, "VIDIOC_STREAM%s\n", on ? "ON" : "OFF"); v4l2_dbg(1, debug, sd, "VIDIOC_STREAM%s\n", enable ? "ON" : "OFF"); vpx3220_write(client, 0xf2, (on ? 0x1b : 0x00)); break; vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00)); return 0; } case VIDIOC_QUERYCTRL: static int vpx3220_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { struct v4l2_queryctrl *qc = arg; switch (qc->id) { case V4L2_CID_BRIGHTNESS: v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); Loading @@ -435,12 +441,12 @@ static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) default: return -EINVAL; } break; return 0; } case VIDIOC_G_CTRL: static int vpx3220_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct v4l2_control *ctrl = arg; struct vpx3220 *decoder = to_vpx3220(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: Loading @@ -458,75 +464,94 @@ static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) default: return -EINVAL; } break; return 0; } case VIDIOC_S_CTRL: static int vpx3220_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct v4l2_control *ctrl = arg; struct vpx3220 *decoder = to_vpx3220(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (decoder->bright != ctrl->value) { decoder->bright = ctrl->value; vpx3220_write(client, 0xe6, decoder->bright); vpx3220_write(sd, 0xe6, decoder->bright); } break; case V4L2_CID_CONTRAST: if (decoder->contrast != ctrl->value) { /* Bit 7 and 8 is for noise shaping */ decoder->contrast = ctrl->value; vpx3220_write(client, 0xe7, decoder->contrast + 192); vpx3220_write(sd, 0xe7, decoder->contrast + 192); } break; case V4L2_CID_SATURATION: if (decoder->sat != ctrl->value) { decoder->sat = ctrl->value; vpx3220_fp_write(client, 0xa0, decoder->sat); vpx3220_fp_write(sd, 0xa0, decoder->sat); } break; case V4L2_CID_HUE: if (decoder->hue != ctrl->value) { decoder->hue = ctrl->value; vpx3220_fp_write(client, 0x1c, decoder->hue); vpx3220_fp_write(sd, 0x1c, decoder->hue); } break; default: return -EINVAL; } break; return 0; } default: return -EINVAL; } static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct vpx3220 *decoder = to_vpx3220(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); return 0; return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); } static int vpx3220_init_client(struct i2c_client *client) static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) { vpx3220_write_block(client, init_common, sizeof(init_common)); vpx3220_write_fp_block(client, init_fp, sizeof(init_fp) >> 1); /* Default to PAL */ vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1); return 0; return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); } /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops vpx3220_core_ops = { .g_chip_ident = vpx3220_g_chip_ident, .init = vpx3220_init, .g_ctrl = vpx3220_g_ctrl, .s_ctrl = vpx3220_s_ctrl, .queryctrl = vpx3220_queryctrl, }; static const struct v4l2_subdev_tuner_ops vpx3220_tuner_ops = { .s_std = vpx3220_s_std, }; static const struct v4l2_subdev_video_ops vpx3220_video_ops = { .s_routing = vpx3220_s_routing, .s_stream = vpx3220_s_stream, .querystd = vpx3220_querystd, .g_input_status = vpx3220_g_input_status, }; static const struct v4l2_subdev_ops vpx3220_ops = { .core = &vpx3220_core_ops, .tuner = &vpx3220_tuner_ops, .video = &vpx3220_video_ops, }; /* ----------------------------------------------------------------------- * Client management code */ static unsigned short normal_i2c[] = { 0x86 >> 1, 0x8e >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; static int vpx3220_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct vpx3220 *decoder; struct v4l2_subdev *sd; const char *name = NULL; u8 ver; u16 pn; Loading @@ -539,6 +564,8 @@ static int vpx3220_probe(struct i2c_client *client, decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL); if (decoder == NULL) return -ENOMEM; sd = &decoder->sd; v4l2_i2c_subdev_init(sd, client, &vpx3220_ops); decoder->norm = V4L2_STD_PAL; decoder->input = 0; decoder->enable = 1; Loading @@ -546,11 +573,11 @@ static int vpx3220_probe(struct i2c_client *client, decoder->contrast = 32768; decoder->hue = 32768; decoder->sat = 32768; i2c_set_clientdata(client, decoder); ver = i2c_smbus_read_byte_data(client, 0x00); pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) + i2c_smbus_read_byte_data(client, 0x01); decoder->ident = V4L2_IDENT_VPX3220A; if (ver == 0xec) { switch (pn) { case 0x4680: Loading @@ -558,26 +585,34 @@ static int vpx3220_probe(struct i2c_client *client, break; case 0x4260: name = "vpx3216b"; decoder->ident = V4L2_IDENT_VPX3216B; break; case 0x4280: name = "vpx3214c"; decoder->ident = V4L2_IDENT_VPX3214C; break; } } if (name) v4l_info(client, "%s found @ 0x%x (%s)\n", name, v4l2_info(sd, "%s found @ 0x%x (%s)\n", name, client->addr << 1, client->adapter->name); else v4l_info(client, "chip (%02x:%04x) found @ 0x%x (%s)\n", v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n", ver, pn, client->addr << 1, client->adapter->name); vpx3220_init_client(client); vpx3220_write_block(sd, init_common, sizeof(init_common)); vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); /* Default to PAL */ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); return 0; } static int vpx3220_remove(struct i2c_client *client) { kfree(i2c_get_clientdata(client)); struct v4l2_subdev *sd = i2c_get_clientdata(client); v4l2_device_unregister_subdev(sd); kfree(to_vpx3220(sd)); return 0; } Loading include/media/v4l2-chip-ident.h +5 −0 Original line number Diff line number Diff line Loading @@ -88,6 +88,11 @@ enum { /* module vp27smpx: just ident 2700 */ V4L2_IDENT_VP27SMPX = 2700, /* module vpx3220: reserved range: 3210-3229 */ V4L2_IDENT_VPX3214C = 3214, V4L2_IDENT_VPX3216B = 3216, V4L2_IDENT_VPX3220A = 3220, /* module tvp5150 */ V4L2_IDENT_TVP5150 = 5150, Loading Loading
drivers/media/video/vpx3220.c +266 −231 Original line number Diff line number Diff line Loading @@ -24,11 +24,10 @@ #include <linux/types.h> #include <asm/uaccess.h> #include <linux/i2c.h> #include <media/v4l2-common.h> #include <media/v4l2-i2c-drv-legacy.h> #include <linux/videodev.h> #include <linux/videodev2.h> #include <linux/video_decoder.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver"); MODULE_AUTHOR("Laurent Pinchart"); Loading @@ -38,14 +37,20 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); static unsigned short normal_i2c[] = { 0x86 >> 1, 0x8e >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; #define VPX_TIMEOUT_COUNT 10 /* ----------------------------------------------------------------------- */ struct vpx3220 { struct v4l2_subdev sd; unsigned char reg[255]; v4l2_std_id norm; int ident; int input; int enable; int bright; Loading @@ -54,30 +59,38 @@ struct vpx3220 { int sat; }; static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd) { return container_of(sd, struct vpx3220, sd); } static char *inputs[] = { "internal", "composite", "svideo" }; /* ----------------------------------------------------------------------- */ static inline int vpx3220_write(struct i2c_client *client, u8 reg, u8 value) static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value) { struct i2c_client *client = v4l2_get_subdevdata(sd); struct vpx3220 *decoder = i2c_get_clientdata(client); decoder->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } static inline int vpx3220_read(struct i2c_client *client, u8 reg) static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); return i2c_smbus_read_byte_data(client, reg); } static int vpx3220_fp_status(struct i2c_client *client) static int vpx3220_fp_status(struct v4l2_subdev *sd) { unsigned char status; unsigned int i; for (i = 0; i < VPX_TIMEOUT_COUNT; i++) { status = vpx3220_read(client, 0x29); status = vpx3220_read(sd, 0x29); if (!(status & 4)) return 0; Loading @@ -91,57 +104,60 @@ static int vpx3220_fp_status(struct i2c_client *client) return -1; } static int vpx3220_fp_write(struct i2c_client *client, u8 fpaddr, u16 data) static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data) { struct i2c_client *client = v4l2_get_subdevdata(sd); /* Write the 16-bit address to the FPWR register */ if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) { v4l_dbg(1, debug, client, "%s: failed\n", __func__); v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } if (vpx3220_fp_status(client) < 0) if (vpx3220_fp_status(sd) < 0) return -1; /* Write the 16-bit data to the FPDAT register */ if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) { v4l_dbg(1, debug, client, "%s: failed\n", __func__); v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } return 0; } static u16 vpx3220_fp_read(struct i2c_client *client, u16 fpaddr) static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr) { struct i2c_client *client = v4l2_get_subdevdata(sd); s16 data; /* Write the 16-bit address to the FPRD register */ if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) { v4l_dbg(1, debug, client, "%s: failed\n", __func__); v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } if (vpx3220_fp_status(client) < 0) if (vpx3220_fp_status(sd) < 0) return -1; /* Read the 16-bit data from the FPDAT register */ data = i2c_smbus_read_word_data(client, 0x28); if (data == -1) { v4l_dbg(1, debug, client, "%s: failed\n", __func__); v4l2_dbg(1, debug, sd, "%s: failed\n", __func__); return -1; } return swab16(data); } static int vpx3220_write_block(struct i2c_client *client, const u8 *data, unsigned int len) static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len) { u8 reg; int ret = -1; while (len >= 2) { reg = *data++; ret = vpx3220_write(client, reg, *data++); ret = vpx3220_write(sd, reg, *data++); if (ret < 0) break; len -= 2; Loading @@ -150,7 +166,7 @@ static int vpx3220_write_block(struct i2c_client *client, const u8 *data, unsign return ret; } static int vpx3220_write_fp_block(struct i2c_client *client, static int vpx3220_write_fp_block(struct v4l2_subdev *sd, const u16 *data, unsigned int len) { u8 reg; Loading @@ -158,7 +174,7 @@ static int vpx3220_write_fp_block(struct i2c_client *client, while (len > 1) { reg = *data++; ret |= vpx3220_fp_write(client, reg, *data++); ret |= vpx3220_fp_write(sd, reg, *data++); len -= 2; } Loading Loading @@ -261,44 +277,33 @@ static const unsigned short init_fp[] = { }; static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) { struct vpx3220 *decoder = i2c_get_clientdata(client); switch (cmd) { case VIDIOC_INT_INIT: static int vpx3220_init(struct v4l2_subdev *sd, u32 val) { vpx3220_write_block(client, init_common, sizeof(init_common)); vpx3220_write_fp_block(client, init_fp, sizeof(init_fp) >> 1); if (decoder->norm & V4L2_STD_NTSC) { vpx3220_write_fp_block(client, init_ntsc, sizeof(init_ntsc) >> 1); } else if (decoder->norm & V4L2_STD_PAL) { vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1); } else if (decoder->norm & V4L2_STD_SECAM) { vpx3220_write_fp_block(client, init_secam, sizeof(init_secam) >> 1); } else { vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1); } break; struct vpx3220 *decoder = to_vpx3220(sd); vpx3220_write_block(sd, init_common, sizeof(init_common)); vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); if (decoder->norm & V4L2_STD_NTSC) vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); else if (decoder->norm & V4L2_STD_PAL) vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); else if (decoder->norm & V4L2_STD_SECAM) vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); else vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); return 0; } case VIDIOC_QUERYSTD: case VIDIOC_INT_G_INPUT_STATUS: static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) { int res = V4L2_IN_ST_NO_SIGNAL, status; v4l2_std_id std = 0; v4l_dbg(1, debug, client, "VIDIOC_QUERYSTD/VIDIOC_INT_G_INPUT_STATUS\n"); v4l2_dbg(1, debug, sd, "VIDIOC_QUERYSTD/VIDIOC_INT_G_INPUT_STATUS\n"); status = vpx3220_fp_read(client, 0x0f3); status = vpx3220_fp_read(sd, 0x0f3); v4l_dbg(1, debug, client, "status: 0x%04x\n", status); v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status); if (status < 0) return status; Loading @@ -325,57 +330,62 @@ static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) break; } } if (pstd) *pstd = std; if (pstatus) *pstatus = status; return 0; } if (cmd == VIDIOC_QUERYSTD) *(v4l2_std_id *)arg = std; else *(int *)arg = res; break; static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { return vpx3220_status(sd, NULL, std); } case VIDIOC_S_STD: static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status) { v4l2_std_id *iarg = arg; return vpx3220_status(sd, status, NULL); } static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) { struct vpx3220 *decoder = to_vpx3220(sd); int temp_input; /* Here we back up the input selection because it gets overwritten when we fill the registers with the choosen video norm */ temp_input = vpx3220_fp_read(client, 0xf2); v4l_dbg(1, debug, client, "VIDIOC_S_STD %llx\n", *iarg); if (*iarg & V4L2_STD_NTSC) { vpx3220_write_fp_block(client, init_ntsc, sizeof(init_ntsc) >> 1); v4l_dbg(1, debug, client, "norm switched to NTSC\n"); } else if (*iarg & V4L2_STD_PAL) { vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1); v4l_dbg(1, debug, client, "norm switched to PAL\n"); } else if (*iarg & V4L2_STD_SECAM) { vpx3220_write_fp_block(client, init_secam, sizeof(init_secam) >> 1); v4l_dbg(1, debug, client, "norm switched to SECAM\n"); temp_input = vpx3220_fp_read(sd, 0xf2); v4l2_dbg(1, debug, sd, "VIDIOC_S_STD %llx\n", std); if (std & V4L2_STD_NTSC) { vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); } else if (std & V4L2_STD_PAL) { vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); v4l2_dbg(1, debug, sd, "norm switched to PAL\n"); } else if (std & V4L2_STD_SECAM) { vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1); v4l2_dbg(1, debug, sd, "norm switched to SECAM\n"); } else { return -EINVAL; } decoder->norm = *iarg; decoder->norm = std; /* And here we set the backed up video input again */ vpx3220_fp_write(client, 0xf2, temp_input | 0x0010); vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010); udelay(10); break; return 0; } case VIDIOC_INT_S_VIDEO_ROUTING: static int vpx3220_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) { struct v4l2_routing *route = arg; int data; /* RJ: *iarg = 0: ST8 (PCTV) input *iarg = 1: COMPOSITE input *iarg = 2: SVHS input */ /* RJ: route->input = 0: ST8 (PCTV) input route->input = 1: COMPOSITE input route->input = 2: SVHS input */ const int input[3][2] = { {0x0c, 0}, Loading @@ -386,35 +396,31 @@ static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) if (route->input < 0 || route->input > 2) return -EINVAL; v4l_dbg(1, debug, client, "input switched to %s\n", inputs[route->input]); v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[route->input]); vpx3220_write(client, 0x33, input[route->input][0]); vpx3220_write(sd, 0x33, input[route->input][0]); data = vpx3220_fp_read(client, 0xf2) & ~(0x0020); data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020); if (data < 0) return data; /* 0x0010 is required to latch the setting */ vpx3220_fp_write(client, 0xf2, vpx3220_fp_write(sd, 0xf2, data | (input[route->input][1] << 5) | 0x0010); udelay(10); break; return 0; } case VIDIOC_STREAMON: case VIDIOC_STREAMOFF: static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) { int on = cmd == VIDIOC_STREAMON; v4l_dbg(1, debug, client, "VIDIOC_STREAM%s\n", on ? "ON" : "OFF"); v4l2_dbg(1, debug, sd, "VIDIOC_STREAM%s\n", enable ? "ON" : "OFF"); vpx3220_write(client, 0xf2, (on ? 0x1b : 0x00)); break; vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00)); return 0; } case VIDIOC_QUERYCTRL: static int vpx3220_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { struct v4l2_queryctrl *qc = arg; switch (qc->id) { case V4L2_CID_BRIGHTNESS: v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); Loading @@ -435,12 +441,12 @@ static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) default: return -EINVAL; } break; return 0; } case VIDIOC_G_CTRL: static int vpx3220_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct v4l2_control *ctrl = arg; struct vpx3220 *decoder = to_vpx3220(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: Loading @@ -458,75 +464,94 @@ static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) default: return -EINVAL; } break; return 0; } case VIDIOC_S_CTRL: static int vpx3220_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { struct v4l2_control *ctrl = arg; struct vpx3220 *decoder = to_vpx3220(sd); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: if (decoder->bright != ctrl->value) { decoder->bright = ctrl->value; vpx3220_write(client, 0xe6, decoder->bright); vpx3220_write(sd, 0xe6, decoder->bright); } break; case V4L2_CID_CONTRAST: if (decoder->contrast != ctrl->value) { /* Bit 7 and 8 is for noise shaping */ decoder->contrast = ctrl->value; vpx3220_write(client, 0xe7, decoder->contrast + 192); vpx3220_write(sd, 0xe7, decoder->contrast + 192); } break; case V4L2_CID_SATURATION: if (decoder->sat != ctrl->value) { decoder->sat = ctrl->value; vpx3220_fp_write(client, 0xa0, decoder->sat); vpx3220_fp_write(sd, 0xa0, decoder->sat); } break; case V4L2_CID_HUE: if (decoder->hue != ctrl->value) { decoder->hue = ctrl->value; vpx3220_fp_write(client, 0x1c, decoder->hue); vpx3220_fp_write(sd, 0x1c, decoder->hue); } break; default: return -EINVAL; } break; return 0; } default: return -EINVAL; } static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { struct vpx3220 *decoder = to_vpx3220(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); return 0; return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); } static int vpx3220_init_client(struct i2c_client *client) static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg) { vpx3220_write_block(client, init_common, sizeof(init_common)); vpx3220_write_fp_block(client, init_fp, sizeof(init_fp) >> 1); /* Default to PAL */ vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1); return 0; return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); } /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops vpx3220_core_ops = { .g_chip_ident = vpx3220_g_chip_ident, .init = vpx3220_init, .g_ctrl = vpx3220_g_ctrl, .s_ctrl = vpx3220_s_ctrl, .queryctrl = vpx3220_queryctrl, }; static const struct v4l2_subdev_tuner_ops vpx3220_tuner_ops = { .s_std = vpx3220_s_std, }; static const struct v4l2_subdev_video_ops vpx3220_video_ops = { .s_routing = vpx3220_s_routing, .s_stream = vpx3220_s_stream, .querystd = vpx3220_querystd, .g_input_status = vpx3220_g_input_status, }; static const struct v4l2_subdev_ops vpx3220_ops = { .core = &vpx3220_core_ops, .tuner = &vpx3220_tuner_ops, .video = &vpx3220_video_ops, }; /* ----------------------------------------------------------------------- * Client management code */ static unsigned short normal_i2c[] = { 0x86 >> 1, 0x8e >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; static int vpx3220_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct vpx3220 *decoder; struct v4l2_subdev *sd; const char *name = NULL; u8 ver; u16 pn; Loading @@ -539,6 +564,8 @@ static int vpx3220_probe(struct i2c_client *client, decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL); if (decoder == NULL) return -ENOMEM; sd = &decoder->sd; v4l2_i2c_subdev_init(sd, client, &vpx3220_ops); decoder->norm = V4L2_STD_PAL; decoder->input = 0; decoder->enable = 1; Loading @@ -546,11 +573,11 @@ static int vpx3220_probe(struct i2c_client *client, decoder->contrast = 32768; decoder->hue = 32768; decoder->sat = 32768; i2c_set_clientdata(client, decoder); ver = i2c_smbus_read_byte_data(client, 0x00); pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) + i2c_smbus_read_byte_data(client, 0x01); decoder->ident = V4L2_IDENT_VPX3220A; if (ver == 0xec) { switch (pn) { case 0x4680: Loading @@ -558,26 +585,34 @@ static int vpx3220_probe(struct i2c_client *client, break; case 0x4260: name = "vpx3216b"; decoder->ident = V4L2_IDENT_VPX3216B; break; case 0x4280: name = "vpx3214c"; decoder->ident = V4L2_IDENT_VPX3214C; break; } } if (name) v4l_info(client, "%s found @ 0x%x (%s)\n", name, v4l2_info(sd, "%s found @ 0x%x (%s)\n", name, client->addr << 1, client->adapter->name); else v4l_info(client, "chip (%02x:%04x) found @ 0x%x (%s)\n", v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n", ver, pn, client->addr << 1, client->adapter->name); vpx3220_init_client(client); vpx3220_write_block(sd, init_common, sizeof(init_common)); vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1); /* Default to PAL */ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1); return 0; } static int vpx3220_remove(struct i2c_client *client) { kfree(i2c_get_clientdata(client)); struct v4l2_subdev *sd = i2c_get_clientdata(client); v4l2_device_unregister_subdev(sd); kfree(to_vpx3220(sd)); return 0; } Loading
include/media/v4l2-chip-ident.h +5 −0 Original line number Diff line number Diff line Loading @@ -88,6 +88,11 @@ enum { /* module vp27smpx: just ident 2700 */ V4L2_IDENT_VP27SMPX = 2700, /* module vpx3220: reserved range: 3210-3229 */ V4L2_IDENT_VPX3214C = 3214, V4L2_IDENT_VPX3216B = 3216, V4L2_IDENT_VPX3220A = 3220, /* module tvp5150 */ V4L2_IDENT_TVP5150 = 5150, Loading