1 /* 2 * Copyright (C) 2005-2006 Micronas USA Inc. 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License (Version 2) as 6 * published by the Free Software Foundation. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 * You should have received a copy of the GNU General Public License 14 * along with this program; if not, write to the Free Software Foundation, 15 * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 16 */ 17 18 #include <linux/module.h> 19 #include <linux/init.h> 20 #include <linux/i2c.h> 21 #include <linux/videodev2.h> 22 #include <linux/ioctl.h> 23 #include <linux/slab.h> 24 #include <media/v4l2-subdev.h> 25 #include <media/v4l2-device.h> 26 #include <media/v4l2-ctrls.h> 27 28 #define TW2804_REG_AUTOGAIN 0x02 29 #define TW2804_REG_HUE 0x0f 30 #define TW2804_REG_SATURATION 0x10 31 #define TW2804_REG_CONTRAST 0x11 32 #define TW2804_REG_BRIGHTNESS 0x12 33 #define TW2804_REG_COLOR_KILLER 0x14 34 #define TW2804_REG_GAIN 0x3c 35 #define TW2804_REG_CHROMA_GAIN 0x3d 36 #define TW2804_REG_BLUE_BALANCE 0x3e 37 #define TW2804_REG_RED_BALANCE 0x3f 38 39 struct tw2804 { 40 struct v4l2_subdev sd; 41 struct v4l2_ctrl_handler hdl; 42 u8 channel:2; 43 u8 input:1; 44 int norm; 45 }; 46 47 static const u8 global_registers[] = { 48 0x39, 0x00, 49 0x3a, 0xff, 50 0x3b, 0x84, 51 0x3c, 0x80, 52 0x3d, 0x80, 53 0x3e, 0x82, 54 0x3f, 0x82, 55 0x78, 0x00, 56 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ 57 }; 58 59 static const u8 channel_registers[] = { 60 0x01, 0xc4, 61 0x02, 0xa5, 62 0x03, 0x20, 63 0x04, 0xd0, 64 0x05, 0x20, 65 0x06, 0xd0, 66 0x07, 0x88, 67 0x08, 0x20, 68 0x09, 0x07, 69 0x0a, 0xf0, 70 0x0b, 0x07, 71 0x0c, 0xf0, 72 0x0d, 0x40, 73 0x0e, 0xd2, 74 0x0f, 0x80, 75 0x10, 0x80, 76 0x11, 0x80, 77 0x12, 0x80, 78 0x13, 0x1f, 79 0x14, 0x00, 80 0x15, 0x00, 81 0x16, 0x00, 82 0x17, 0x00, 83 0x18, 0xff, 84 0x19, 0xff, 85 0x1a, 0xff, 86 0x1b, 0xff, 87 0x1c, 0xff, 88 0x1d, 0xff, 89 0x1e, 0xff, 90 0x1f, 0xff, 91 0x20, 0x07, 92 0x21, 0x07, 93 0x22, 0x00, 94 0x23, 0x91, 95 0x24, 0x51, 96 0x25, 0x03, 97 0x26, 0x00, 98 0x27, 0x00, 99 0x28, 0x00, 100 0x29, 0x00, 101 0x2a, 0x00, 102 0x2b, 0x00, 103 0x2c, 0x00, 104 0x2d, 0x00, 105 0x2e, 0x00, 106 0x2f, 0x00, 107 0x30, 0x00, 108 0x31, 0x00, 109 0x32, 0x00, 110 0x33, 0x00, 111 0x34, 0x00, 112 0x35, 0x00, 113 0x36, 0x00, 114 0x37, 0x00, 115 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ 116 }; 117 118 static int write_reg(struct i2c_client *client, u8 reg, u8 value, u8 channel) 119 { 120 return i2c_smbus_write_byte_data(client, reg | (channel << 6), value); 121 } 122 123 static int write_regs(struct i2c_client *client, const u8 *regs, u8 channel) 124 { 125 int ret; 126 int i; 127 128 for (i = 0; regs[i] != 0xff; i += 2) { 129 ret = i2c_smbus_write_byte_data(client, 130 regs[i] | (channel << 6), regs[i + 1]); 131 if (ret < 0) 132 return ret; 133 } 134 return 0; 135 } 136 137 static int read_reg(struct i2c_client *client, u8 reg, u8 channel) 138 { 139 return i2c_smbus_read_byte_data(client, (reg) | (channel << 6)); 140 } 141 142 static inline struct tw2804 *to_state(struct v4l2_subdev *sd) 143 { 144 return container_of(sd, struct tw2804, sd); 145 } 146 147 static inline struct tw2804 *to_state_from_ctrl(struct v4l2_ctrl *ctrl) 148 { 149 return container_of(ctrl->handler, struct tw2804, hdl); 150 } 151 152 static int tw2804_log_status(struct v4l2_subdev *sd) 153 { 154 struct tw2804 *state = to_state(sd); 155 156 v4l2_info(sd, "Standard: %s\n", 157 state->norm & V4L2_STD_525_60 ? "60 Hz" : "50 Hz"); 158 v4l2_info(sd, "Channel: %d\n", state->channel); 159 v4l2_info(sd, "Input: %d\n", state->input); 160 return v4l2_ctrl_subdev_log_status(sd); 161 } 162 163 /* 164 * These volatile controls are needed because all four channels share 165 * these controls. So a change made to them through one channel would 166 * require another channel to be updated. 167 * 168 * Normally this would have been done in a different way, but since the one 169 * board that uses this driver sees this single chip as if it was on four 170 * different i2c adapters (each adapter belonging to a separate instance of 171 * the same USB driver) there is no reliable method that I have found to let 172 * the instances know about each other. 173 * 174 * So implementing these global registers as volatile is the best we can do. 175 */ 176 static int tw2804_g_volatile_ctrl(struct v4l2_ctrl *ctrl) 177 { 178 struct tw2804 *state = to_state_from_ctrl(ctrl); 179 struct i2c_client *client = v4l2_get_subdevdata(&state->sd); 180 181 switch (ctrl->id) { 182 case V4L2_CID_GAIN: 183 ctrl->val = read_reg(client, TW2804_REG_GAIN, 0); 184 return 0; 185 186 case V4L2_CID_CHROMA_GAIN: 187 ctrl->val = read_reg(client, TW2804_REG_CHROMA_GAIN, 0); 188 return 0; 189 190 case V4L2_CID_BLUE_BALANCE: 191 ctrl->val = read_reg(client, TW2804_REG_BLUE_BALANCE, 0); 192 return 0; 193 194 case V4L2_CID_RED_BALANCE: 195 ctrl->val = read_reg(client, TW2804_REG_RED_BALANCE, 0); 196 return 0; 197 } 198 return 0; 199 } 200 201 static int tw2804_s_ctrl(struct v4l2_ctrl *ctrl) 202 { 203 struct tw2804 *state = to_state_from_ctrl(ctrl); 204 struct i2c_client *client = v4l2_get_subdevdata(&state->sd); 205 int addr; 206 int reg; 207 208 switch (ctrl->id) { 209 case V4L2_CID_AUTOGAIN: 210 addr = TW2804_REG_AUTOGAIN; 211 reg = read_reg(client, addr, state->channel); 212 if (reg < 0) 213 return reg; 214 if (ctrl->val == 0) 215 reg &= ~(1 << 7); 216 else 217 reg |= 1 << 7; 218 return write_reg(client, addr, reg, state->channel); 219 220 case V4L2_CID_COLOR_KILLER: 221 addr = TW2804_REG_COLOR_KILLER; 222 reg = read_reg(client, addr, state->channel); 223 if (reg < 0) 224 return reg; 225 reg = (reg & ~(0x03)) | (ctrl->val == 0 ? 0x02 : 0x03); 226 return write_reg(client, addr, reg, state->channel); 227 228 case V4L2_CID_GAIN: 229 return write_reg(client, TW2804_REG_GAIN, ctrl->val, 0); 230 231 case V4L2_CID_CHROMA_GAIN: 232 return write_reg(client, TW2804_REG_CHROMA_GAIN, ctrl->val, 0); 233 234 case V4L2_CID_BLUE_BALANCE: 235 return write_reg(client, TW2804_REG_BLUE_BALANCE, ctrl->val, 0); 236 237 case V4L2_CID_RED_BALANCE: 238 return write_reg(client, TW2804_REG_RED_BALANCE, ctrl->val, 0); 239 240 case V4L2_CID_BRIGHTNESS: 241 return write_reg(client, TW2804_REG_BRIGHTNESS, 242 ctrl->val, state->channel); 243 244 case V4L2_CID_CONTRAST: 245 return write_reg(client, TW2804_REG_CONTRAST, 246 ctrl->val, state->channel); 247 248 case V4L2_CID_SATURATION: 249 return write_reg(client, TW2804_REG_SATURATION, 250 ctrl->val, state->channel); 251 252 case V4L2_CID_HUE: 253 return write_reg(client, TW2804_REG_HUE, 254 ctrl->val, state->channel); 255 256 default: 257 break; 258 } 259 return -EINVAL; 260 } 261 262 static int tw2804_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) 263 { 264 struct tw2804 *dec = to_state(sd); 265 struct i2c_client *client = v4l2_get_subdevdata(sd); 266 bool is_60hz = norm & V4L2_STD_525_60; 267 u8 regs[] = { 268 0x01, is_60hz ? 0xc4 : 0x84, 269 0x09, is_60hz ? 0x07 : 0x04, 270 0x0a, is_60hz ? 0xf0 : 0x20, 271 0x0b, is_60hz ? 0x07 : 0x04, 272 0x0c, is_60hz ? 0xf0 : 0x20, 273 0x0d, is_60hz ? 0x40 : 0x4a, 274 0x16, is_60hz ? 0x00 : 0x40, 275 0x17, is_60hz ? 0x00 : 0x40, 276 0x20, is_60hz ? 0x07 : 0x0f, 277 0x21, is_60hz ? 0x07 : 0x0f, 278 0xff, 0xff, 279 }; 280 281 write_regs(client, regs, dec->channel); 282 dec->norm = norm; 283 return 0; 284 } 285 286 static int tw2804_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, 287 u32 config) 288 { 289 struct tw2804 *dec = to_state(sd); 290 struct i2c_client *client = v4l2_get_subdevdata(sd); 291 int reg; 292 293 if (config && config - 1 != dec->channel) { 294 if (config > 4) { 295 dev_err(&client->dev, 296 "channel %d is not between 1 and 4!\n", config); 297 return -EINVAL; 298 } 299 dec->channel = config - 1; 300 dev_dbg(&client->dev, "initializing TW2804 channel %d\n", 301 dec->channel); 302 if (dec->channel == 0 && 303 write_regs(client, global_registers, 0) < 0) { 304 dev_err(&client->dev, 305 "error initializing TW2804 global registers\n"); 306 return -EIO; 307 } 308 if (write_regs(client, channel_registers, dec->channel) < 0) { 309 dev_err(&client->dev, 310 "error initializing TW2804 channel %d\n", 311 dec->channel); 312 return -EIO; 313 } 314 } 315 316 if (input > 1) 317 return -EINVAL; 318 319 if (input == dec->input) 320 return 0; 321 322 reg = read_reg(client, 0x22, dec->channel); 323 324 if (reg >= 0) { 325 if (input == 0) 326 reg &= ~(1 << 2); 327 else 328 reg |= 1 << 2; 329 reg = write_reg(client, 0x22, reg, dec->channel); 330 } 331 332 if (reg >= 0) 333 dec->input = input; 334 else 335 return reg; 336 return 0; 337 } 338 339 static const struct v4l2_ctrl_ops tw2804_ctrl_ops = { 340 .g_volatile_ctrl = tw2804_g_volatile_ctrl, 341 .s_ctrl = tw2804_s_ctrl, 342 }; 343 344 static const struct v4l2_subdev_video_ops tw2804_video_ops = { 345 .s_routing = tw2804_s_video_routing, 346 }; 347 348 static const struct v4l2_subdev_core_ops tw2804_core_ops = { 349 .log_status = tw2804_log_status, 350 .s_std = tw2804_s_std, 351 }; 352 353 static const struct v4l2_subdev_ops tw2804_ops = { 354 .core = &tw2804_core_ops, 355 .video = &tw2804_video_ops, 356 }; 357 358 static int tw2804_probe(struct i2c_client *client, 359 const struct i2c_device_id *id) 360 { 361 struct i2c_adapter *adapter = client->adapter; 362 struct tw2804 *state; 363 struct v4l2_subdev *sd; 364 struct v4l2_ctrl *ctrl; 365 int err; 366 367 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 368 return -ENODEV; 369 370 state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); 371 if (state == NULL) 372 return -ENOMEM; 373 sd = &state->sd; 374 v4l2_i2c_subdev_init(sd, client, &tw2804_ops); 375 state->channel = -1; 376 state->norm = V4L2_STD_NTSC; 377 378 v4l2_ctrl_handler_init(&state->hdl, 10); 379 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, 380 V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); 381 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, 382 V4L2_CID_CONTRAST, 0, 255, 1, 128); 383 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, 384 V4L2_CID_SATURATION, 0, 255, 1, 128); 385 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, 386 V4L2_CID_HUE, 0, 255, 1, 128); 387 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, 388 V4L2_CID_COLOR_KILLER, 0, 1, 1, 0); 389 v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, 390 V4L2_CID_AUTOGAIN, 0, 1, 1, 0); 391 ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, 392 V4L2_CID_GAIN, 0, 255, 1, 128); 393 if (ctrl) 394 ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; 395 ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, 396 V4L2_CID_CHROMA_GAIN, 0, 255, 1, 128); 397 if (ctrl) 398 ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; 399 ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, 400 V4L2_CID_BLUE_BALANCE, 0, 255, 1, 122); 401 if (ctrl) 402 ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; 403 ctrl = v4l2_ctrl_new_std(&state->hdl, &tw2804_ctrl_ops, 404 V4L2_CID_RED_BALANCE, 0, 255, 1, 122); 405 if (ctrl) 406 ctrl->flags |= V4L2_CTRL_FLAG_VOLATILE; 407 sd->ctrl_handler = &state->hdl; 408 err = state->hdl.error; 409 if (err) { 410 v4l2_ctrl_handler_free(&state->hdl); 411 return err; 412 } 413 414 v4l_info(client, "chip found @ 0x%02x (%s)\n", 415 client->addr << 1, client->adapter->name); 416 417 return 0; 418 } 419 420 static int tw2804_remove(struct i2c_client *client) 421 { 422 struct v4l2_subdev *sd = i2c_get_clientdata(client); 423 struct tw2804 *state = to_state(sd); 424 425 v4l2_device_unregister_subdev(sd); 426 v4l2_ctrl_handler_free(&state->hdl); 427 return 0; 428 } 429 430 static const struct i2c_device_id tw2804_id[] = { 431 { "tw2804", 0 }, 432 { } 433 }; 434 MODULE_DEVICE_TABLE(i2c, tw2804_id); 435 436 static struct i2c_driver tw2804_driver = { 437 .driver = { 438 .name = "tw2804", 439 }, 440 .probe = tw2804_probe, 441 .remove = tw2804_remove, 442 .id_table = tw2804_id, 443 }; 444 445 module_i2c_driver(tw2804_driver); 446 447 MODULE_LICENSE("GPL v2"); 448 MODULE_DESCRIPTION("TW2804/TW2802 V4L2 i2c driver"); 449 MODULE_AUTHOR("Micronas USA Inc"); 450