Commit | Line | Data |
---|---|---|
866b8695 GKH |
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> | |
866b8695 | 20 | #include <linux/i2c.h> |
df20d69e | 21 | #include <linux/videodev2.h> |
866b8695 GKH |
22 | #include <linux/ioctl.h> |
23 | ||
24 | #include "wis-i2c.h" | |
25 | ||
26 | struct wis_saa7113 { | |
27 | int norm; | |
28 | int brightness; | |
29 | int contrast; | |
30 | int saturation; | |
31 | int hue; | |
32 | }; | |
33 | ||
34 | static u8 initial_registers[] = | |
35 | { | |
36 | 0x01, 0x08, | |
37 | 0x02, 0xc0, | |
38 | 0x03, 0x33, | |
39 | 0x04, 0x00, | |
40 | 0x05, 0x00, | |
41 | 0x06, 0xe9, | |
42 | 0x07, 0x0d, | |
43 | 0x08, 0xd8, | |
44 | 0x09, 0x40, | |
45 | 0x0a, 0x80, | |
46 | 0x0b, 0x47, | |
47 | 0x0c, 0x40, | |
48 | 0x0d, 0x00, | |
49 | 0x0e, 0x01, | |
50 | 0x0f, 0x2a, | |
51 | 0x10, 0x40, | |
52 | 0x11, 0x0c, | |
53 | 0x12, 0xfe, | |
54 | 0x13, 0x00, | |
55 | 0x14, 0x00, | |
56 | 0x15, 0x04, | |
57 | 0x16, 0x00, | |
58 | 0x17, 0x00, | |
59 | 0x18, 0x00, | |
60 | 0x19, 0x00, | |
61 | 0x1a, 0x00, | |
62 | 0x1b, 0x00, | |
63 | 0x1c, 0x00, | |
64 | 0x1d, 0x00, | |
65 | 0x1e, 0x00, | |
66 | 0x1f, 0xc8, | |
67 | 0x40, 0x00, | |
68 | 0x41, 0xff, | |
69 | 0x42, 0xff, | |
70 | 0x43, 0xff, | |
71 | 0x44, 0xff, | |
72 | 0x45, 0xff, | |
73 | 0x46, 0xff, | |
74 | 0x47, 0xff, | |
75 | 0x48, 0xff, | |
76 | 0x49, 0xff, | |
77 | 0x4a, 0xff, | |
78 | 0x4b, 0xff, | |
79 | 0x4c, 0xff, | |
80 | 0x4d, 0xff, | |
81 | 0x4e, 0xff, | |
82 | 0x4f, 0xff, | |
83 | 0x50, 0xff, | |
84 | 0x51, 0xff, | |
85 | 0x52, 0xff, | |
86 | 0x53, 0xff, | |
87 | 0x54, 0xff, | |
88 | 0x55, 0xff, | |
89 | 0x56, 0xff, | |
90 | 0x57, 0xff, | |
91 | 0x58, 0x00, | |
92 | 0x59, 0x54, | |
93 | 0x5a, 0x07, | |
94 | 0x5b, 0x83, | |
95 | 0x5c, 0x00, | |
96 | 0x5d, 0x00, | |
97 | 0x5e, 0x00, | |
98 | 0x5f, 0x00, | |
99 | 0x60, 0x00, | |
100 | 0x61, 0x00, | |
101 | 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ | |
102 | }; | |
103 | ||
104 | static int write_reg(struct i2c_client *client, u8 reg, u8 value) | |
105 | { | |
106 | return i2c_smbus_write_byte_data(client, reg, value); | |
107 | } | |
108 | ||
109 | static int write_regs(struct i2c_client *client, u8 *regs) | |
110 | { | |
111 | int i; | |
112 | ||
113 | for (i = 0; regs[i] != 0x00; i += 2) | |
114 | if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) | |
115 | return -1; | |
116 | return 0; | |
117 | } | |
118 | ||
119 | static int wis_saa7113_command(struct i2c_client *client, | |
120 | unsigned int cmd, void *arg) | |
121 | { | |
122 | struct wis_saa7113 *dec = i2c_get_clientdata(client); | |
123 | ||
124 | switch (cmd) { | |
df20d69e | 125 | case VIDIOC_S_INPUT: |
866b8695 GKH |
126 | { |
127 | int *input = arg; | |
128 | ||
129 | i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input); | |
130 | i2c_smbus_write_byte_data(client, 0x09, | |
131 | *input < 6 ? 0x40 : 0x80); | |
132 | break; | |
133 | } | |
df20d69e | 134 | case VIDIOC_S_STD: |
866b8695 | 135 | { |
df20d69e | 136 | v4l2_std_id *input = arg; |
866b8695 | 137 | dec->norm = *input; |
df20d69e | 138 | if (dec->norm & V4L2_STD_NTSC) { |
866b8695 GKH |
139 | write_reg(client, 0x0e, 0x01); |
140 | write_reg(client, 0x10, 0x40); | |
df20d69e RC |
141 | } else if (dec->norm & V4L2_STD_PAL) { |
142 | write_reg(client, 0x0e, 0x01); | |
143 | write_reg(client, 0x10, 0x48); | |
144 | } else if (dec->norm * V4L2_STD_SECAM) { | |
866b8695 GKH |
145 | write_reg(client, 0x0e, 0x50); |
146 | write_reg(client, 0x10, 0x48); | |
866b8695 GKH |
147 | } |
148 | break; | |
149 | } | |
150 | case VIDIOC_QUERYCTRL: | |
151 | { | |
152 | struct v4l2_queryctrl *ctrl = arg; | |
153 | ||
154 | switch (ctrl->id) { | |
155 | case V4L2_CID_BRIGHTNESS: | |
156 | ctrl->type = V4L2_CTRL_TYPE_INTEGER; | |
157 | strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); | |
158 | ctrl->minimum = 0; | |
159 | ctrl->maximum = 255; | |
160 | ctrl->step = 1; | |
161 | ctrl->default_value = 128; | |
162 | ctrl->flags = 0; | |
163 | break; | |
164 | case V4L2_CID_CONTRAST: | |
165 | ctrl->type = V4L2_CTRL_TYPE_INTEGER; | |
166 | strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); | |
167 | ctrl->minimum = 0; | |
168 | ctrl->maximum = 127; | |
169 | ctrl->step = 1; | |
170 | ctrl->default_value = 71; | |
171 | ctrl->flags = 0; | |
172 | break; | |
173 | case V4L2_CID_SATURATION: | |
174 | ctrl->type = V4L2_CTRL_TYPE_INTEGER; | |
175 | strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); | |
176 | ctrl->minimum = 0; | |
177 | ctrl->maximum = 127; | |
178 | ctrl->step = 1; | |
179 | ctrl->default_value = 64; | |
180 | ctrl->flags = 0; | |
181 | break; | |
182 | case V4L2_CID_HUE: | |
183 | ctrl->type = V4L2_CTRL_TYPE_INTEGER; | |
184 | strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); | |
185 | ctrl->minimum = -128; | |
186 | ctrl->maximum = 127; | |
187 | ctrl->step = 1; | |
188 | ctrl->default_value = 0; | |
189 | ctrl->flags = 0; | |
190 | break; | |
191 | } | |
192 | break; | |
193 | } | |
194 | case VIDIOC_S_CTRL: | |
195 | { | |
196 | struct v4l2_control *ctrl = arg; | |
197 | ||
198 | switch (ctrl->id) { | |
199 | case V4L2_CID_BRIGHTNESS: | |
200 | if (ctrl->value > 255) | |
201 | dec->brightness = 255; | |
202 | else if (ctrl->value < 0) | |
203 | dec->brightness = 0; | |
204 | else | |
205 | dec->brightness = ctrl->value; | |
206 | write_reg(client, 0x0a, dec->brightness); | |
207 | break; | |
208 | case V4L2_CID_CONTRAST: | |
209 | if (ctrl->value > 127) | |
210 | dec->contrast = 127; | |
211 | else if (ctrl->value < 0) | |
212 | dec->contrast = 0; | |
213 | else | |
214 | dec->contrast = ctrl->value; | |
215 | write_reg(client, 0x0b, dec->contrast); | |
216 | break; | |
217 | case V4L2_CID_SATURATION: | |
218 | if (ctrl->value > 127) | |
219 | dec->saturation = 127; | |
220 | else if (ctrl->value < 0) | |
221 | dec->saturation = 0; | |
222 | else | |
223 | dec->saturation = ctrl->value; | |
224 | write_reg(client, 0x0c, dec->saturation); | |
225 | break; | |
226 | case V4L2_CID_HUE: | |
227 | if (ctrl->value > 127) | |
228 | dec->hue = 127; | |
229 | else if (ctrl->value < -128) | |
230 | dec->hue = -128; | |
231 | else | |
232 | dec->hue = ctrl->value; | |
233 | write_reg(client, 0x0d, dec->hue); | |
234 | break; | |
235 | } | |
236 | break; | |
237 | } | |
238 | case VIDIOC_G_CTRL: | |
239 | { | |
240 | struct v4l2_control *ctrl = arg; | |
241 | ||
242 | switch (ctrl->id) { | |
243 | case V4L2_CID_BRIGHTNESS: | |
244 | ctrl->value = dec->brightness; | |
245 | break; | |
246 | case V4L2_CID_CONTRAST: | |
247 | ctrl->value = dec->contrast; | |
248 | break; | |
249 | case V4L2_CID_SATURATION: | |
250 | ctrl->value = dec->saturation; | |
251 | break; | |
252 | case V4L2_CID_HUE: | |
253 | ctrl->value = dec->hue; | |
254 | break; | |
255 | } | |
256 | break; | |
257 | } | |
258 | default: | |
259 | break; | |
260 | } | |
261 | return 0; | |
262 | } | |
263 | ||
264 | static struct i2c_driver wis_saa7113_driver; | |
265 | ||
266 | static struct i2c_client wis_saa7113_client_templ = { | |
267 | .name = "SAA7113 (WIS)", | |
268 | .driver = &wis_saa7113_driver, | |
269 | }; | |
270 | ||
271 | static int wis_saa7113_detect(struct i2c_adapter *adapter, int addr, int kind) | |
272 | { | |
273 | struct i2c_client *client; | |
274 | struct wis_saa7113 *dec; | |
275 | ||
276 | if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) | |
277 | return 0; | |
278 | ||
279 | client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); | |
280 | if (client == NULL) | |
281 | return -ENOMEM; | |
282 | memcpy(client, &wis_saa7113_client_templ, | |
283 | sizeof(wis_saa7113_client_templ)); | |
284 | client->adapter = adapter; | |
285 | client->addr = addr; | |
286 | ||
287 | dec = kmalloc(sizeof(struct wis_saa7113), GFP_KERNEL); | |
288 | if (dec == NULL) { | |
289 | kfree(client); | |
290 | return -ENOMEM; | |
291 | } | |
df20d69e | 292 | dec->norm = V4L2_STD_NTSC; |
866b8695 GKH |
293 | dec->brightness = 128; |
294 | dec->contrast = 71; | |
295 | dec->saturation = 64; | |
296 | dec->hue = 0; | |
297 | i2c_set_clientdata(client, dec); | |
298 | ||
299 | printk(KERN_DEBUG | |
300 | "wis-saa7113: initializing SAA7113 at address %d on %s\n", | |
301 | addr, adapter->name); | |
302 | ||
303 | if (write_regs(client, initial_registers) < 0) { | |
304 | printk(KERN_ERR | |
305 | "wis-saa7113: error initializing SAA7113\n"); | |
306 | kfree(client); | |
307 | kfree(dec); | |
308 | return 0; | |
309 | } | |
310 | ||
311 | i2c_attach_client(client); | |
312 | return 0; | |
313 | } | |
314 | ||
315 | static int wis_saa7113_detach(struct i2c_client *client) | |
316 | { | |
317 | struct wis_saa7113 *dec = i2c_get_clientdata(client); | |
318 | int r; | |
319 | ||
320 | r = i2c_detach_client(client); | |
321 | if (r < 0) | |
322 | return r; | |
323 | ||
324 | kfree(client); | |
325 | kfree(dec); | |
326 | return 0; | |
327 | } | |
328 | ||
329 | static struct i2c_driver wis_saa7113_driver = { | |
330 | .driver = { | |
331 | .name = "WIS SAA7113 I2C driver", | |
332 | }, | |
333 | .id = I2C_DRIVERID_WIS_SAA7113, | |
334 | .detach_client = wis_saa7113_detach, | |
335 | .command = wis_saa7113_command, | |
336 | }; | |
337 | ||
338 | static int __init wis_saa7113_init(void) | |
339 | { | |
340 | int r; | |
341 | ||
342 | r = i2c_add_driver(&wis_saa7113_driver); | |
343 | if (r < 0) | |
344 | return r; | |
345 | return wis_i2c_add_driver(wis_saa7113_driver.id, wis_saa7113_detect); | |
346 | } | |
347 | ||
348 | static void __exit wis_saa7113_cleanup(void) | |
349 | { | |
350 | wis_i2c_del_driver(wis_saa7113_detect); | |
351 | i2c_del_driver(&wis_saa7113_driver); | |
352 | } | |
353 | ||
354 | module_init(wis_saa7113_init); | |
355 | module_exit(wis_saa7113_cleanup); | |
356 | ||
357 | MODULE_LICENSE("GPL v2"); |