blob: 3484e36b68016f4d2c58953ed9fa60dc3d4c6e7a [file] [log] [blame]
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 planb - PlanB frame grabber driver
3
4 PlanB is used in the 7x00/8x00 series of PowerMacintosh
5 Computers as video input DMA controller.
6
7 Copyright (C) 1998 Michel Lanners (mlan@cpu.lu)
8
9 Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de)
10
11 Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu)
12
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
17
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26*/
27
28/* $Id: planb.c,v 1.18 1999/05/02 17:36:34 mlan Exp $ */
29
30#include <linux/init.h>
31#include <linux/errno.h>
32#include <linux/module.h>
33#include <linux/kernel.h>
34#include <linux/major.h>
35#include <linux/slab.h>
36#include <linux/types.h>
37#include <linux/pci.h>
38#include <linux/delay.h>
39#include <linux/vmalloc.h>
40#include <linux/mm.h>
41#include <linux/sched.h>
42#include <linux/videodev.h>
Mauro Carvalho Chehab5e87efa2006-06-05 10:26:32 -030043#include <media/v4l2-common.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <linux/wait.h>
45#include <asm/uaccess.h>
46#include <asm/io.h>
47#include <asm/prom.h>
48#include <asm/dbdma.h>
49#include <asm/pgtable.h>
50#include <asm/page.h>
51#include <asm/irq.h>
Ingo Molnar3593cab2006-02-07 06:49:14 -020052#include <linux/mutex.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
54#include "planb.h"
55#include "saa7196.h"
56
57/* Would you mind for some ugly debugging? */
58#if 0
59#define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */
60#else
61#define DEBUG(x...) /* Don't debug driver */
62#endif
63
64#if 0
65#define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */
66#else
67#define IDEBUG(x...) /* Don't debug interrupt part */
68#endif
69
70/* Ever seen a Mac with more than 1 of these? */
71#define PLANB_MAX 1
72
73static int planb_num;
74static struct planb planbs[PLANB_MAX];
75static volatile struct planb_registers *planb_regs;
76
77static int def_norm = PLANB_DEF_NORM; /* default norm */
78static int video_nr = -1;
79
Eric Sesterhenn / snakebyte9b565eb2006-01-13 14:10:23 -020080module_param(def_norm, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070081MODULE_PARM_DESC(def_norm, "Default startup norm (0=PAL, 1=NTSC, 2=SECAM)");
Eric Sesterhenn / snakebyte9b565eb2006-01-13 14:10:23 -020082module_param(video_nr, int, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083MODULE_LICENSE("GPL");
84
85
86/* ------------------ PlanB Exported Functions ------------------ */
87static long planb_write(struct video_device *, const char *, unsigned long, int);
88static long planb_read(struct video_device *, char *, unsigned long, int);
89static int planb_open(struct video_device *, int);
90static void planb_close(struct video_device *);
91static int planb_ioctl(struct video_device *, unsigned int, void *);
92static int planb_init_done(struct video_device *);
93static int planb_mmap(struct video_device *, const char *, unsigned long);
94static void planb_irq(int, void *, struct pt_regs *);
95static void release_planb(void);
96int init_planbs(struct video_init *);
97
98/* ------------------ PlanB Internal Functions ------------------ */
99static int planb_prepare_open(struct planb *);
100static void planb_prepare_close(struct planb *);
101static void saa_write_reg(unsigned char, unsigned char);
102static unsigned char saa_status(int, struct planb *);
103static void saa_set(unsigned char, unsigned char, struct planb *);
104static void saa_init_regs(struct planb *);
105static int grabbuf_alloc(struct planb *);
106static int vgrab(struct planb *, struct video_mmap *);
107static void add_clip(struct planb *, struct video_clip *);
108static void fill_cmd_buff(struct planb *);
109static void cmd_buff(struct planb *);
110static volatile struct dbdma_cmd *setup_grab_cmd(int, struct planb *);
111static void overlay_start(struct planb *);
112static void overlay_stop(struct planb *);
113static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *, unsigned short,
114 unsigned int);
115static inline void tab_cmd_store(volatile struct dbdma_cmd *, unsigned int,
116 unsigned int);
117static inline void tab_cmd_gen(volatile struct dbdma_cmd *, unsigned short,
118 unsigned short, unsigned int, unsigned int);
119static int init_planb(struct planb *);
120static int find_planb(void);
121static void planb_pre_capture(int, int, struct planb *);
122static volatile struct dbdma_cmd *cmd_geo_setup(volatile struct dbdma_cmd *,
123 int, int, int, int, int, struct planb *);
124static inline void planb_dbdma_stop(volatile struct dbdma_regs *);
125static unsigned int saa_geo_setup(int, int, int, int, struct planb *);
126static inline int overlay_is_active(struct planb *);
127
128/*******************************/
129/* Memory management functions */
130/*******************************/
131
132static int grabbuf_alloc(struct planb *pb)
133{
134 int i, npage;
135
136 npage = MAX_GBUFFERS * ((PLANB_MAX_FBUF / PAGE_SIZE + 1)
137#ifndef PLANB_GSCANLINE
138 + MAX_LNUM
139#endif /* PLANB_GSCANLINE */
140 );
141 if ((pb->rawbuf = (unsigned char**) kmalloc (npage
142 * sizeof(unsigned long), GFP_KERNEL)) == 0)
143 return -ENOMEM;
144 for (i = 0; i < npage; i++) {
145 pb->rawbuf[i] = (unsigned char *)__get_free_pages(GFP_KERNEL
146 |GFP_DMA, 0);
147 if (!pb->rawbuf[i])
148 break;
149 SetPageReserved(virt_to_page(pb->rawbuf[i]));
150 }
151 if (i-- < npage) {
152 printk(KERN_DEBUG "PlanB: init_grab: grab buffer not allocated\n");
153 for (; i > 0; i--) {
154 ClearPageReserved(virt_to_page(pb->rawbuf[i]));
155 free_pages((unsigned long)pb->rawbuf[i], 0);
156 }
157 kfree(pb->rawbuf);
158 return -ENOBUFS;
159 }
160 pb->rawbuf_size = npage;
161 return 0;
162}
163
164/*****************************/
165/* Hardware access functions */
166/*****************************/
167
168static void saa_write_reg(unsigned char addr, unsigned char val)
169{
170 planb_regs->saa_addr = addr; eieio();
171 planb_regs->saa_regval = val; eieio();
172 return;
173}
174
175/* return status byte 0 or 1: */
176static unsigned char saa_status(int byte, struct planb *pb)
177{
178 saa_regs[pb->win.norm][SAA7196_STDC] =
179 (saa_regs[pb->win.norm][SAA7196_STDC] & ~2) | ((byte & 1) << 1);
180 saa_write_reg (SAA7196_STDC, saa_regs[pb->win.norm][SAA7196_STDC]);
181
182 /* Let's wait 30msec for this one */
183 msleep_interruptible(30);
184
185 return (unsigned char)in_8 (&planb_regs->saa_status);
186}
187
188static void saa_set(unsigned char addr, unsigned char val, struct planb *pb)
189{
190 if(saa_regs[pb->win.norm][addr] != val) {
191 saa_regs[pb->win.norm][addr] = val;
192 saa_write_reg (addr, val);
193 }
194 return;
195}
196
197static void saa_init_regs(struct planb *pb)
198{
199 int i;
200
201 for (i = 0; i < SAA7196_NUMREGS; i++)
202 saa_write_reg (i, saa_regs[pb->win.norm][i]);
203}
204
205static unsigned int saa_geo_setup(int width, int height, int interlace, int bpp,
206 struct planb *pb)
207{
208 int ht, norm = pb->win.norm;
209
210 switch(bpp) {
211 case 2:
212 /* RGB555+a 1x16-bit + 16-bit transparent */
213 saa_regs[norm][SAA7196_FMTS] &= ~0x3;
214 break;
215 case 1:
216 case 4:
217 /* RGB888 1x24-bit + 8-bit transparent */
218 saa_regs[norm][SAA7196_FMTS] &= ~0x1;
219 saa_regs[norm][SAA7196_FMTS] |= 0x2;
220 break;
221 default:
222 return -EINVAL;
223 }
224 ht = (interlace ? height / 2 : height);
225 saa_regs[norm][SAA7196_OUTPIX] = (unsigned char) (width & 0x00ff);
226 saa_regs[norm][SAA7196_HFILT] = (saa_regs[norm][SAA7196_HFILT] & ~0x3)
227 | (width >> 8 & 0x3);
228 saa_regs[norm][SAA7196_OUTLINE] = (unsigned char) (ht & 0xff);
229 saa_regs[norm][SAA7196_VYP] = (saa_regs[norm][SAA7196_VYP] & ~0x3)
230 | (ht >> 8 & 0x3);
231 /* feed both fields if interlaced, or else feed only even fields */
232 saa_regs[norm][SAA7196_FMTS] = (interlace) ?
233 (saa_regs[norm][SAA7196_FMTS] & ~0x60)
234 : (saa_regs[norm][SAA7196_FMTS] | 0x60);
235 /* transparent mode; extended format enabled */
236 saa_regs[norm][SAA7196_DPATH] |= 0x3;
237
238 return 0;
239}
240
241/***************************/
242/* DBDMA support functions */
243/***************************/
244
245static inline void planb_dbdma_restart(volatile struct dbdma_regs *ch)
246{
247 out_le32(&ch->control, PLANB_CLR(RUN));
248 out_le32(&ch->control, PLANB_SET(RUN|WAKE) | PLANB_CLR(PAUSE));
249}
250
251static inline void planb_dbdma_stop(volatile struct dbdma_regs *ch)
252{
253 int i = 0;
254
255 out_le32(&ch->control, PLANB_CLR(RUN) | PLANB_SET(FLUSH));
256 while((in_le32(&ch->status) == (ACTIVE | FLUSH)) && (i < 999)) {
257 IDEBUG("PlanB: waiting for DMA to stop\n");
258 i++;
259 }
260}
261
262static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *ch,
263 unsigned short command, unsigned int cmd_dep)
264{
265 st_le16(&ch->command, command);
266 st_le32(&ch->cmd_dep, cmd_dep);
267}
268
269static inline void tab_cmd_store(volatile struct dbdma_cmd *ch,
270 unsigned int phy_addr, unsigned int cmd_dep)
271{
272 st_le16(&ch->command, STORE_WORD | KEY_SYSTEM);
273 st_le16(&ch->req_count, 4);
274 st_le32(&ch->phy_addr, phy_addr);
275 st_le32(&ch->cmd_dep, cmd_dep);
276}
277
278static inline void tab_cmd_gen(volatile struct dbdma_cmd *ch,
279 unsigned short command, unsigned short req_count,
280 unsigned int phy_addr, unsigned int cmd_dep)
281{
282 st_le16(&ch->command, command);
283 st_le16(&ch->req_count, req_count);
284 st_le32(&ch->phy_addr, phy_addr);
285 st_le32(&ch->cmd_dep, cmd_dep);
286}
287
288static volatile struct dbdma_cmd *cmd_geo_setup(
289 volatile struct dbdma_cmd *c1, int width, int height, int interlace,
290 int bpp, int clip, struct planb *pb)
291{
292 int norm = pb->win.norm;
293
294 if((saa_geo_setup(width, height, interlace, bpp, pb)) != 0)
295 return (volatile struct dbdma_cmd *)NULL;
296 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
297 SAA7196_FMTS);
298 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
299 saa_regs[norm][SAA7196_FMTS]);
300 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
301 SAA7196_DPATH);
302 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
303 saa_regs[norm][SAA7196_DPATH]);
304 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->even),
305 bpp | ((clip)? PLANB_CLIPMASK: 0));
306 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->odd),
307 bpp | ((clip)? PLANB_CLIPMASK: 0));
308 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
309 SAA7196_OUTPIX);
310 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
311 saa_regs[norm][SAA7196_OUTPIX]);
312 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
313 SAA7196_HFILT);
314 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
315 saa_regs[norm][SAA7196_HFILT]);
316 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
317 SAA7196_OUTLINE);
318 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
319 saa_regs[norm][SAA7196_OUTLINE]);
320 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
321 SAA7196_VYP);
322 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
323 saa_regs[norm][SAA7196_VYP]);
324 return c1;
325}
326
327/******************************/
328/* misc. supporting functions */
329/******************************/
330
331static inline void planb_lock(struct planb *pb)
332{
Ingo Molnar3593cab2006-02-07 06:49:14 -0200333 mutex_lock(&pb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334}
335
336static inline void planb_unlock(struct planb *pb)
337{
Ingo Molnar3593cab2006-02-07 06:49:14 -0200338 mutex_unlock(&pb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339}
340
341/***************/
342/* Driver Core */
343/***************/
344
345static int planb_prepare_open(struct planb *pb)
346{
347 int i, size;
348
349 /* allocate memory for two plus alpha command buffers (size: max lines,
350 plus 40 commands handling, plus 1 alignment), plus dummy command buf,
351 plus clipmask buffer, plus frame grabbing status */
352 size = (pb->tab_size*(2+MAX_GBUFFERS*TAB_FACTOR)+1+MAX_GBUFFERS
353 * PLANB_DUMMY)*sizeof(struct dbdma_cmd)
354 +(PLANB_MAXLINES*((PLANB_MAXPIXELS+7)& ~7))/8
355 +MAX_GBUFFERS*sizeof(unsigned int);
356 if ((pb->priv_space = kmalloc (size, GFP_KERNEL)) == 0)
357 return -ENOMEM;
358 memset ((void *) pb->priv_space, 0, size);
359 pb->overlay_last1 = pb->ch1_cmd = (volatile struct dbdma_cmd *)
360 DBDMA_ALIGN (pb->priv_space);
361 pb->overlay_last2 = pb->ch2_cmd = pb->ch1_cmd + pb->tab_size;
362 pb->ch1_cmd_phys = virt_to_bus(pb->ch1_cmd);
363 pb->cap_cmd[0] = pb->ch2_cmd + pb->tab_size;
364 pb->pre_cmd[0] = pb->cap_cmd[0] + pb->tab_size * TAB_FACTOR;
365 for (i = 1; i < MAX_GBUFFERS; i++) {
366 pb->cap_cmd[i] = pb->pre_cmd[i-1] + PLANB_DUMMY;
367 pb->pre_cmd[i] = pb->cap_cmd[i] + pb->tab_size * TAB_FACTOR;
368 }
369 pb->frame_stat=(volatile unsigned int *)(pb->pre_cmd[MAX_GBUFFERS-1]
370 + PLANB_DUMMY);
371 pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS);
372
373 pb->rawbuf = NULL;
374 pb->rawbuf_size = 0;
375 pb->grabbing = 0;
376 for (i = 0; i < MAX_GBUFFERS; i++) {
377 pb->frame_stat[i] = GBUFFER_UNUSED;
378 pb->gwidth[i] = 0;
379 pb->gheight[i] = 0;
380 pb->gfmt[i] = 0;
381 pb->gnorm_switch[i] = 0;
382#ifndef PLANB_GSCANLINE
383 pb->lsize[i] = 0;
384 pb->lnum[i] = 0;
385#endif /* PLANB_GSCANLINE */
386 }
387 pb->gcount = 0;
388 pb->suspend = 0;
389 pb->last_fr = -999;
390 pb->prev_last_fr = -999;
391
392 /* Reset DMA controllers */
393 planb_dbdma_stop(&pb->planb_base->ch2);
394 planb_dbdma_stop(&pb->planb_base->ch1);
395
396 return 0;
397}
398
399static void planb_prepare_close(struct planb *pb)
400{
401 int i;
402
403 /* make sure the dma's are idle */
404 planb_dbdma_stop(&pb->planb_base->ch2);
405 planb_dbdma_stop(&pb->planb_base->ch1);
406 /* free kernel memory of command buffers */
407 if(pb->priv_space != 0) {
408 kfree (pb->priv_space);
409 pb->priv_space = 0;
410 pb->cmd_buff_inited = 0;
411 }
412 if(pb->rawbuf) {
413 for (i = 0; i < pb->rawbuf_size; i++) {
414 ClearPageReserved(virt_to_page(pb->rawbuf[i]));
415 free_pages((unsigned long)pb->rawbuf[i], 0);
416 }
417 kfree(pb->rawbuf);
418 }
419 pb->rawbuf = NULL;
420}
421
422/*****************************/
423/* overlay support functions */
424/*****************************/
425
426static inline int overlay_is_active(struct planb *pb)
427{
428 unsigned int size = pb->tab_size * sizeof(struct dbdma_cmd);
429 unsigned int caddr = (unsigned)in_le32(&pb->planb_base->ch1.cmdptr);
430
431 return (in_le32(&pb->overlay_last1->cmd_dep) == pb->ch1_cmd_phys)
432 && (caddr < (pb->ch1_cmd_phys + size))
433 && (caddr >= (unsigned)pb->ch1_cmd_phys);
434}
435
436static void overlay_start(struct planb *pb)
437{
438
439 DEBUG("PlanB: overlay_start()\n");
440
441 if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
442
443 DEBUG("PlanB: presumably, grabbing is in progress...\n");
444
445 planb_dbdma_stop(&pb->planb_base->ch2);
446 out_le32 (&pb->planb_base->ch2.cmdptr,
447 virt_to_bus(pb->ch2_cmd));
448 planb_dbdma_restart(&pb->planb_base->ch2);
449 st_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
450 tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
451 DBDMA_NOP | BR_ALWAYS,
452 virt_to_bus(pb->ch1_cmd));
453 eieio();
454 pb->prev_last_fr = pb->last_fr;
455 pb->last_fr = -2;
456 if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
457 IDEBUG("PlanB: became inactive "
458 "in the mean time... reactivating\n");
459 planb_dbdma_stop(&pb->planb_base->ch1);
460 out_le32 (&pb->planb_base->ch1.cmdptr,
461 virt_to_bus(pb->ch1_cmd));
462 planb_dbdma_restart(&pb->planb_base->ch1);
463 }
464 } else {
465
466 DEBUG("PlanB: currently idle, so can do whatever\n");
467
468 planb_dbdma_stop(&pb->planb_base->ch2);
469 planb_dbdma_stop(&pb->planb_base->ch1);
470 st_le32 (&pb->planb_base->ch2.cmdptr,
471 virt_to_bus(pb->ch2_cmd));
472 st_le32 (&pb->planb_base->ch1.cmdptr,
473 virt_to_bus(pb->ch1_cmd));
474 out_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
475 planb_dbdma_restart(&pb->planb_base->ch2);
476 planb_dbdma_restart(&pb->planb_base->ch1);
477 pb->last_fr = -1;
478 }
479 return;
480}
481
482static void overlay_stop(struct planb *pb)
483{
484 DEBUG("PlanB: overlay_stop()\n");
485
486 if(pb->last_fr == -1) {
487
488 DEBUG("PlanB: no grabbing, it seems...\n");
489
490 planb_dbdma_stop(&pb->planb_base->ch2);
491 planb_dbdma_stop(&pb->planb_base->ch1);
492 pb->last_fr = -999;
493 } else if(pb->last_fr == -2) {
494 unsigned int cmd_dep;
495 tab_cmd_dbdma(pb->cap_cmd[pb->prev_last_fr], DBDMA_STOP, 0);
496 eieio();
497 cmd_dep = (unsigned int)in_le32(&pb->overlay_last1->cmd_dep);
498 if(overlay_is_active(pb)) {
499
500 DEBUG("PlanB: overlay is currently active\n");
501
502 planb_dbdma_stop(&pb->planb_base->ch2);
503 planb_dbdma_stop(&pb->planb_base->ch1);
504 if(cmd_dep != pb->ch1_cmd_phys) {
505 out_le32(&pb->planb_base->ch1.cmdptr,
506 virt_to_bus(pb->overlay_last1));
507 planb_dbdma_restart(&pb->planb_base->ch1);
508 }
509 }
510 pb->last_fr = pb->prev_last_fr;
511 pb->prev_last_fr = -999;
512 }
513 return;
514}
515
516static void suspend_overlay(struct planb *pb)
517{
518 int fr = -1;
519 struct dbdma_cmd last;
520
521 DEBUG("PlanB: suspend_overlay: %d\n", pb->suspend);
522
523 if(pb->suspend++)
524 return;
525 if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
526 if(pb->last_fr == -2) {
527 fr = pb->prev_last_fr;
528 memcpy(&last, (void*)pb->last_cmd[fr], sizeof(last));
529 tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
530 }
531 if(overlay_is_active(pb)) {
532 planb_dbdma_stop(&pb->planb_base->ch2);
533 planb_dbdma_stop(&pb->planb_base->ch1);
534 pb->suspended.overlay = 1;
535 pb->suspended.frame = fr;
536 memcpy(&pb->suspended.cmd, &last, sizeof(last));
537 return;
538 }
539 }
540 pb->suspended.overlay = 0;
541 pb->suspended.frame = fr;
542 memcpy(&pb->suspended.cmd, &last, sizeof(last));
543 return;
544}
545
546static void resume_overlay(struct planb *pb)
547{
548
549 DEBUG("PlanB: resume_overlay: %d\n", pb->suspend);
550
551 if(pb->suspend > 1)
552 return;
553 if(pb->suspended.frame != -1) {
554 memcpy((void*)pb->last_cmd[pb->suspended.frame],
555 &pb->suspended.cmd, sizeof(pb->suspended.cmd));
556 }
557 if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
558 goto finish;
559 }
560 if(pb->suspended.overlay) {
561
562 DEBUG("PlanB: overlay being resumed\n");
563
564 st_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
565 st_le16 (&pb->ch2_cmd->command, DBDMA_NOP);
566 /* Set command buffer addresses */
567 st_le32(&pb->planb_base->ch1.cmdptr,
568 virt_to_bus(pb->overlay_last1));
569 out_le32(&pb->planb_base->ch2.cmdptr,
570 virt_to_bus(pb->overlay_last2));
571 /* Start the DMA controller */
572 out_le32 (&pb->planb_base->ch2.control,
573 PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
574 out_le32 (&pb->planb_base->ch1.control,
575 PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
576 } else if(pb->suspended.frame != -1) {
577 out_le32(&pb->planb_base->ch1.cmdptr,
578 virt_to_bus(pb->last_cmd[pb->suspended.frame]));
579 out_le32 (&pb->planb_base->ch1.control,
580 PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
581 }
582
583finish:
584 pb->suspend--;
585 wake_up_interruptible(&pb->suspendq);
586}
587
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300588static void add_clip(struct planb *pb, struct video_clip *clip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589{
590 volatile unsigned char *base;
591 int xc = clip->x, yc = clip->y;
592 int wc = clip->width, hc = clip->height;
593 int ww = pb->win.width, hw = pb->win.height;
594 int x, y, xtmp1, xtmp2;
595
596 DEBUG("PlanB: clip %dx%d+%d+%d\n", wc, hc, xc, yc);
597
598 if(xc < 0) {
599 wc += xc;
600 xc = 0;
601 }
602 if(yc < 0) {
603 hc += yc;
604 yc = 0;
605 }
606 if(xc + wc > ww)
607 wc = ww - xc;
608 if(wc <= 0) /* Nothing to do */
609 return;
610 if(yc + hc > hw)
611 hc = hw - yc;
612
613 for (y = yc; y < yc+hc; y++) {
614 xtmp1=xc>>3;
615 xtmp2=(xc+wc)>>3;
616 base = pb->mask + y*96;
617 if(xc != 0 || wc >= 8)
618 *(base + xtmp1) &= (unsigned char)(0x00ff &
619 (0xff00 >> (xc&7)));
620 for (x = xtmp1 + 1; x < xtmp2; x++) {
621 *(base + x) = 0;
622 }
623 if(xc < (ww & ~0x7))
624 *(base + xtmp2) &= (unsigned char)(0x00ff >>
625 ((xc+wc) & 7));
626 }
627
628 return;
629}
630
631static void fill_cmd_buff(struct planb *pb)
632{
633 int restore = 0;
634 volatile struct dbdma_cmd last;
635
636 DEBUG("PlanB: fill_cmd_buff()\n");
637
638 if(pb->overlay_last1 != pb->ch1_cmd) {
639 restore = 1;
640 last = *(pb->overlay_last1);
641 }
642 memset ((void *) pb->ch1_cmd, 0, 2 * pb->tab_size
643 * sizeof(struct dbdma_cmd));
644 cmd_buff (pb);
645 if(restore)
646 *(pb->overlay_last1) = last;
647 if(pb->suspended.overlay) {
648 unsigned long jump_addr = in_le32(&pb->overlay_last1->cmd_dep);
649 if(jump_addr != pb->ch1_cmd_phys) {
650 int i;
651
652 DEBUG("PlanB: adjusting ch1's jump address\n");
653
654 for(i = 0; i < MAX_GBUFFERS; i++) {
655 if(pb->need_pre_capture[i]) {
656 if(jump_addr == virt_to_bus(pb->pre_cmd[i]))
657 goto found;
658 } else {
659 if(jump_addr == virt_to_bus(pb->cap_cmd[i]))
660 goto found;
661 }
662 }
663
664 DEBUG("PlanB: not found...\n");
665
666 goto out;
667found:
668 if(pb->need_pre_capture[i])
669 out_le32(&pb->pre_cmd[i]->phy_addr,
670 virt_to_bus(pb->overlay_last1));
671 else
672 out_le32(&pb->cap_cmd[i]->phy_addr,
673 virt_to_bus(pb->overlay_last1));
674 }
675 }
676out:
677 pb->cmd_buff_inited = 1;
678
679 return;
680}
681
682static void cmd_buff(struct planb *pb)
683{
684 int i, bpp, count, nlines, stepsize, interlace;
685 unsigned long base, jump, addr_com, addr_dep;
686 volatile struct dbdma_cmd *c1 = pb->ch1_cmd;
687 volatile struct dbdma_cmd *c2 = pb->ch2_cmd;
688
689 interlace = pb->win.interlace;
690 bpp = pb->win.bpp;
691 count = (bpp * ((pb->win.x + pb->win.width > pb->win.swidth) ?
692 (pb->win.swidth - pb->win.x) : pb->win.width));
693 nlines = ((pb->win.y + pb->win.height > pb->win.sheight) ?
694 (pb->win.sheight - pb->win.y) : pb->win.height);
695
696 /* Do video in: */
697
698 /* Preamble commands: */
699 addr_com = virt_to_bus(c1);
700 addr_dep = virt_to_bus(&c1->cmd_dep);
701 tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
702 jump = virt_to_bus(c1+16); /* 14 by cmd_geo_setup() and 2 for padding */
703 if((c1 = cmd_geo_setup(c1, pb->win.width, pb->win.height, interlace,
704 bpp, 1, pb)) == NULL) {
705 printk(KERN_WARNING "PlanB: encountered serious problems\n");
706 tab_cmd_dbdma(pb->ch1_cmd + 1, DBDMA_STOP, 0);
707 tab_cmd_dbdma(pb->ch2_cmd + 1, DBDMA_STOP, 0);
708 return;
709 }
710 tab_cmd_store(c1++, addr_com, (unsigned)(DBDMA_NOP | BR_ALWAYS) << 16);
711 tab_cmd_store(c1++, addr_dep, jump);
712 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
713 PLANB_SET(FIELD_SYNC));
714 /* (1) wait for field sync to be set */
715 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
716 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
717 PLANB_SET(ODD_FIELD));
718 /* wait for field sync to be cleared */
719 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
720 /* if not odd field, wait until field sync is set again */
721 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
722 /* assert ch_sync to ch2 */
723 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control),
724 PLANB_SET(CH_SYNC));
725 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
726 PLANB_SET(DMA_ABORT));
727
728 base = (pb->frame_buffer_phys + pb->offset + pb->win.y * (pb->win.bpl
729 + pb->win.pad) + pb->win.x * bpp);
730
731 if (interlace) {
732 stepsize = 2;
733 jump = virt_to_bus(c1 + (nlines + 1) / 2);
734 } else {
735 stepsize = 1;
736 jump = virt_to_bus(c1 + nlines);
737 }
738
739 /* even field data: */
740 for (i=0; i < nlines; i += stepsize, c1++)
741 tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET,
742 count, base + i * (pb->win.bpl + pb->win.pad), jump);
743
744 /* For non-interlaced, we use even fields only */
745 if (!interlace)
746 goto cmd_tab_data_end;
747
748 /* Resync to odd field */
749 /* (2) wait for field sync to be set */
750 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
751 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
752 PLANB_SET(ODD_FIELD));
753 /* wait for field sync to be cleared */
754 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
755 /* if not odd field, wait until field sync is set again */
756 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
757 /* assert ch_sync to ch2 */
758 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control),
759 PLANB_SET(CH_SYNC));
760 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
761 PLANB_SET(DMA_ABORT));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300762
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 /* odd field data: */
764 jump = virt_to_bus(c1 + nlines / 2);
765 for (i=1; i < nlines; i += stepsize, c1++)
766 tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
767 base + i * (pb->win.bpl + pb->win.pad), jump);
768
769 /* And jump back to the start */
770cmd_tab_data_end:
771 pb->overlay_last1 = c1; /* keep a pointer to the last command */
772 tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch1_cmd));
773
774 /* Clipmask command buffer */
775
776 /* Preamble commands: */
777 tab_cmd_dbdma(c2++, DBDMA_NOP, 0);
778 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel),
779 PLANB_SET(CH_SYNC));
780 /* wait until ch1 asserts ch_sync */
781 tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0);
782 /* clear ch_sync asserted by ch1 */
783 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.control),
784 PLANB_CLR(CH_SYNC));
785 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel),
786 PLANB_SET(FIELD_SYNC));
787 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
788 PLANB_SET(ODD_FIELD));
789
790 /* jump to end of even field if appropriate */
791 /* this points to (interlace)? pos. C: pos. B */
792 jump = (interlace) ? virt_to_bus(c2 + (nlines + 1) / 2 + 2):
793 virt_to_bus(c2 + nlines + 2);
794 /* if odd field, skip over to odd field clipmasking */
795 tab_cmd_dbdma(c2++, DBDMA_NOP | BR_IFSET, jump);
796
797 /* even field mask: */
798 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
799 PLANB_SET(DMA_ABORT));
800 /* this points to pos. B */
801 jump = (interlace) ? virt_to_bus(c2 + nlines + 1):
802 virt_to_bus(c2 + nlines);
803 base = virt_to_bus(pb->mask);
804 for (i=0; i < nlines; i += stepsize, c2++)
805 tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96,
806 base + i * 96, jump);
807
808 /* For non-interlaced, we use only even fields */
809 if(!interlace)
810 goto cmd_tab_mask_end;
811
812 /* odd field mask: */
813/* C */ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
814 PLANB_SET(DMA_ABORT));
815 /* this points to pos. B */
816 jump = virt_to_bus(c2 + nlines / 2);
817 base = virt_to_bus(pb->mask);
818 for (i=1; i < nlines; i += 2, c2++) /* abort if set */
819 tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96,
820 base + i * 96, jump);
821
822 /* Inform channel 1 and jump back to start */
823cmd_tab_mask_end:
824 /* ok, I just realized this is kind of flawed. */
825 /* this part is reached only after odd field clipmasking. */
826 /* wanna clean up? */
827 /* wait for field sync to be set */
828 /* corresponds to fsync (1) of ch1 */
829/* B */ tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0);
830 /* restart ch1, meant to clear any dead bit or something */
831 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control),
832 PLANB_CLR(RUN));
833 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control),
834 PLANB_SET(RUN));
835 pb->overlay_last2 = c2; /* keep a pointer to the last command */
836 /* start over even field clipmasking */
837 tab_cmd_dbdma(c2, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch2_cmd));
838
839 eieio();
840 return;
841}
842
843/*********************************/
844/* grabdisplay support functions */
845/*********************************/
846
847static int palette2fmt[] = {
848 0,
849 PLANB_GRAY,
850 0,
851 0,
852 0,
853 PLANB_COLOUR32,
854 PLANB_COLOUR15,
855 0,
856 0,
857 0,
858 0,
859 0,
860 0,
861 0,
862 0,
863};
864
865#define PLANB_PALETTE_MAX 15
866
867static int vgrab(struct planb *pb, struct video_mmap *mp)
868{
869 unsigned int fr = mp->frame;
870 unsigned int format;
871
872 if(pb->rawbuf==NULL) {
873 int err;
874 if((err=grabbuf_alloc(pb)))
875 return err;
876 }
877
878 IDEBUG("PlanB: grab %d: %dx%d(%u)\n", pb->grabbing,
879 mp->width, mp->height, fr);
880
881 if(pb->grabbing >= MAX_GBUFFERS)
882 return -ENOBUFS;
883 if(fr > (MAX_GBUFFERS - 1) || fr < 0)
884 return -EINVAL;
885 if(mp->height <= 0 || mp->width <= 0)
886 return -EINVAL;
887 if(mp->format < 0 || mp->format >= PLANB_PALETTE_MAX)
888 return -EINVAL;
889 if((format = palette2fmt[mp->format]) == 0)
890 return -EINVAL;
891 if (mp->height * mp->width * format > PLANB_MAX_FBUF) /* format = bpp */
892 return -EINVAL;
893
894 planb_lock(pb);
895 if(mp->width != pb->gwidth[fr] || mp->height != pb->gheight[fr] ||
896 format != pb->gfmt[fr] || (pb->gnorm_switch[fr])) {
897 int i;
898#ifndef PLANB_GSCANLINE
899 unsigned int osize = pb->gwidth[fr] * pb->gheight[fr]
900 * pb->gfmt[fr];
901 unsigned int nsize = mp->width * mp->height * format;
902#endif
903
904 IDEBUG("PlanB: gwidth = %d, gheight = %d, mp->format = %u\n",
905 mp->width, mp->height, mp->format);
906
907#ifndef PLANB_GSCANLINE
908 if(pb->gnorm_switch[fr])
909 nsize = 0;
910 if (nsize < osize) {
911 for(i = pb->gbuf_idx[fr]; osize > 0; i++) {
912 memset((void *)pb->rawbuf[i], 0, PAGE_SIZE);
913 osize -= PAGE_SIZE;
914 }
915 }
916 for(i = pb->l_fr_addr_idx[fr]; i < pb->l_fr_addr_idx[fr]
917 + pb->lnum[fr]; i++)
918 memset((void *)pb->rawbuf[i], 0, PAGE_SIZE);
919#else
920/* XXX TODO */
921/*
922 if(pb->gnorm_switch[fr])
923 memset((void *)pb->gbuffer[fr], 0,
924 pb->gbytes_per_line * pb->gheight[fr]);
925 else {
926 if(mp->
927 for(i = 0; i < pb->gheight[fr]; i++) {
928 memset((void *)(pb->gbuffer[fr]
929 + pb->gbytes_per_line * i
930 }
931 }
932*/
933#endif
934 pb->gwidth[fr] = mp->width;
935 pb->gheight[fr] = mp->height;
936 pb->gfmt[fr] = format;
937 pb->last_cmd[fr] = setup_grab_cmd(fr, pb);
938 planb_pre_capture(fr, pb->gfmt[fr], pb); /* gfmt = bpp */
939 pb->need_pre_capture[fr] = 1;
940 pb->gnorm_switch[fr] = 0;
941 } else
942 pb->need_pre_capture[fr] = 0;
943 pb->frame_stat[fr] = GBUFFER_GRABBING;
944 if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
945
946 IDEBUG("PlanB: ch1 inactive, initiating grabbing\n");
947
948 planb_dbdma_stop(&pb->planb_base->ch1);
949 if(pb->need_pre_capture[fr]) {
950
951 IDEBUG("PlanB: padding pre-capture sequence\n");
952
953 out_le32 (&pb->planb_base->ch1.cmdptr,
954 virt_to_bus(pb->pre_cmd[fr]));
955 } else {
956 tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
957 tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
958 /* let's be on the safe side. here is not timing critical. */
959 tab_cmd_dbdma((pb->cap_cmd[fr] + 1), DBDMA_NOP, 0);
960 out_le32 (&pb->planb_base->ch1.cmdptr,
961 virt_to_bus(pb->cap_cmd[fr]));
962 }
963 planb_dbdma_restart(&pb->planb_base->ch1);
964 pb->last_fr = fr;
965 } else {
966 int i;
967
968 IDEBUG("PlanB: ch1 active, grabbing being queued\n");
969
970 if((pb->last_fr == -1) || ((pb->last_fr == -2) &&
971 overlay_is_active(pb))) {
972
973 IDEBUG("PlanB: overlay is active, grabbing defered\n");
974
975 tab_cmd_dbdma(pb->last_cmd[fr],
976 DBDMA_NOP | BR_ALWAYS,
977 virt_to_bus(pb->ch1_cmd));
978 if(pb->need_pre_capture[fr]) {
979
980 IDEBUG("PlanB: padding pre-capture sequence\n");
981
982 tab_cmd_store(pb->pre_cmd[fr],
983 virt_to_bus(&pb->overlay_last1->cmd_dep),
984 virt_to_bus(pb->ch1_cmd));
985 eieio();
986 out_le32 (&pb->overlay_last1->cmd_dep,
987 virt_to_bus(pb->pre_cmd[fr]));
988 } else {
989 tab_cmd_store(pb->cap_cmd[fr],
990 virt_to_bus(&pb->overlay_last1->cmd_dep),
991 virt_to_bus(pb->ch1_cmd));
992 tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
993 DBDMA_NOP, 0);
994 eieio();
995 out_le32 (&pb->overlay_last1->cmd_dep,
996 virt_to_bus(pb->cap_cmd[fr]));
997 }
998 for(i = 0; overlay_is_active(pb) && i < 999; i++)
999 IDEBUG("PlanB: waiting for overlay done\n");
1000 tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0);
1001 pb->prev_last_fr = fr;
1002 pb->last_fr = -2;
1003 } else if(pb->last_fr == -2) {
1004
1005 IDEBUG("PlanB: mixed mode detected, grabbing"
1006 " will be done before activating overlay\n");
1007
1008 tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0);
1009 if(pb->need_pre_capture[fr]) {
1010
1011 IDEBUG("PlanB: padding pre-capture sequence\n");
1012
1013 tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr],
1014 DBDMA_NOP | BR_ALWAYS,
1015 virt_to_bus(pb->pre_cmd[fr]));
1016 eieio();
1017 } else {
1018 tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
1019 if(pb->gwidth[pb->prev_last_fr] !=
1020 pb->gwidth[fr]
1021 || pb->gheight[pb->prev_last_fr] !=
1022 pb->gheight[fr]
1023 || pb->gfmt[pb->prev_last_fr] !=
1024 pb->gfmt[fr])
1025 tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
1026 DBDMA_NOP, 0);
1027 else
1028 tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
1029 DBDMA_NOP | BR_ALWAYS,
1030 virt_to_bus(pb->cap_cmd[fr] + 16));
1031 tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr],
1032 DBDMA_NOP | BR_ALWAYS,
1033 virt_to_bus(pb->cap_cmd[fr]));
1034 eieio();
1035 }
1036 tab_cmd_dbdma(pb->last_cmd[fr],
1037 DBDMA_NOP | BR_ALWAYS,
1038 virt_to_bus(pb->ch1_cmd));
1039 eieio();
1040 pb->prev_last_fr = fr;
1041 pb->last_fr = -2;
1042 } else {
1043
1044 IDEBUG("PlanB: active grabbing session detected\n");
1045
1046 if(pb->need_pre_capture[fr]) {
1047
1048 IDEBUG("PlanB: padding pre-capture sequence\n");
1049
1050 tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
1051 DBDMA_NOP | BR_ALWAYS,
1052 virt_to_bus(pb->pre_cmd[fr]));
1053 eieio();
1054 } else {
1055 tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
1056 tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
1057 if(pb->gwidth[pb->last_fr] != pb->gwidth[fr]
1058 || pb->gheight[pb->last_fr] !=
1059 pb->gheight[fr]
1060 || pb->gfmt[pb->last_fr] !=
1061 pb->gfmt[fr])
1062 tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
1063 DBDMA_NOP, 0);
1064 else
1065 tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
1066 DBDMA_NOP | BR_ALWAYS,
1067 virt_to_bus(pb->cap_cmd[fr] + 16));
1068 tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
1069 DBDMA_NOP | BR_ALWAYS,
1070 virt_to_bus(pb->cap_cmd[fr]));
1071 eieio();
1072 }
1073 pb->last_fr = fr;
1074 }
1075 if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
1076
1077 IDEBUG("PlanB: became inactive in the mean time..."
1078 "reactivating\n");
1079
1080 planb_dbdma_stop(&pb->planb_base->ch1);
1081 out_le32 (&pb->planb_base->ch1.cmdptr,
1082 virt_to_bus(pb->cap_cmd[fr]));
1083 planb_dbdma_restart(&pb->planb_base->ch1);
1084 }
1085 }
1086 pb->grabbing++;
1087 planb_unlock(pb);
1088
1089 return 0;
1090}
1091
1092static void planb_pre_capture(int fr, int bpp, struct planb *pb)
1093{
1094 volatile struct dbdma_cmd *c1 = pb->pre_cmd[fr];
1095 int interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0;
1096
1097 tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
1098 if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace,
1099 bpp, 0, pb)) == NULL) {
1100 printk(KERN_WARNING "PlanB: encountered some problems\n");
1101 tab_cmd_dbdma(pb->pre_cmd[fr] + 1, DBDMA_STOP, 0);
1102 return;
1103 }
1104 /* Sync to even field */
1105 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
1106 PLANB_SET(FIELD_SYNC));
1107 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
1108 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1109 PLANB_SET(ODD_FIELD));
1110 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
1111 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
1112 tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0);
1113 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1114 PLANB_SET(DMA_ABORT));
1115 /* For non-interlaced, we use even fields only */
1116 if (pb->gheight[fr] <= pb->maxlines/2)
1117 goto cmd_tab_data_end;
1118 /* Sync to odd field */
1119 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
1120 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1121 PLANB_SET(ODD_FIELD));
1122 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
1123 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
1124 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1125 PLANB_SET(DMA_ABORT));
1126cmd_tab_data_end:
1127 tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->cap_cmd[fr]));
1128
1129 eieio();
1130}
1131
1132static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb)
1133{
1134 int i, bpp, count, nlines, stepsize, interlace;
1135#ifdef PLANB_GSCANLINE
1136 int scanline;
1137#else
1138 int nlpp, leftover1;
1139 unsigned long base;
1140#endif
1141 unsigned long jump;
1142 int pagei;
1143 volatile struct dbdma_cmd *c1;
1144 volatile struct dbdma_cmd *jump_addr;
1145
1146 c1 = pb->cap_cmd[fr];
1147 interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0;
1148 bpp = pb->gfmt[fr]; /* gfmt = bpp */
1149 count = bpp * pb->gwidth[fr];
1150 nlines = pb->gheight[fr];
1151#ifdef PLANB_GSCANLINE
1152 scanline = pb->gbytes_per_line;
1153#else
1154 pb->lsize[fr] = count;
1155 pb->lnum[fr] = 0;
1156#endif
1157
1158 /* Do video in: */
1159
1160 /* Preamble commands: */
1161 tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
1162 tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(c1 + 16)); c1++;
1163 if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace,
1164 bpp, 0, pb)) == NULL) {
1165 printk(KERN_WARNING "PlanB: encountered serious problems\n");
1166 tab_cmd_dbdma(pb->cap_cmd[fr] + 1, DBDMA_STOP, 0);
1167 return (pb->cap_cmd[fr] + 2);
1168 }
1169 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
1170 PLANB_SET(FIELD_SYNC));
1171 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
1172 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1173 PLANB_SET(ODD_FIELD));
1174 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
1175 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
1176 tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0);
1177 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1178 PLANB_SET(DMA_ABORT));
1179
1180 if (interlace) {
1181 stepsize = 2;
1182 jump_addr = c1 + TAB_FACTOR * (nlines + 1) / 2;
1183 } else {
1184 stepsize = 1;
1185 jump_addr = c1 + TAB_FACTOR * nlines;
1186 }
1187 jump = virt_to_bus(jump_addr);
1188
1189 /* even field data: */
1190
1191 pagei = pb->gbuf_idx[fr];
1192#ifdef PLANB_GSCANLINE
1193 for (i = 0; i < nlines; i += stepsize) {
1194 tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
1195 virt_to_bus(pb->rawbuf[pagei
1196 + i * scanline / PAGE_SIZE]), jump);
1197 }
1198#else
1199 i = 0;
1200 leftover1 = 0;
1201 do {
1202 int j;
1203
1204 base = virt_to_bus(pb->rawbuf[pagei]);
1205 nlpp = (PAGE_SIZE - leftover1) / count / stepsize;
1206 for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++)
1207 tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET,
1208 count, base + count * j * stepsize + leftover1, jump);
1209 if(i < nlines) {
1210 int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1;
1211
1212 if(lov0 == 0)
1213 leftover1 = 0;
1214 else {
1215 if(lov0 >= count) {
1216 tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, base
1217 + count * nlpp * stepsize + leftover1, jump);
1218 } else {
1219 pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei]
1220 + count * nlpp * stepsize + leftover1;
1221 pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1;
1222 pb->l_to_next_size[fr][pb->lnum[fr]] = count - lov0;
1223 tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
1224 virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr]
1225 + pb->lnum[fr]]), jump);
1226 if(++pb->lnum[fr] > MAX_LNUM)
1227 pb->lnum[fr]--;
1228 }
1229 leftover1 = count * stepsize - lov0;
1230 i += stepsize;
1231 }
1232 }
1233 pagei++;
1234 } while(i < nlines);
1235 tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump);
1236 c1 = jump_addr;
1237#endif /* PLANB_GSCANLINE */
1238
1239 /* For non-interlaced, we use even fields only */
1240 if (!interlace)
1241 goto cmd_tab_data_end;
1242
1243 /* Sync to odd field */
1244 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
1245 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1246 PLANB_SET(ODD_FIELD));
1247 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
1248 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
1249 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1250 PLANB_SET(DMA_ABORT));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001251
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 /* odd field data: */
1253 jump_addr = c1 + TAB_FACTOR * nlines / 2;
1254 jump = virt_to_bus(jump_addr);
1255#ifdef PLANB_GSCANLINE
1256 for (i = 1; i < nlines; i += stepsize) {
1257 tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
1258 virt_to_bus(pb->rawbuf[pagei
1259 + i * scanline / PAGE_SIZE]), jump);
1260 }
1261#else
1262 i = 1;
1263 leftover1 = 0;
1264 pagei = pb->gbuf_idx[fr];
1265 if(nlines <= 1)
1266 goto skip;
1267 do {
1268 int j;
1269
1270 base = virt_to_bus(pb->rawbuf[pagei]);
1271 nlpp = (PAGE_SIZE - leftover1) / count / stepsize;
1272 if(leftover1 >= count) {
1273 tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
1274 base + leftover1 - count, jump);
1275 i += stepsize;
1276 }
1277 for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++)
1278 tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
1279 base + count * (j * stepsize + 1) + leftover1, jump);
1280 if(i < nlines) {
1281 int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1;
1282
1283 if(lov0 == 0)
1284 leftover1 = 0;
1285 else {
1286 if(lov0 > count) {
1287 pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei]
1288 + count * (nlpp * stepsize + 1) + leftover1;
1289 pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1;
1290 pb->l_to_next_size[fr][pb->lnum[fr]] = count * stepsize
1291 - lov0;
1292 tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
1293 virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr]
1294 + pb->lnum[fr]]), jump);
1295 if(++pb->lnum[fr] > MAX_LNUM)
1296 pb->lnum[fr]--;
1297 i += stepsize;
1298 }
1299 leftover1 = count * stepsize - lov0;
1300 }
1301 }
1302 pagei++;
1303 } while(i < nlines);
1304skip:
1305 tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump);
1306 c1 = jump_addr;
1307#endif /* PLANB_GSCANLINE */
1308
1309cmd_tab_data_end:
1310 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->intr_stat),
1311 (fr << 9) | PLANB_FRM_IRQ | PLANB_GEN_IRQ);
1312 /* stop it */
1313 tab_cmd_dbdma(c1, DBDMA_STOP, 0);
1314
1315 eieio();
1316 return c1;
1317}
1318
1319static void planb_irq(int irq, void *dev_id, struct pt_regs * regs)
1320{
1321 unsigned int stat, astat;
1322 struct planb *pb = (struct planb *)dev_id;
1323
1324 IDEBUG("PlanB: planb_irq()\n");
1325
1326 /* get/clear interrupt status bits */
1327 eieio();
1328 stat = in_le32(&pb->planb_base->intr_stat);
1329 astat = stat & pb->intr_mask;
1330 out_le32(&pb->planb_base->intr_stat, PLANB_FRM_IRQ
1331 & ~astat & stat & ~PLANB_GEN_IRQ);
1332 IDEBUG("PlanB: stat = %X, astat = %X\n", stat, astat);
1333
1334 if(astat & PLANB_FRM_IRQ) {
1335 unsigned int fr = stat >> 9;
1336#ifndef PLANB_GSCANLINE
1337 int i;
1338#endif
1339 IDEBUG("PlanB: PLANB_FRM_IRQ\n");
1340
1341 pb->gcount++;
1342
1343 IDEBUG("PlanB: grab %d: fr = %d, gcount = %d\n",
1344 pb->grabbing, fr, pb->gcount);
1345#ifndef PLANB_GSCANLINE
1346 IDEBUG("PlanB: %d * %d bytes are being copied over\n",
1347 pb->lnum[fr], pb->lsize[fr]);
1348 for(i = 0; i < pb->lnum[fr]; i++) {
1349 int first = pb->lsize[fr] - pb->l_to_next_size[fr][i];
1350
1351 memcpy(pb->l_to_addr[fr][i],
1352 pb->rawbuf[pb->l_fr_addr_idx[fr] + i],
1353 first);
1354 memcpy(pb->rawbuf[pb->l_to_next_idx[fr][i]],
1355 pb->rawbuf[pb->l_fr_addr_idx[fr] + i] + first,
1356 pb->l_to_next_size[fr][i]);
1357 }
1358#endif
1359 pb->frame_stat[fr] = GBUFFER_DONE;
1360 pb->grabbing--;
1361 wake_up_interruptible(&pb->capq);
1362 return;
1363 }
1364 /* incorrect interrupts? */
1365 pb->intr_mask = PLANB_CLR_IRQ;
1366 out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
1367 printk(KERN_ERR "PlanB: IRQ lockup, cleared intrrupts"
1368 " unconditionally\n");
1369}
1370
1371/*******************************
1372 * Device Operations functions *
1373 *******************************/
1374
1375static int planb_open(struct video_device *dev, int mode)
1376{
1377 struct planb *pb = (struct planb *)dev;
1378
1379 if (pb->user == 0) {
1380 int err;
1381 if((err = planb_prepare_open(pb)) != 0)
1382 return err;
1383 }
1384 pb->user++;
1385
1386 DEBUG("PlanB: device opened\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001387 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388}
1389
1390static void planb_close(struct video_device *dev)
1391{
1392 struct planb *pb = (struct planb *)dev;
1393
1394 if(pb->user < 1) /* ??? */
1395 return;
1396 planb_lock(pb);
1397 if (pb->user == 1) {
1398 if (pb->overlay) {
1399 planb_dbdma_stop(&pb->planb_base->ch2);
1400 planb_dbdma_stop(&pb->planb_base->ch1);
1401 pb->overlay = 0;
1402 }
1403 planb_prepare_close(pb);
1404 }
1405 pb->user--;
1406 planb_unlock(pb);
1407
1408 DEBUG("PlanB: device closed\n");
1409}
1410
1411static long planb_read(struct video_device *v, char *buf, unsigned long count,
1412 int nonblock)
1413{
1414 DEBUG("planb: read request\n");
1415 return -EINVAL;
1416}
1417
1418static long planb_write(struct video_device *v, const char *buf,
1419 unsigned long count, int nonblock)
1420{
1421 DEBUG("planb: write request\n");
1422 return -EINVAL;
1423}
1424
1425static int planb_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
1426{
1427 struct planb *pb=(struct planb *)dev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001428
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429 switch (cmd)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001430 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001431 case VIDIOCGCAP:
1432 {
1433 struct video_capability b;
1434
1435 DEBUG("PlanB: IOCTL VIDIOCGCAP\n");
1436
1437 strcpy (b.name, pb->video_dev.name);
1438 b.type = VID_TYPE_OVERLAY | VID_TYPE_CLIPPING |
1439 VID_TYPE_FRAMERAM | VID_TYPE_SCALES |
1440 VID_TYPE_CAPTURE;
1441 b.channels = 2; /* composite & svhs */
1442 b.audios = 0;
1443 b.maxwidth = PLANB_MAXPIXELS;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001444 b.maxheight = PLANB_MAXLINES;
1445 b.minwidth = 32; /* wild guess */
1446 b.minheight = 32;
1447 if (copy_to_user(arg,&b,sizeof(b)))
1448 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 return 0;
1450 }
1451 case VIDIOCSFBUF:
1452 {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001453 struct video_buffer v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454 unsigned short bpp;
1455 unsigned int fmt;
1456
1457 DEBUG("PlanB: IOCTL VIDIOCSFBUF\n");
1458
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001459 if (!capable(CAP_SYS_ADMIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 || !capable(CAP_SYS_RAWIO))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001461 return -EPERM;
1462 if (copy_from_user(&v, arg,sizeof(v)))
1463 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464 planb_lock(pb);
1465 switch(v.depth) {
1466 case 8:
1467 bpp = 1;
1468 fmt = PLANB_GRAY;
1469 break;
1470 case 15:
1471 case 16:
1472 bpp = 2;
1473 fmt = PLANB_COLOUR15;
1474 break;
1475 case 24:
1476 case 32:
1477 bpp = 4;
1478 fmt = PLANB_COLOUR32;
1479 break;
1480 default:
1481 planb_unlock(pb);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001482 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 }
1484 if (bpp * v.width > v.bytesperline) {
1485 planb_unlock(pb);
1486 return -EINVAL;
1487 }
1488 pb->win.bpp = bpp;
1489 pb->win.color_fmt = fmt;
1490 pb->frame_buffer_phys = (unsigned long) v.base;
1491 pb->win.sheight = v.height;
1492 pb->win.swidth = v.width;
1493 pb->picture.depth = pb->win.depth = v.depth;
1494 pb->win.bpl = pb->win.bpp * pb->win.swidth;
1495 pb->win.pad = v.bytesperline - pb->win.bpl;
1496
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001497 DEBUG("PlanB: Display at %p is %d by %d, bytedepth %d,"
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 " bpl %d (+ %d)\n", v.base, v.width,v.height,
1499 pb->win.bpp, pb->win.bpl, pb->win.pad);
1500
1501 pb->cmd_buff_inited = 0;
1502 if(pb->overlay) {
1503 suspend_overlay(pb);
1504 fill_cmd_buff(pb);
1505 resume_overlay(pb);
1506 }
1507 planb_unlock(pb);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001508 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 }
1510 case VIDIOCGFBUF:
1511 {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001512 struct video_buffer v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513
1514 DEBUG("PlanB: IOCTL VIDIOCGFBUF\n");
1515
1516 v.base = (void *)pb->frame_buffer_phys;
1517 v.height = pb->win.sheight;
1518 v.width = pb->win.swidth;
1519 v.depth = pb->win.depth;
1520 v.bytesperline = pb->win.bpl + pb->win.pad;
1521 if (copy_to_user(arg, &v, sizeof(v)))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001522 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 return 0;
1524 }
1525 case VIDIOCCAPTURE:
1526 {
1527 int i;
1528
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001529 if(copy_from_user(&i, arg, sizeof(i)))
1530 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001531 if(i==0) {
1532 DEBUG("PlanB: IOCTL VIDIOCCAPTURE Stop\n");
1533
1534 if (!(pb->overlay))
1535 return 0;
1536 planb_lock(pb);
1537 pb->overlay = 0;
1538 overlay_stop(pb);
1539 planb_unlock(pb);
1540 } else {
1541 DEBUG("PlanB: IOCTL VIDIOCCAPTURE Start\n");
1542
1543 if (pb->frame_buffer_phys == 0 ||
1544 pb->win.width == 0 ||
1545 pb->win.height == 0)
1546 return -EINVAL;
1547 if (pb->overlay)
1548 return 0;
1549 planb_lock(pb);
1550 pb->overlay = 1;
1551 if(!(pb->cmd_buff_inited))
1552 fill_cmd_buff(pb);
1553 overlay_start(pb);
1554 planb_unlock(pb);
1555 }
1556 return 0;
1557 }
1558 case VIDIOCGCHAN:
1559 {
1560 struct video_channel v;
1561
1562 DEBUG("PlanB: IOCTL VIDIOCGCHAN\n");
1563
1564 if(copy_from_user(&v, arg,sizeof(v)))
1565 return -EFAULT;
1566 v.flags = 0;
1567 v.tuners = 0;
1568 v.type = VIDEO_TYPE_CAMERA;
1569 v.norm = pb->win.norm;
1570 switch(v.channel)
1571 {
1572 case 0:
1573 strcpy(v.name,"Composite");
1574 break;
1575 case 1:
1576 strcpy(v.name,"SVHS");
1577 break;
1578 default:
1579 return -EINVAL;
1580 break;
1581 }
1582 if(copy_to_user(arg,&v,sizeof(v)))
1583 return -EFAULT;
1584
1585 return 0;
1586 }
1587 case VIDIOCSCHAN:
1588 {
1589 struct video_channel v;
1590
1591 DEBUG("PlanB: IOCTL VIDIOCSCHAN\n");
1592
1593 if(copy_from_user(&v, arg, sizeof(v)))
1594 return -EFAULT;
1595
1596 if (v.norm != pb->win.norm) {
1597 int i, maxlines;
1598
1599 switch (v.norm)
1600 {
1601 case VIDEO_MODE_PAL:
1602 case VIDEO_MODE_SECAM:
1603 maxlines = PLANB_MAXLINES;
1604 break;
1605 case VIDEO_MODE_NTSC:
1606 maxlines = PLANB_NTSC_MAXLINES;
1607 break;
1608 default:
1609 return -EINVAL;
1610 break;
1611 }
1612 planb_lock(pb);
1613 /* empty the grabbing queue */
1614 wait_event(pb->capq, !pb->grabbing);
1615 pb->maxlines = maxlines;
1616 pb->win.norm = v.norm;
1617 /* Stop overlay if running */
1618 suspend_overlay(pb);
1619 for(i = 0; i < MAX_GBUFFERS; i++)
1620 pb->gnorm_switch[i] = 1;
1621 /* I know it's an overkill, but.... */
1622 fill_cmd_buff(pb);
1623 /* ok, now init it accordingly */
1624 saa_init_regs (pb);
1625 /* restart overlay if it was running */
1626 resume_overlay(pb);
1627 planb_unlock(pb);
1628 }
1629
1630 switch(v.channel)
1631 {
1632 case 0: /* Composite */
1633 saa_set (SAA7196_IOCC,
1634 ((saa_regs[pb->win.norm][SAA7196_IOCC] &
1635 ~7) | 3), pb);
1636 break;
1637 case 1: /* SVHS */
1638 saa_set (SAA7196_IOCC,
1639 ((saa_regs[pb->win.norm][SAA7196_IOCC] &
1640 ~7) | 4), pb);
1641 break;
1642 default:
1643 return -EINVAL;
1644 break;
1645 }
1646
1647 return 0;
1648 }
1649 case VIDIOCGPICT:
1650 {
1651 struct video_picture vp = pb->picture;
1652
1653 DEBUG("PlanB: IOCTL VIDIOCGPICT\n");
1654
1655 switch(pb->win.color_fmt) {
1656 case PLANB_GRAY:
1657 vp.palette = VIDEO_PALETTE_GREY;
1658 case PLANB_COLOUR15:
1659 vp.palette = VIDEO_PALETTE_RGB555;
1660 break;
1661 case PLANB_COLOUR32:
1662 vp.palette = VIDEO_PALETTE_RGB32;
1663 break;
1664 default:
1665 vp.palette = 0;
1666 break;
1667 }
1668
1669 if(copy_to_user(arg,&vp,sizeof(vp)))
1670 return -EFAULT;
1671 return 0;
1672 }
1673 case VIDIOCSPICT:
1674 {
1675 struct video_picture vp;
1676
1677 DEBUG("PlanB: IOCTL VIDIOCSPICT\n");
1678
1679 if(copy_from_user(&vp,arg,sizeof(vp)))
1680 return -EFAULT;
1681 pb->picture = vp;
1682 /* Should we do sanity checks here? */
1683 saa_set (SAA7196_BRIG, (unsigned char)
1684 ((pb->picture.brightness) >> 8), pb);
1685 saa_set (SAA7196_HUEC, (unsigned char)
1686 ((pb->picture.hue) >> 8) ^ 0x80, pb);
1687 saa_set (SAA7196_CSAT, (unsigned char)
1688 ((pb->picture.colour) >> 9), pb);
1689 saa_set (SAA7196_CONT, (unsigned char)
1690 ((pb->picture.contrast) >> 9), pb);
1691
1692 return 0;
1693 }
1694 case VIDIOCSWIN:
1695 {
1696 struct video_window vw;
1697 struct video_clip clip;
1698 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001699
Linus Torvalds1da177e2005-04-16 15:20:36 -07001700 DEBUG("PlanB: IOCTL VIDIOCSWIN\n");
1701
1702 if(copy_from_user(&vw,arg,sizeof(vw)))
1703 return -EFAULT;
1704
1705 planb_lock(pb);
1706 /* Stop overlay if running */
1707 suspend_overlay(pb);
1708 pb->win.interlace = (vw.height > pb->maxlines/2)? 1: 0;
1709 if (pb->win.x != vw.x ||
1710 pb->win.y != vw.y ||
1711 pb->win.width != vw.width ||
1712 pb->win.height != vw.height ||
1713 !pb->cmd_buff_inited) {
1714 pb->win.x = vw.x;
1715 pb->win.y = vw.y;
1716 pb->win.width = vw.width;
1717 pb->win.height = vw.height;
1718 fill_cmd_buff(pb);
1719 }
1720 /* Reset clip mask */
1721 memset ((void *) pb->mask, 0xff, (pb->maxlines
1722 * ((PLANB_MAXPIXELS + 7) & ~7)) / 8);
1723 /* Add any clip rects */
1724 for (i = 0; i < vw.clipcount; i++) {
1725 if (copy_from_user(&clip, vw.clips + i,
1726 sizeof(struct video_clip)))
1727 return -EFAULT;
1728 add_clip(pb, &clip);
1729 }
1730 /* restart overlay if it was running */
1731 resume_overlay(pb);
1732 planb_unlock(pb);
1733 return 0;
1734 }
1735 case VIDIOCGWIN:
1736 {
1737 struct video_window vw;
1738
1739 DEBUG("PlanB: IOCTL VIDIOCGWIN\n");
1740
1741 vw.x=pb->win.x;
1742 vw.y=pb->win.y;
1743 vw.width=pb->win.width;
1744 vw.height=pb->win.height;
1745 vw.chromakey=0;
1746 vw.flags=0;
1747 if(pb->win.interlace)
1748 vw.flags|=VIDEO_WINDOW_INTERLACE;
1749 if(copy_to_user(arg,&vw,sizeof(vw)))
1750 return -EFAULT;
1751 return 0;
1752 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001753 case VIDIOCSYNC: {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 int i;
1755
1756 IDEBUG("PlanB: IOCTL VIDIOCSYNC\n");
1757
1758 if(copy_from_user((void *)&i,arg,sizeof(int)))
1759 return -EFAULT;
1760
1761 IDEBUG("PlanB: sync to frame %d\n", i);
1762
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001763 if(i > (MAX_GBUFFERS - 1) || i < 0)
1764 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001765chk_grab:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001766 switch (pb->frame_stat[i]) {
1767 case GBUFFER_UNUSED:
1768 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769 case GBUFFER_GRABBING:
1770 IDEBUG("PlanB: waiting for grab"
1771 " done (%d)\n", i);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001772 interruptible_sleep_on(&pb->capq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 if(signal_pending(current))
1774 return -EINTR;
1775 goto chk_grab;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001776 case GBUFFER_DONE:
1777 pb->frame_stat[i] = GBUFFER_UNUSED;
1778 break;
1779 }
1780 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 }
1782
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001783 case VIDIOCMCAPTURE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001784 {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001785 struct video_mmap vm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001786 volatile unsigned int status;
1787
1788 IDEBUG("PlanB: IOCTL VIDIOCMCAPTURE\n");
1789
1790 if(copy_from_user((void *) &vm,(void *)arg,sizeof(vm)))
1791 return -EFAULT;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001792 status = pb->frame_stat[vm.frame];
1793 if (status != GBUFFER_UNUSED)
1794 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001796 return vgrab(pb, &vm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001797 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001798
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799 case VIDIOCGMBUF:
1800 {
1801 int i;
1802 struct video_mbuf vm;
1803
1804 DEBUG("PlanB: IOCTL VIDIOCGMBUF\n");
1805
1806 memset(&vm, 0 , sizeof(vm));
1807 vm.size = PLANB_MAX_FBUF * MAX_GBUFFERS;
1808 vm.frames = MAX_GBUFFERS;
1809 for(i = 0; i<MAX_GBUFFERS; i++)
1810 vm.offsets[i] = PLANB_MAX_FBUF * i;
1811 if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
1812 return -EFAULT;
1813 return 0;
1814 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001815
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 case PLANBIOCGSAAREGS:
1817 {
1818 struct planb_saa_regs preg;
1819
1820 DEBUG("PlanB: IOCTL PLANBIOCGSAAREGS\n");
1821
1822 if(copy_from_user(&preg, arg, sizeof(preg)))
1823 return -EFAULT;
1824 if(preg.addr >= SAA7196_NUMREGS)
1825 return -EINVAL;
1826 preg.val = saa_regs[pb->win.norm][preg.addr];
1827 if(copy_to_user((void *)arg, (void *)&preg,
1828 sizeof(preg)))
1829 return -EFAULT;
1830 return 0;
1831 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001832
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 case PLANBIOCSSAAREGS:
1834 {
1835 struct planb_saa_regs preg;
1836
1837 DEBUG("PlanB: IOCTL PLANBIOCSSAAREGS\n");
1838
1839 if(copy_from_user(&preg, arg, sizeof(preg)))
1840 return -EFAULT;
1841 if(preg.addr >= SAA7196_NUMREGS)
1842 return -EINVAL;
1843 saa_set (preg.addr, preg.val, pb);
1844 return 0;
1845 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001846
Linus Torvalds1da177e2005-04-16 15:20:36 -07001847 case PLANBIOCGSTAT:
1848 {
1849 struct planb_stat_regs pstat;
1850
1851 DEBUG("PlanB: IOCTL PLANBIOCGSTAT\n");
1852
1853 pstat.ch1_stat = in_le32(&pb->planb_base->ch1.status);
1854 pstat.ch2_stat = in_le32(&pb->planb_base->ch2.status);
1855 pstat.saa_stat0 = saa_status(0, pb);
1856 pstat.saa_stat1 = saa_status(1, pb);
1857
1858 if(copy_to_user((void *)arg, (void *)&pstat,
1859 sizeof(pstat)))
1860 return -EFAULT;
1861 return 0;
1862 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001863
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864 case PLANBIOCSMODE: {
1865 int v;
1866
1867 DEBUG("PlanB: IOCTL PLANBIOCSMODE\n");
1868
1869 if(copy_from_user(&v, arg, sizeof(v)))
1870 return -EFAULT;
1871
1872 switch(v)
1873 {
1874 case PLANB_TV_MODE:
1875 saa_set (SAA7196_STDC,
1876 (saa_regs[pb->win.norm][SAA7196_STDC] &
1877 0x7f), pb);
1878 break;
1879 case PLANB_VTR_MODE:
1880 saa_set (SAA7196_STDC,
1881 (saa_regs[pb->win.norm][SAA7196_STDC] |
1882 0x80), pb);
1883 break;
1884 default:
1885 return -EINVAL;
1886 break;
1887 }
1888 pb->win.mode = v;
1889 return 0;
1890 }
1891 case PLANBIOCGMODE: {
1892 int v=pb->win.mode;
1893
1894 DEBUG("PlanB: IOCTL PLANBIOCGMODE\n");
1895
1896 if(copy_to_user(arg,&v,sizeof(v)))
1897 return -EFAULT;
1898 return 0;
1899 }
1900#ifdef PLANB_GSCANLINE
1901 case PLANBG_GRAB_BPL: {
1902 int v=pb->gbytes_per_line;
1903
1904 DEBUG("PlanB: IOCTL PLANBG_GRAB_BPL\n");
1905
1906 if(copy_to_user(arg,&v,sizeof(v)))
1907 return -EFAULT;
1908 return 0;
1909 }
1910#endif /* PLANB_GSCANLINE */
1911 case PLANB_INTR_DEBUG: {
1912 int i;
1913
1914 DEBUG("PlanB: IOCTL PLANB_INTR_DEBUG\n");
1915
1916 if(copy_from_user(&i, arg, sizeof(i)))
1917 return -EFAULT;
1918
1919 /* avoid hang ups all together */
1920 for (i = 0; i < MAX_GBUFFERS; i++) {
1921 if(pb->frame_stat[i] == GBUFFER_GRABBING) {
1922 pb->frame_stat[i] = GBUFFER_DONE;
1923 }
1924 }
1925 if(pb->grabbing)
1926 pb->grabbing--;
1927 wake_up_interruptible(&pb->capq);
1928 return 0;
1929 }
1930 case PLANB_INV_REGS: {
1931 int i;
1932 struct planb_any_regs any;
1933
1934 DEBUG("PlanB: IOCTL PLANB_INV_REGS\n");
1935
1936 if(copy_from_user(&any, arg, sizeof(any)))
1937 return -EFAULT;
1938 if(any.offset < 0 || any.offset + any.bytes > 0x400)
1939 return -EINVAL;
1940 if(any.bytes > 128)
1941 return -EINVAL;
1942 for (i = 0; i < any.bytes; i++) {
1943 any.data[i] =
1944 in_8((unsigned char *)pb->planb_base
1945 + any.offset + i);
1946 }
1947 if(copy_to_user(arg,&any,sizeof(any)))
1948 return -EFAULT;
1949 return 0;
1950 }
1951 default:
1952 {
1953 DEBUG("PlanB: Unimplemented IOCTL\n");
1954 return -ENOIOCTLCMD;
1955 }
1956 /* Some IOCTLs are currently unsupported on PlanB */
1957 case VIDIOCGTUNER: {
1958 DEBUG("PlanB: IOCTL VIDIOCGTUNER\n");
1959 goto unimplemented; }
1960 case VIDIOCSTUNER: {
1961 DEBUG("PlanB: IOCTL VIDIOCSTUNER\n");
1962 goto unimplemented; }
1963 case VIDIOCSFREQ: {
1964 DEBUG("PlanB: IOCTL VIDIOCSFREQ\n");
1965 goto unimplemented; }
1966 case VIDIOCGFREQ: {
1967 DEBUG("PlanB: IOCTL VIDIOCGFREQ\n");
1968 goto unimplemented; }
1969 case VIDIOCKEY: {
1970 DEBUG("PlanB: IOCTL VIDIOCKEY\n");
1971 goto unimplemented; }
1972 case VIDIOCSAUDIO: {
1973 DEBUG("PlanB: IOCTL VIDIOCSAUDIO\n");
1974 goto unimplemented; }
1975 case VIDIOCGAUDIO: {
1976 DEBUG("PlanB: IOCTL VIDIOCGAUDIO\n");
1977 goto unimplemented; }
1978unimplemented:
1979 DEBUG(" Unimplemented\n");
1980 return -ENOIOCTLCMD;
1981 }
1982 return 0;
1983}
1984
1985static int planb_mmap(struct vm_area_struct *vma, struct video_device *dev, const char *adr, unsigned long size)
1986{
1987 int i;
1988 struct planb *pb = (struct planb *)dev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001989 unsigned long start = (unsigned long)adr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990
1991 if (size > MAX_GBUFFERS * PLANB_MAX_FBUF)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001992 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 if (!pb->rawbuf) {
1994 int err;
1995 if((err=grabbuf_alloc(pb)))
1996 return err;
1997 }
1998 for (i = 0; i < pb->rawbuf_size; i++) {
1999 unsigned long pfn;
2000
2001 pfn = virt_to_phys((void *)pb->rawbuf[i]) >> PAGE_SHIFT;
2002 if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED))
2003 return -EAGAIN;
2004 start += PAGE_SIZE;
2005 if (size <= PAGE_SIZE)
2006 break;
2007 size -= PAGE_SIZE;
2008 }
2009 return 0;
2010}
2011
2012static struct video_device planb_template=
2013{
2014 .owner = THIS_MODULE,
2015 .name = PLANB_DEVICE_NAME,
2016 .type = VID_TYPE_OVERLAY,
2017 .hardware = VID_HARDWARE_PLANB,
2018 .open = planb_open,
2019 .close = planb_close,
2020 .read = planb_read,
2021 .write = planb_write,
2022 .ioctl = planb_ioctl,
2023 .mmap = planb_mmap, /* mmap? */
2024};
2025
2026static int init_planb(struct planb *pb)
2027{
2028 unsigned char saa_rev;
2029 int i, result;
2030
2031 memset ((void *) &pb->win, 0, sizeof (struct planb_window));
2032 /* Simple sanity check */
2033 if(def_norm >= NUM_SUPPORTED_NORM || def_norm < 0) {
2034 printk(KERN_ERR "PlanB: Option(s) invalid\n");
2035 return -2;
2036 }
2037 pb->win.norm = def_norm;
2038 pb->win.mode = PLANB_TV_MODE; /* TV mode */
2039 pb->win.interlace=1;
2040 pb->win.x=0;
2041 pb->win.y=0;
2042 pb->win.width=768; /* 640 */
2043 pb->win.height=576; /* 480 */
2044 pb->maxlines=576;
2045#if 0
2046 btv->win.cropwidth=768; /* 640 */
2047 btv->win.cropheight=576; /* 480 */
2048 btv->win.cropx=0;
2049 btv->win.cropy=0;
2050#endif
2051 pb->win.pad=0;
2052 pb->win.bpp=4;
2053 pb->win.depth=32;
2054 pb->win.color_fmt=PLANB_COLOUR32;
2055 pb->win.bpl=1024*pb->win.bpp;
2056 pb->win.swidth=1024;
2057 pb->win.sheight=768;
2058#ifdef PLANB_GSCANLINE
2059 if((pb->gbytes_per_line = PLANB_MAXPIXELS * 4) > PAGE_SIZE
2060 || (pb->gbytes_per_line <= 0))
2061 return -3;
2062 else {
2063 /* page align pb->gbytes_per_line for DMA purpose */
2064 for(i = PAGE_SIZE; pb->gbytes_per_line < (i>>1);)
2065 i>>=1;
2066 pb->gbytes_per_line = i;
2067 }
2068#endif
2069 pb->tab_size = PLANB_MAXLINES + 40;
2070 pb->suspend = 0;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002071 mutex_init(&pb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 pb->ch1_cmd = 0;
2073 pb->ch2_cmd = 0;
2074 pb->mask = 0;
2075 pb->priv_space = 0;
2076 pb->offset = 0;
2077 pb->user = 0;
2078 pb->overlay = 0;
2079 init_waitqueue_head(&pb->suspendq);
2080 pb->cmd_buff_inited = 0;
2081 pb->frame_buffer_phys = 0;
2082
2083 /* Reset DMA controllers */
2084 planb_dbdma_stop(&pb->planb_base->ch2);
2085 planb_dbdma_stop(&pb->planb_base->ch1);
2086
2087 saa_rev = (saa_status(0, pb) & 0xf0) >> 4;
2088 printk(KERN_INFO "PlanB: SAA7196 video processor rev. %d\n", saa_rev);
2089 /* Initialize the SAA registers in memory and on chip */
2090 saa_init_regs (pb);
2091
2092 /* clear interrupt mask */
2093 pb->intr_mask = PLANB_CLR_IRQ;
2094
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002095 result = request_irq(pb->irq, planb_irq, 0, "PlanB", (void *)pb);
2096 if (result < 0) {
2097 if (result==-EINVAL)
2098 printk(KERN_ERR "PlanB: Bad irq number (%d) "
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099 "or handler\n", (int)pb->irq);
2100 else if (result==-EBUSY)
2101 printk(KERN_ERR "PlanB: I don't know why, "
2102 "but IRQ %d is busy\n", (int)pb->irq);
2103 return result;
2104 }
2105 disable_irq(pb->irq);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002106
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107 /* Now add the template and register the device unit. */
2108 memcpy(&pb->video_dev,&planb_template,sizeof(planb_template));
2109
2110 pb->picture.brightness=0x90<<8;
2111 pb->picture.contrast = 0x70 << 8;
2112 pb->picture.colour = 0x70<<8;
2113 pb->picture.hue = 0x8000;
2114 pb->picture.whiteness = 0;
2115 pb->picture.depth = pb->win.depth;
2116
2117 pb->frame_stat=NULL;
2118 init_waitqueue_head(&pb->capq);
2119 for(i=0; i<MAX_GBUFFERS; i++) {
2120 pb->gbuf_idx[i] = PLANB_MAX_FBUF * i / PAGE_SIZE;
2121 pb->gwidth[i]=0;
2122 pb->gheight[i]=0;
2123 pb->gfmt[i]=0;
2124 pb->cap_cmd[i]=NULL;
2125#ifndef PLANB_GSCANLINE
2126 pb->l_fr_addr_idx[i] = MAX_GBUFFERS * (PLANB_MAX_FBUF
2127 / PAGE_SIZE + 1) + MAX_LNUM * i;
2128 pb->lsize[i] = 0;
2129 pb->lnum[i] = 0;
2130#endif
2131 }
2132 pb->rawbuf=NULL;
2133 pb->grabbing=0;
2134
2135 /* enable interrupts */
2136 out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
2137 pb->intr_mask = PLANB_FRM_IRQ;
2138 enable_irq(pb->irq);
2139
2140 if(video_register_device(&pb->video_dev, VFL_TYPE_GRABBER, video_nr)<0)
2141 return -1;
2142
2143 return 0;
2144}
2145
2146/*
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002147 * Scan for a PlanB controller, request the irq and map the io memory
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148 */
2149
2150static int find_planb(void)
2151{
2152 struct planb *pb;
2153 struct device_node *planb_devices;
2154 unsigned char dev_fn, confreg, bus;
2155 unsigned int old_base, new_base;
2156 unsigned int irq;
2157 struct pci_dev *pdev;
2158 int rc;
2159
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +11002160 if (!machine_is(powermac))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 return 0;
2162
2163 planb_devices = find_devices("planb");
2164 if (planb_devices == 0) {
2165 planb_num=0;
2166 printk(KERN_WARNING "PlanB: no device found!\n");
2167 return planb_num;
2168 }
2169
2170 if (planb_devices->next != NULL)
2171 printk(KERN_ERR "Warning: only using first PlanB device!\n");
2172 pb = &planbs[0];
2173 planb_num = 1;
2174
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002175 if (planb_devices->n_addrs != 1) {
2176 printk (KERN_WARNING "PlanB: expecting 1 address for planb "
2177 "(got %d)", planb_devices->n_addrs);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 return 0;
2179 }
2180
2181 if (planb_devices->n_intrs == 0) {
2182 printk(KERN_WARNING "PlanB: no intrs for device %s\n",
2183 planb_devices->full_name);
2184 return 0;
2185 } else {
2186 irq = planb_devices->intrs[0].line;
2187 }
2188
2189 /* Initialize PlanB's PCI registers */
2190
2191 /* There is a bug with the way OF assigns addresses
2192 to the devices behind the chaos bridge.
2193 control needs only 0x1000 of space, but decodes only
2194 the upper 16 bits. It therefore occupies a full 64K.
2195 OF assigns the planb controller memory within this space;
2196 so we need to change that here in order to access planb. */
2197
2198 /* We remap to 0xf1000000 in hope that nobody uses it ! */
2199
2200 bus = (planb_devices->addrs[0].space >> 16) & 0xff;
2201 dev_fn = (planb_devices->addrs[0].space >> 8) & 0xff;
2202 confreg = planb_devices->addrs[0].space & 0xff;
2203 old_base = planb_devices->addrs[0].address;
2204 new_base = 0xf1000000;
2205
2206 DEBUG("PlanB: Found on bus %d, dev %d, func %d, "
2207 "membase 0x%x (base reg. 0x%x)\n",
2208 bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg);
2209
2210 pdev = pci_find_slot (bus, dev_fn);
2211 if (!pdev) {
2212 printk(KERN_ERR "planb: cannot find slot\n");
2213 goto err_out;
2214 }
2215
2216 /* Enable response in memory space, bus mastering,
2217 use memory write and invalidate */
2218 rc = pci_enable_device(pdev);
2219 if (rc) {
2220 printk(KERN_ERR "planb: cannot enable PCI device %s\n",
2221 pci_name(pdev));
2222 goto err_out;
2223 }
2224 rc = pci_set_mwi(pdev);
2225 if (rc) {
2226 printk(KERN_ERR "planb: cannot enable MWI on PCI device %s\n",
2227 pci_name(pdev));
2228 goto err_out_disable;
2229 }
2230 pci_set_master(pdev);
2231
2232 /* Set the new base address */
2233 pci_write_config_dword (pdev, confreg, new_base);
2234
2235 planb_regs = (volatile struct planb_registers *)
2236 ioremap (new_base, 0x400);
2237 pb->planb_base = planb_regs;
2238 pb->planb_base_phys = (struct planb_registers *)new_base;
2239 pb->irq = irq;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002240
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241 return planb_num;
2242
2243err_out_disable:
2244 pci_disable_device(pdev);
2245err_out:
2246 /* FIXME handle error */ /* comment moved from pci_find_slot, above */
2247 return 0;
2248}
2249
2250static void release_planb(void)
2251{
2252 int i;
2253 struct planb *pb;
2254
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002255 for (i=0;i<planb_num; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002256 {
2257 pb=&planbs[i];
2258
2259 /* stop and flash DMAs unconditionally */
2260 planb_dbdma_stop(&pb->planb_base->ch2);
2261 planb_dbdma_stop(&pb->planb_base->ch1);
2262
2263 /* clear and free interrupts */
2264 pb->intr_mask = PLANB_CLR_IRQ;
2265 out_le32 (&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
2266 free_irq(pb->irq, pb);
2267
2268 /* make sure all allocated memory are freed */
2269 planb_prepare_close(pb);
2270
2271 printk(KERN_INFO "PlanB: unregistering with v4l\n");
2272 video_unregister_device(&pb->video_dev);
2273
2274 /* note that iounmap() does nothing on the PPC right now */
2275 iounmap ((void *)pb->planb_base);
2276 }
2277}
2278
2279static int __init init_planbs(void)
2280{
2281 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002282
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 if (find_planb()<=0)
2284 return -EIO;
2285
2286 for (i=0; i<planb_num; i++) {
2287 if (init_planb(&planbs[i])<0) {
2288 printk(KERN_ERR "PlanB: error registering device %d"
2289 " with v4l\n", i);
2290 release_planb();
2291 return -EIO;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002292 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 printk(KERN_INFO "PlanB: registered device %d with v4l\n", i);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002294 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 return 0;
2296}
2297
2298static void __exit exit_planbs(void)
2299{
2300 release_planb();
2301}
2302
2303module_init(init_planbs);
2304module_exit(exit_planbs);