0001
0002
0003
0004
0005
0006 #include <linux/module.h>
0007 #include <linux/init.h>
0008 #include <linux/i2c.h>
0009 #include <linux/videodev2.h>
0010 #include <linux/ioctl.h>
0011 #include <linux/slab.h>
0012 #include <media/v4l2-subdev.h>
0013 #include <media/v4l2-device.h>
0014 #include <media/v4l2-ctrls.h>
0015
0016 #define TW2804_REG_AUTOGAIN 0x02
0017 #define TW2804_REG_HUE 0x0f
0018 #define TW2804_REG_SATURATION 0x10
0019 #define TW2804_REG_CONTRAST 0x11
0020 #define TW2804_REG_BRIGHTNESS 0x12
0021 #define TW2804_REG_COLOR_KILLER 0x14
0022 #define TW2804_REG_GAIN 0x3c
0023 #define TW2804_REG_CHROMA_GAIN 0x3d
0024 #define TW2804_REG_BLUE_BALANCE 0x3e
0025 #define TW2804_REG_RED_BALANCE 0x3f
0026
0027 struct tw2804 {
0028 struct v4l2_subdev sd;
0029 struct v4l2_ctrl_handler hdl;
0030 u8 channel:2;
0031 u8 input:1;
0032 int norm;
0033 };
0034
0035 static const u8 global_registers[] = {
0036 0x39, 0x00,
0037 0x3a, 0xff,
0038 0x3b, 0x84,
0039 0x3c, 0x80,
0040 0x3d, 0x80,
0041 0x3e, 0x82,
0042 0x3f, 0x82,
0043 0x78, 0x00,
0044 0xff, 0xff,
0045 };
0046
0047 static const u8 channel_registers[] = {
0048 0x01, 0xc4,
0049 0x02, 0xa5,
0050 0x03, 0x20,
0051 0x04, 0xd0,
0052 0x05, 0x20,
0053 0x06, 0xd0,
0054 0x07, 0x88,
0055 0x08, 0x20,
0056 0x09, 0x07,
0057 0x0a, 0xf0,
0058 0x0b, 0x07,
0059 0x0c, 0xf0,
0060 0x0d, 0x40,
0061 0x0e, 0xd2,
0062 0x0f, 0x80,
0063 0x10, 0x80,
0064 0x11, 0x80,
0065 0x12, 0x80,
0066 0x13, 0x1f,
0067 0x14, 0x00,
0068 0x15, 0x00,
0069 0x16, 0x00,
0070 0x17, 0x00,
0071 0x18, 0xff,
0072 0x19, 0xff,
0073 0x1a, 0xff,
0074 0x1b, 0xff,
0075 0x1c, 0xff,
0076 0x1d, 0xff,
0077 0x1e, 0xff,
0078 0x1f, 0xff,
0079 0x20, 0x07,
0080 0x21, 0x07,
0081 0x22, 0x00,
0082 0x23, 0x91,
0083 0x24, 0x51,
0084 0x25, 0x03,
0085 0x26, 0x00,
0086 0x27, 0x00,
0087 0x28, 0x00,
0088 0x29, 0x00,
0089 0x2a, 0x00,
0090 0x2b, 0x00,
0091 0x2c, 0x00,
0092 0x2d, 0x00,
0093 0x2e, 0x00,
0094 0x2f, 0x00,
0095 0x30, 0x00,
0096 0x31, 0x00,
0097 0x32, 0x00,
0098 0x33, 0x00,
0099 0x34, 0x00,
0100 0x35, 0x00,
0101 0x36, 0x00,
0102 0x37, 0x00,
0103 0xff, 0xff,
0104 };
0105
0106 static int write_reg(struct i2c_client *client, u8 reg, u8 value, u8 channel)
0107 {
0108 return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
0109 }
0110
0111 static int write_regs(struct i2c_client *client, const u8 *regs, u8 channel)
0112 {
0113 int ret;
0114 int i;
0115
0116 for (i = 0; regs[i] != 0xff; i += 2) {
0117 ret = i2c_smbus_write_byte_data(client,
0118 regs[i] | (channel << 6), regs[i + 1]);
0119 if (ret < 0)
0120 return ret;
0121 }
0122 return 0;
0123 }
0124
0125 static int read_reg(struct i2c_client *client, u8 reg, u8 channel)
0126 {
0127 return i2c_smbus_read_byte_data(client, (reg) | (channel << 6));
0128 }
0129
0130 static inline struct tw2804 *to_state(struct v4l2_subdev *sd)
0131 {
0132 return container_of(sd, struct tw2804, sd);
0133 }
0134
0135 static inline struct tw2804 *to_state_from_ctrl(struct v4l2_ctrl *ctrl)
0136 {
0137 return container_of(ctrl->handler, struct tw2804, hdl);
0138 }
0139
0140 static int tw2804_log_status(struct v4l2_subdev *sd)
0141 {
0142 struct tw2804 *state = to_state(sd);
0143
0144 v4l2_info(sd, "Standard: %s\n",
0145 state->norm & V4L2_STD_525_60 ? "60 Hz" : "50 Hz");
0146 v4l2_info(sd, "Channel: %d\n", state->channel);
0147 v4l2_info(sd, "Input: %d\n", state->input);
0148 return v4l2_ctrl_subdev_log_status(sd);
0149 }
0150
0151
0152
0153
0154
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164 static int tw2804_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
0165 {
0166 struct tw2804 *state = to_state_from_ctrl(ctrl);
0167 struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
0168
0169 switch (ctrl->id) {
0170 case V4L2_CID_GAIN:
0171 ctrl->val = read_reg(client, TW2804_REG_GAIN, 0);
0172 return 0;
0173
0174 case V4L2_CID_CHROMA_GAIN:
0175 ctrl->val = read_reg(client, TW2804_REG_CHROMA_GAIN, 0);
0176 return 0;
0177
0178 case V4L2_CID_BLUE_BALANCE:
0179 ctrl->val = read_reg(client, TW2804_REG_BLUE_BALANCE, 0);
0180 return 0;
0181
0182 case V4L2_CID_RED_BALANCE:
0183 ctrl->val = read_reg(client, TW2804_REG_RED_BALANCE, 0);
0184 return 0;
0185 }
0186 return 0;
0187 }
0188
0189 static int tw2804_s_ctrl(struct v4l2_ctrl *ctrl)
0190 {
0191 struct tw2804 *state = to_state_from_ctrl(ctrl);
0192 struct i2c_client *client = v4l2_get_subdevdata(&state->sd);
0193 int addr;
0194 int reg;
0195
0196 switch (ctrl->id) {
0197 case V4L2_CID_AUTOGAIN:
0198 addr = TW2804_REG_AUTOGAIN;
0199 reg = read_reg(client, addr, state->channel);
0200 if (reg < 0)
0201 return reg;
0202 if (ctrl->val == 0)
0203 reg &= ~(1 << 7);
0204 else
0205 reg |= 1 << 7;
0206 return write_reg(client, addr, reg, state->channel);
0207
0208 case V4L2_CID_COLOR_KILLER:
0209 addr = TW2804_REG_COLOR_KILLER;
0210 reg = read_reg(client, addr, state->channel);
0211 if (reg < 0)
0212 return reg;
0213 reg = (reg & ~(0x03)) | (ctrl->val == 0 ? 0x02 : 0x03);
0214 return write_reg(client, addr, reg, state->channel);
0215
0216 case V4L2_CID_GAIN:
0217 return write_reg(client, TW2804_REG_GAIN, ctrl->val, 0);
0218
0219 case V4L2_CID_CHROMA_GAIN:
0220 return write_reg(client, TW2804_REG_CHROMA_GAIN, ctrl->val, 0);
0221
0222 case V4L2_CID_BLUE_BALANCE:
0223 return write_reg(client, TW2804_REG_BLUE_BALANCE, ctrl->val, 0);
0224
0225 case V4L2_CID_RED_BALANCE:
0226 return write_reg(client, TW2804_REG_RED_BALANCE, ctrl->val, 0);
0227
0228 case V4L2_CID_BRIGHTNESS:
0229 return write_reg(client, TW2804_REG_BRIGHTNESS,
0230 ctrl->val, state->channel);
0231
0232 case V4L2_CID_CONTRAST:
0233 return write_reg(client, TW2804_REG_CONTRAST,
0234 ctrl->val, state->channel);
0235
0236 case V4L2_CID_SATURATION:
0237 return write_reg(client, TW2804_REG_SATURATION,
0238 ctrl->val, state->channel);
0239
0240 case V4L2_CID_HUE:
0241 return write_reg(client, TW2804_REG_HUE,
0242 ctrl->val, state->channel);
0243
0244 default:
0245 break;
0246 }
0247 return -EINVAL;
0248 }
0249
0250 static int tw2804_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
0251 {
0252 struct tw2804 *dec = to_state(sd);
0253 struct i2c_client *client = v4l2_get_subdevdata(sd);
0254 bool is_60hz = norm & V4L2_STD_525_60;
0255 u8 regs[] = {
0256 0x01, is_60hz ? 0xc4 : 0x84,
0257 0x09, is_60hz ? 0x07 : 0x04,
0258 0x0a, is_60hz ? 0xf0 : 0x20,
0259 0x0b, is_60hz ? 0x07 : 0x04,
0260 0x0c, is_60hz ? 0xf0 : 0x20,
0261 0x0d, is_60hz ? 0x40 : 0x4a,
0262 0x16, is_60hz ? 0x00 : 0x40,
0263 0x17, is_60hz ? 0x00 : 0x40,
0264 0x20, is_60hz ? 0x07 : 0x0f,
0265 0x21, is_60hz ? 0x07 : 0x0f,
0266 0xff, 0xff,
0267 };
0268
0269 write_regs(client, regs, dec->channel);
0270 dec->norm = norm;
0271 return 0;
0272 }
0273
0274 static int tw2804_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output,
0275 u32 config)
0276 {
0277 struct tw2804 *dec = to_state(sd);
0278 struct i2c_client *client = v4l2_get_subdevdata(sd);
0279 int reg;
0280
0281 if (config && config - 1 != dec->channel) {
0282 if (config > 4) {
0283 dev_err(&client->dev,
0284 "channel %d is not between 1 and 4!\n", config);
0285 return -EINVAL;
0286 }
0287 dec->channel = config - 1;
0288 dev_dbg(&client->dev, "initializing TW2804 channel %d\n",
0289 dec->channel);
0290 if (dec->channel == 0 &&
0291 write_regs(client, global_registers, 0) < 0) {
0292 dev_err(&client->dev,
0293 "error initializing TW2804 global registers\n");
0294 return -EIO;
0295 }
0296 if (write_regs(client, channel_registers, dec->channel) < 0) {
0297 dev_err(&client->dev,
0298 "error initializing TW2804 channel %d\n",
0299 dec->channel);
0300 return -EIO;
0301 }
0302 }
0303
0304 if (input > 1)
0305 return -EINVAL;
0306
0307 if (input == dec->input)
0308 return 0;
0309
0310 reg = read_reg(client, 0x22, dec->channel);
0311
0312 if (reg >= 0) {
0313 if (input == 0)
0314 reg &= ~(1 << 2);
0315 else
0316 reg |= 1 << 2;
0317 reg = write_reg(client, 0x22, reg, dec->channel);
0318 }
0319
0320 if (reg >= 0)
0321 dec->input = input;
0322 else
0323 return reg;
0324 return 0;
0325 }
0326
0327 static const struct v4l2_ctrl_ops tw2804_ctrl_ops = {
0328 .g_volatile_ctrl = tw2804_g_volatile_ctrl,
0329 .s_ctrl = tw2804_s_ctrl,
0330 };
0331
0332 static const struct v4l2_subdev_video_ops tw2804_video_ops = {
0333 .s_std = tw2804_s_std,
0334 .s_routing = tw2804_s_video_routing,
0335 };
0336
0337 static const struct v4l2_subdev_core_ops tw2804_core_ops = {
0338 .log_status = tw2804_log_status,
0339 };
0340
0341 static const struct v4l2_subdev_ops tw2804_ops = {
0342 .core = &tw2804_core_ops,
0343 .video = &tw2804_video_ops,
0344 };
0345
0346 static int tw2804_probe(struct i2c_client *client,
0347 const struct i2c_device_id *id)
0348 {
0349 struct i2c_adapter *adapter = client->adapter;
0350 struct tw2804 *state;
0351 struct v4l2_subdev *sd;
0352 struct v4l2_ctrl *ctrl;
0353 int err;
0354
0355 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
0356 return -ENODEV;
0357
0358 state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL);
0359 if (state == NULL)
0360 return -ENOMEM;
0361 sd = &state->sd;
0362 v4l2_i2c_subdev_init(sd, client, &tw2804_ops);
0363 state->channel = -1;
0364 state->norm = V4L2_STD_NTSC;
0365
0366 v4l2_ctrl_handler_init(&state->hdl, 10);
0367 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
0368 V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
0369 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
0370 V4L2_CID_CONTRAST, 0, 255, 1, 128);
0371 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
0372 V4L2_CID_SATURATION, 0, 255, 1, 128);
0373 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
0374 V4L2_CID_HUE, 0, 255, 1, 128);
0375 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
0376 V4L2_CID_COLOR_KILLER, 0, 1, 1, 0);
0377 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
0378 V4L2_CID_AUTOGAIN, 0, 1, 1, 0);
0379 ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
0380 V4L2_CID_GAIN, 0, 255, 1, 128);
0381 if (ctrl)
0382 ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
0383 ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
0384 V4L2_CID_CHROMA_GAIN, 0, 255, 1, 128);
0385 if (ctrl)
0386 ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
0387 ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
0388 V4L2_CID_BLUE_BALANCE, 0, 255, 1, 122);
0389 if (ctrl)
0390 ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
0391 ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops,
0392 V4L2_CID_RED_BALANCE, 0, 255, 1, 122);
0393 if (ctrl)
0394 ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE;
0395 sd->ctrl_handler = &state->hdl;
0396 err = state->hdl.error;
0397 if (err) {
0398 v4l2_ctrl_handler_free(&state->hdl);
0399 return err;
0400 }
0401
0402 v4l_info(client, "chip found @ 0x%02x (%s)\n",
0403 client->addr << 1, client->adapter->name);
0404
0405 return 0;
0406 }
0407
0408 static int tw2804_remove(struct i2c_client *client)
0409 {
0410 struct v4l2_subdev *sd = i2c_get_clientdata(client);
0411 struct tw2804 *state = to_state(sd);
0412
0413 v4l2_device_unregister_subdev(sd);
0414 v4l2_ctrl_handler_free(&state->hdl);
0415 return 0;
0416 }
0417
0418 static const struct i2c_device_id tw2804_id[] = {
0419 { "tw2804", 0 },
0420 { }
0421 };
0422 MODULE_DEVICE_TABLE(i2c, tw2804_id);
0423
0424 static struct i2c_driver tw2804_driver = {
0425 .driver = {
0426 .name = "tw2804",
0427 },
0428 .probe = tw2804_probe,
0429 .remove = tw2804_remove,
0430 .id_table = tw2804_id,
0431 };
0432
0433 module_i2c_driver(tw2804_driver);
0434
0435 MODULE_LICENSE("GPL v2");
0436 MODULE_DESCRIPTION("TW2804/TW2802 V4L2 i2c driver");
0437 MODULE_AUTHOR("Micronas USA Inc");