blob: 5c12b4d384595013bbd7667c7d5f0b70a035f837 [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>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090023#include <linux/slab.h>
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -080024
25#include "wis-i2c.h"
26
27struct wis_saa7113 {
28 int norm;
29 int brightness;
30 int contrast;
31 int saturation;
32 int hue;
33};
34
35static u8 initial_registers[] =
36{
37 0x01, 0x08,
38 0x02, 0xc0,
39 0x03, 0x33,
40 0x04, 0x00,
41 0x05, 0x00,
42 0x06, 0xe9,
43 0x07, 0x0d,
44 0x08, 0xd8,
45 0x09, 0x40,
46 0x0a, 0x80,
47 0x0b, 0x47,
48 0x0c, 0x40,
49 0x0d, 0x00,
50 0x0e, 0x01,
51 0x0f, 0x2a,
52 0x10, 0x40,
53 0x11, 0x0c,
54 0x12, 0xfe,
55 0x13, 0x00,
56 0x14, 0x00,
57 0x15, 0x04,
58 0x16, 0x00,
59 0x17, 0x00,
60 0x18, 0x00,
61 0x19, 0x00,
62 0x1a, 0x00,
63 0x1b, 0x00,
64 0x1c, 0x00,
65 0x1d, 0x00,
66 0x1e, 0x00,
67 0x1f, 0xc8,
68 0x40, 0x00,
69 0x41, 0xff,
70 0x42, 0xff,
71 0x43, 0xff,
72 0x44, 0xff,
73 0x45, 0xff,
74 0x46, 0xff,
75 0x47, 0xff,
76 0x48, 0xff,
77 0x49, 0xff,
78 0x4a, 0xff,
79 0x4b, 0xff,
80 0x4c, 0xff,
81 0x4d, 0xff,
82 0x4e, 0xff,
83 0x4f, 0xff,
84 0x50, 0xff,
85 0x51, 0xff,
86 0x52, 0xff,
87 0x53, 0xff,
88 0x54, 0xff,
89 0x55, 0xff,
90 0x56, 0xff,
91 0x57, 0xff,
92 0x58, 0x00,
93 0x59, 0x54,
94 0x5a, 0x07,
95 0x5b, 0x83,
96 0x5c, 0x00,
97 0x5d, 0x00,
98 0x5e, 0x00,
99 0x5f, 0x00,
100 0x60, 0x00,
101 0x61, 0x00,
102 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
103};
104
105static int write_reg(struct i2c_client *client, u8 reg, u8 value)
106{
107 return i2c_smbus_write_byte_data(client, reg, value);
108}
109
110static int write_regs(struct i2c_client *client, u8 *regs)
111{
112 int i;
113
114 for (i = 0; regs[i] != 0x00; i += 2)
115 if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
116 return -1;
117 return 0;
118}
119
120static int wis_saa7113_command(struct i2c_client *client,
121 unsigned int cmd, void *arg)
122{
123 struct wis_saa7113 *dec = i2c_get_clientdata(client);
124
125 switch (cmd) {
Ross Cohendf20d692008-09-29 22:36:24 -0400126 case VIDIOC_S_INPUT:
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800127 {
128 int *input = arg;
129
130 i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input);
131 i2c_smbus_write_byte_data(client, 0x09,
132 *input < 6 ? 0x40 : 0x80);
133 break;
134 }
Ross Cohendf20d692008-09-29 22:36:24 -0400135 case VIDIOC_S_STD:
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800136 {
Ross Cohendf20d692008-09-29 22:36:24 -0400137 v4l2_std_id *input = arg;
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800138 dec->norm = *input;
Ross Cohendf20d692008-09-29 22:36:24 -0400139 if (dec->norm & V4L2_STD_NTSC) {
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800140 write_reg(client, 0x0e, 0x01);
141 write_reg(client, 0x10, 0x40);
Ross Cohendf20d692008-09-29 22:36:24 -0400142 } else if (dec->norm & V4L2_STD_PAL) {
143 write_reg(client, 0x0e, 0x01);
144 write_reg(client, 0x10, 0x48);
145 } else if (dec->norm * V4L2_STD_SECAM) {
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800146 write_reg(client, 0x0e, 0x50);
147 write_reg(client, 0x10, 0x48);
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800148 }
149 break;
150 }
151 case VIDIOC_QUERYCTRL:
152 {
153 struct v4l2_queryctrl *ctrl = arg;
154
155 switch (ctrl->id) {
156 case V4L2_CID_BRIGHTNESS:
157 ctrl->type = V4L2_CTRL_TYPE_INTEGER;
158 strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
159 ctrl->minimum = 0;
160 ctrl->maximum = 255;
161 ctrl->step = 1;
162 ctrl->default_value = 128;
163 ctrl->flags = 0;
164 break;
165 case V4L2_CID_CONTRAST:
166 ctrl->type = V4L2_CTRL_TYPE_INTEGER;
167 strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
168 ctrl->minimum = 0;
169 ctrl->maximum = 127;
170 ctrl->step = 1;
171 ctrl->default_value = 71;
172 ctrl->flags = 0;
173 break;
174 case V4L2_CID_SATURATION:
175 ctrl->type = V4L2_CTRL_TYPE_INTEGER;
176 strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
177 ctrl->minimum = 0;
178 ctrl->maximum = 127;
179 ctrl->step = 1;
180 ctrl->default_value = 64;
181 ctrl->flags = 0;
182 break;
183 case V4L2_CID_HUE:
184 ctrl->type = V4L2_CTRL_TYPE_INTEGER;
185 strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
186 ctrl->minimum = -128;
187 ctrl->maximum = 127;
188 ctrl->step = 1;
189 ctrl->default_value = 0;
190 ctrl->flags = 0;
191 break;
192 }
193 break;
194 }
195 case VIDIOC_S_CTRL:
196 {
197 struct v4l2_control *ctrl = arg;
198
199 switch (ctrl->id) {
200 case V4L2_CID_BRIGHTNESS:
201 if (ctrl->value > 255)
202 dec->brightness = 255;
203 else if (ctrl->value < 0)
204 dec->brightness = 0;
205 else
206 dec->brightness = ctrl->value;
207 write_reg(client, 0x0a, dec->brightness);
208 break;
209 case V4L2_CID_CONTRAST:
210 if (ctrl->value > 127)
211 dec->contrast = 127;
212 else if (ctrl->value < 0)
213 dec->contrast = 0;
214 else
215 dec->contrast = ctrl->value;
216 write_reg(client, 0x0b, dec->contrast);
217 break;
218 case V4L2_CID_SATURATION:
219 if (ctrl->value > 127)
220 dec->saturation = 127;
221 else if (ctrl->value < 0)
222 dec->saturation = 0;
223 else
224 dec->saturation = ctrl->value;
225 write_reg(client, 0x0c, dec->saturation);
226 break;
227 case V4L2_CID_HUE:
228 if (ctrl->value > 127)
229 dec->hue = 127;
230 else if (ctrl->value < -128)
231 dec->hue = -128;
232 else
233 dec->hue = ctrl->value;
234 write_reg(client, 0x0d, dec->hue);
235 break;
236 }
237 break;
238 }
239 case VIDIOC_G_CTRL:
240 {
241 struct v4l2_control *ctrl = arg;
242
243 switch (ctrl->id) {
244 case V4L2_CID_BRIGHTNESS:
245 ctrl->value = dec->brightness;
246 break;
247 case V4L2_CID_CONTRAST:
248 ctrl->value = dec->contrast;
249 break;
250 case V4L2_CID_SATURATION:
251 ctrl->value = dec->saturation;
252 break;
253 case V4L2_CID_HUE:
254 ctrl->value = dec->hue;
255 break;
256 }
257 break;
258 }
259 default:
260 break;
261 }
262 return 0;
263}
264
Jean Delvare74005162009-04-21 21:47:22 +0200265static int wis_saa7113_probe(struct i2c_client *client,
266 const struct i2c_device_id *id)
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800267{
Jean Delvare74005162009-04-21 21:47:22 +0200268 struct i2c_adapter *adapter = client->adapter;
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800269 struct wis_saa7113 *dec;
270
271 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
Jean Delvare74005162009-04-21 21:47:22 +0200272 return -ENODEV;
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800273
274 dec = kmalloc(sizeof(struct wis_saa7113), GFP_KERNEL);
Jean Delvare74005162009-04-21 21:47:22 +0200275 if (dec == NULL)
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800276 return -ENOMEM;
Jean Delvare74005162009-04-21 21:47:22 +0200277
Ross Cohendf20d692008-09-29 22:36:24 -0400278 dec->norm = V4L2_STD_NTSC;
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800279 dec->brightness = 128;
280 dec->contrast = 71;
281 dec->saturation = 64;
282 dec->hue = 0;
283 i2c_set_clientdata(client, dec);
284
285 printk(KERN_DEBUG
286 "wis-saa7113: initializing SAA7113 at address %d on %s\n",
Jean Delvare74005162009-04-21 21:47:22 +0200287 client->addr, adapter->name);
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800288
289 if (write_regs(client, initial_registers) < 0) {
290 printk(KERN_ERR
291 "wis-saa7113: error initializing SAA7113\n");
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800292 kfree(dec);
Jean Delvare74005162009-04-21 21:47:22 +0200293 return -ENODEV;
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800294 }
295
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800296 return 0;
297}
298
Jean Delvare74005162009-04-21 21:47:22 +0200299static int wis_saa7113_remove(struct i2c_client *client)
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800300{
301 struct wis_saa7113 *dec = i2c_get_clientdata(client);
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800302
Jean Delvare74005162009-04-21 21:47:22 +0200303 i2c_set_clientdata(client, NULL);
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800304 kfree(dec);
305 return 0;
306}
307
Németh Mártonb76a3262010-01-10 00:18:41 +0100308static const struct i2c_device_id wis_saa7113_id[] = {
Jean Delvare74005162009-04-21 21:47:22 +0200309 { "wis_saa7113", 0 },
310 { }
311};
312
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800313static struct i2c_driver wis_saa7113_driver = {
314 .driver = {
315 .name = "WIS SAA7113 I2C driver",
316 },
Jean Delvare74005162009-04-21 21:47:22 +0200317 .probe = wis_saa7113_probe,
318 .remove = wis_saa7113_remove,
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800319 .command = wis_saa7113_command,
Jean Delvare74005162009-04-21 21:47:22 +0200320 .id_table = wis_saa7113_id,
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800321};
322
323static int __init wis_saa7113_init(void)
324{
Jean Delvare74005162009-04-21 21:47:22 +0200325 return i2c_add_driver(&wis_saa7113_driver);
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800326}
327
328static void __exit wis_saa7113_cleanup(void)
329{
Greg Kroah-Hartman866b8692008-02-15 16:53:09 -0800330 i2c_del_driver(&wis_saa7113_driver);
331}
332
333module_init(wis_saa7113_init);
334module_exit(wis_saa7113_cleanup);
335
336MODULE_LICENSE("GPL v2");