Rev 425 | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
425 | giacomo | 1 | /* |
2 | saa7111 - Philips SAA7111A video decoder driver version 0.0.3 |
||
3 | |||
4 | Copyright (C) 1998 Dave Perks <dperks@ibm.net> |
||
5 | |||
6 | Slight changes for video timing and attachment output by |
||
7 | Wolfgang Scherr <scherr@net4you.net> |
||
8 | |||
9 | This program is free software; you can redistribute it and/or modify |
||
10 | it under the terms of the GNU General Public License as published by |
||
11 | the Free Software Foundation; either version 2 of the License, or |
||
12 | (at your option) any later version. |
||
13 | |||
14 | This program is distributed in the hope that it will be useful, |
||
15 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
16 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
17 | GNU General Public License for more details. |
||
18 | |||
19 | You should have received a copy of the GNU General Public License |
||
20 | along with this program; if not, write to the Free Software |
||
21 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
||
22 | */ |
||
23 | |||
24 | #include <linux/module.h> |
||
25 | #include <linux/init.h> |
||
26 | #include <linux/delay.h> |
||
27 | #include <linux/errno.h> |
||
28 | #include <linux/fs.h> |
||
29 | #include <linux/kernel.h> |
||
30 | #include <linux/major.h> |
||
31 | #include <linux/slab.h> |
||
32 | #include <linux/mm.h> |
||
33 | #include <linux/sched.h> |
||
34 | |||
35 | #include <linux/videodev.h> |
||
36 | #include <linux/version.h> |
||
37 | #include <linux/i2c.h> |
||
38 | |||
39 | #include <linux/video_decoder.h> |
||
40 | |||
41 | #define DEBUG(x) /* Debug driver */ |
||
42 | |||
43 | /* ----------------------------------------------------------------------- */ |
||
44 | |||
45 | struct saa7111 { |
||
46 | struct i2c_client *client; |
||
47 | int addr; |
||
48 | struct semaphore lock; |
||
49 | unsigned char reg[32]; |
||
50 | |||
51 | int norm; |
||
52 | int input; |
||
53 | int enable; |
||
54 | int bright; |
||
55 | int contrast; |
||
56 | int hue; |
||
57 | int sat; |
||
58 | }; |
||
59 | |||
60 | static unsigned short normal_i2c[] = { 0x24, 0x25, I2C_CLIENT_END }; |
||
61 | static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; |
||
62 | |||
63 | I2C_CLIENT_INSMOD; |
||
64 | |||
65 | static struct i2c_client client_template; |
||
66 | /* ----------------------------------------------------------------------- */ |
||
67 | static int saa7111_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind) |
||
68 | { |
||
69 | int i; |
||
70 | struct saa7111 *decoder; |
||
71 | struct i2c_client *client; |
||
72 | |||
73 | /* who wrote this? init[] is used for i2c_master_send() which expects an array that |
||
74 | will be used for the 'buf' part of an i2c message unchanged. so, the first byte |
||
75 | needs to be the subaddress to start with, then follow the data bytes... */ |
||
76 | static const unsigned char init[] = { |
||
77 | 0x00, /* start address */ |
||
78 | |||
79 | 0x00, /* 00 - ID byte */ |
||
80 | 0x00, /* 01 - reserved */ |
||
81 | |||
82 | /*front end */ |
||
83 | 0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */ |
||
84 | 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */ |
||
85 | 0x00, /* 04 - GAI1=256 */ |
||
86 | 0x00, /* 05 - GAI2=256 */ |
||
87 | |||
88 | /* decoder */ |
||
89 | 0xf3, /* 06 - HSB at 13(50Hz) / 17(60Hz) pixels after end of last line */ |
||
90 | 0x13, /* 07 - HSS at 113(50Hz) / 117(60Hz) pixels after end of last line */ |
||
91 | 0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1, HPLL=0, VNOI=0 */ |
||
92 | 0x01, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0, UPTCV=0, APER=1 */ |
||
93 | 0x80, /* 0a - BRIG=128 */ |
||
94 | 0x47, /* 0b - CONT=1.109 */ |
||
95 | 0x40, /* 0c - SATN=1.0 */ |
||
96 | 0x00, /* 0d - HUE=0 */ |
||
97 | 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0, FCTC=0, CHBW=1 */ |
||
98 | 0x00, /* 0f - reserved */ |
||
99 | 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */ |
||
100 | 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1, OEYC=1, OEHV=1, VIPB=0, COLO=0 */ |
||
101 | 0x00, /* 12 - output control 2 */ |
||
102 | 0x00, /* 13 - output control 3 */ |
||
103 | 0x00, /* 14 - reserved */ |
||
104 | 0x00, /* 15 - VBI */ |
||
105 | 0x00, /* 16 - VBI */ |
||
106 | 0x00, /* 17 - VBI */ |
||
107 | }; |
||
108 | client = kmalloc(sizeof(*client), GFP_KERNEL); |
||
109 | if(client == NULL) |
||
110 | return -ENOMEM; |
||
111 | client_template.adapter = adap; |
||
112 | client_template.addr = addr; |
||
113 | memcpy(client, &client_template, sizeof(*client)); |
||
114 | |||
115 | decoder = kmalloc(sizeof(*decoder), GFP_KERNEL); |
||
116 | if (decoder == NULL) |
||
117 | { |
||
118 | kfree(client); |
||
119 | return -ENOMEM; |
||
120 | } |
||
121 | |||
122 | memset(decoder, 0, sizeof(*decoder)); |
||
123 | decoder->client = client; |
||
124 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) |
||
125 | client->data = decoder; |
||
126 | #else |
||
127 | i2c_set_clientdata(client, decoder); |
||
128 | #endif |
||
129 | decoder->addr = addr; |
||
130 | decoder->norm = VIDEO_MODE_NTSC; |
||
131 | decoder->input = 0; |
||
132 | decoder->enable = 1; |
||
133 | decoder->bright = 32768; |
||
134 | decoder->contrast = 32768; |
||
135 | decoder->hue = 32768; |
||
136 | decoder->sat = 32768; |
||
137 | |||
138 | i = i2c_master_send(client, init, sizeof(init)); |
||
139 | if (i < 0) { |
||
140 | printk(KERN_ERR "%s_attach: init status %d\n", |
||
141 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) |
||
142 | client->name, i); |
||
143 | #else |
||
144 | client->dev.name, i); |
||
145 | #endif |
||
146 | } else { |
||
147 | printk(KERN_INFO "%s_attach: chip version %x @ 0x%08x\n", |
||
148 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) |
||
149 | client->name, i2c_smbus_read_byte_data(client, 0x00) >> 4,addr); |
||
150 | #else |
||
151 | client->dev.name, i2c_smbus_read_byte_data(client, 0x00) >> 4,addr); |
||
152 | #endif |
||
153 | } |
||
154 | |||
155 | init_MUTEX(&decoder->lock); |
||
156 | i2c_attach_client(client); |
||
157 | MOD_INC_USE_COUNT; |
||
158 | return 0; |
||
159 | } |
||
160 | static int saa7111_probe(struct i2c_adapter *adap) |
||
161 | { |
||
162 | /* probing unknown devices on any Matrox i2c-bus takes ages due to the |
||
163 | slow bit banging algorithm used. because of the fact a saa7111(a) |
||
164 | is *never* present on a Matrox gfx card, we can skip such adapters |
||
165 | here */ |
||
166 | if( 0 != (adap->id & I2C_HW_B_G400)) { |
||
167 | return -ENODEV; |
||
168 | } |
||
169 | |||
170 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) |
||
171 | printk("saa7111: probing %s i2c adapter [id=0x%x]\n", |
||
172 | adap->name,adap->id); |
||
173 | #else |
||
174 | printk("saa7111: probing %s i2c adapter [id=0x%x]\n", |
||
175 | adap->dev.name,adap->id); |
||
176 | #endif |
||
177 | return i2c_probe(adap, &addr_data, saa7111_attach); |
||
178 | } |
||
179 | |||
180 | static int saa7111_detach(struct i2c_client *client) |
||
181 | { |
||
182 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) |
||
183 | struct saa7111 *decoder = client->data; |
||
184 | #else |
||
185 | struct saa7111 *decoder = i2c_get_clientdata(client); |
||
186 | #endif |
||
187 | i2c_detach_client(client); |
||
188 | kfree(decoder); |
||
189 | kfree(client); |
||
190 | MOD_DEC_USE_COUNT; |
||
191 | return 0; |
||
192 | } |
||
193 | |||
194 | static int saa7111_command(struct i2c_client *client, unsigned int cmd, |
||
195 | void *arg) |
||
196 | { |
||
197 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) |
||
198 | struct saa7111 *decoder = client->data; |
||
199 | #else |
||
200 | struct saa7111 *decoder = i2c_get_clientdata(client); |
||
201 | #endif |
||
202 | switch (cmd) { |
||
203 | |||
204 | #if defined(DECODER_DUMP) |
||
205 | case DECODER_DUMP: |
||
206 | { |
||
207 | int i; |
||
208 | |||
209 | for (i = 0; i < 32; i += 16) { |
||
210 | int j; |
||
211 | |||
212 | #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) |
||
213 | printk("KERN_DEBUG %s: %03x", client->name, |
||
214 | #else |
||
215 | printk("KERN_DEBUG %s: %03x", client->dev.name, |
||
216 | #endif |
||
217 | i); |
||
218 | for (j = 0; j < 16; ++j) { |
||
219 | printk(" %02x", |
||
220 | i2c_smbus_read_byte_data(client, |
||
221 | i + j)); |
||
222 | } |
||
223 | printk("\n"); |
||
224 | } |
||
225 | } |
||
226 | break; |
||
227 | #endif /* defined(DECODER_DUMP) */ |
||
228 | case DECODER_GET_CAPABILITIES: |
||
229 | { |
||
230 | struct video_decoder_capability *cap = arg; |
||
231 | |||
232 | cap->flags |
||
233 | = VIDEO_DECODER_PAL |
||
234 | | VIDEO_DECODER_NTSC |
||
235 | | VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR; |
||
236 | cap->inputs = 8; |
||
237 | cap->outputs = 1; |
||
238 | } |
||
239 | break; |
||
240 | case DECODER_GET_STATUS: |
||
241 | { |
||
242 | int *iarg = arg; |
||
243 | int status; |
||
244 | int res; |
||
245 | |||
246 | status = i2c_smbus_read_byte_data(client, 0x1f); |
||
247 | res = 0; |
||
248 | if ((status & (1 << 6)) == 0) { |
||
249 | res |= DECODER_STATUS_GOOD; |
||
250 | } |
||
251 | switch (decoder->norm) { |
||
252 | case VIDEO_MODE_NTSC: |
||
253 | res |= DECODER_STATUS_NTSC; |
||
254 | break; |
||
255 | case VIDEO_MODE_PAL: |
||
256 | res |= DECODER_STATUS_PAL; |
||
257 | break; |
||
258 | default: |
||
259 | case VIDEO_MODE_AUTO: |
||
260 | if ((status & (1 << 5)) != 0) { |
||
261 | res |= DECODER_STATUS_NTSC; |
||
262 | } else { |
||
263 | res |= DECODER_STATUS_PAL; |
||
264 | } |
||
265 | break; |
||
266 | } |
||
267 | if ((status & (1 << 0)) != 0) { |
||
268 | res |= DECODER_STATUS_COLOR; |
||
269 | } |
||
270 | *iarg = res; |
||
271 | } |
||
272 | break; |
||
273 | |||
274 | case DECODER_SET_NORM: |
||
275 | { |
||
276 | int *iarg = arg; |
||
277 | |||
278 | switch (*iarg) { |
||
279 | |||
280 | case VIDEO_MODE_NTSC: |
||
281 | i2c_smbus_write_byte_data(client, 0x08, |
||
282 | (decoder-> |
||
283 | reg[0x08] & 0x3f) | 0x40); |
||
284 | break; |
||
285 | |||
286 | case VIDEO_MODE_PAL: |
||
287 | i2c_smbus_write_byte_data(client, 0x08, |
||
288 | (decoder-> |
||
289 | reg[0x08] & 0x3f) | 0x00); |
||
290 | break; |
||
291 | |||
292 | case VIDEO_MODE_AUTO: |
||
293 | i2c_smbus_write_byte_data(client, 0x08, |
||
294 | (decoder-> |
||
295 | reg[0x08] & 0x3f) | 0x80); |
||
296 | break; |
||
297 | |||
298 | default: |
||
299 | return -EINVAL; |
||
300 | |||
301 | } |
||
302 | decoder->norm = *iarg; |
||
303 | } |
||
304 | break; |
||
305 | |||
306 | case DECODER_SET_INPUT: |
||
307 | { |
||
308 | int *iarg = arg; |
||
309 | |||
310 | if (*iarg < 0 || *iarg > 7) { |
||
311 | return -EINVAL; |
||
312 | } |
||
313 | |||
314 | if (decoder->input != *iarg) { |
||
315 | decoder->input = *iarg; |
||
316 | /* select mode */ |
||
317 | i2c_smbus_write_byte_data(client, 0x02, |
||
318 | (decoder-> |
||
319 | reg[0x02] & 0xf8) | |
||
320 | decoder->input); |
||
321 | /* bypass chrominance trap for modes 4..7 */ |
||
322 | i2c_smbus_write_byte_data(client, 0x09, |
||
323 | (decoder-> |
||
324 | reg[0x09] & 0x7f) | |
||
325 | ((decoder->input > |
||
326 | 3) ? 0x80 : 0)); |
||
327 | } |
||
328 | } |
||
329 | break; |
||
330 | |||
331 | case DECODER_SET_OUTPUT: |
||
332 | { |
||
333 | int *iarg = arg; |
||
334 | |||
335 | /* not much choice of outputs */ |
||
336 | if (*iarg != 0) { |
||
337 | return -EINVAL; |
||
338 | } |
||
339 | } |
||
340 | break; |
||
341 | |||
342 | case DECODER_ENABLE_OUTPUT: |
||
343 | { |
||
344 | int *iarg = arg; |
||
345 | int enable = (*iarg != 0); |
||
346 | |||
347 | if (decoder->enable != enable) { |
||
348 | decoder->enable = enable; |
||
349 | |||
350 | // RJ: If output should be disabled (for playing videos), we also need a open PLL. |
||
351 | // The input is set to 0 (where no input source is connected), although this |
||
352 | // is not necessary. |
||
353 | // |
||
354 | // If output should be enabled, we have to reverse the above. |
||
355 | |||
356 | if (decoder->enable) { |
||
357 | i2c_smbus_write_byte_data(client, 0x02, |
||
358 | (decoder-> |
||
359 | reg[0x02] & 0xf8) | |
||
360 | decoder->input); |
||
361 | i2c_smbus_write_byte_data(client, 0x08, |
||
362 | (decoder-> |
||
363 | reg[0x08] & 0xfb)); |
||
364 | i2c_smbus_write_byte_data(client, 0x11, |
||
365 | (decoder-> |
||
366 | reg[0x11] & 0xf3) | |
||
367 | 0x0c); |
||
368 | } else { |
||
369 | i2c_smbus_write_byte_data(client, 0x02, |
||
370 | (decoder-> |
||
371 | reg[0x02] & 0xf8)); |
||
372 | i2c_smbus_write_byte_data(client, 0x08, |
||
373 | (decoder-> |
||
374 | reg[0x08] & 0xfb) | |
||
375 | 0x04); |
||
376 | i2c_smbus_write_byte_data(client, 0x11, |
||
377 | (decoder-> |
||
378 | reg[0x11] & 0xf3)); |
||
379 | } |
||
380 | } |
||
381 | } |
||
382 | break; |
||
383 | |||
384 | case DECODER_SET_PICTURE: |
||
385 | { |
||
386 | struct video_picture *pic = arg; |
||
387 | |||
388 | if (decoder->bright != pic->brightness) { |
||
389 | /* We want 0 to 255 we get 0-65535 */ |
||
390 | decoder->bright = pic->brightness; |
||
391 | i2c_smbus_write_byte_data(client, 0x0a, |
||
392 | decoder->bright >> 8); |
||
393 | } |
||
394 | if (decoder->contrast != pic->contrast) { |
||
395 | /* We want 0 to 127 we get 0-65535 */ |
||
396 | decoder->contrast = pic->contrast; |
||
397 | i2c_smbus_write_byte_data(client, 0x0b, |
||
398 | decoder->contrast >> 9); |
||
399 | } |
||
400 | if (decoder->sat != pic->colour) { |
||
401 | /* We want 0 to 127 we get 0-65535 */ |
||
402 | decoder->sat = pic->colour; |
||
403 | i2c_smbus_write_byte_data(client, 0x0c, |
||
404 | decoder->sat >> 9); |
||
405 | } |
||
406 | if (decoder->hue != pic->hue) { |
||
407 | /* We want -128 to 127 we get 0-65535 */ |
||
408 | decoder->hue = pic->hue; |
||
409 | i2c_smbus_write_byte_data(client, 0x0d, |
||
410 | (decoder->hue - 32768) >> 8); |
||
411 | } |
||
412 | } |
||
413 | break; |
||
414 | |||
415 | default: |
||
416 | return -EINVAL; |
||
417 | } |
||
418 | |||
419 | return 0; |
||
420 | } |
||
421 | |||
422 | /* ----------------------------------------------------------------------- */ |
||
423 | |||
424 | static struct i2c_driver i2c_driver_saa7111 = { |
||
425 | #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,54) |
||
426 | .owner = THIS_MODULE, |
||
427 | #endif |
||
428 | .name = "saa7111", /* name */ |
||
429 | .id = I2C_DRIVERID_SAA7111A, /* ID */ |
||
430 | .flags = I2C_DF_NOTIFY, |
||
431 | .attach_adapter = saa7111_probe, |
||
432 | .detach_client = saa7111_detach, |
||
433 | .command = saa7111_command |
||
434 | }; |
||
435 | |||
436 | static struct i2c_client client_template = { |
||
437 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) |
||
438 | .name = "saa7111_client", |
||
439 | #else |
||
440 | .dev = { |
||
441 | .name = "saa7111_client", |
||
442 | }, |
||
443 | #endif |
||
444 | .id = -1, |
||
445 | .driver = &i2c_driver_saa7111 |
||
446 | }; |
||
447 | |||
448 | static int saa7111_init(void) |
||
449 | { |
||
450 | return i2c_add_driver(&i2c_driver_saa7111); |
||
451 | } |
||
452 | |||
453 | static void saa7111_exit(void) |
||
454 | { |
||
455 | i2c_del_driver(&i2c_driver_saa7111); |
||
456 | } |
||
457 | |||
458 | module_init(saa7111_init); |
||
459 | module_exit(saa7111_exit); |
||
460 | MODULE_LICENSE("GPL"); |