diff options
Diffstat (limited to 'drivers/media/video/saa5249.c')
-rw-r--r-- | drivers/media/video/saa5249.c | 650 |
1 files changed, 0 insertions, 650 deletions
diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c deleted file mode 100644 index 31ff27df4cbf..000000000000 --- a/drivers/media/video/saa5249.c +++ /dev/null | |||
@@ -1,650 +0,0 @@ | |||
1 | /* | ||
2 | * Modified in order to keep it compatible both with new and old videotext IOCTLs by | ||
3 | * Michael Geng <linux@MichaelGeng.de> | ||
4 | * | ||
5 | * Cleaned up to use existing videodev interface and allow the idea | ||
6 | * of multiple teletext decoders on the video4linux iface. Changed i2c | ||
7 | * to cover addressing clashes on device busses. It's also rebuilt so | ||
8 | * you can add arbitary multiple teletext devices to Linux video4linux | ||
9 | * now (well 32 anyway). | ||
10 | * | ||
11 | * Alan Cox <alan@lxorguk.ukuu.org.uk> | ||
12 | * | ||
13 | * The original driver was heavily modified to match the i2c interface | ||
14 | * It was truncated to use the WinTV boards, too. | ||
15 | * | ||
16 | * Copyright (c) 1998 Richard Guenther <richard.guenther@student.uni-tuebingen.de> | ||
17 | * | ||
18 | * Derived From | ||
19 | * | ||
20 | * vtx.c: | ||
21 | * This is a loadable character-device-driver for videotext-interfaces | ||
22 | * (aka teletext). Please check the Makefile/README for a list of supported | ||
23 | * interfaces. | ||
24 | * | ||
25 | * Copyright (c) 1994-97 Martin Buck <martin-2.buck@student.uni-ulm.de> | ||
26 | * | ||
27 | * | ||
28 | * This program is free software; you can redistribute it and/or modify | ||
29 | * it under the terms of the GNU General Public License as published by | ||
30 | * the Free Software Foundation; either version 2 of the License, or | ||
31 | * (at your option) any later version. | ||
32 | * | ||
33 | * This program is distributed in the hope that it will be useful, | ||
34 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
35 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
36 | * GNU General Public License for more details. | ||
37 | * | ||
38 | * You should have received a copy of the GNU General Public License | ||
39 | * along with this program; if not, write to the Free Software | ||
40 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | ||
41 | * USA. | ||
42 | */ | ||
43 | |||
44 | #include <linux/module.h> | ||
45 | #include <linux/kernel.h> | ||
46 | #include <linux/mm.h> | ||
47 | #include <linux/init.h> | ||
48 | #include <linux/i2c.h> | ||
49 | #include <linux/mutex.h> | ||
50 | #include <linux/delay.h> | ||
51 | #include <linux/videotext.h> | ||
52 | #include <linux/videodev2.h> | ||
53 | #include <linux/slab.h> | ||
54 | #include <media/v4l2-device.h> | ||
55 | #include <media/v4l2-chip-ident.h> | ||
56 | #include <media/v4l2-ioctl.h> | ||
57 | #include <media/v4l2-i2c-drv.h> | ||
58 | |||
59 | MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); | ||
60 | MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver"); | ||
61 | MODULE_LICENSE("GPL"); | ||
62 | |||
63 | |||
64 | #define VTX_VER_MAJ 1 | ||
65 | #define VTX_VER_MIN 8 | ||
66 | |||
67 | |||
68 | #define NUM_DAUS 4 | ||
69 | #define NUM_BUFS 8 | ||
70 | |||
71 | static const int disp_modes[8][3] = | ||
72 | { | ||
73 | { 0x46, 0x03, 0x03 }, /* DISPOFF */ | ||
74 | { 0x46, 0xcc, 0xcc }, /* DISPNORM */ | ||
75 | { 0x44, 0x0f, 0x0f }, /* DISPTRANS */ | ||
76 | { 0x46, 0xcc, 0x46 }, /* DISPINS */ | ||
77 | { 0x44, 0x03, 0x03 }, /* DISPOFF, interlaced */ | ||
78 | { 0x44, 0xcc, 0xcc }, /* DISPNORM, interlaced */ | ||
79 | { 0x44, 0x0f, 0x0f }, /* DISPTRANS, interlaced */ | ||
80 | { 0x44, 0xcc, 0x46 } /* DISPINS, interlaced */ | ||
81 | }; | ||
82 | |||
83 | |||
84 | |||
85 | #define PAGE_WAIT msecs_to_jiffies(300) /* Time between requesting page and */ | ||
86 | /* checking status bits */ | ||
87 | #define PGBUF_EXPIRE msecs_to_jiffies(15000) /* Time to wait before retransmitting */ | ||
88 | /* page regardless of infobits */ | ||
89 | typedef struct { | ||
90 | u8 pgbuf[VTX_VIRTUALSIZE]; /* Page-buffer */ | ||
91 | u8 laststat[10]; /* Last value of infobits for DAU */ | ||
92 | u8 sregs[7]; /* Page-request registers */ | ||
93 | unsigned long expire; /* Time when page will be expired */ | ||
94 | unsigned clrfound : 1; /* VTXIOCCLRFOUND has been called */ | ||
95 | unsigned stopped : 1; /* VTXIOCSTOPDAU has been called */ | ||
96 | } vdau_t; | ||
97 | |||
98 | struct saa5249_device | ||
99 | { | ||
100 | struct v4l2_subdev sd; | ||
101 | struct video_device *vdev; | ||
102 | vdau_t vdau[NUM_DAUS]; /* Data for virtual DAUs (the 5249 only has one */ | ||
103 | /* real DAU, so we have to simulate some more) */ | ||
104 | int vtx_use_count; | ||
105 | int is_searching[NUM_DAUS]; | ||
106 | int disp_mode; | ||
107 | int virtual_mode; | ||
108 | unsigned long in_use; | ||
109 | struct mutex lock; | ||
110 | }; | ||
111 | |||
112 | static inline struct saa5249_device *to_dev(struct v4l2_subdev *sd) | ||
113 | { | ||
114 | return container_of(sd, struct saa5249_device, sd); | ||
115 | } | ||
116 | |||
117 | |||
118 | #define CCTWR 34 /* I²C write/read-address of vtx-chip */ | ||
119 | #define CCTRD 35 | ||
120 | #define NOACK_REPEAT 10 /* Retry access this many times on failure */ | ||
121 | #define CLEAR_DELAY msecs_to_jiffies(50) /* Time required to clear a page */ | ||
122 | #define READY_TIMEOUT msecs_to_jiffies(30) /* Time to wait for ready signal of I2C-bus interface */ | ||
123 | #define INIT_DELAY 500 /* Time in usec to wait at initialization of CEA interface */ | ||
124 | #define START_DELAY 10 /* Time in usec to wait before starting write-cycle (CEA) */ | ||
125 | |||
126 | #define VTX_DEV_MINOR 0 | ||
127 | |||
128 | static struct video_device saa_template; /* Declared near bottom */ | ||
129 | |||
130 | /* | ||
131 | * Wait the given number of jiffies (10ms). This calls the scheduler, so the actual | ||
132 | * delay may be longer. | ||
133 | */ | ||
134 | |||
135 | static void jdelay(unsigned long delay) | ||
136 | { | ||
137 | sigset_t oldblocked = current->blocked; | ||
138 | |||
139 | spin_lock_irq(¤t->sighand->siglock); | ||
140 | sigfillset(¤t->blocked); | ||
141 | recalc_sigpending(); | ||
142 | spin_unlock_irq(¤t->sighand->siglock); | ||
143 | msleep_interruptible(jiffies_to_msecs(delay)); | ||
144 | |||
145 | spin_lock_irq(¤t->sighand->siglock); | ||
146 | current->blocked = oldblocked; | ||
147 | recalc_sigpending(); | ||
148 | spin_unlock_irq(¤t->sighand->siglock); | ||
149 | } | ||
150 | |||
151 | |||
152 | /* | ||
153 | * I2C interfaces | ||
154 | */ | ||
155 | |||
156 | static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data) | ||
157 | { | ||
158 | struct i2c_client *client = v4l2_get_subdevdata(&t->sd); | ||
159 | char buf[64]; | ||
160 | |||
161 | buf[0] = reg; | ||
162 | memcpy(buf+1, data, count); | ||
163 | |||
164 | if (i2c_master_send(client, buf, count + 1) == count + 1) | ||
165 | return 0; | ||
166 | return -1; | ||
167 | } | ||
168 | |||
169 | static int i2c_senddata(struct saa5249_device *t, ...) | ||
170 | { | ||
171 | unsigned char buf[64]; | ||
172 | int v; | ||
173 | int ct = 0; | ||
174 | va_list argp; | ||
175 | va_start(argp,t); | ||
176 | |||
177 | while ((v = va_arg(argp, int)) != -1) | ||
178 | buf[ct++] = v; | ||
179 | |||
180 | va_end(argp); | ||
181 | return i2c_sendbuf(t, buf[0], ct-1, buf+1); | ||
182 | } | ||
183 | |||
184 | /* Get count number of bytes from I²C-device at address adr, store them in buf. Start & stop | ||
185 | * handshaking is done by this routine, ack will be sent after the last byte to inhibit further | ||
186 | * sending of data. If uaccess is 'true', data is written to user-space with put_user. | ||
187 | * Returns -1 if I²C-device didn't send acknowledge, 0 otherwise | ||
188 | */ | ||
189 | |||
190 | static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf) | ||
191 | { | ||
192 | struct i2c_client *client = v4l2_get_subdevdata(&t->sd); | ||
193 | |||
194 | if (i2c_master_recv(client, buf, count) != count) | ||
195 | return -1; | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | |||
200 | /* | ||
201 | * Standard character-device-driver functions | ||
202 | */ | ||
203 | |||
204 | static long do_saa5249_ioctl(struct file *file, unsigned int cmd, void *arg) | ||
205 | { | ||
206 | static int virtual_mode = false; | ||
207 | struct saa5249_device *t = video_drvdata(file); | ||
208 | |||
209 | switch (cmd) { | ||
210 | case VTXIOCGETINFO: | ||
211 | { | ||
212 | vtx_info_t *info = arg; | ||
213 | info->version_major = VTX_VER_MAJ; | ||
214 | info->version_minor = VTX_VER_MIN; | ||
215 | info->numpages = NUM_DAUS; | ||
216 | /*info->cct_type = CCT_TYPE;*/ | ||
217 | return 0; | ||
218 | } | ||
219 | |||
220 | case VTXIOCCLRPAGE: | ||
221 | { | ||
222 | vtx_pagereq_t *req = arg; | ||
223 | |||
224 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | ||
225 | return -EINVAL; | ||
226 | memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); | ||
227 | t->vdau[req->pgbuf].clrfound = true; | ||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | case VTXIOCCLRFOUND: | ||
232 | { | ||
233 | vtx_pagereq_t *req = arg; | ||
234 | |||
235 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | ||
236 | return -EINVAL; | ||
237 | t->vdau[req->pgbuf].clrfound = true; | ||
238 | return 0; | ||
239 | } | ||
240 | |||
241 | case VTXIOCPAGEREQ: | ||
242 | { | ||
243 | vtx_pagereq_t *req = arg; | ||
244 | if (!(req->pagemask & PGMASK_PAGE)) | ||
245 | req->page = 0; | ||
246 | if (!(req->pagemask & PGMASK_HOUR)) | ||
247 | req->hour = 0; | ||
248 | if (!(req->pagemask & PGMASK_MINUTE)) | ||
249 | req->minute = 0; | ||
250 | if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */ | ||
251 | return -EINVAL; | ||
252 | req->page &= 0x7ff; | ||
253 | if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f || | ||
254 | req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | ||
255 | return -EINVAL; | ||
256 | t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100); | ||
257 | t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf); | ||
258 | t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf); | ||
259 | t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10); | ||
260 | t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf); | ||
261 | t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10); | ||
262 | t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf); | ||
263 | t->vdau[req->pgbuf].stopped = false; | ||
264 | t->vdau[req->pgbuf].clrfound = true; | ||
265 | t->is_searching[req->pgbuf] = true; | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | case VTXIOCGETSTAT: | ||
270 | { | ||
271 | vtx_pagereq_t *req = arg; | ||
272 | u8 infobits[10]; | ||
273 | vtx_pageinfo_t info; | ||
274 | int a; | ||
275 | |||
276 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | ||
277 | return -EINVAL; | ||
278 | if (!t->vdau[req->pgbuf].stopped) { | ||
279 | if (i2c_senddata(t, 2, 0, -1) || | ||
280 | i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) || | ||
281 | i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) || | ||
282 | i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) || | ||
283 | i2c_senddata(t, 8, 0, 25, 0, -1)) | ||
284 | return -EIO; | ||
285 | jdelay(PAGE_WAIT); | ||
286 | if (i2c_getdata(t, 10, infobits)) | ||
287 | return -EIO; | ||
288 | |||
289 | if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */ | ||
290 | (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) || | ||
291 | time_after_eq(jiffies, t->vdau[req->pgbuf].expire))) | ||
292 | { /* check if new page arrived */ | ||
293 | if (i2c_senddata(t, 8, 0, 0, 0, -1) || | ||
294 | i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf)) | ||
295 | return -EIO; | ||
296 | t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE; | ||
297 | memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE); | ||
298 | if (t->virtual_mode) { | ||
299 | /* Packet X/24 */ | ||
300 | if (i2c_senddata(t, 8, 0, 0x20, 0, -1) || | ||
301 | i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40)) | ||
302 | return -EIO; | ||
303 | /* Packet X/27/0 */ | ||
304 | if (i2c_senddata(t, 8, 0, 0x21, 0, -1) || | ||
305 | i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40)) | ||
306 | return -EIO; | ||
307 | /* Packet 8/30/0...8/30/15 | ||
308 | * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30, | ||
309 | * so we should undo this here. | ||
310 | */ | ||
311 | if (i2c_senddata(t, 8, 0, 0x22, 0, -1) || | ||
312 | i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40)) | ||
313 | return -EIO; | ||
314 | } | ||
315 | t->vdau[req->pgbuf].clrfound = false; | ||
316 | memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits)); | ||
317 | } else { | ||
318 | memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); | ||
319 | } | ||
320 | } else { | ||
321 | memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); | ||
322 | } | ||
323 | |||
324 | info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f); | ||
325 | if (info.pagenum < 0x100) | ||
326 | info.pagenum += 0x800; | ||
327 | info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f); | ||
328 | info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f); | ||
329 | info.charset = ((infobits[7] >> 1) & 7); | ||
330 | info.delete = !!(infobits[3] & 8); | ||
331 | info.headline = !!(infobits[5] & 4); | ||
332 | info.subtitle = !!(infobits[5] & 8); | ||
333 | info.supp_header = !!(infobits[6] & 1); | ||
334 | info.update = !!(infobits[6] & 2); | ||
335 | info.inter_seq = !!(infobits[6] & 4); | ||
336 | info.dis_disp = !!(infobits[6] & 8); | ||
337 | info.serial = !!(infobits[7] & 1); | ||
338 | info.notfound = !!(infobits[8] & 0x10); | ||
339 | info.pblf = !!(infobits[9] & 0x20); | ||
340 | info.hamming = 0; | ||
341 | for (a = 0; a <= 7; a++) { | ||
342 | if (infobits[a] & 0xf0) { | ||
343 | info.hamming = 1; | ||
344 | break; | ||
345 | } | ||
346 | } | ||
347 | if (t->vdau[req->pgbuf].clrfound) | ||
348 | info.notfound = 1; | ||
349 | if (copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t))) | ||
350 | return -EFAULT; | ||
351 | if (!info.hamming && !info.notfound) | ||
352 | t->is_searching[req->pgbuf] = false; | ||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | case VTXIOCGETPAGE: | ||
357 | { | ||
358 | vtx_pagereq_t *req = arg; | ||
359 | int start, end; | ||
360 | |||
361 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 || | ||
362 | req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE)) | ||
363 | return -EINVAL; | ||
364 | if (copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1)) | ||
365 | return -EFAULT; | ||
366 | |||
367 | /* | ||
368 | * Always read the time directly from SAA5249 | ||
369 | */ | ||
370 | |||
371 | if (req->start <= 39 && req->end >= 32) { | ||
372 | int len; | ||
373 | char buf[16]; | ||
374 | start = max(req->start, 32); | ||
375 | end = min(req->end, 39); | ||
376 | len = end - start + 1; | ||
377 | if (i2c_senddata(t, 8, 0, 0, start, -1) || | ||
378 | i2c_getdata(t, len, buf)) | ||
379 | return -EIO; | ||
380 | if (copy_to_user(req->buffer + start - req->start, buf, len)) | ||
381 | return -EFAULT; | ||
382 | } | ||
383 | /* Insert the current header if DAU is still searching for a page */ | ||
384 | if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf]) { | ||
385 | char buf[32]; | ||
386 | int len; | ||
387 | |||
388 | start = max(req->start, 7); | ||
389 | end = min(req->end, 31); | ||
390 | len = end - start + 1; | ||
391 | if (i2c_senddata(t, 8, 0, 0, start, -1) || | ||
392 | i2c_getdata(t, len, buf)) | ||
393 | return -EIO; | ||
394 | if (copy_to_user(req->buffer + start - req->start, buf, len)) | ||
395 | return -EFAULT; | ||
396 | } | ||
397 | return 0; | ||
398 | } | ||
399 | |||
400 | case VTXIOCSTOPDAU: | ||
401 | { | ||
402 | vtx_pagereq_t *req = arg; | ||
403 | |||
404 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | ||
405 | return -EINVAL; | ||
406 | t->vdau[req->pgbuf].stopped = true; | ||
407 | t->is_searching[req->pgbuf] = false; | ||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | case VTXIOCPUTPAGE: | ||
412 | case VTXIOCSETDISP: | ||
413 | case VTXIOCPUTSTAT: | ||
414 | return 0; | ||
415 | |||
416 | case VTXIOCCLRCACHE: | ||
417 | { | ||
418 | if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11, | ||
419 | ' ', ' ', ' ', ' ', ' ', ' ', | ||
420 | ' ', ' ', ' ', ' ', ' ', ' ', | ||
421 | ' ', ' ', ' ', ' ', ' ', ' ', | ||
422 | ' ', ' ', ' ', ' ', ' ', ' ', | ||
423 | -1)) | ||
424 | return -EIO; | ||
425 | if (i2c_senddata(t, 3, 0x20, -1)) | ||
426 | return -EIO; | ||
427 | jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */ | ||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | case VTXIOCSETVIRT: | ||
432 | { | ||
433 | /* The SAA5249 has virtual-row reception turned on always */ | ||
434 | t->virtual_mode = (int)(long)arg; | ||
435 | return 0; | ||
436 | } | ||
437 | } | ||
438 | return -EINVAL; | ||
439 | } | ||
440 | |||
441 | /* | ||
442 | * Translates old vtx IOCTLs to new ones | ||
443 | * | ||
444 | * This keeps new kernel versions compatible with old userspace programs. | ||
445 | */ | ||
446 | static inline unsigned int vtx_fix_command(unsigned int cmd) | ||
447 | { | ||
448 | switch (cmd) { | ||
449 | case VTXIOCGETINFO_OLD: | ||
450 | cmd = VTXIOCGETINFO; | ||
451 | break; | ||
452 | case VTXIOCCLRPAGE_OLD: | ||
453 | cmd = VTXIOCCLRPAGE; | ||
454 | break; | ||
455 | case VTXIOCCLRFOUND_OLD: | ||
456 | cmd = VTXIOCCLRFOUND; | ||
457 | break; | ||
458 | case VTXIOCPAGEREQ_OLD: | ||
459 | cmd = VTXIOCPAGEREQ; | ||
460 | break; | ||
461 | case VTXIOCGETSTAT_OLD: | ||
462 | cmd = VTXIOCGETSTAT; | ||
463 | break; | ||
464 | case VTXIOCGETPAGE_OLD: | ||
465 | cmd = VTXIOCGETPAGE; | ||
466 | break; | ||
467 | case VTXIOCSTOPDAU_OLD: | ||
468 | cmd = VTXIOCSTOPDAU; | ||
469 | break; | ||
470 | case VTXIOCPUTPAGE_OLD: | ||
471 | cmd = VTXIOCPUTPAGE; | ||
472 | break; | ||
473 | case VTXIOCSETDISP_OLD: | ||
474 | cmd = VTXIOCSETDISP; | ||
475 | break; | ||
476 | case VTXIOCPUTSTAT_OLD: | ||
477 | cmd = VTXIOCPUTSTAT; | ||
478 | break; | ||
479 | case VTXIOCCLRCACHE_OLD: | ||
480 | cmd = VTXIOCCLRCACHE; | ||
481 | break; | ||
482 | case VTXIOCSETVIRT_OLD: | ||
483 | cmd = VTXIOCSETVIRT; | ||
484 | break; | ||
485 | } | ||
486 | return cmd; | ||
487 | } | ||
488 | |||
489 | /* | ||
490 | * Handle the locking | ||
491 | */ | ||
492 | |||
493 | static long saa5249_ioctl(struct file *file, | ||
494 | unsigned int cmd, unsigned long arg) | ||
495 | { | ||
496 | struct saa5249_device *t = video_drvdata(file); | ||
497 | long err; | ||
498 | |||
499 | cmd = vtx_fix_command(cmd); | ||
500 | mutex_lock(&t->lock); | ||
501 | err = video_usercopy(file, cmd, arg, do_saa5249_ioctl); | ||
502 | mutex_unlock(&t->lock); | ||
503 | return err; | ||
504 | } | ||
505 | |||
506 | static int saa5249_open(struct file *file) | ||
507 | { | ||
508 | struct saa5249_device *t = video_drvdata(file); | ||
509 | int pgbuf; | ||
510 | |||
511 | if (test_and_set_bit(0, &t->in_use)) | ||
512 | return -EBUSY; | ||
513 | |||
514 | if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */ | ||
515 | /* Turn off parity checks (we do this ourselves) */ | ||
516 | i2c_senddata(t, 1, disp_modes[t->disp_mode][0], 0, -1) || | ||
517 | /* Display TV-picture, no virtual rows */ | ||
518 | i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) | ||
519 | /* Set display to page 4 */ | ||
520 | { | ||
521 | clear_bit(0, &t->in_use); | ||
522 | return -EIO; | ||
523 | } | ||
524 | |||
525 | for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { | ||
526 | memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); | ||
527 | memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); | ||
528 | memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); | ||
529 | t->vdau[pgbuf].expire = 0; | ||
530 | t->vdau[pgbuf].clrfound = true; | ||
531 | t->vdau[pgbuf].stopped = true; | ||
532 | t->is_searching[pgbuf] = false; | ||
533 | } | ||
534 | t->virtual_mode = false; | ||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | |||
539 | |||
540 | static int saa5249_release(struct file *file) | ||
541 | { | ||
542 | struct saa5249_device *t = video_drvdata(file); | ||
543 | |||
544 | i2c_senddata(t, 1, 0x20, -1); /* Turn off CCT */ | ||
545 | i2c_senddata(t, 5, 3, 3, -1); /* Turn off TV-display */ | ||
546 | clear_bit(0, &t->in_use); | ||
547 | return 0; | ||
548 | } | ||
549 | |||
550 | static const struct v4l2_file_operations saa_fops = { | ||
551 | .owner = THIS_MODULE, | ||
552 | .open = saa5249_open, | ||
553 | .release = saa5249_release, | ||
554 | .ioctl = saa5249_ioctl, | ||
555 | }; | ||
556 | |||
557 | static struct video_device saa_template = | ||
558 | { | ||
559 | .name = "saa5249", | ||
560 | .fops = &saa_fops, | ||
561 | .release = video_device_release, | ||
562 | }; | ||
563 | |||
564 | static int saa5249_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) | ||
565 | { | ||
566 | struct i2c_client *client = v4l2_get_subdevdata(sd); | ||
567 | |||
568 | return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA5249, 0); | ||
569 | } | ||
570 | |||
571 | static const struct v4l2_subdev_core_ops saa5249_core_ops = { | ||
572 | .g_chip_ident = saa5249_g_chip_ident, | ||
573 | }; | ||
574 | |||
575 | static const struct v4l2_subdev_ops saa5249_ops = { | ||
576 | .core = &saa5249_core_ops, | ||
577 | }; | ||
578 | |||
579 | static int saa5249_probe(struct i2c_client *client, | ||
580 | const struct i2c_device_id *id) | ||
581 | { | ||
582 | int pgbuf; | ||
583 | int err; | ||
584 | struct saa5249_device *t; | ||
585 | struct v4l2_subdev *sd; | ||
586 | |||
587 | v4l_info(client, "chip found @ 0x%x (%s)\n", | ||
588 | client->addr << 1, client->adapter->name); | ||
589 | v4l_info(client, "VideoText version %d.%d\n", | ||
590 | VTX_VER_MAJ, VTX_VER_MIN); | ||
591 | t = kzalloc(sizeof(*t), GFP_KERNEL); | ||
592 | if (t == NULL) | ||
593 | return -ENOMEM; | ||
594 | sd = &t->sd; | ||
595 | v4l2_i2c_subdev_init(sd, client, &saa5249_ops); | ||
596 | mutex_init(&t->lock); | ||
597 | |||
598 | /* Now create a video4linux device */ | ||
599 | t->vdev = video_device_alloc(); | ||
600 | if (t->vdev == NULL) { | ||
601 | kfree(t); | ||
602 | kfree(client); | ||
603 | return -ENOMEM; | ||
604 | } | ||
605 | memcpy(t->vdev, &saa_template, sizeof(*t->vdev)); | ||
606 | |||
607 | for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { | ||
608 | memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); | ||
609 | memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); | ||
610 | memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); | ||
611 | t->vdau[pgbuf].expire = 0; | ||
612 | t->vdau[pgbuf].clrfound = true; | ||
613 | t->vdau[pgbuf].stopped = true; | ||
614 | t->is_searching[pgbuf] = false; | ||
615 | } | ||
616 | video_set_drvdata(t->vdev, t); | ||
617 | |||
618 | /* Register it */ | ||
619 | err = video_register_device(t->vdev, VFL_TYPE_VTX, -1); | ||
620 | if (err < 0) { | ||
621 | video_device_release(t->vdev); | ||
622 | kfree(t); | ||
623 | return err; | ||
624 | } | ||
625 | return 0; | ||
626 | } | ||
627 | |||
628 | static int saa5249_remove(struct i2c_client *client) | ||
629 | { | ||
630 | struct v4l2_subdev *sd = i2c_get_clientdata(client); | ||
631 | struct saa5249_device *t = to_dev(sd); | ||
632 | |||
633 | video_unregister_device(t->vdev); | ||
634 | v4l2_device_unregister_subdev(sd); | ||
635 | kfree(t); | ||
636 | return 0; | ||
637 | } | ||
638 | |||
639 | static const struct i2c_device_id saa5249_id[] = { | ||
640 | { "saa5249", 0 }, | ||
641 | { } | ||
642 | }; | ||
643 | MODULE_DEVICE_TABLE(i2c, saa5249_id); | ||
644 | |||
645 | static struct v4l2_i2c_driver_data v4l2_i2c_data = { | ||
646 | .name = "saa5249", | ||
647 | .probe = saa5249_probe, | ||
648 | .remove = saa5249_remove, | ||
649 | .id_table = saa5249_id, | ||
650 | }; | ||