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