1a000e9a0SHans Verkuil /* 2a000e9a0SHans Verkuil * Copyright (C) 2005-2006 Micronas USA Inc. 3a000e9a0SHans Verkuil * 4a000e9a0SHans Verkuil * This program is free software; you can redistribute it and/or modify 5a000e9a0SHans Verkuil * it under the terms of the GNU General Public License (Version 2) as 6a000e9a0SHans Verkuil * published by the Free Software Foundation. 7a000e9a0SHans Verkuil * 8a000e9a0SHans Verkuil * This program is distributed in the hope that it will be useful, 9a000e9a0SHans Verkuil * but WITHOUT ANY WARRANTY; without even the implied warranty of 10a000e9a0SHans Verkuil * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11a000e9a0SHans Verkuil * GNU General Public License for more details. 12a000e9a0SHans Verkuil * 13a000e9a0SHans Verkuil * You should have received a copy of the GNU General Public License 14a000e9a0SHans Verkuil * along with this program; if not, write to the Free Software Foundation, 15a000e9a0SHans Verkuil * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 16a000e9a0SHans Verkuil */ 17a000e9a0SHans Verkuil 18a000e9a0SHans Verkuil #include <linux/module.h> 19a000e9a0SHans Verkuil #include <linux/init.h> 20a000e9a0SHans Verkuil #include <linux/version.h> 21a000e9a0SHans Verkuil #include <linux/i2c.h> 22a000e9a0SHans Verkuil #include <linux/videodev2.h> 23a000e9a0SHans Verkuil #include <linux/ioctl.h> 24a000e9a0SHans Verkuil #include <linux/slab.h> 25a000e9a0SHans Verkuil #include <media/v4l2-device.h> 26a000e9a0SHans Verkuil #include <media/v4l2-ctrls.h> 27a000e9a0SHans Verkuil 28a000e9a0SHans Verkuil MODULE_DESCRIPTION("TW9906 I2C subdev driver"); 29a000e9a0SHans Verkuil MODULE_LICENSE("GPL v2"); 30a000e9a0SHans Verkuil 31a000e9a0SHans Verkuil struct tw9906 { 32a000e9a0SHans Verkuil struct v4l2_subdev sd; 33a000e9a0SHans Verkuil struct v4l2_ctrl_handler hdl; 34a000e9a0SHans Verkuil v4l2_std_id norm; 35a000e9a0SHans Verkuil }; 36a000e9a0SHans Verkuil 37a000e9a0SHans Verkuil static inline struct tw9906 *to_state(struct v4l2_subdev *sd) 38a000e9a0SHans Verkuil { 39a000e9a0SHans Verkuil return container_of(sd, struct tw9906, sd); 40a000e9a0SHans Verkuil } 41a000e9a0SHans Verkuil 42a000e9a0SHans Verkuil static const u8 initial_registers[] = { 43a000e9a0SHans Verkuil 0x02, 0x40, /* input 0, composite */ 44a000e9a0SHans Verkuil 0x03, 0xa2, /* correct digital format */ 45a000e9a0SHans Verkuil 0x05, 0x81, /* or 0x01 for PAL */ 46a000e9a0SHans Verkuil 0x07, 0x02, /* window */ 47a000e9a0SHans Verkuil 0x08, 0x14, /* window */ 48a000e9a0SHans Verkuil 0x09, 0xf0, /* window */ 49a000e9a0SHans Verkuil 0x0a, 0x10, /* window */ 50a000e9a0SHans Verkuil 0x0b, 0xd0, /* window */ 51a000e9a0SHans Verkuil 0x0d, 0x00, /* scaling */ 52a000e9a0SHans Verkuil 0x0e, 0x11, /* scaling */ 53a000e9a0SHans Verkuil 0x0f, 0x00, /* scaling */ 54a000e9a0SHans Verkuil 0x10, 0x00, /* brightness */ 55a000e9a0SHans Verkuil 0x11, 0x60, /* contrast */ 56a000e9a0SHans Verkuil 0x12, 0x11, /* sharpness */ 57a000e9a0SHans Verkuil 0x13, 0x7e, /* U gain */ 58a000e9a0SHans Verkuil 0x14, 0x7e, /* V gain */ 59a000e9a0SHans Verkuil 0x15, 0x00, /* hue */ 60a000e9a0SHans Verkuil 0x19, 0x57, /* vbi */ 61a000e9a0SHans Verkuil 0x1a, 0x0f, 62a000e9a0SHans Verkuil 0x1b, 0x40, 63a000e9a0SHans Verkuil 0x29, 0x03, 64a000e9a0SHans Verkuil 0x55, 0x00, 65a000e9a0SHans Verkuil 0x6b, 0x26, 66a000e9a0SHans Verkuil 0x6c, 0x36, 67a000e9a0SHans Verkuil 0x6d, 0xf0, 68a000e9a0SHans Verkuil 0x6e, 0x41, 69a000e9a0SHans Verkuil 0x6f, 0x13, 70a000e9a0SHans Verkuil 0xad, 0x70, 71a000e9a0SHans Verkuil 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ 72a000e9a0SHans Verkuil }; 73a000e9a0SHans Verkuil 74a000e9a0SHans Verkuil static int write_reg(struct v4l2_subdev *sd, u8 reg, u8 value) 75a000e9a0SHans Verkuil { 76a000e9a0SHans Verkuil struct i2c_client *client = v4l2_get_subdevdata(sd); 77a000e9a0SHans Verkuil 78a000e9a0SHans Verkuil return i2c_smbus_write_byte_data(client, reg, value); 79a000e9a0SHans Verkuil } 80a000e9a0SHans Verkuil 81a000e9a0SHans Verkuil static int write_regs(struct v4l2_subdev *sd, const u8 *regs) 82a000e9a0SHans Verkuil { 83a000e9a0SHans Verkuil int i; 84a000e9a0SHans Verkuil 85a000e9a0SHans Verkuil for (i = 0; regs[i] != 0x00; i += 2) 86a000e9a0SHans Verkuil if (write_reg(sd, regs[i], regs[i + 1]) < 0) 87a000e9a0SHans Verkuil return -1; 88a000e9a0SHans Verkuil return 0; 89a000e9a0SHans Verkuil } 90a000e9a0SHans Verkuil 91a000e9a0SHans Verkuil static int tw9906_s_video_routing(struct v4l2_subdev *sd, u32 input, 92a000e9a0SHans Verkuil u32 output, u32 config) 93a000e9a0SHans Verkuil { 94a000e9a0SHans Verkuil write_reg(sd, 0x02, 0x40 | (input << 1)); 95a000e9a0SHans Verkuil return 0; 96a000e9a0SHans Verkuil } 97a000e9a0SHans Verkuil 98a000e9a0SHans Verkuil static int tw9906_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) 99a000e9a0SHans Verkuil { 100a000e9a0SHans Verkuil struct tw9906 *dec = to_state(sd); 101a000e9a0SHans Verkuil bool is_60hz = norm & V4L2_STD_525_60; 102a000e9a0SHans Verkuil u8 regs[] = { 103a000e9a0SHans Verkuil 0x05, is_60hz ? 0x81 : 0x01, 104a000e9a0SHans Verkuil 0x07, is_60hz ? 0x02 : 0x12, 105a000e9a0SHans Verkuil 0x08, is_60hz ? 0x14 : 0x18, 106a000e9a0SHans Verkuil 0x09, is_60hz ? 0xf0 : 0x20, 107a000e9a0SHans Verkuil 0, 0, 108a000e9a0SHans Verkuil }; 109a000e9a0SHans Verkuil 110a000e9a0SHans Verkuil write_regs(sd, regs); 111a000e9a0SHans Verkuil dec->norm = norm; 112a000e9a0SHans Verkuil return 0; 113a000e9a0SHans Verkuil } 114a000e9a0SHans Verkuil 115a000e9a0SHans Verkuil static int tw9906_s_ctrl(struct v4l2_ctrl *ctrl) 116a000e9a0SHans Verkuil { 117a000e9a0SHans Verkuil struct tw9906 *dec = container_of(ctrl->handler, struct tw9906, hdl); 118a000e9a0SHans Verkuil struct v4l2_subdev *sd = &dec->sd; 119a000e9a0SHans Verkuil 120a000e9a0SHans Verkuil switch (ctrl->id) { 121a000e9a0SHans Verkuil case V4L2_CID_BRIGHTNESS: 122a000e9a0SHans Verkuil write_reg(sd, 0x10, ctrl->val); 123a000e9a0SHans Verkuil break; 124a000e9a0SHans Verkuil case V4L2_CID_CONTRAST: 125a000e9a0SHans Verkuil write_reg(sd, 0x11, ctrl->val); 126a000e9a0SHans Verkuil break; 127a000e9a0SHans Verkuil case V4L2_CID_HUE: 128a000e9a0SHans Verkuil write_reg(sd, 0x15, ctrl->val); 129a000e9a0SHans Verkuil break; 130a000e9a0SHans Verkuil default: 131a000e9a0SHans Verkuil return -EINVAL; 132a000e9a0SHans Verkuil } 133a000e9a0SHans Verkuil return 0; 134a000e9a0SHans Verkuil } 135a000e9a0SHans Verkuil 136a000e9a0SHans Verkuil static int tw9906_log_status(struct v4l2_subdev *sd) 137a000e9a0SHans Verkuil { 138a000e9a0SHans Verkuil struct tw9906 *dec = to_state(sd); 139a000e9a0SHans Verkuil bool is_60hz = dec->norm & V4L2_STD_525_60; 140a000e9a0SHans Verkuil 141a000e9a0SHans Verkuil v4l2_info(sd, "Standard: %d Hz\n", is_60hz ? 60 : 50); 142a000e9a0SHans Verkuil v4l2_ctrl_subdev_log_status(sd); 143a000e9a0SHans Verkuil return 0; 144a000e9a0SHans Verkuil } 145a000e9a0SHans Verkuil 146a000e9a0SHans Verkuil /* --------------------------------------------------------------------------*/ 147a000e9a0SHans Verkuil 148a000e9a0SHans Verkuil static const struct v4l2_ctrl_ops tw9906_ctrl_ops = { 149a000e9a0SHans Verkuil .s_ctrl = tw9906_s_ctrl, 150a000e9a0SHans Verkuil }; 151a000e9a0SHans Verkuil 152a000e9a0SHans Verkuil static const struct v4l2_subdev_core_ops tw9906_core_ops = { 153a000e9a0SHans Verkuil .log_status = tw9906_log_status, 154a000e9a0SHans Verkuil .s_std = tw9906_s_std, 155a000e9a0SHans Verkuil }; 156a000e9a0SHans Verkuil 157a000e9a0SHans Verkuil static const struct v4l2_subdev_video_ops tw9906_video_ops = { 158a000e9a0SHans Verkuil .s_routing = tw9906_s_video_routing, 159a000e9a0SHans Verkuil }; 160a000e9a0SHans Verkuil 161a000e9a0SHans Verkuil static const struct v4l2_subdev_ops tw9906_ops = { 162a000e9a0SHans Verkuil .core = &tw9906_core_ops, 163a000e9a0SHans Verkuil .video = &tw9906_video_ops, 164a000e9a0SHans Verkuil }; 165a000e9a0SHans Verkuil 166a000e9a0SHans Verkuil static int tw9906_probe(struct i2c_client *client, 167a000e9a0SHans Verkuil const struct i2c_device_id *id) 168a000e9a0SHans Verkuil { 169a000e9a0SHans Verkuil struct tw9906 *dec; 170a000e9a0SHans Verkuil struct v4l2_subdev *sd; 171a000e9a0SHans Verkuil struct v4l2_ctrl_handler *hdl; 172a000e9a0SHans Verkuil 173a000e9a0SHans Verkuil /* Check if the adapter supports the needed features */ 174a000e9a0SHans Verkuil if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 175a000e9a0SHans Verkuil return -EIO; 176a000e9a0SHans Verkuil 177a000e9a0SHans Verkuil v4l_info(client, "chip found @ 0x%02x (%s)\n", 178a000e9a0SHans Verkuil client->addr << 1, client->adapter->name); 179a000e9a0SHans Verkuil 180a000e9a0SHans Verkuil dec = kzalloc(sizeof(struct tw9906), GFP_KERNEL); 181a000e9a0SHans Verkuil if (dec == NULL) 182a000e9a0SHans Verkuil return -ENOMEM; 183a000e9a0SHans Verkuil sd = &dec->sd; 184a000e9a0SHans Verkuil v4l2_i2c_subdev_init(sd, client, &tw9906_ops); 185a000e9a0SHans Verkuil hdl = &dec->hdl; 186a000e9a0SHans Verkuil v4l2_ctrl_handler_init(hdl, 4); 187a000e9a0SHans Verkuil v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops, 188a000e9a0SHans Verkuil V4L2_CID_BRIGHTNESS, -128, 127, 1, 0); 189a000e9a0SHans Verkuil v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops, 190a000e9a0SHans Verkuil V4L2_CID_CONTRAST, 0, 255, 1, 0x60); 191a000e9a0SHans Verkuil v4l2_ctrl_new_std(hdl, &tw9906_ctrl_ops, 192a000e9a0SHans Verkuil V4L2_CID_HUE, -128, 127, 1, 0); 193a000e9a0SHans Verkuil sd->ctrl_handler = hdl; 194a000e9a0SHans Verkuil if (hdl->error) { 195a000e9a0SHans Verkuil int err = hdl->error; 196a000e9a0SHans Verkuil 197a000e9a0SHans Verkuil v4l2_ctrl_handler_free(hdl); 198a000e9a0SHans Verkuil kfree(dec); 199a000e9a0SHans Verkuil return err; 200a000e9a0SHans Verkuil } 201a000e9a0SHans Verkuil 202a000e9a0SHans Verkuil /* Initialize tw9906 */ 203a000e9a0SHans Verkuil dec->norm = V4L2_STD_NTSC; 204a000e9a0SHans Verkuil 205a000e9a0SHans Verkuil if (write_regs(sd, initial_registers) < 0) { 206a000e9a0SHans Verkuil v4l2_err(client, "error initializing TW9906\n"); 207a000e9a0SHans Verkuil kfree(dec); 208a000e9a0SHans Verkuil return -EINVAL; 209a000e9a0SHans Verkuil } 210a000e9a0SHans Verkuil 211a000e9a0SHans Verkuil return 0; 212a000e9a0SHans Verkuil } 213a000e9a0SHans Verkuil 214a000e9a0SHans Verkuil static int tw9906_remove(struct i2c_client *client) 215a000e9a0SHans Verkuil { 216a000e9a0SHans Verkuil struct v4l2_subdev *sd = i2c_get_clientdata(client); 217a000e9a0SHans Verkuil 218a000e9a0SHans Verkuil v4l2_device_unregister_subdev(sd); 219a000e9a0SHans Verkuil v4l2_ctrl_handler_free(&to_state(sd)->hdl); 220a000e9a0SHans Verkuil kfree(to_state(sd)); 221a000e9a0SHans Verkuil return 0; 222a000e9a0SHans Verkuil } 223a000e9a0SHans Verkuil 224a000e9a0SHans Verkuil /* ----------------------------------------------------------------------- */ 225a000e9a0SHans Verkuil 226a000e9a0SHans Verkuil static const struct i2c_device_id tw9906_id[] = { 227a000e9a0SHans Verkuil { "tw9906", 0 }, 228a000e9a0SHans Verkuil { } 229a000e9a0SHans Verkuil }; 230a000e9a0SHans Verkuil MODULE_DEVICE_TABLE(i2c, tw9906_id); 231a000e9a0SHans Verkuil 232a000e9a0SHans Verkuil static struct i2c_driver tw9906_driver = { 233a000e9a0SHans Verkuil .driver = { 234a000e9a0SHans Verkuil .owner = THIS_MODULE, 235a000e9a0SHans Verkuil .name = "tw9906", 236a000e9a0SHans Verkuil }, 237a000e9a0SHans Verkuil .probe = tw9906_probe, 238a000e9a0SHans Verkuil .remove = tw9906_remove, 239a000e9a0SHans Verkuil .id_table = tw9906_id, 240a000e9a0SHans Verkuil }; 241a000e9a0SHans Verkuil module_i2c_driver(tw9906_driver); 242