diff options
-rw-r--r-- | drivers/media/video/bt819.c | 477 | ||||
-rw-r--r-- | include/media/v4l2-chip-ident.h | 5 |
2 files changed, 257 insertions, 225 deletions
diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index b8109a1..ce2a8f3 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -29,15 +29,14 @@ */ #include <linux/module.h> -#include <linux/delay.h> #include <linux/types.h> #include <linux/ioctl.h> #include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> -#include <linux/videodev.h> -#include <linux/video_decoder.h> -#include <media/v4l2-common.h> +#include <linux/videodev2.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); @@ -48,13 +47,18 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); +static unsigned short normal_i2c[] = { 0x8a >> 1, I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + /* ----------------------------------------------------------------------- */ struct bt819 { + struct v4l2_subdev sd; unsigned char reg[32]; - int initialized; v4l2_std_id norm; + int ident; int input; int enable; int bright; @@ -63,6 +67,11 @@ struct bt819 { int sat; }; +static inline struct bt819 *to_bt819(struct v4l2_subdev *sd) +{ + return container_of(sd, struct bt819, sd); +} + struct timing { int hactive; int hdelay; @@ -80,24 +89,23 @@ static struct timing timing_data[] = { /* ----------------------------------------------------------------------- */ -static inline int bt819_write(struct i2c_client *client, u8 reg, u8 value) +static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value) { - struct bt819 *decoder = i2c_get_clientdata(client); + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); decoder->reg[reg] = value; return i2c_smbus_write_byte_data(client, reg, value); } -static inline int bt819_setbit(struct i2c_client *client, u8 reg, u8 bit, u8 value) +static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value) { - struct bt819 *decoder = i2c_get_clientdata(client); - - return bt819_write(client, reg, + return bt819_write(decoder, reg, (decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0)); } -static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned int len) +static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len) { + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); int ret = -1; u8 reg; @@ -105,7 +113,6 @@ static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned * the adapter understands raw I2C */ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { /* do raw I2C, not smbus compatible */ - struct bt819 *decoder = i2c_get_clientdata(client); u8 block_data[32]; int block_len; @@ -126,7 +133,8 @@ static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned /* do some slow I2C emulation kind of thing */ while (len >= 2) { reg = *data++; - if ((ret = bt819_write(client, reg, *data++)) < 0) + ret = bt819_write(decoder, reg, *data++); + if (ret < 0) break; len -= 2; } @@ -135,15 +143,15 @@ static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned return ret; } -static inline int bt819_read(struct i2c_client *client, u8 reg) +static inline int bt819_read(struct bt819 *decoder, u8 reg) { + struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd); + return i2c_smbus_read_byte_data(client, reg); } -static int bt819_init(struct i2c_client *client) +static int bt819_init(struct v4l2_subdev *sd) { - struct bt819 *decoder = i2c_get_clientdata(client); - static unsigned char init[] = { /*0x1f, 0x00,*/ /* Reset */ 0x01, 0x59, /* 0x01 input format */ @@ -178,6 +186,7 @@ static int bt819_init(struct i2c_client *client) 0x1a, 0x80, /* 0x1a ADC Interface */ }; + struct bt819 *decoder = to_bt819(sd); struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0]; init[0x03 * 2 - 1] = @@ -194,277 +203,297 @@ static int bt819_init(struct i2c_client *client) /* 0x15 in array is address 0x19 */ init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93; /* Chroma burst delay */ /* reset */ - bt819_write(client, 0x1f, 0x00); + bt819_write(decoder, 0x1f, 0x00); mdelay(1); /* init */ - return bt819_write_block(client, init, sizeof(init)); + return bt819_write_block(decoder, init, sizeof(init)); } /* ----------------------------------------------------------------------- */ -static int bt819_command(struct i2c_client *client, unsigned cmd, void *arg) +static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd) { - int temp; + struct bt819 *decoder = to_bt819(sd); + int status = bt819_read(decoder, 0x00); + int res = V4L2_IN_ST_NO_SIGNAL; + v4l2_std_id std; + + if ((status & 0x80)) + res = 0; + + if ((status & 0x10)) + std = V4L2_STD_PAL; + else + std = V4L2_STD_NTSC; + if (pstd) + *pstd = std; + if (pstatus) + *pstatus = status; + + v4l2_dbg(1, debug, sd, "get status %x\n", status); + return 0; +} - struct bt819 *decoder = i2c_get_clientdata(client); +static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) +{ + return bt819_status(sd, NULL, std); +} - if (!decoder->initialized) { /* First call to bt819_init could be */ - bt819_init(client); /* without #FRST = 0 */ - decoder->initialized = 1; +static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + return bt819_status(sd, status, NULL); +} + +static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct bt819 *decoder = to_bt819(sd); + struct timing *timing = NULL; + + v4l2_dbg(1, debug, sd, "set norm %llx\n", std); + + if (std & V4L2_STD_NTSC) { + bt819_setbit(decoder, 0x01, 0, 1); + bt819_setbit(decoder, 0x01, 1, 0); + bt819_setbit(decoder, 0x01, 5, 0); + bt819_write(decoder, 0x18, 0x68); + bt819_write(decoder, 0x19, 0x5d); + /* bt819_setbit(decoder, 0x1a, 5, 1); */ + timing = &timing_data[1]; + } else if (std & V4L2_STD_PAL) { + bt819_setbit(decoder, 0x01, 0, 1); + bt819_setbit(decoder, 0x01, 1, 1); + bt819_setbit(decoder, 0x01, 5, 1); + bt819_write(decoder, 0x18, 0x7f); + bt819_write(decoder, 0x19, 0x72); + /* bt819_setbit(decoder, 0x1a, 5, 0); */ + timing = &timing_data[0]; + } else { + v4l2_dbg(1, debug, sd, "unsupported norm %llx\n", std); + return -EINVAL; } + bt819_write(decoder, 0x03, + (((timing->vdelay >> 8) & 0x03) << 6) | + (((timing->vactive >> 8) & 0x03) << 4) | + (((timing->hdelay >> 8) & 0x03) << 2) | + ((timing->hactive >> 8) & 0x03)); + bt819_write(decoder, 0x04, timing->vdelay & 0xff); + bt819_write(decoder, 0x05, timing->vactive & 0xff); + bt819_write(decoder, 0x06, timing->hdelay & 0xff); + bt819_write(decoder, 0x07, timing->hactive & 0xff); + bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff); + bt819_write(decoder, 0x09, timing->hscale & 0xff); + decoder->norm = std; + return 0; +} - switch (cmd) { - case VIDIOC_INT_INIT: - /* This is just for testing!!! */ - bt819_init(client); - break; +static int bt819_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +{ + struct bt819 *decoder = to_bt819(sd); - case VIDIOC_QUERYSTD: - case VIDIOC_INT_G_INPUT_STATUS: { - int *iarg = arg; - v4l2_std_id *istd = arg; - int status; - int res = V4L2_IN_ST_NO_SIGNAL; - v4l2_std_id std; - - status = bt819_read(client, 0x00); - if ((status & 0x80)) - res = 0; - - if ((status & 0x10)) - std = V4L2_STD_PAL; - else - std = V4L2_STD_NTSC; - if (cmd == VIDIOC_QUERYSTD) - *istd = std; - else - *iarg = res; - - v4l_dbg(1, debug, client, "get status %x\n", *iarg); - break; - } + v4l2_dbg(1, debug, sd, "set input %x\n", route->input); - case VIDIOC_S_STD: - { - v4l2_std_id *iarg = arg; - struct timing *timing = NULL; - - v4l_dbg(1, debug, client, "set norm %llx\n", *iarg); - - if (*iarg & V4L2_STD_NTSC) { - bt819_setbit(client, 0x01, 0, 1); - bt819_setbit(client, 0x01, 1, 0); - bt819_setbit(client, 0x01, 5, 0); - bt819_write(client, 0x18, 0x68); - bt819_write(client, 0x19, 0x5d); - /* bt819_setbit(client, 0x1a, 5, 1); */ - timing = &timing_data[1]; - } else if (*iarg & V4L2_STD_PAL) { - bt819_setbit(client, 0x01, 0, 1); - bt819_setbit(client, 0x01, 1, 1); - bt819_setbit(client, 0x01, 5, 1); - bt819_write(client, 0x18, 0x7f); - bt819_write(client, 0x19, 0x72); - /* bt819_setbit(client, 0x1a, 5, 0); */ - timing = &timing_data[0]; - } else { - v4l_dbg(1, debug, client, "unsupported norm %llx\n", *iarg); - return -EINVAL; - } -/* case VIDEO_MODE_AUTO: - bt819_setbit(client, 0x01, 0, 0); - bt819_setbit(client, 0x01, 1, 0);*/ - - bt819_write(client, 0x03, - (((timing->vdelay >> 8) & 0x03) << 6) | - (((timing->vactive >> 8) & 0x03) << 4) | - (((timing->hdelay >> 8) & 0x03) << 2) | - ((timing->hactive >> 8) & 0x03)); - bt819_write(client, 0x04, timing->vdelay & 0xff); - bt819_write(client, 0x05, timing->vactive & 0xff); - bt819_write(client, 0x06, timing->hdelay & 0xff); - bt819_write(client, 0x07, timing->hactive & 0xff); - bt819_write(client, 0x08, (timing->hscale >> 8) & 0xff); - bt819_write(client, 0x09, timing->hscale & 0xff); - decoder->norm = *iarg; - break; - } + if (route->input < 0 || route->input > 7) + return -EINVAL; - case VIDIOC_INT_S_VIDEO_ROUTING: - { - struct v4l2_routing *route = arg; - - v4l_dbg(1, debug, client, "set input %x\n", route->input); - - if (route->input < 0 || route->input > 7) - return -EINVAL; - - if (decoder->input != route->input) { - decoder->input = route->input; - /* select mode */ - if (decoder->input == 0) { - bt819_setbit(client, 0x0b, 6, 0); - bt819_setbit(client, 0x1a, 1, 1); - } else { - bt819_setbit(client, 0x0b, 6, 1); - bt819_setbit(client, 0x1a, 1, 0); - } + if (decoder->input != route->input) { + decoder->input = route->input; + /* select mode */ + if (decoder->input == 0) { + bt819_setbit(decoder, 0x0b, 6, 0); + bt819_setbit(decoder, 0x1a, 1, 1); + } else { + bt819_setbit(decoder, 0x0b, 6, 1); + bt819_setbit(decoder, 0x1a, 1, 0); } - break; } + return 0; +} - case VIDIOC_STREAMON: - case VIDIOC_STREAMOFF: - { - int enable = cmd == VIDIOC_STREAMON; +static int bt819_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct bt819 *decoder = to_bt819(sd); - v4l_dbg(1, debug, client, "enable output %x\n", enable); + v4l2_dbg(1, debug, sd, "enable output %x\n", enable); - if (decoder->enable != enable) { - decoder->enable = enable; - bt819_setbit(client, 0x16, 7, !enable); - } - break; + if (decoder->enable != enable) { + decoder->enable = enable; + bt819_setbit(decoder, 0x16, 7, !enable); } + return 0; +} - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *qc = arg; - - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - break; - - case V4L2_CID_CONTRAST: - v4l2_ctrl_query_fill(qc, 0, 511, 1, 256); - break; +static int bt819_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); + break; - case V4L2_CID_SATURATION: - v4l2_ctrl_query_fill(qc, 0, 511, 1, 256); - break; + case V4L2_CID_CONTRAST: + v4l2_ctrl_query_fill(qc, 0, 511, 1, 256); + break; - case V4L2_CID_HUE: - v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - break; + case V4L2_CID_SATURATION: + v4l2_ctrl_query_fill(qc, 0, 511, 1, 256); + break; - default: - return -EINVAL; - } + case V4L2_CID_HUE: + v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); break; + + default: + return -EINVAL; } + return 0; +} - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; +static int bt819_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct bt819 *decoder = to_bt819(sd); + int temp; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (decoder->bright != ctrl->value) { - decoder->bright = ctrl->value; - bt819_write(client, 0x0a, decoder->bright); - } + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (decoder->bright == ctrl->value) break; + decoder->bright = ctrl->value; + bt819_write(decoder, 0x0a, decoder->bright); + break; - case V4L2_CID_CONTRAST: - if (decoder->contrast != ctrl->value) { - decoder->contrast = ctrl->value; - bt819_write(client, 0x0c, - decoder->contrast & 0xff); - bt819_setbit(client, 0x0b, 2, - ((decoder->contrast >> 8) & 0x01)); - } + case V4L2_CID_CONTRAST: + if (decoder->contrast == ctrl->value) break; + decoder->contrast = ctrl->value; + bt819_write(decoder, 0x0c, decoder->contrast & 0xff); + bt819_setbit(decoder, 0x0b, 2, ((decoder->contrast >> 8) & 0x01)); + break; - case V4L2_CID_SATURATION: - if (decoder->sat != ctrl->value) { - decoder->sat = ctrl->value; - bt819_write(client, 0x0d, - (decoder->sat >> 7) & 0xff); - bt819_setbit(client, 0x0b, 1, - ((decoder->sat >> 15) & 0x01)); - - /* Ratio between U gain and V gain must stay the same as - the ratio between the default U and V gain values. */ - temp = (decoder->sat * 180) / 254; - bt819_write(client, 0x0e, (temp >> 7) & 0xff); - bt819_setbit(client, 0x0b, 0, (temp >> 15) & 0x01); - } + case V4L2_CID_SATURATION: + if (decoder->sat == ctrl->value) break; + decoder->sat = ctrl->value; + bt819_write(decoder, 0x0d, (decoder->sat >> 7) & 0xff); + bt819_setbit(decoder, 0x0b, 1, ((decoder->sat >> 15) & 0x01)); + + /* Ratio between U gain and V gain must stay the same as + the ratio between the default U and V gain values. */ + temp = (decoder->sat * 180) / 254; + bt819_write(decoder, 0x0e, (temp >> 7) & 0xff); + bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01); + break; - case V4L2_CID_HUE: - if (decoder->hue != ctrl->value) { - decoder->hue = ctrl->value; - bt819_write(client, 0x0f, decoder->hue); - } + case V4L2_CID_HUE: + if (decoder->hue == ctrl->value) break; - default: - return -EINVAL; - } + decoder->hue = ctrl->value; + bt819_write(decoder, 0x0f, decoder->hue); break; + + default: + return -EINVAL; } + return 0; +} - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; +static int bt819_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct bt819 *decoder = to_bt819(sd); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = decoder->bright; - break; - case V4L2_CID_CONTRAST: - ctrl->value = decoder->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = decoder->sat; - break; - case V4L2_CID_HUE: - ctrl->value = decoder->hue; - break; - default: - return -EINVAL; - } + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = decoder->bright; + break; + case V4L2_CID_CONTRAST: + ctrl->value = decoder->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = decoder->sat; + break; + case V4L2_CID_HUE: + ctrl->value = decoder->hue; break; - } - default: return -EINVAL; } - return 0; } +static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) +{ + struct bt819 *decoder = to_bt819(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0); +} + +static int bt819_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); +} + /* ----------------------------------------------------------------------- */ -static unsigned short normal_i2c[] = { 0x8a >> 1, I2C_CLIENT_END }; +static const struct v4l2_subdev_core_ops bt819_core_ops = { + .g_chip_ident = bt819_g_chip_ident, + .g_ctrl = bt819_g_ctrl, + .s_ctrl = bt819_s_ctrl, + .queryctrl = bt819_queryctrl, +}; -I2C_CLIENT_INSMOD; +static const struct v4l2_subdev_tuner_ops bt819_tuner_ops = { + .s_std = bt819_s_std, +}; + +static const struct v4l2_subdev_video_ops bt819_video_ops = { + .s_routing = bt819_s_routing, + .s_stream = bt819_s_stream, + .querystd = bt819_querystd, + .g_input_status = bt819_g_input_status, +}; + +static const struct v4l2_subdev_ops bt819_ops = { + .core = &bt819_core_ops, + .tuner = &bt819_tuner_ops, + .video = &bt819_video_ops, +}; + +/* ----------------------------------------------------------------------- */ static int bt819_probe(struct i2c_client *client, const struct i2c_device_id *id) { int i, ver; struct bt819 *decoder; + struct v4l2_subdev *sd; const char *name; /* Check if the adapter supports the needed features */ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - ver = bt819_read(client, 0x17); + decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); + if (decoder == NULL) + return -ENOMEM; + sd = &decoder->sd; + v4l2_i2c_subdev_init(sd, client, &bt819_ops); + + ver = bt819_read(decoder, 0x17); switch (ver & 0xf0) { case 0x70: name = "bt819a"; + decoder->ident = V4L2_IDENT_BT819A; break; case 0x60: name = "bt817a"; + decoder->ident = V4L2_IDENT_BT817A; break; case 0x20: name = "bt815a"; + decoder->ident = V4L2_IDENT_BT815A; break; default: - v4l_dbg(1, debug, client, + v4l2_dbg(1, debug, sd, "unknown chip version 0x%02x\n", ver); return -ENODEV; } @@ -472,28 +501,26 @@ static int bt819_probe(struct i2c_client *client, v4l_info(client, "%s found @ 0x%x (%s)\n", name, client->addr << 1, client->adapter->name); - decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL); - if (decoder == NULL) - return -ENOMEM; decoder->norm = V4L2_STD_NTSC; decoder->input = 0; decoder->enable = 1; decoder->bright = 0; decoder->contrast = 0xd8; /* 100% of original signal */ decoder->hue = 0; - decoder->sat = 0xfe; /* 100% of original signal */ - decoder->initialized = 0; - i2c_set_clientdata(client, decoder); + decoder->sat = 0xfe; /* 100% of original signal */ - i = bt819_init(client); + i = bt819_init(sd); if (i < 0) - v4l_dbg(1, debug, client, "init status %d\n", i); + v4l2_dbg(1, debug, sd, "init status %d\n", i); return 0; } static int bt819_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_bt819(sd)); return 0; } diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index cfb236e..0766106 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -71,6 +71,11 @@ enum { V4L2_IDENT_CX23416 = 416, V4L2_IDENT_CX23418 = 418, + /* module bt819: reserved range 810-819 */ + V4L2_IDENT_BT815A = 815, + V4L2_IDENT_BT817A = 817, + V4L2_IDENT_BT819A = 819, + /* module bt866: just ident 866 */ V4L2_IDENT_BT866 = 866, |