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