diff options
Diffstat (limited to 'drivers/media/video/saa5249.c')
-rw-r--r-- | drivers/media/video/saa5249.c | 704 |
1 files changed, 311 insertions, 393 deletions
diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c index 0d639738d4e6..3bb959c25d9d 100644 --- a/drivers/media/video/saa5249.c +++ b/drivers/media/video/saa5249.c | |||
@@ -15,8 +15,6 @@ | |||
15 | * | 15 | * |
16 | * Copyright (c) 1998 Richard Guenther <richard.guenther@student.uni-tuebingen.de> | 16 | * Copyright (c) 1998 Richard Guenther <richard.guenther@student.uni-tuebingen.de> |
17 | * | 17 | * |
18 | * $Id: saa5249.c,v 1.1 1998/03/30 22:23:23 alan Exp $ | ||
19 | * | ||
20 | * Derived From | 18 | * Derived From |
21 | * | 19 | * |
22 | * vtx.c: | 20 | * vtx.c: |
@@ -45,33 +43,28 @@ | |||
45 | 43 | ||
46 | #include <linux/module.h> | 44 | #include <linux/module.h> |
47 | #include <linux/kernel.h> | 45 | #include <linux/kernel.h> |
48 | #include <linux/sched.h> | ||
49 | #include <linux/mm.h> | 46 | #include <linux/mm.h> |
50 | #include <linux/errno.h> | ||
51 | #include <linux/delay.h> | ||
52 | #include <linux/ioport.h> | ||
53 | #include <linux/slab.h> | ||
54 | #include <linux/init.h> | 47 | #include <linux/init.h> |
55 | #include <stdarg.h> | ||
56 | #include <linux/i2c.h> | 48 | #include <linux/i2c.h> |
49 | #include <linux/smp_lock.h> | ||
50 | #include <linux/mutex.h> | ||
51 | #include <linux/delay.h> | ||
57 | #include <linux/videotext.h> | 52 | #include <linux/videotext.h> |
58 | #include <linux/videodev.h> | 53 | #include <linux/videodev.h> |
59 | #include <media/v4l2-common.h> | 54 | #include <media/v4l2-common.h> |
60 | #include <media/v4l2-ioctl.h> | 55 | #include <media/v4l2-ioctl.h> |
61 | #include <linux/mutex.h> | 56 | #include <media/v4l2-i2c-drv-legacy.h> |
62 | |||
63 | 57 | ||
64 | #include <asm/io.h> | 58 | MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); |
65 | #include <asm/uaccess.h> | 59 | MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver"); |
60 | MODULE_LICENSE("GPL"); | ||
66 | 61 | ||
67 | #define VTX_VER_MAJ 1 | 62 | #define VTX_VER_MAJ 1 |
68 | #define VTX_VER_MIN 8 | 63 | #define VTX_VER_MIN 8 |
69 | 64 | ||
70 | 65 | ||
71 | |||
72 | #define NUM_DAUS 4 | 66 | #define NUM_DAUS 4 |
73 | #define NUM_BUFS 8 | 67 | #define NUM_BUFS 8 |
74 | #define IF_NAME "SAA5249" | ||
75 | 68 | ||
76 | static const int disp_modes[8][3] = | 69 | static const int disp_modes[8][3] = |
77 | { | 70 | { |
@@ -109,6 +102,7 @@ struct saa5249_device | |||
109 | int disp_mode; | 102 | int disp_mode; |
110 | int virtual_mode; | 103 | int virtual_mode; |
111 | struct i2c_client *client; | 104 | struct i2c_client *client; |
105 | unsigned long in_use; | ||
112 | struct mutex lock; | 106 | struct mutex lock; |
113 | }; | 107 | }; |
114 | 108 | ||
@@ -123,125 +117,8 @@ struct saa5249_device | |||
123 | 117 | ||
124 | #define VTX_DEV_MINOR 0 | 118 | #define VTX_DEV_MINOR 0 |
125 | 119 | ||
126 | /* General defines and debugging support */ | ||
127 | |||
128 | #define RESCHED do { cond_resched(); } while(0) | ||
129 | |||
130 | static struct video_device saa_template; /* Declared near bottom */ | 120 | static struct video_device saa_template; /* Declared near bottom */ |
131 | 121 | ||
132 | /* Addresses to scan */ | ||
133 | static unsigned short normal_i2c[] = {34>>1,I2C_CLIENT_END}; | ||
134 | |||
135 | I2C_CLIENT_INSMOD; | ||
136 | |||
137 | static struct i2c_client client_template; | ||
138 | |||
139 | static int saa5249_attach(struct i2c_adapter *adap, int addr, int kind) | ||
140 | { | ||
141 | int pgbuf; | ||
142 | int err; | ||
143 | struct i2c_client *client; | ||
144 | struct video_device *vd; | ||
145 | struct saa5249_device *t; | ||
146 | |||
147 | printk(KERN_INFO "saa5249: teletext chip found.\n"); | ||
148 | client=kmalloc(sizeof(*client), GFP_KERNEL); | ||
149 | if(client==NULL) | ||
150 | return -ENOMEM; | ||
151 | client_template.adapter = adap; | ||
152 | client_template.addr = addr; | ||
153 | memcpy(client, &client_template, sizeof(*client)); | ||
154 | t = kzalloc(sizeof(*t), GFP_KERNEL); | ||
155 | if(t==NULL) | ||
156 | { | ||
157 | kfree(client); | ||
158 | return -ENOMEM; | ||
159 | } | ||
160 | strlcpy(client->name, IF_NAME, I2C_NAME_SIZE); | ||
161 | mutex_init(&t->lock); | ||
162 | |||
163 | /* | ||
164 | * Now create a video4linux device | ||
165 | */ | ||
166 | |||
167 | vd = kmalloc(sizeof(struct video_device), GFP_KERNEL); | ||
168 | if(vd==NULL) | ||
169 | { | ||
170 | kfree(t); | ||
171 | kfree(client); | ||
172 | return -ENOMEM; | ||
173 | } | ||
174 | i2c_set_clientdata(client, vd); | ||
175 | memcpy(vd, &saa_template, sizeof(*vd)); | ||
176 | |||
177 | for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) | ||
178 | { | ||
179 | memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); | ||
180 | memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); | ||
181 | memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); | ||
182 | t->vdau[pgbuf].expire = 0; | ||
183 | t->vdau[pgbuf].clrfound = true; | ||
184 | t->vdau[pgbuf].stopped = true; | ||
185 | t->is_searching[pgbuf] = false; | ||
186 | } | ||
187 | vd->priv=t; | ||
188 | |||
189 | |||
190 | /* | ||
191 | * Register it | ||
192 | */ | ||
193 | |||
194 | if((err=video_register_device(vd, VFL_TYPE_VTX,-1))<0) | ||
195 | { | ||
196 | kfree(t); | ||
197 | kfree(vd); | ||
198 | kfree(client); | ||
199 | return err; | ||
200 | } | ||
201 | t->client = client; | ||
202 | i2c_attach_client(client); | ||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | /* | ||
207 | * We do most of the hard work when we become a device on the i2c. | ||
208 | */ | ||
209 | |||
210 | static int saa5249_probe(struct i2c_adapter *adap) | ||
211 | { | ||
212 | if (adap->class & I2C_CLASS_TV_ANALOG) | ||
213 | return i2c_probe(adap, &addr_data, saa5249_attach); | ||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static int saa5249_detach(struct i2c_client *client) | ||
218 | { | ||
219 | struct video_device *vd = i2c_get_clientdata(client); | ||
220 | i2c_detach_client(client); | ||
221 | video_unregister_device(vd); | ||
222 | kfree(vd->priv); | ||
223 | kfree(vd); | ||
224 | kfree(client); | ||
225 | return 0; | ||
226 | } | ||
227 | |||
228 | /* new I2C driver support */ | ||
229 | |||
230 | static struct i2c_driver i2c_driver_videotext = | ||
231 | { | ||
232 | .driver = { | ||
233 | .name = IF_NAME, /* name */ | ||
234 | }, | ||
235 | .id = I2C_DRIVERID_SAA5249, /* in i2c.h */ | ||
236 | .attach_adapter = saa5249_probe, | ||
237 | .detach_client = saa5249_detach, | ||
238 | }; | ||
239 | |||
240 | static struct i2c_client client_template = { | ||
241 | .driver = &i2c_driver_videotext, | ||
242 | .name = "(unset)", | ||
243 | }; | ||
244 | |||
245 | /* | 122 | /* |
246 | * Wait the given number of jiffies (10ms). This calls the scheduler, so the actual | 123 | * Wait the given number of jiffies (10ms). This calls the scheduler, so the actual |
247 | * delay may be longer. | 124 | * delay may be longer. |
@@ -275,7 +152,7 @@ static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data) | |||
275 | buf[0] = reg; | 152 | buf[0] = reg; |
276 | memcpy(buf+1, data, count); | 153 | memcpy(buf+1, data, count); |
277 | 154 | ||
278 | if(i2c_master_send(t->client, buf, count+1)==count+1) | 155 | if (i2c_master_send(t->client, buf, count + 1) == count + 1) |
279 | return 0; | 156 | return 0; |
280 | return -1; | 157 | return -1; |
281 | } | 158 | } |
@@ -317,246 +194,236 @@ static int do_saa5249_ioctl(struct inode *inode, struct file *file, | |||
317 | unsigned int cmd, void *arg) | 194 | unsigned int cmd, void *arg) |
318 | { | 195 | { |
319 | static int virtual_mode = false; | 196 | static int virtual_mode = false; |
320 | struct video_device *vd = video_devdata(file); | 197 | struct saa5249_device *t = video_drvdata(file); |
321 | struct saa5249_device *t=vd->priv; | ||
322 | 198 | ||
323 | switch(cmd) | 199 | switch (cmd) { |
200 | case VTXIOCGETINFO: | ||
324 | { | 201 | { |
325 | case VTXIOCGETINFO: | 202 | vtx_info_t *info = arg; |
326 | { | 203 | info->version_major = VTX_VER_MAJ; |
327 | vtx_info_t *info = arg; | 204 | info->version_minor = VTX_VER_MIN; |
328 | info->version_major = VTX_VER_MAJ; | 205 | info->numpages = NUM_DAUS; |
329 | info->version_minor = VTX_VER_MIN; | 206 | /*info->cct_type = CCT_TYPE;*/ |
330 | info->numpages = NUM_DAUS; | 207 | return 0; |
331 | /*info->cct_type = CCT_TYPE;*/ | 208 | } |
332 | return 0; | ||
333 | } | ||
334 | 209 | ||
335 | case VTXIOCCLRPAGE: | 210 | case VTXIOCCLRPAGE: |
336 | { | 211 | { |
337 | vtx_pagereq_t *req = arg; | 212 | vtx_pagereq_t *req = arg; |
338 | 213 | ||
339 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | 214 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) |
340 | return -EINVAL; | 215 | return -EINVAL; |
341 | memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); | 216 | memset(t->vdau[req->pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); |
342 | t->vdau[req->pgbuf].clrfound = true; | 217 | t->vdau[req->pgbuf].clrfound = true; |
343 | return 0; | 218 | return 0; |
344 | } | 219 | } |
345 | 220 | ||
346 | case VTXIOCCLRFOUND: | 221 | case VTXIOCCLRFOUND: |
347 | { | 222 | { |
348 | vtx_pagereq_t *req = arg; | 223 | vtx_pagereq_t *req = arg; |
349 | 224 | ||
350 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | 225 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) |
351 | return -EINVAL; | 226 | return -EINVAL; |
352 | t->vdau[req->pgbuf].clrfound = true; | 227 | t->vdau[req->pgbuf].clrfound = true; |
353 | return 0; | 228 | return 0; |
354 | } | 229 | } |
355 | 230 | ||
356 | case VTXIOCPAGEREQ: | 231 | case VTXIOCPAGEREQ: |
357 | { | 232 | { |
358 | vtx_pagereq_t *req = arg; | 233 | vtx_pagereq_t *req = arg; |
359 | if (!(req->pagemask & PGMASK_PAGE)) | 234 | if (!(req->pagemask & PGMASK_PAGE)) |
360 | req->page = 0; | 235 | req->page = 0; |
361 | if (!(req->pagemask & PGMASK_HOUR)) | 236 | if (!(req->pagemask & PGMASK_HOUR)) |
362 | req->hour = 0; | 237 | req->hour = 0; |
363 | if (!(req->pagemask & PGMASK_MINUTE)) | 238 | if (!(req->pagemask & PGMASK_MINUTE)) |
364 | req->minute = 0; | 239 | req->minute = 0; |
365 | if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */ | 240 | if (req->page < 0 || req->page > 0x8ff) /* 7FF ?? */ |
366 | return -EINVAL; | 241 | return -EINVAL; |
367 | req->page &= 0x7ff; | 242 | req->page &= 0x7ff; |
368 | if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f || | 243 | if (req->hour < 0 || req->hour > 0x3f || req->minute < 0 || req->minute > 0x7f || |
369 | req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | 244 | req->pagemask < 0 || req->pagemask >= PGMASK_MAX || req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) |
370 | return -EINVAL; | 245 | return -EINVAL; |
371 | t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100); | 246 | t->vdau[req->pgbuf].sregs[0] = (req->pagemask & PG_HUND ? 0x10 : 0) | (req->page / 0x100); |
372 | t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf); | 247 | t->vdau[req->pgbuf].sregs[1] = (req->pagemask & PG_TEN ? 0x10 : 0) | ((req->page / 0x10) & 0xf); |
373 | t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf); | 248 | t->vdau[req->pgbuf].sregs[2] = (req->pagemask & PG_UNIT ? 0x10 : 0) | (req->page & 0xf); |
374 | t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10); | 249 | t->vdau[req->pgbuf].sregs[3] = (req->pagemask & HR_TEN ? 0x10 : 0) | (req->hour / 0x10); |
375 | t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf); | 250 | t->vdau[req->pgbuf].sregs[4] = (req->pagemask & HR_UNIT ? 0x10 : 0) | (req->hour & 0xf); |
376 | t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10); | 251 | t->vdau[req->pgbuf].sregs[5] = (req->pagemask & MIN_TEN ? 0x10 : 0) | (req->minute / 0x10); |
377 | t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf); | 252 | t->vdau[req->pgbuf].sregs[6] = (req->pagemask & MIN_UNIT ? 0x10 : 0) | (req->minute & 0xf); |
378 | t->vdau[req->pgbuf].stopped = false; | 253 | t->vdau[req->pgbuf].stopped = false; |
379 | t->vdau[req->pgbuf].clrfound = true; | 254 | t->vdau[req->pgbuf].clrfound = true; |
380 | t->is_searching[req->pgbuf] = true; | 255 | t->is_searching[req->pgbuf] = true; |
381 | return 0; | 256 | return 0; |
382 | } | 257 | } |
383 | 258 | ||
384 | case VTXIOCGETSTAT: | 259 | case VTXIOCGETSTAT: |
385 | { | 260 | { |
386 | vtx_pagereq_t *req = arg; | 261 | vtx_pagereq_t *req = arg; |
387 | u8 infobits[10]; | 262 | u8 infobits[10]; |
388 | vtx_pageinfo_t info; | 263 | vtx_pageinfo_t info; |
389 | int a; | 264 | int a; |
390 | 265 | ||
391 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | 266 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) |
392 | return -EINVAL; | 267 | return -EINVAL; |
393 | if (!t->vdau[req->pgbuf].stopped) | 268 | if (!t->vdau[req->pgbuf].stopped) { |
394 | { | 269 | if (i2c_senddata(t, 2, 0, -1) || |
395 | if (i2c_senddata(t, 2, 0, -1) || | 270 | i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) || |
396 | i2c_sendbuf(t, 3, sizeof(t->vdau[0].sregs), t->vdau[req->pgbuf].sregs) || | 271 | i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) || |
397 | i2c_senddata(t, 8, 0, 25, 0, ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', -1) || | 272 | i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) || |
398 | i2c_senddata(t, 2, 0, t->vdau[req->pgbuf].sregs[0] | 8, -1) || | 273 | i2c_senddata(t, 8, 0, 25, 0, -1)) |
399 | i2c_senddata(t, 8, 0, 25, 0, -1)) | 274 | return -EIO; |
400 | return -EIO; | 275 | jdelay(PAGE_WAIT); |
401 | jdelay(PAGE_WAIT); | 276 | if (i2c_getdata(t, 10, infobits)) |
402 | if (i2c_getdata(t, 10, infobits)) | 277 | return -EIO; |
403 | return -EIO; | ||
404 | 278 | ||
405 | if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */ | 279 | if (!(infobits[8] & 0x10) && !(infobits[7] & 0xf0) && /* check FOUND-bit */ |
406 | (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) || | 280 | (memcmp(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)) || |
407 | time_after_eq(jiffies, t->vdau[req->pgbuf].expire))) | 281 | time_after_eq(jiffies, t->vdau[req->pgbuf].expire))) |
408 | { /* check if new page arrived */ | 282 | { /* check if new page arrived */ |
409 | if (i2c_senddata(t, 8, 0, 0, 0, -1) || | 283 | if (i2c_senddata(t, 8, 0, 0, 0, -1) || |
410 | i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf)) | 284 | i2c_getdata(t, VTX_PAGESIZE, t->vdau[req->pgbuf].pgbuf)) |
285 | return -EIO; | ||
286 | t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE; | ||
287 | memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE); | ||
288 | if (t->virtual_mode) { | ||
289 | /* Packet X/24 */ | ||
290 | if (i2c_senddata(t, 8, 0, 0x20, 0, -1) || | ||
291 | i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40)) | ||
292 | return -EIO; | ||
293 | /* Packet X/27/0 */ | ||
294 | if (i2c_senddata(t, 8, 0, 0x21, 0, -1) || | ||
295 | i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40)) | ||
296 | return -EIO; | ||
297 | /* Packet 8/30/0...8/30/15 | ||
298 | * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30, | ||
299 | * so we should undo this here. | ||
300 | */ | ||
301 | if (i2c_senddata(t, 8, 0, 0x22, 0, -1) || | ||
302 | i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40)) | ||
411 | return -EIO; | 303 | return -EIO; |
412 | t->vdau[req->pgbuf].expire = jiffies + PGBUF_EXPIRE; | ||
413 | memset(t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE, ' ', VTX_VIRTUALSIZE - VTX_PAGESIZE); | ||
414 | if (t->virtual_mode) | ||
415 | { | ||
416 | /* Packet X/24 */ | ||
417 | if (i2c_senddata(t, 8, 0, 0x20, 0, -1) || | ||
418 | i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 20 * 40)) | ||
419 | return -EIO; | ||
420 | /* Packet X/27/0 */ | ||
421 | if (i2c_senddata(t, 8, 0, 0x21, 0, -1) || | ||
422 | i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 16 * 40)) | ||
423 | return -EIO; | ||
424 | /* Packet 8/30/0...8/30/15 | ||
425 | * FIXME: AFAIK, the 5249 does hamming-decoding for some bytes in packet 8/30, | ||
426 | * so we should undo this here. | ||
427 | */ | ||
428 | if (i2c_senddata(t, 8, 0, 0x22, 0, -1) || | ||
429 | i2c_getdata(t, 40, t->vdau[req->pgbuf].pgbuf + VTX_PAGESIZE + 23 * 40)) | ||
430 | return -EIO; | ||
431 | } | ||
432 | t->vdau[req->pgbuf].clrfound = false; | ||
433 | memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits)); | ||
434 | } | ||
435 | else | ||
436 | { | ||
437 | memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); | ||
438 | } | 304 | } |
439 | } | 305 | t->vdau[req->pgbuf].clrfound = false; |
440 | else | 306 | memcpy(t->vdau[req->pgbuf].laststat, infobits, sizeof(infobits)); |
441 | { | 307 | } else { |
442 | memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); | 308 | memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); |
443 | } | 309 | } |
310 | } else { | ||
311 | memcpy(infobits, t->vdau[req->pgbuf].laststat, sizeof(infobits)); | ||
312 | } | ||
444 | 313 | ||
445 | info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f); | 314 | info.pagenum = ((infobits[8] << 8) & 0x700) | ((infobits[1] << 4) & 0xf0) | (infobits[0] & 0x0f); |
446 | if (info.pagenum < 0x100) | 315 | if (info.pagenum < 0x100) |
447 | info.pagenum += 0x800; | 316 | info.pagenum += 0x800; |
448 | info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f); | 317 | info.hour = ((infobits[5] << 4) & 0x30) | (infobits[4] & 0x0f); |
449 | info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f); | 318 | info.minute = ((infobits[3] << 4) & 0x70) | (infobits[2] & 0x0f); |
450 | info.charset = ((infobits[7] >> 1) & 7); | 319 | info.charset = ((infobits[7] >> 1) & 7); |
451 | info.delete = !!(infobits[3] & 8); | 320 | info.delete = !!(infobits[3] & 8); |
452 | info.headline = !!(infobits[5] & 4); | 321 | info.headline = !!(infobits[5] & 4); |
453 | info.subtitle = !!(infobits[5] & 8); | 322 | info.subtitle = !!(infobits[5] & 8); |
454 | info.supp_header = !!(infobits[6] & 1); | 323 | info.supp_header = !!(infobits[6] & 1); |
455 | info.update = !!(infobits[6] & 2); | 324 | info.update = !!(infobits[6] & 2); |
456 | info.inter_seq = !!(infobits[6] & 4); | 325 | info.inter_seq = !!(infobits[6] & 4); |
457 | info.dis_disp = !!(infobits[6] & 8); | 326 | info.dis_disp = !!(infobits[6] & 8); |
458 | info.serial = !!(infobits[7] & 1); | 327 | info.serial = !!(infobits[7] & 1); |
459 | info.notfound = !!(infobits[8] & 0x10); | 328 | info.notfound = !!(infobits[8] & 0x10); |
460 | info.pblf = !!(infobits[9] & 0x20); | 329 | info.pblf = !!(infobits[9] & 0x20); |
461 | info.hamming = 0; | 330 | info.hamming = 0; |
462 | for (a = 0; a <= 7; a++) | 331 | for (a = 0; a <= 7; a++) { |
463 | { | 332 | if (infobits[a] & 0xf0) { |
464 | if (infobits[a] & 0xf0) | 333 | info.hamming = 1; |
465 | { | 334 | break; |
466 | info.hamming = 1; | ||
467 | break; | ||
468 | } | ||
469 | } | ||
470 | if (t->vdau[req->pgbuf].clrfound) | ||
471 | info.notfound = 1; | ||
472 | if(copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t))) | ||
473 | return -EFAULT; | ||
474 | if (!info.hamming && !info.notfound) | ||
475 | { | ||
476 | t->is_searching[req->pgbuf] = false; | ||
477 | } | 335 | } |
478 | return 0; | ||
479 | } | 336 | } |
337 | if (t->vdau[req->pgbuf].clrfound) | ||
338 | info.notfound = 1; | ||
339 | if (copy_to_user(req->buffer, &info, sizeof(vtx_pageinfo_t))) | ||
340 | return -EFAULT; | ||
341 | if (!info.hamming && !info.notfound) | ||
342 | t->is_searching[req->pgbuf] = false; | ||
343 | return 0; | ||
344 | } | ||
480 | 345 | ||
481 | case VTXIOCGETPAGE: | 346 | case VTXIOCGETPAGE: |
482 | { | 347 | { |
483 | vtx_pagereq_t *req = arg; | 348 | vtx_pagereq_t *req = arg; |
484 | int start, end; | 349 | int start, end; |
485 | 350 | ||
486 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 || | 351 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS || req->start < 0 || |
487 | req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE)) | 352 | req->start > req->end || req->end >= (virtual_mode ? VTX_VIRTUALSIZE : VTX_PAGESIZE)) |
488 | return -EINVAL; | 353 | return -EINVAL; |
489 | if(copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1)) | 354 | if (copy_to_user(req->buffer, &t->vdau[req->pgbuf].pgbuf[req->start], req->end - req->start + 1)) |
355 | return -EFAULT; | ||
356 | |||
357 | /* | ||
358 | * Always read the time directly from SAA5249 | ||
359 | */ | ||
360 | |||
361 | if (req->start <= 39 && req->end >= 32) { | ||
362 | int len; | ||
363 | char buf[16]; | ||
364 | start = max(req->start, 32); | ||
365 | end = min(req->end, 39); | ||
366 | len = end - start + 1; | ||
367 | if (i2c_senddata(t, 8, 0, 0, start, -1) || | ||
368 | i2c_getdata(t, len, buf)) | ||
369 | return -EIO; | ||
370 | if (copy_to_user(req->buffer + start - req->start, buf, len)) | ||
371 | return -EFAULT; | ||
372 | } | ||
373 | /* Insert the current header if DAU is still searching for a page */ | ||
374 | if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf]) { | ||
375 | char buf[32]; | ||
376 | int len; | ||
377 | |||
378 | start = max(req->start, 7); | ||
379 | end = min(req->end, 31); | ||
380 | len = end - start + 1; | ||
381 | if (i2c_senddata(t, 8, 0, 0, start, -1) || | ||
382 | i2c_getdata(t, len, buf)) | ||
383 | return -EIO; | ||
384 | if (copy_to_user(req->buffer + start - req->start, buf, len)) | ||
490 | return -EFAULT; | 385 | return -EFAULT; |
491 | |||
492 | /* | ||
493 | * Always read the time directly from SAA5249 | ||
494 | */ | ||
495 | |||
496 | if (req->start <= 39 && req->end >= 32) | ||
497 | { | ||
498 | int len; | ||
499 | char buf[16]; | ||
500 | start = max(req->start, 32); | ||
501 | end = min(req->end, 39); | ||
502 | len=end-start+1; | ||
503 | if (i2c_senddata(t, 8, 0, 0, start, -1) || | ||
504 | i2c_getdata(t, len, buf)) | ||
505 | return -EIO; | ||
506 | if(copy_to_user(req->buffer+start-req->start, buf, len)) | ||
507 | return -EFAULT; | ||
508 | } | ||
509 | /* Insert the current header if DAU is still searching for a page */ | ||
510 | if (req->start <= 31 && req->end >= 7 && t->is_searching[req->pgbuf]) | ||
511 | { | ||
512 | char buf[32]; | ||
513 | int len; | ||
514 | start = max(req->start, 7); | ||
515 | end = min(req->end, 31); | ||
516 | len=end-start+1; | ||
517 | if (i2c_senddata(t, 8, 0, 0, start, -1) || | ||
518 | i2c_getdata(t, len, buf)) | ||
519 | return -EIO; | ||
520 | if(copy_to_user(req->buffer+start-req->start, buf, len)) | ||
521 | return -EFAULT; | ||
522 | } | ||
523 | return 0; | ||
524 | } | 386 | } |
387 | return 0; | ||
388 | } | ||
525 | 389 | ||
526 | case VTXIOCSTOPDAU: | 390 | case VTXIOCSTOPDAU: |
527 | { | 391 | { |
528 | vtx_pagereq_t *req = arg; | 392 | vtx_pagereq_t *req = arg; |
529 | 393 | ||
530 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) | 394 | if (req->pgbuf < 0 || req->pgbuf >= NUM_DAUS) |
531 | return -EINVAL; | 395 | return -EINVAL; |
532 | t->vdau[req->pgbuf].stopped = true; | 396 | t->vdau[req->pgbuf].stopped = true; |
533 | t->is_searching[req->pgbuf] = false; | 397 | t->is_searching[req->pgbuf] = false; |
534 | return 0; | 398 | return 0; |
535 | } | 399 | } |
536 | 400 | ||
537 | case VTXIOCPUTPAGE: | 401 | case VTXIOCPUTPAGE: |
538 | case VTXIOCSETDISP: | 402 | case VTXIOCSETDISP: |
539 | case VTXIOCPUTSTAT: | 403 | case VTXIOCPUTSTAT: |
540 | return 0; | 404 | return 0; |
541 | 405 | ||
542 | case VTXIOCCLRCACHE: | 406 | case VTXIOCCLRCACHE: |
543 | { | 407 | { |
544 | if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11, | 408 | if (i2c_senddata(t, 0, NUM_DAUS, 0, 8, -1) || i2c_senddata(t, 11, |
545 | ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', | 409 | ' ', ' ', ' ', ' ', ' ', ' ', |
546 | ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ', -1)) | 410 | ' ', ' ', ' ', ' ', ' ', ' ', |
547 | return -EIO; | 411 | ' ', ' ', ' ', ' ', ' ', ' ', |
548 | if (i2c_senddata(t, 3, 0x20, -1)) | 412 | ' ', ' ', ' ', ' ', ' ', ' ', |
549 | return -EIO; | 413 | -1)) |
550 | jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */ | 414 | return -EIO; |
551 | return 0; | 415 | if (i2c_senddata(t, 3, 0x20, -1)) |
552 | } | 416 | return -EIO; |
417 | jdelay(10 * CLEAR_DELAY); /* I have no idea how long we have to wait here */ | ||
418 | return 0; | ||
419 | } | ||
553 | 420 | ||
554 | case VTXIOCSETVIRT: | 421 | case VTXIOCSETVIRT: |
555 | { | 422 | { |
556 | /* The SAA5249 has virtual-row reception turned on always */ | 423 | /* The SAA5249 has virtual-row reception turned on always */ |
557 | t->virtual_mode = (int)(long)arg; | 424 | t->virtual_mode = (int)(long)arg; |
558 | return 0; | 425 | return 0; |
559 | } | 426 | } |
560 | } | 427 | } |
561 | return -EINVAL; | 428 | return -EINVAL; |
562 | } | 429 | } |
@@ -616,8 +483,7 @@ static inline unsigned int vtx_fix_command(unsigned int cmd) | |||
616 | static int saa5249_ioctl(struct inode *inode, struct file *file, | 483 | static int saa5249_ioctl(struct inode *inode, struct file *file, |
617 | unsigned int cmd, unsigned long arg) | 484 | unsigned int cmd, unsigned long arg) |
618 | { | 485 | { |
619 | struct video_device *vd = video_devdata(file); | 486 | struct saa5249_device *t = video_drvdata(file); |
620 | struct saa5249_device *t=vd->priv; | ||
621 | int err; | 487 | int err; |
622 | 488 | ||
623 | cmd = vtx_fix_command(cmd); | 489 | cmd = vtx_fix_command(cmd); |
@@ -629,32 +495,27 @@ static int saa5249_ioctl(struct inode *inode, struct file *file, | |||
629 | 495 | ||
630 | static int saa5249_open(struct inode *inode, struct file *file) | 496 | static int saa5249_open(struct inode *inode, struct file *file) |
631 | { | 497 | { |
632 | struct video_device *vd = video_devdata(file); | 498 | struct saa5249_device *t = video_drvdata(file); |
633 | struct saa5249_device *t=vd->priv; | 499 | int pgbuf; |
634 | int err,pgbuf; | ||
635 | 500 | ||
636 | err = video_exclusive_open(inode,file); | 501 | if (t->client == NULL) |
637 | if (err < 0) | 502 | return -ENODEV; |
638 | return err; | ||
639 | 503 | ||
640 | if (t->client==NULL) { | 504 | if (test_and_set_bit(0, &t->in_use)) |
641 | err = -ENODEV; | 505 | return -EBUSY; |
642 | goto fail; | ||
643 | } | ||
644 | 506 | ||
645 | if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */ | 507 | if (i2c_senddata(t, 0, 0, -1) || /* Select R11 */ |
646 | /* Turn off parity checks (we do this ourselves) */ | 508 | /* Turn off parity checks (we do this ourselves) */ |
647 | i2c_senddata(t, 1, disp_modes[t->disp_mode][0], 0, -1) || | 509 | i2c_senddata(t, 1, disp_modes[t->disp_mode][0], 0, -1) || |
648 | /* Display TV-picture, no virtual rows */ | 510 | /* Display TV-picture, no virtual rows */ |
649 | i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) /* Set display to page 4 */ | 511 | i2c_senddata(t, 4, NUM_DAUS, disp_modes[t->disp_mode][1], disp_modes[t->disp_mode][2], 7, -1)) |
650 | 512 | /* Set display to page 4 */ | |
651 | { | 513 | { |
652 | err = -EIO; | 514 | clear_bit(0, &t->in_use); |
653 | goto fail; | 515 | return -EIO; |
654 | } | 516 | } |
655 | 517 | ||
656 | for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) | 518 | for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { |
657 | { | ||
658 | memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); | 519 | memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); |
659 | memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); | 520 | memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); |
660 | memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); | 521 | memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); |
@@ -665,39 +526,20 @@ static int saa5249_open(struct inode *inode, struct file *file) | |||
665 | } | 526 | } |
666 | t->virtual_mode = false; | 527 | t->virtual_mode = false; |
667 | return 0; | 528 | return 0; |
668 | |||
669 | fail: | ||
670 | video_exclusive_release(inode,file); | ||
671 | return err; | ||
672 | } | 529 | } |
673 | 530 | ||
674 | 531 | ||
675 | 532 | ||
676 | static int saa5249_release(struct inode *inode, struct file *file) | 533 | static int saa5249_release(struct inode *inode, struct file *file) |
677 | { | 534 | { |
678 | struct video_device *vd = video_devdata(file); | 535 | struct saa5249_device *t = video_drvdata(file); |
679 | struct saa5249_device *t=vd->priv; | 536 | |
680 | i2c_senddata(t, 1, 0x20, -1); /* Turn off CCT */ | 537 | i2c_senddata(t, 1, 0x20, -1); /* Turn off CCT */ |
681 | i2c_senddata(t, 5, 3, 3, -1); /* Turn off TV-display */ | 538 | i2c_senddata(t, 5, 3, 3, -1); /* Turn off TV-display */ |
682 | video_exclusive_release(inode,file); | 539 | clear_bit(0, &t->in_use); |
683 | return 0; | 540 | return 0; |
684 | } | 541 | } |
685 | 542 | ||
686 | static int __init init_saa_5249 (void) | ||
687 | { | ||
688 | printk(KERN_INFO "SAA5249 driver (" IF_NAME " interface) for VideoText version %d.%d\n", | ||
689 | VTX_VER_MAJ, VTX_VER_MIN); | ||
690 | return i2c_add_driver(&i2c_driver_videotext); | ||
691 | } | ||
692 | |||
693 | static void __exit cleanup_saa_5249 (void) | ||
694 | { | ||
695 | i2c_del_driver(&i2c_driver_videotext); | ||
696 | } | ||
697 | |||
698 | module_init(init_saa_5249); | ||
699 | module_exit(cleanup_saa_5249); | ||
700 | |||
701 | static const struct file_operations saa_fops = { | 543 | static const struct file_operations saa_fops = { |
702 | .owner = THIS_MODULE, | 544 | .owner = THIS_MODULE, |
703 | .open = saa5249_open, | 545 | .open = saa5249_open, |
@@ -711,8 +553,84 @@ static const struct file_operations saa_fops = { | |||
711 | 553 | ||
712 | static struct video_device saa_template = | 554 | static struct video_device saa_template = |
713 | { | 555 | { |
714 | .name = IF_NAME, | 556 | .name = "saa5249", |
715 | .fops = &saa_fops, | 557 | .fops = &saa_fops, |
558 | .release = video_device_release, | ||
716 | }; | 559 | }; |
717 | 560 | ||
718 | MODULE_LICENSE("GPL"); | 561 | /* Addresses to scan */ |
562 | static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; | ||
563 | |||
564 | I2C_CLIENT_INSMOD; | ||
565 | |||
566 | static int saa5249_probe(struct i2c_client *client, | ||
567 | const struct i2c_device_id *id) | ||
568 | { | ||
569 | int pgbuf; | ||
570 | int err; | ||
571 | struct video_device *vd; | ||
572 | struct saa5249_device *t; | ||
573 | |||
574 | v4l_info(client, "chip found @ 0x%x (%s)\n", | ||
575 | client->addr << 1, client->adapter->name); | ||
576 | v4l_info(client, "VideoText version %d.%d\n", | ||
577 | VTX_VER_MAJ, VTX_VER_MIN); | ||
578 | t = kzalloc(sizeof(*t), GFP_KERNEL); | ||
579 | if (t == NULL) | ||
580 | return -ENOMEM; | ||
581 | mutex_init(&t->lock); | ||
582 | |||
583 | /* Now create a video4linux device */ | ||
584 | vd = kmalloc(sizeof(struct video_device), GFP_KERNEL); | ||
585 | if (vd == NULL) { | ||
586 | kfree(client); | ||
587 | return -ENOMEM; | ||
588 | } | ||
589 | i2c_set_clientdata(client, vd); | ||
590 | memcpy(vd, &saa_template, sizeof(*vd)); | ||
591 | |||
592 | for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) { | ||
593 | memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf)); | ||
594 | memset(t->vdau[pgbuf].sregs, 0, sizeof(t->vdau[0].sregs)); | ||
595 | memset(t->vdau[pgbuf].laststat, 0, sizeof(t->vdau[0].laststat)); | ||
596 | t->vdau[pgbuf].expire = 0; | ||
597 | t->vdau[pgbuf].clrfound = true; | ||
598 | t->vdau[pgbuf].stopped = true; | ||
599 | t->is_searching[pgbuf] = false; | ||
600 | } | ||
601 | video_set_drvdata(vd, t); | ||
602 | |||
603 | /* Register it */ | ||
604 | err = video_register_device(vd, VFL_TYPE_VTX, -1); | ||
605 | if (err < 0) { | ||
606 | kfree(t); | ||
607 | kfree(vd); | ||
608 | return err; | ||
609 | } | ||
610 | t->client = client; | ||
611 | return 0; | ||
612 | } | ||
613 | |||
614 | static int saa5249_remove(struct i2c_client *client) | ||
615 | { | ||
616 | struct video_device *vd = i2c_get_clientdata(client); | ||
617 | |||
618 | video_unregister_device(vd); | ||
619 | kfree(video_get_drvdata(vd)); | ||
620 | kfree(vd); | ||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | static const struct i2c_device_id saa5249_id[] = { | ||
625 | { "saa5249", 0 }, | ||
626 | { } | ||
627 | }; | ||
628 | MODULE_DEVICE_TABLE(i2c, saa5249_id); | ||
629 | |||
630 | static struct v4l2_i2c_driver_data v4l2_i2c_data = { | ||
631 | .name = "saa5249", | ||
632 | .driverid = I2C_DRIVERID_SAA5249, | ||
633 | .probe = saa5249_probe, | ||
634 | .remove = saa5249_remove, | ||
635 | .id_table = saa5249_id, | ||
636 | }; | ||