blob: 0f2b4a0ceccfd566610ba940843e4bfde3203956 [file] [log] [blame]
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -08001/*
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>
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -080020#include <linux/i2c.h>
Ross Cohendf20d692008-09-29 22:36:24 -040021#include <linux/videodev2.h>
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -080022#include <linux/ioctl.h>
23
24#include "wis-i2c.h"
25
26struct wis_saa7115 {
27 int norm;
28 int brightness;
29 int contrast;
30 int saturation;
31 int hue;
32};
33
34static u8 initial_registers[] =
35{
36 0x01, 0x08,
37 0x02, 0xc0,
38 0x03, 0x20,
39 0x04, 0x80,
40 0x05, 0x80,
41 0x06, 0xeb,
42 0x07, 0xe0,
43 0x08, 0xf0, /* always toggle FID */
44 0x09, 0x40,
45 0x0a, 0x80,
46 0x0b, 0x40,
47 0x0c, 0x40,
48 0x0d, 0x00,
49 0x0e, 0x03,
50 0x0f, 0x2a,
51 0x10, 0x0e,
52 0x11, 0x00,
53 0x12, 0x8d,
54 0x13, 0x00,
55 0x14, 0x00,
56 0x15, 0x11,
57 0x16, 0x01,
58 0x17, 0xda,
59 0x18, 0x40,
60 0x19, 0x80,
61 0x1a, 0x00,
62 0x1b, 0x42,
63 0x1c, 0xa9,
64 0x30, 0x66,
65 0x31, 0x90,
66 0x32, 0x01,
67 0x34, 0x00,
68 0x35, 0x00,
69 0x36, 0x20,
70 0x38, 0x03,
71 0x39, 0x20,
72 0x3a, 0x88,
73 0x40, 0x00,
74 0x41, 0xff,
75 0x42, 0xff,
76 0x43, 0xff,
77 0x44, 0xff,
78 0x45, 0xff,
79 0x46, 0xff,
80 0x47, 0xff,
81 0x48, 0xff,
82 0x49, 0xff,
83 0x4a, 0xff,
84 0x4b, 0xff,
85 0x4c, 0xff,
86 0x4d, 0xff,
87 0x4e, 0xff,
88 0x4f, 0xff,
89 0x50, 0xff,
90 0x51, 0xff,
91 0x52, 0xff,
92 0x53, 0xff,
93 0x54, 0xf4 /*0xff*/,
94 0x55, 0xff,
95 0x56, 0xff,
96 0x57, 0xff,
97 0x58, 0x40,
98 0x59, 0x47,
99 0x5a, 0x06 /*0x03*/,
100 0x5b, 0x83,
101 0x5d, 0x06,
102 0x5e, 0x00,
103 0x80, 0x30, /* window defined scaler operation, task A and B enabled */
104 0x81, 0x03, /* use scaler datapath generated V */
105 0x83, 0x00,
106 0x84, 0x00,
107 0x85, 0x00,
108 0x86, 0x45,
109 0x87, 0x31,
110 0x88, 0xc0,
111 0x90, 0x02, /* task A process top field */
112 0x91, 0x08,
113 0x92, 0x09,
114 0x93, 0x80,
115 0x94, 0x06,
116 0x95, 0x00,
117 0x96, 0xc0,
118 0x97, 0x02,
119 0x98, 0x12,
120 0x99, 0x00,
121 0x9a, 0xf2,
122 0x9b, 0x00,
123 0x9c, 0xd0,
124 0x9d, 0x02,
125 0x9e, 0xf2,
126 0x9f, 0x00,
127 0xa0, 0x01,
128 0xa1, 0x01,
129 0xa2, 0x01,
130 0xa4, 0x80,
131 0xa5, 0x40,
132 0xa6, 0x40,
133 0xa8, 0x00,
134 0xa9, 0x04,
135 0xaa, 0x00,
136 0xac, 0x00,
137 0xad, 0x02,
138 0xae, 0x00,
139 0xb0, 0x00,
140 0xb1, 0x04,
141 0xb2, 0x00,
142 0xb3, 0x04,
143 0xb4, 0x00,
144 0xb8, 0x00,
145 0xbc, 0x00,
146 0xc0, 0x03, /* task B process bottom field */
147 0xc1, 0x08,
148 0xc2, 0x09,
149 0xc3, 0x80,
150 0xc4, 0x06,
151 0xc5, 0x00,
152 0xc6, 0xc0,
153 0xc7, 0x02,
154 0xc8, 0x12,
155 0xc9, 0x00,
156 0xca, 0xf2,
157 0xcb, 0x00,
158 0xcc, 0xd0,
159 0xcd, 0x02,
160 0xce, 0xf2,
161 0xcf, 0x00,
162 0xd0, 0x01,
163 0xd1, 0x01,
164 0xd2, 0x01,
165 0xd4, 0x80,
166 0xd5, 0x40,
167 0xd6, 0x40,
168 0xd8, 0x00,
169 0xd9, 0x04,
170 0xda, 0x00,
171 0xdc, 0x00,
172 0xdd, 0x02,
173 0xde, 0x00,
174 0xe0, 0x00,
175 0xe1, 0x04,
176 0xe2, 0x00,
177 0xe3, 0x04,
178 0xe4, 0x00,
179 0xe8, 0x00,
180 0x88, 0xf0, /* End of original static list */
181 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
182};
183
184static int write_reg(struct i2c_client *client, u8 reg, u8 value)
185{
186 return i2c_smbus_write_byte_data(client, reg, value);
187}
188
189static int write_regs(struct i2c_client *client, u8 *regs)
190{
191 int i;
192
193 for (i = 0; regs[i] != 0x00; i += 2)
194 if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
195 return -1;
196 return 0;
197}
198
199static int wis_saa7115_command(struct i2c_client *client,
200 unsigned int cmd, void *arg)
201{
202 struct wis_saa7115 *dec = i2c_get_clientdata(client);
203
204 switch (cmd) {
Ross Cohendf20d692008-09-29 22:36:24 -0400205 case VIDIOC_S_INPUT:
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800206 {
207 int *input = arg;
208
209 i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input);
210 i2c_smbus_write_byte_data(client, 0x09,
211 *input < 6 ? 0x40 : 0xC0);
212 break;
213 }
214 case DECODER_SET_RESOLUTION:
215 {
216 struct video_decoder_resolution *res = arg;
217 /* Course-grained scaler */
218 int h_integer_scaler = res->width < 704 ? 704 / res->width : 1;
219 /* Fine-grained scaler to take care of remainder */
220 int h_scaling_increment = (704 / h_integer_scaler) *
221 1024 / res->width;
222 /* Fine-grained scaler only */
Ross Cohendf20d692008-09-29 22:36:24 -0400223 int v_scaling_increment = (dec->norm & V4L2_STD_NTSC ?
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800224 240 : 288) * 1024 / res->height;
225 u8 regs[] = {
226 0x88, 0xc0,
227 0x9c, res->width & 0xff,
228 0x9d, res->width >> 8,
229 0x9e, res->height & 0xff,
230 0x9f, res->height >> 8,
231 0xa0, h_integer_scaler,
232 0xa1, 1,
233 0xa2, 1,
234 0xa8, h_scaling_increment & 0xff,
235 0xa9, h_scaling_increment >> 8,
236 0xac, (h_scaling_increment / 2) & 0xff,
237 0xad, (h_scaling_increment / 2) >> 8,
238 0xb0, v_scaling_increment & 0xff,
239 0xb1, v_scaling_increment >> 8,
240 0xb2, v_scaling_increment & 0xff,
241 0xb3, v_scaling_increment >> 8,
242 0xcc, res->width & 0xff,
243 0xcd, res->width >> 8,
244 0xce, res->height & 0xff,
245 0xcf, res->height >> 8,
246 0xd0, h_integer_scaler,
247 0xd1, 1,
248 0xd2, 1,
249 0xd8, h_scaling_increment & 0xff,
250 0xd9, h_scaling_increment >> 8,
251 0xdc, (h_scaling_increment / 2) & 0xff,
252 0xdd, (h_scaling_increment / 2) >> 8,
253 0xe0, v_scaling_increment & 0xff,
254 0xe1, v_scaling_increment >> 8,
255 0xe2, v_scaling_increment & 0xff,
256 0xe3, v_scaling_increment >> 8,
257 0x88, 0xf0,
258 0, 0,
259 };
260 write_regs(client, regs);
261 break;
262 }
Ross Cohendf20d692008-09-29 22:36:24 -0400263 case VIDIOC_S_STD:
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800264 {
Ross Cohendf20d692008-09-29 22:36:24 -0400265 v4l2_std_id *input = arg;
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800266 u8 regs[] = {
267 0x88, 0xc0,
Ross Cohendf20d692008-09-29 22:36:24 -0400268 0x98, *input & V4L2_STD_NTSC ? 0x12 : 0x16,
269 0x9a, *input & V4L2_STD_NTSC ? 0xf2 : 0x20,
270 0x9b, *input & V4L2_STD_NTSC ? 0x00 : 0x01,
271 0xc8, *input & V4L2_STD_NTSC ? 0x12 : 0x16,
272 0xca, *input & V4L2_STD_NTSC ? 0xf2 : 0x20,
273 0xcb, *input & V4L2_STD_NTSC ? 0x00 : 0x01,
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800274 0x88, 0xf0,
Ross Cohendf20d692008-09-29 22:36:24 -0400275 0x30, *input & V4L2_STD_NTSC ? 0x66 : 0x00,
276 0x31, *input & V4L2_STD_NTSC ? 0x90 : 0xe0,
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800277 0, 0,
278 };
279 write_regs(client, regs);
280 dec->norm = *input;
281 break;
282 }
283 case VIDIOC_QUERYCTRL:
284 {
285 struct v4l2_queryctrl *ctrl = arg;
286
287 switch (ctrl->id) {
288 case V4L2_CID_BRIGHTNESS:
289 ctrl->type = V4L2_CTRL_TYPE_INTEGER;
290 strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
291 ctrl->minimum = 0;
292 ctrl->maximum = 255;
293 ctrl->step = 1;
294 ctrl->default_value = 128;
295 ctrl->flags = 0;
296 break;
297 case V4L2_CID_CONTRAST:
298 ctrl->type = V4L2_CTRL_TYPE_INTEGER;
299 strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
300 ctrl->minimum = 0;
301 ctrl->maximum = 127;
302 ctrl->step = 1;
303 ctrl->default_value = 64;
304 ctrl->flags = 0;
305 break;
306 case V4L2_CID_SATURATION:
307 ctrl->type = V4L2_CTRL_TYPE_INTEGER;
308 strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
309 ctrl->minimum = 0;
310 ctrl->maximum = 127;
311 ctrl->step = 1;
312 ctrl->default_value = 64;
313 ctrl->flags = 0;
314 break;
315 case V4L2_CID_HUE:
316 ctrl->type = V4L2_CTRL_TYPE_INTEGER;
317 strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
318 ctrl->minimum = -128;
319 ctrl->maximum = 127;
320 ctrl->step = 1;
321 ctrl->default_value = 0;
322 ctrl->flags = 0;
323 break;
324 }
325 break;
326 }
327 case VIDIOC_S_CTRL:
328 {
329 struct v4l2_control *ctrl = arg;
330
331 switch (ctrl->id) {
332 case V4L2_CID_BRIGHTNESS:
333 if (ctrl->value > 255)
334 dec->brightness = 255;
335 else if (ctrl->value < 0)
336 dec->brightness = 0;
337 else
338 dec->brightness = ctrl->value;
339 write_reg(client, 0x0a, dec->brightness);
340 break;
341 case V4L2_CID_CONTRAST:
342 if (ctrl->value > 127)
343 dec->contrast = 127;
344 else if (ctrl->value < 0)
345 dec->contrast = 0;
346 else
347 dec->contrast = ctrl->value;
348 write_reg(client, 0x0b, dec->contrast);
349 break;
350 case V4L2_CID_SATURATION:
351 if (ctrl->value > 127)
352 dec->saturation = 127;
353 else if (ctrl->value < 0)
354 dec->saturation = 0;
355 else
356 dec->saturation = ctrl->value;
357 write_reg(client, 0x0c, dec->saturation);
358 break;
359 case V4L2_CID_HUE:
360 if (ctrl->value > 127)
361 dec->hue = 127;
362 else if (ctrl->value < -128)
363 dec->hue = -128;
364 else
365 dec->hue = ctrl->value;
366 write_reg(client, 0x0d, dec->hue);
367 break;
368 }
369 break;
370 }
371 case VIDIOC_G_CTRL:
372 {
373 struct v4l2_control *ctrl = arg;
374
375 switch (ctrl->id) {
376 case V4L2_CID_BRIGHTNESS:
377 ctrl->value = dec->brightness;
378 break;
379 case V4L2_CID_CONTRAST:
380 ctrl->value = dec->contrast;
381 break;
382 case V4L2_CID_SATURATION:
383 ctrl->value = dec->saturation;
384 break;
385 case V4L2_CID_HUE:
386 ctrl->value = dec->hue;
387 break;
388 }
389 break;
390 }
391 default:
392 break;
393 }
394 return 0;
395}
396
Jean Delvare74005162009-04-21 21:47:22 +0200397static int wis_saa7115_probe(struct i2c_client *client,
398 const struct i2c_device_id *id)
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800399{
Jean Delvare74005162009-04-21 21:47:22 +0200400 struct i2c_adapter *adapter = client->adapter;
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800401 struct wis_saa7115 *dec;
402
403 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
Jean Delvare74005162009-04-21 21:47:22 +0200404 return -ENODEV;
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800405
406 dec = kmalloc(sizeof(struct wis_saa7115), GFP_KERNEL);
Jean Delvare74005162009-04-21 21:47:22 +0200407 if (dec == NULL)
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800408 return -ENOMEM;
Jean Delvare74005162009-04-21 21:47:22 +0200409
Ross Cohendf20d692008-09-29 22:36:24 -0400410 dec->norm = V4L2_STD_NTSC;
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800411 dec->brightness = 128;
412 dec->contrast = 64;
413 dec->saturation = 64;
414 dec->hue = 0;
415 i2c_set_clientdata(client, dec);
416
417 printk(KERN_DEBUG
418 "wis-saa7115: initializing SAA7115 at address %d on %s\n",
Jean Delvare74005162009-04-21 21:47:22 +0200419 client->addr, adapter->name);
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800420
421 if (write_regs(client, initial_registers) < 0) {
422 printk(KERN_ERR
423 "wis-saa7115: error initializing SAA7115\n");
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800424 kfree(dec);
Jean Delvare74005162009-04-21 21:47:22 +0200425 return -ENODEV;
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800426 }
427
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800428 return 0;
429}
430
Jean Delvare74005162009-04-21 21:47:22 +0200431static int wis_saa7115_remove(struct i2c_client *client)
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800432{
433 struct wis_saa7115 *dec = i2c_get_clientdata(client);
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800434
Jean Delvare74005162009-04-21 21:47:22 +0200435 i2c_set_clientdata(client, NULL);
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800436 kfree(dec);
437 return 0;
438}
439
Németh Mártonb76a3262010-01-10 00:18:41 +0100440static const struct i2c_device_id wis_saa7115_id[] = {
Jean Delvare74005162009-04-21 21:47:22 +0200441 { "wis_saa7115", 0 },
442 { }
443};
444
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800445static struct i2c_driver wis_saa7115_driver = {
446 .driver = {
447 .name = "WIS SAA7115 I2C driver",
448 },
Jean Delvare74005162009-04-21 21:47:22 +0200449 .probe = wis_saa7115_probe,
450 .remove = wis_saa7115_remove,
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800451 .command = wis_saa7115_command,
Jean Delvare74005162009-04-21 21:47:22 +0200452 .id_table = wis_saa7115_id,
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800453};
454
455static int __init wis_saa7115_init(void)
456{
Jean Delvare74005162009-04-21 21:47:22 +0200457 return i2c_add_driver(&wis_saa7115_driver);
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800458}
459
460static void __exit wis_saa7115_cleanup(void)
461{
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800462 i2c_del_driver(&wis_saa7115_driver);
463}
464
465module_init(wis_saa7115_init);
466module_exit(wis_saa7115_cleanup);
467
468MODULE_LICENSE("GPL v2");