Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging-2.6
[linux-2.6] / drivers / staging / go7007 / wis-tw2804.c
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/version.h>
21 #include <linux/i2c.h>
22 #include <linux/videodev2.h>
23 #include <linux/ioctl.h>
24
25 #include "wis-i2c.h"
26
27 struct wis_tw2804 {
28         int channel;
29         int norm;
30         int brightness;
31         int contrast;
32         int saturation;
33         int hue;
34 };
35
36 static 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
48 static 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
108 static 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
113 static 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
124 static 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
295 static struct i2c_driver wis_tw2804_driver;
296
297 static struct i2c_client wis_tw2804_client_templ = {
298         .name           = "TW2804 (WIS)",
299         .driver         = &wis_tw2804_driver,
300 };
301
302 static int wis_tw2804_detect(struct i2c_adapter *adapter, int addr, int kind)
303 {
304         struct i2c_client *client;
305         struct wis_tw2804 *dec;
306
307         if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
308                 return 0;
309
310         client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
311         if (client == NULL)
312                 return -ENOMEM;
313         memcpy(client, &wis_tw2804_client_templ,
314                         sizeof(wis_tw2804_client_templ));
315         client->adapter = adapter;
316         client->addr = addr;
317
318         dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL);
319         if (dec == NULL) {
320                 kfree(client);
321                 return -ENOMEM;
322         }
323         dec->channel = -1;
324         dec->norm = V4L2_STD_NTSC;
325         dec->brightness = 128;
326         dec->contrast = 128;
327         dec->saturation = 128;
328         dec->hue = 128;
329         i2c_set_clientdata(client, dec);
330
331         printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n",
332                 addr, adapter->name);
333
334         i2c_attach_client(client);
335         return 0;
336 }
337
338 static int wis_tw2804_detach(struct i2c_client *client)
339 {
340         struct wis_tw2804 *dec = i2c_get_clientdata(client);
341         int r;
342
343         r = i2c_detach_client(client);
344         if (r < 0)
345                 return r;
346
347         kfree(client);
348         kfree(dec);
349         return 0;
350 }
351
352 static struct i2c_driver wis_tw2804_driver = {
353         .driver = {
354                 .name   = "WIS TW2804 I2C driver",
355         },
356         .id             = I2C_DRIVERID_WIS_TW2804,
357         .detach_client  = wis_tw2804_detach,
358         .command        = wis_tw2804_command,
359 };
360
361 static int __init wis_tw2804_init(void)
362 {
363         int r;
364
365         r = i2c_add_driver(&wis_tw2804_driver);
366         if (r < 0)
367                 return r;
368         return wis_i2c_add_driver(wis_tw2804_driver.id, wis_tw2804_detect);
369 }
370
371 static void __exit wis_tw2804_cleanup(void)
372 {
373         wis_i2c_del_driver(wis_tw2804_detect);
374         i2c_del_driver(&wis_tw2804_driver);
375 }
376
377 module_init(wis_tw2804_init);
378 module_exit(wis_tw2804_cleanup);
379
380 MODULE_LICENSE("GPL v2");