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 25#include "wis-i2c.h" 26 27struct wis_tw2804 { 28 int channel; 29 int norm; 30 int brightness; 31 int contrast; 32 int saturation; 33 int hue; 34}; 35 36static u8 global_registers[] = 37{ 38 0x39, 0x00, 39 0x3a, 0xff, 40 0x3b, 0x84, 41 0x3c, 0x80, 42 0x3d, 0x80, 43 0x3e, 0x82, 44 0x3f, 0x82, 45 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ 46}; 47 48static u8 channel_registers[] = 49{ 50 0x01, 0xc4, 51 0x02, 0xa5, 52 0x03, 0x20, 53 0x04, 0xd0, 54 0x05, 0x20, 55 0x06, 0xd0, 56 0x07, 0x88, 57 0x08, 0x20, 58 0x09, 0x07, 59 0x0a, 0xf0, 60 0x0b, 0x07, 61 0x0c, 0xf0, 62 0x0d, 0x40, 63 0x0e, 0xd2, 64 0x0f, 0x80, 65 0x10, 0x80, 66 0x11, 0x80, 67 0x12, 0x80, 68 0x13, 0x1f, 69 0x14, 0x00, 70 0x15, 0x00, 71 0x16, 0x00, 72 0x17, 0x00, 73 0x18, 0xff, 74 0x19, 0xff, 75 0x1a, 0xff, 76 0x1b, 0xff, 77 0x1c, 0xff, 78 0x1d, 0xff, 79 0x1e, 0xff, 80 0x1f, 0xff, 81 0x20, 0x07, 82 0x21, 0x07, 83 0x22, 0x00, 84 0x23, 0x91, 85 0x24, 0x51, 86 0x25, 0x03, 87 0x26, 0x00, 88 0x27, 0x00, 89 0x28, 0x00, 90 0x29, 0x00, 91 0x2a, 0x00, 92 0x2b, 0x00, 93 0x2c, 0x00, 94 0x2d, 0x00, 95 0x2e, 0x00, 96 0x2f, 0x00, 97 0x30, 0x00, 98 0x31, 0x00, 99 0x32, 0x00, 100 0x33, 0x00, 101 0x34, 0x00, 102 0x35, 0x00, 103 0x36, 0x00, 104 0x37, 0x00, 105 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ 106}; 107 108static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel) 109{ 110 return i2c_smbus_write_byte_data(client, reg | (channel << 6), value); 111} 112 113static int write_regs(struct i2c_client *client, u8 *regs, int channel) 114{ 115 int i; 116 117 for (i = 0; regs[i] != 0xff; i += 2) 118 if (i2c_smbus_write_byte_data(client, 119 regs[i] | (channel << 6), regs[i + 1]) < 0) 120 return -1; 121 return 0; 122} 123 124static int wis_tw2804_command(struct i2c_client *client, 125 unsigned int cmd, void *arg) 126{ 127 struct wis_tw2804 *dec = i2c_get_clientdata(client); 128 129 if (cmd == DECODER_SET_CHANNEL) { 130 int *input = arg; 131 132 if (*input < 0 || *input > 3) { 133 printk(KERN_ERR "wis-tw2804: channel %d is not " 134 "between 0 and 3!\n", *input); 135 return 0; 136 } 137 dec->channel = *input; 138 printk(KERN_DEBUG "wis-tw2804: initializing TW2804 " 139 "channel %d\n", dec->channel); 140 if (dec->channel == 0 && 141 write_regs(client, global_registers, 0) < 0) { 142 printk(KERN_ERR "wis-tw2804: error initializing " 143 "TW2804 global registers\n"); 144 return 0; 145 } 146 if (write_regs(client, channel_registers, dec->channel) < 0) { 147 printk(KERN_ERR "wis-tw2804: error initializing " 148 "TW2804 channel %d\n", dec->channel); 149 return 0; 150 } 151 return 0; 152 } 153 154 if (dec->channel < 0) { 155 printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until " 156 "channel number is set\n", cmd); 157 return 0; 158 } 159 160 switch (cmd) { 161 case VIDIOC_S_STD: 162 { 163 v4l2_std_id *input = arg; 164 u8 regs[] = { 165 0x01, *input & V4L2_STD_NTSC ? 0xc4 : 0x84, 166 0x09, *input & V4L2_STD_NTSC ? 0x07 : 0x04, 167 0x0a, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, 168 0x0b, *input & V4L2_STD_NTSC ? 0x07 : 0x04, 169 0x0c, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, 170 0x0d, *input & V4L2_STD_NTSC ? 0x40 : 0x4a, 171 0x16, *input & V4L2_STD_NTSC ? 0x00 : 0x40, 172 0x17, *input & V4L2_STD_NTSC ? 0x00 : 0x40, 173 0x20, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, 174 0x21, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, 175 0xff, 0xff, 176 }; 177 write_regs(client, regs, dec->channel); 178 dec->norm = *input; 179 break; 180 } 181 case VIDIOC_QUERYCTRL: 182 { 183 struct v4l2_queryctrl *ctrl = arg; 184 185 switch (ctrl->id) { 186 case V4L2_CID_BRIGHTNESS: 187 ctrl->type = V4L2_CTRL_TYPE_INTEGER; 188 strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); 189 ctrl->minimum = 0; 190 ctrl->maximum = 255; 191 ctrl->step = 1; 192 ctrl->default_value = 128; 193 ctrl->flags = 0; 194 break; 195 case V4L2_CID_CONTRAST: 196 ctrl->type = V4L2_CTRL_TYPE_INTEGER; 197 strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); 198 ctrl->minimum = 0; 199 ctrl->maximum = 255; 200 ctrl->step = 1; 201 ctrl->default_value = 128; 202 ctrl->flags = 0; 203 break; 204 case V4L2_CID_SATURATION: 205 ctrl->type = V4L2_CTRL_TYPE_INTEGER; 206 strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); 207 ctrl->minimum = 0; 208 ctrl->maximum = 255; 209 ctrl->step = 1; 210 ctrl->default_value = 128; 211 ctrl->flags = 0; 212 break; 213 case V4L2_CID_HUE: 214 ctrl->type = V4L2_CTRL_TYPE_INTEGER; 215 strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); 216 ctrl->minimum = 0; 217 ctrl->maximum = 255; 218 ctrl->step = 1; 219 ctrl->default_value = 128; 220 ctrl->flags = 0; 221 break; 222 } 223 break; 224 } 225 case VIDIOC_S_CTRL: 226 { 227 struct v4l2_control *ctrl = arg; 228 229 switch (ctrl->id) { 230 case V4L2_CID_BRIGHTNESS: 231 if (ctrl->value > 255) 232 dec->brightness = 255; 233 else if (ctrl->value < 0) 234 dec->brightness = 0; 235 else 236 dec->brightness = ctrl->value; 237 write_reg(client, 0x12, dec->brightness, dec->channel); 238 break; 239 case V4L2_CID_CONTRAST: 240 if (ctrl->value > 255) 241 dec->contrast = 255; 242 else if (ctrl->value < 0) 243 dec->contrast = 0; 244 else 245 dec->contrast = ctrl->value; 246 write_reg(client, 0x11, dec->contrast, dec->channel); 247 break; 248 case V4L2_CID_SATURATION: 249 if (ctrl->value > 255) 250 dec->saturation = 255; 251 else if (ctrl->value < 0) 252 dec->saturation = 0; 253 else 254 dec->saturation = ctrl->value; 255 write_reg(client, 0x10, dec->saturation, dec->channel); 256 break; 257 case V4L2_CID_HUE: 258 if (ctrl->value > 255) 259 dec->hue = 255; 260 else if (ctrl->value < 0) 261 dec->hue = 0; 262 else 263 dec->hue = ctrl->value; 264 write_reg(client, 0x0f, dec->hue, dec->channel); 265 break; 266 } 267 break; 268 } 269 case VIDIOC_G_CTRL: 270 { 271 struct v4l2_control *ctrl = arg; 272 273 switch (ctrl->id) { 274 case V4L2_CID_BRIGHTNESS: 275 ctrl->value = dec->brightness; 276 break; 277 case V4L2_CID_CONTRAST: 278 ctrl->value = dec->contrast; 279 break; 280 case V4L2_CID_SATURATION: 281 ctrl->value = dec->saturation; 282 break; 283 case V4L2_CID_HUE: 284 ctrl->value = dec->hue; 285 break; 286 } 287 break; 288 } 289 default: 290 break; 291 } 292 return 0; 293} 294 295static int wis_tw2804_probe(struct i2c_client *client, 296 const struct i2c_device_id *id) 297{ 298 struct i2c_adapter *adapter = client->adapter; 299 struct wis_tw2804 *dec; 300 301 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 302 return -ENODEV; 303 304 dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL); 305 if (dec == NULL) 306 return -ENOMEM; 307 308 dec->channel = -1; 309 dec->norm = V4L2_STD_NTSC; 310 dec->brightness = 128; 311 dec->contrast = 128; 312 dec->saturation = 128; 313 dec->hue = 128; 314 i2c_set_clientdata(client, dec); 315 316 printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n", 317 client->addr, adapter->name); 318 319 return 0; 320} 321 322static int wis_tw2804_remove(struct i2c_client *client) 323{ 324 struct wis_tw2804 *dec = i2c_get_clientdata(client); 325 326 kfree(dec); 327 return 0; 328} 329 330static const struct i2c_device_id wis_tw2804_id[] = { 331 { "wis_tw2804", 0 }, 332 { } 333}; 334 335static struct i2c_driver wis_tw2804_driver = { 336 .driver = { 337 .name = "WIS TW2804 I2C driver", 338 }, 339 .probe = wis_tw2804_probe, 340 .remove = wis_tw2804_remove, 341 .command = wis_tw2804_command, 342 .id_table = wis_tw2804_id, 343}; 344 345static int __init wis_tw2804_init(void) 346{ 347 return i2c_add_driver(&wis_tw2804_driver); 348} 349 350static void __exit wis_tw2804_cleanup(void) 351{ 352 i2c_del_driver(&wis_tw2804_driver); 353} 354 355module_init(wis_tw2804_init); 356module_exit(wis_tw2804_cleanup); 357 358MODULE_LICENSE("GPL v2"); 359