blob: 0ef73d9d5848554054889c5b83540db000b3fc7a [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);
David Howells7d12e782006-10-05 14:55:46 +010094static void planb_irq(int, void *);
Linus Torvalds1da177e2005-04-16 15:20:36 -070095static 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 );
Robert P. J. Day5cbded52006-12-13 00:35:56 -0800141 if ((pb->rawbuf = kmalloc(npage
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 * 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);
Yoann Padioleaudd00cc42007-07-19 01:49:03 -0700356 if ((pb->priv_space = kzalloc (size, GFP_KERNEL)) == 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 pb->overlay_last1 = pb->ch1_cmd = (volatile struct dbdma_cmd *)
359 DBDMA_ALIGN (pb->priv_space);
360 pb->overlay_last2 = pb->ch2_cmd = pb->ch1_cmd + pb->tab_size;
361 pb->ch1_cmd_phys = virt_to_bus(pb->ch1_cmd);
362 pb->cap_cmd[0] = pb->ch2_cmd + pb->tab_size;
363 pb->pre_cmd[0] = pb->cap_cmd[0] + pb->tab_size * TAB_FACTOR;
364 for (i = 1; i < MAX_GBUFFERS; i++) {
365 pb->cap_cmd[i] = pb->pre_cmd[i-1] + PLANB_DUMMY;
366 pb->pre_cmd[i] = pb->cap_cmd[i] + pb->tab_size * TAB_FACTOR;
367 }
368 pb->frame_stat=(volatile unsigned int *)(pb->pre_cmd[MAX_GBUFFERS-1]
369 + PLANB_DUMMY);
370 pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS);
371
372 pb->rawbuf = NULL;
373 pb->rawbuf_size = 0;
374 pb->grabbing = 0;
375 for (i = 0; i < MAX_GBUFFERS; i++) {
376 pb->frame_stat[i] = GBUFFER_UNUSED;
377 pb->gwidth[i] = 0;
378 pb->gheight[i] = 0;
379 pb->gfmt[i] = 0;
380 pb->gnorm_switch[i] = 0;
381#ifndef PLANB_GSCANLINE
382 pb->lsize[i] = 0;
383 pb->lnum[i] = 0;
384#endif /* PLANB_GSCANLINE */
385 }
386 pb->gcount = 0;
387 pb->suspend = 0;
388 pb->last_fr = -999;
389 pb->prev_last_fr = -999;
390
391 /* Reset DMA controllers */
392 planb_dbdma_stop(&pb->planb_base->ch2);
393 planb_dbdma_stop(&pb->planb_base->ch1);
394
395 return 0;
396}
397
398static void planb_prepare_close(struct planb *pb)
399{
400 int i;
401
402 /* make sure the dma's are idle */
403 planb_dbdma_stop(&pb->planb_base->ch2);
404 planb_dbdma_stop(&pb->planb_base->ch1);
405 /* free kernel memory of command buffers */
406 if(pb->priv_space != 0) {
407 kfree (pb->priv_space);
408 pb->priv_space = 0;
409 pb->cmd_buff_inited = 0;
410 }
411 if(pb->rawbuf) {
412 for (i = 0; i < pb->rawbuf_size; i++) {
413 ClearPageReserved(virt_to_page(pb->rawbuf[i]));
414 free_pages((unsigned long)pb->rawbuf[i], 0);
415 }
416 kfree(pb->rawbuf);
417 }
418 pb->rawbuf = NULL;
419}
420
421/*****************************/
422/* overlay support functions */
423/*****************************/
424
425static inline int overlay_is_active(struct planb *pb)
426{
427 unsigned int size = pb->tab_size * sizeof(struct dbdma_cmd);
428 unsigned int caddr = (unsigned)in_le32(&pb->planb_base->ch1.cmdptr);
429
430 return (in_le32(&pb->overlay_last1->cmd_dep) == pb->ch1_cmd_phys)
431 && (caddr < (pb->ch1_cmd_phys + size))
432 && (caddr >= (unsigned)pb->ch1_cmd_phys);
433}
434
435static void overlay_start(struct planb *pb)
436{
437
438 DEBUG("PlanB: overlay_start()\n");
439
440 if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
441
442 DEBUG("PlanB: presumably, grabbing is in progress...\n");
443
444 planb_dbdma_stop(&pb->planb_base->ch2);
445 out_le32 (&pb->planb_base->ch2.cmdptr,
446 virt_to_bus(pb->ch2_cmd));
447 planb_dbdma_restart(&pb->planb_base->ch2);
448 st_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
449 tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
450 DBDMA_NOP | BR_ALWAYS,
451 virt_to_bus(pb->ch1_cmd));
452 eieio();
453 pb->prev_last_fr = pb->last_fr;
454 pb->last_fr = -2;
455 if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
456 IDEBUG("PlanB: became inactive "
457 "in the mean time... reactivating\n");
458 planb_dbdma_stop(&pb->planb_base->ch1);
459 out_le32 (&pb->planb_base->ch1.cmdptr,
460 virt_to_bus(pb->ch1_cmd));
461 planb_dbdma_restart(&pb->planb_base->ch1);
462 }
463 } else {
464
465 DEBUG("PlanB: currently idle, so can do whatever\n");
466
467 planb_dbdma_stop(&pb->planb_base->ch2);
468 planb_dbdma_stop(&pb->planb_base->ch1);
469 st_le32 (&pb->planb_base->ch2.cmdptr,
470 virt_to_bus(pb->ch2_cmd));
471 st_le32 (&pb->planb_base->ch1.cmdptr,
472 virt_to_bus(pb->ch1_cmd));
473 out_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
474 planb_dbdma_restart(&pb->planb_base->ch2);
475 planb_dbdma_restart(&pb->planb_base->ch1);
476 pb->last_fr = -1;
477 }
478 return;
479}
480
481static void overlay_stop(struct planb *pb)
482{
483 DEBUG("PlanB: overlay_stop()\n");
484
485 if(pb->last_fr == -1) {
486
487 DEBUG("PlanB: no grabbing, it seems...\n");
488
489 planb_dbdma_stop(&pb->planb_base->ch2);
490 planb_dbdma_stop(&pb->planb_base->ch1);
491 pb->last_fr = -999;
492 } else if(pb->last_fr == -2) {
493 unsigned int cmd_dep;
494 tab_cmd_dbdma(pb->cap_cmd[pb->prev_last_fr], DBDMA_STOP, 0);
495 eieio();
496 cmd_dep = (unsigned int)in_le32(&pb->overlay_last1->cmd_dep);
497 if(overlay_is_active(pb)) {
498
499 DEBUG("PlanB: overlay is currently active\n");
500
501 planb_dbdma_stop(&pb->planb_base->ch2);
502 planb_dbdma_stop(&pb->planb_base->ch1);
503 if(cmd_dep != pb->ch1_cmd_phys) {
504 out_le32(&pb->planb_base->ch1.cmdptr,
505 virt_to_bus(pb->overlay_last1));
506 planb_dbdma_restart(&pb->planb_base->ch1);
507 }
508 }
509 pb->last_fr = pb->prev_last_fr;
510 pb->prev_last_fr = -999;
511 }
512 return;
513}
514
515static void suspend_overlay(struct planb *pb)
516{
517 int fr = -1;
518 struct dbdma_cmd last;
519
520 DEBUG("PlanB: suspend_overlay: %d\n", pb->suspend);
521
522 if(pb->suspend++)
523 return;
524 if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
525 if(pb->last_fr == -2) {
526 fr = pb->prev_last_fr;
527 memcpy(&last, (void*)pb->last_cmd[fr], sizeof(last));
528 tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
529 }
530 if(overlay_is_active(pb)) {
531 planb_dbdma_stop(&pb->planb_base->ch2);
532 planb_dbdma_stop(&pb->planb_base->ch1);
533 pb->suspended.overlay = 1;
534 pb->suspended.frame = fr;
535 memcpy(&pb->suspended.cmd, &last, sizeof(last));
536 return;
537 }
538 }
539 pb->suspended.overlay = 0;
540 pb->suspended.frame = fr;
541 memcpy(&pb->suspended.cmd, &last, sizeof(last));
542 return;
543}
544
545static void resume_overlay(struct planb *pb)
546{
547
548 DEBUG("PlanB: resume_overlay: %d\n", pb->suspend);
549
550 if(pb->suspend > 1)
551 return;
552 if(pb->suspended.frame != -1) {
553 memcpy((void*)pb->last_cmd[pb->suspended.frame],
554 &pb->suspended.cmd, sizeof(pb->suspended.cmd));
555 }
556 if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
557 goto finish;
558 }
559 if(pb->suspended.overlay) {
560
561 DEBUG("PlanB: overlay being resumed\n");
562
563 st_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
564 st_le16 (&pb->ch2_cmd->command, DBDMA_NOP);
565 /* Set command buffer addresses */
566 st_le32(&pb->planb_base->ch1.cmdptr,
567 virt_to_bus(pb->overlay_last1));
568 out_le32(&pb->planb_base->ch2.cmdptr,
569 virt_to_bus(pb->overlay_last2));
570 /* Start the DMA controller */
571 out_le32 (&pb->planb_base->ch2.control,
572 PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
573 out_le32 (&pb->planb_base->ch1.control,
574 PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
575 } else if(pb->suspended.frame != -1) {
576 out_le32(&pb->planb_base->ch1.cmdptr,
577 virt_to_bus(pb->last_cmd[pb->suspended.frame]));
578 out_le32 (&pb->planb_base->ch1.control,
579 PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
580 }
581
582finish:
583 pb->suspend--;
584 wake_up_interruptible(&pb->suspendq);
585}
586
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300587static void add_clip(struct planb *pb, struct video_clip *clip)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588{
589 volatile unsigned char *base;
590 int xc = clip->x, yc = clip->y;
591 int wc = clip->width, hc = clip->height;
592 int ww = pb->win.width, hw = pb->win.height;
593 int x, y, xtmp1, xtmp2;
594
595 DEBUG("PlanB: clip %dx%d+%d+%d\n", wc, hc, xc, yc);
596
597 if(xc < 0) {
598 wc += xc;
599 xc = 0;
600 }
601 if(yc < 0) {
602 hc += yc;
603 yc = 0;
604 }
605 if(xc + wc > ww)
606 wc = ww - xc;
607 if(wc <= 0) /* Nothing to do */
608 return;
609 if(yc + hc > hw)
610 hc = hw - yc;
611
612 for (y = yc; y < yc+hc; y++) {
613 xtmp1=xc>>3;
614 xtmp2=(xc+wc)>>3;
615 base = pb->mask + y*96;
616 if(xc != 0 || wc >= 8)
617 *(base + xtmp1) &= (unsigned char)(0x00ff &
618 (0xff00 >> (xc&7)));
619 for (x = xtmp1 + 1; x < xtmp2; x++) {
620 *(base + x) = 0;
621 }
622 if(xc < (ww & ~0x7))
623 *(base + xtmp2) &= (unsigned char)(0x00ff >>
624 ((xc+wc) & 7));
625 }
626
627 return;
628}
629
630static void fill_cmd_buff(struct planb *pb)
631{
632 int restore = 0;
633 volatile struct dbdma_cmd last;
634
635 DEBUG("PlanB: fill_cmd_buff()\n");
636
637 if(pb->overlay_last1 != pb->ch1_cmd) {
638 restore = 1;
639 last = *(pb->overlay_last1);
640 }
641 memset ((void *) pb->ch1_cmd, 0, 2 * pb->tab_size
642 * sizeof(struct dbdma_cmd));
643 cmd_buff (pb);
644 if(restore)
645 *(pb->overlay_last1) = last;
646 if(pb->suspended.overlay) {
647 unsigned long jump_addr = in_le32(&pb->overlay_last1->cmd_dep);
648 if(jump_addr != pb->ch1_cmd_phys) {
649 int i;
650
651 DEBUG("PlanB: adjusting ch1's jump address\n");
652
653 for(i = 0; i < MAX_GBUFFERS; i++) {
654 if(pb->need_pre_capture[i]) {
655 if(jump_addr == virt_to_bus(pb->pre_cmd[i]))
656 goto found;
657 } else {
658 if(jump_addr == virt_to_bus(pb->cap_cmd[i]))
659 goto found;
660 }
661 }
662
663 DEBUG("PlanB: not found...\n");
664
665 goto out;
666found:
667 if(pb->need_pre_capture[i])
668 out_le32(&pb->pre_cmd[i]->phy_addr,
669 virt_to_bus(pb->overlay_last1));
670 else
671 out_le32(&pb->cap_cmd[i]->phy_addr,
672 virt_to_bus(pb->overlay_last1));
673 }
674 }
675out:
676 pb->cmd_buff_inited = 1;
677
678 return;
679}
680
681static void cmd_buff(struct planb *pb)
682{
683 int i, bpp, count, nlines, stepsize, interlace;
684 unsigned long base, jump, addr_com, addr_dep;
685 volatile struct dbdma_cmd *c1 = pb->ch1_cmd;
686 volatile struct dbdma_cmd *c2 = pb->ch2_cmd;
687
688 interlace = pb->win.interlace;
689 bpp = pb->win.bpp;
690 count = (bpp * ((pb->win.x + pb->win.width > pb->win.swidth) ?
691 (pb->win.swidth - pb->win.x) : pb->win.width));
692 nlines = ((pb->win.y + pb->win.height > pb->win.sheight) ?
693 (pb->win.sheight - pb->win.y) : pb->win.height);
694
695 /* Do video in: */
696
697 /* Preamble commands: */
698 addr_com = virt_to_bus(c1);
699 addr_dep = virt_to_bus(&c1->cmd_dep);
700 tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
701 jump = virt_to_bus(c1+16); /* 14 by cmd_geo_setup() and 2 for padding */
702 if((c1 = cmd_geo_setup(c1, pb->win.width, pb->win.height, interlace,
703 bpp, 1, pb)) == NULL) {
704 printk(KERN_WARNING "PlanB: encountered serious problems\n");
705 tab_cmd_dbdma(pb->ch1_cmd + 1, DBDMA_STOP, 0);
706 tab_cmd_dbdma(pb->ch2_cmd + 1, DBDMA_STOP, 0);
707 return;
708 }
709 tab_cmd_store(c1++, addr_com, (unsigned)(DBDMA_NOP | BR_ALWAYS) << 16);
710 tab_cmd_store(c1++, addr_dep, jump);
711 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
712 PLANB_SET(FIELD_SYNC));
713 /* (1) wait for field sync to be set */
714 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
715 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
716 PLANB_SET(ODD_FIELD));
717 /* wait for field sync to be cleared */
718 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
719 /* if not odd field, wait until field sync is set again */
720 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
721 /* assert ch_sync to ch2 */
722 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control),
723 PLANB_SET(CH_SYNC));
724 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
725 PLANB_SET(DMA_ABORT));
726
727 base = (pb->frame_buffer_phys + pb->offset + pb->win.y * (pb->win.bpl
728 + pb->win.pad) + pb->win.x * bpp);
729
730 if (interlace) {
731 stepsize = 2;
732 jump = virt_to_bus(c1 + (nlines + 1) / 2);
733 } else {
734 stepsize = 1;
735 jump = virt_to_bus(c1 + nlines);
736 }
737
738 /* even field data: */
739 for (i=0; i < nlines; i += stepsize, c1++)
740 tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET,
741 count, base + i * (pb->win.bpl + pb->win.pad), jump);
742
743 /* For non-interlaced, we use even fields only */
744 if (!interlace)
745 goto cmd_tab_data_end;
746
747 /* Resync to odd field */
748 /* (2) wait for field sync to be set */
749 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
750 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
751 PLANB_SET(ODD_FIELD));
752 /* wait for field sync to be cleared */
753 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
754 /* if not odd field, wait until field sync is set again */
755 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
756 /* assert ch_sync to ch2 */
757 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control),
758 PLANB_SET(CH_SYNC));
759 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
760 PLANB_SET(DMA_ABORT));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -0300761
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 /* odd field data: */
763 jump = virt_to_bus(c1 + nlines / 2);
764 for (i=1; i < nlines; i += stepsize, c1++)
765 tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
766 base + i * (pb->win.bpl + pb->win.pad), jump);
767
768 /* And jump back to the start */
769cmd_tab_data_end:
770 pb->overlay_last1 = c1; /* keep a pointer to the last command */
771 tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch1_cmd));
772
773 /* Clipmask command buffer */
774
775 /* Preamble commands: */
776 tab_cmd_dbdma(c2++, DBDMA_NOP, 0);
777 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel),
778 PLANB_SET(CH_SYNC));
779 /* wait until ch1 asserts ch_sync */
780 tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0);
781 /* clear ch_sync asserted by ch1 */
782 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.control),
783 PLANB_CLR(CH_SYNC));
784 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel),
785 PLANB_SET(FIELD_SYNC));
786 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
787 PLANB_SET(ODD_FIELD));
788
789 /* jump to end of even field if appropriate */
790 /* this points to (interlace)? pos. C: pos. B */
791 jump = (interlace) ? virt_to_bus(c2 + (nlines + 1) / 2 + 2):
792 virt_to_bus(c2 + nlines + 2);
793 /* if odd field, skip over to odd field clipmasking */
794 tab_cmd_dbdma(c2++, DBDMA_NOP | BR_IFSET, jump);
795
796 /* even field mask: */
797 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
798 PLANB_SET(DMA_ABORT));
799 /* this points to pos. B */
800 jump = (interlace) ? virt_to_bus(c2 + nlines + 1):
801 virt_to_bus(c2 + nlines);
802 base = virt_to_bus(pb->mask);
803 for (i=0; i < nlines; i += stepsize, c2++)
804 tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96,
805 base + i * 96, jump);
806
807 /* For non-interlaced, we use only even fields */
808 if(!interlace)
809 goto cmd_tab_mask_end;
810
811 /* odd field mask: */
812/* C */ tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
813 PLANB_SET(DMA_ABORT));
814 /* this points to pos. B */
815 jump = virt_to_bus(c2 + nlines / 2);
816 base = virt_to_bus(pb->mask);
817 for (i=1; i < nlines; i += 2, c2++) /* abort if set */
818 tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96,
819 base + i * 96, jump);
820
821 /* Inform channel 1 and jump back to start */
822cmd_tab_mask_end:
823 /* ok, I just realized this is kind of flawed. */
824 /* this part is reached only after odd field clipmasking. */
825 /* wanna clean up? */
826 /* wait for field sync to be set */
827 /* corresponds to fsync (1) of ch1 */
828/* B */ tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0);
829 /* restart ch1, meant to clear any dead bit or something */
830 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control),
831 PLANB_CLR(RUN));
832 tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control),
833 PLANB_SET(RUN));
834 pb->overlay_last2 = c2; /* keep a pointer to the last command */
835 /* start over even field clipmasking */
836 tab_cmd_dbdma(c2, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch2_cmd));
837
838 eieio();
839 return;
840}
841
842/*********************************/
843/* grabdisplay support functions */
844/*********************************/
845
846static int palette2fmt[] = {
Michael Krufkyb930e1d2007-08-27 18:16:54 -0300847 0,
848 PLANB_GRAY,
849 0,
850 0,
851 0,
852 PLANB_COLOUR32,
853 PLANB_COLOUR15,
854 0,
855 0,
856 0,
857 0,
858 0,
859 0,
860 0,
861 0,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862};
863
864#define PLANB_PALETTE_MAX 15
865
866static int vgrab(struct planb *pb, struct video_mmap *mp)
867{
868 unsigned int fr = mp->frame;
869 unsigned int format;
870
871 if(pb->rawbuf==NULL) {
872 int err;
873 if((err=grabbuf_alloc(pb)))
874 return err;
875 }
876
877 IDEBUG("PlanB: grab %d: %dx%d(%u)\n", pb->grabbing,
878 mp->width, mp->height, fr);
879
880 if(pb->grabbing >= MAX_GBUFFERS)
881 return -ENOBUFS;
882 if(fr > (MAX_GBUFFERS - 1) || fr < 0)
883 return -EINVAL;
884 if(mp->height <= 0 || mp->width <= 0)
885 return -EINVAL;
886 if(mp->format < 0 || mp->format >= PLANB_PALETTE_MAX)
887 return -EINVAL;
888 if((format = palette2fmt[mp->format]) == 0)
889 return -EINVAL;
890 if (mp->height * mp->width * format > PLANB_MAX_FBUF) /* format = bpp */
891 return -EINVAL;
892
893 planb_lock(pb);
894 if(mp->width != pb->gwidth[fr] || mp->height != pb->gheight[fr] ||
895 format != pb->gfmt[fr] || (pb->gnorm_switch[fr])) {
896 int i;
897#ifndef PLANB_GSCANLINE
898 unsigned int osize = pb->gwidth[fr] * pb->gheight[fr]
899 * pb->gfmt[fr];
900 unsigned int nsize = mp->width * mp->height * format;
901#endif
902
903 IDEBUG("PlanB: gwidth = %d, gheight = %d, mp->format = %u\n",
904 mp->width, mp->height, mp->format);
905
906#ifndef PLANB_GSCANLINE
907 if(pb->gnorm_switch[fr])
908 nsize = 0;
909 if (nsize < osize) {
910 for(i = pb->gbuf_idx[fr]; osize > 0; i++) {
911 memset((void *)pb->rawbuf[i], 0, PAGE_SIZE);
912 osize -= PAGE_SIZE;
913 }
914 }
915 for(i = pb->l_fr_addr_idx[fr]; i < pb->l_fr_addr_idx[fr]
916 + pb->lnum[fr]; i++)
917 memset((void *)pb->rawbuf[i], 0, PAGE_SIZE);
918#else
919/* XXX TODO */
920/*
921 if(pb->gnorm_switch[fr])
922 memset((void *)pb->gbuffer[fr], 0,
923 pb->gbytes_per_line * pb->gheight[fr]);
924 else {
925 if(mp->
926 for(i = 0; i < pb->gheight[fr]; i++) {
927 memset((void *)(pb->gbuffer[fr]
928 + pb->gbytes_per_line * i
929 }
930 }
931*/
932#endif
933 pb->gwidth[fr] = mp->width;
934 pb->gheight[fr] = mp->height;
935 pb->gfmt[fr] = format;
936 pb->last_cmd[fr] = setup_grab_cmd(fr, pb);
937 planb_pre_capture(fr, pb->gfmt[fr], pb); /* gfmt = bpp */
938 pb->need_pre_capture[fr] = 1;
939 pb->gnorm_switch[fr] = 0;
940 } else
941 pb->need_pre_capture[fr] = 0;
942 pb->frame_stat[fr] = GBUFFER_GRABBING;
943 if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
944
945 IDEBUG("PlanB: ch1 inactive, initiating grabbing\n");
946
947 planb_dbdma_stop(&pb->planb_base->ch1);
948 if(pb->need_pre_capture[fr]) {
949
950 IDEBUG("PlanB: padding pre-capture sequence\n");
951
952 out_le32 (&pb->planb_base->ch1.cmdptr,
953 virt_to_bus(pb->pre_cmd[fr]));
954 } else {
955 tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
956 tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
957 /* let's be on the safe side. here is not timing critical. */
958 tab_cmd_dbdma((pb->cap_cmd[fr] + 1), DBDMA_NOP, 0);
959 out_le32 (&pb->planb_base->ch1.cmdptr,
960 virt_to_bus(pb->cap_cmd[fr]));
961 }
962 planb_dbdma_restart(&pb->planb_base->ch1);
963 pb->last_fr = fr;
964 } else {
965 int i;
966
967 IDEBUG("PlanB: ch1 active, grabbing being queued\n");
968
969 if((pb->last_fr == -1) || ((pb->last_fr == -2) &&
970 overlay_is_active(pb))) {
971
972 IDEBUG("PlanB: overlay is active, grabbing defered\n");
973
974 tab_cmd_dbdma(pb->last_cmd[fr],
975 DBDMA_NOP | BR_ALWAYS,
976 virt_to_bus(pb->ch1_cmd));
977 if(pb->need_pre_capture[fr]) {
978
979 IDEBUG("PlanB: padding pre-capture sequence\n");
980
981 tab_cmd_store(pb->pre_cmd[fr],
982 virt_to_bus(&pb->overlay_last1->cmd_dep),
983 virt_to_bus(pb->ch1_cmd));
984 eieio();
985 out_le32 (&pb->overlay_last1->cmd_dep,
986 virt_to_bus(pb->pre_cmd[fr]));
987 } else {
988 tab_cmd_store(pb->cap_cmd[fr],
989 virt_to_bus(&pb->overlay_last1->cmd_dep),
990 virt_to_bus(pb->ch1_cmd));
991 tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
992 DBDMA_NOP, 0);
993 eieio();
994 out_le32 (&pb->overlay_last1->cmd_dep,
995 virt_to_bus(pb->cap_cmd[fr]));
996 }
997 for(i = 0; overlay_is_active(pb) && i < 999; i++)
998 IDEBUG("PlanB: waiting for overlay done\n");
999 tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0);
1000 pb->prev_last_fr = fr;
1001 pb->last_fr = -2;
1002 } else if(pb->last_fr == -2) {
1003
1004 IDEBUG("PlanB: mixed mode detected, grabbing"
1005 " will be done before activating overlay\n");
1006
1007 tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0);
1008 if(pb->need_pre_capture[fr]) {
1009
1010 IDEBUG("PlanB: padding pre-capture sequence\n");
1011
1012 tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr],
1013 DBDMA_NOP | BR_ALWAYS,
1014 virt_to_bus(pb->pre_cmd[fr]));
1015 eieio();
1016 } else {
1017 tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
1018 if(pb->gwidth[pb->prev_last_fr] !=
1019 pb->gwidth[fr]
1020 || pb->gheight[pb->prev_last_fr] !=
1021 pb->gheight[fr]
1022 || pb->gfmt[pb->prev_last_fr] !=
1023 pb->gfmt[fr])
1024 tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
1025 DBDMA_NOP, 0);
1026 else
1027 tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
1028 DBDMA_NOP | BR_ALWAYS,
1029 virt_to_bus(pb->cap_cmd[fr] + 16));
1030 tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr],
1031 DBDMA_NOP | BR_ALWAYS,
1032 virt_to_bus(pb->cap_cmd[fr]));
1033 eieio();
1034 }
1035 tab_cmd_dbdma(pb->last_cmd[fr],
1036 DBDMA_NOP | BR_ALWAYS,
1037 virt_to_bus(pb->ch1_cmd));
1038 eieio();
1039 pb->prev_last_fr = fr;
1040 pb->last_fr = -2;
1041 } else {
1042
1043 IDEBUG("PlanB: active grabbing session detected\n");
1044
1045 if(pb->need_pre_capture[fr]) {
1046
1047 IDEBUG("PlanB: padding pre-capture sequence\n");
1048
1049 tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
1050 DBDMA_NOP | BR_ALWAYS,
1051 virt_to_bus(pb->pre_cmd[fr]));
1052 eieio();
1053 } else {
1054 tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
1055 tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
1056 if(pb->gwidth[pb->last_fr] != pb->gwidth[fr]
1057 || pb->gheight[pb->last_fr] !=
1058 pb->gheight[fr]
1059 || pb->gfmt[pb->last_fr] !=
1060 pb->gfmt[fr])
1061 tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
1062 DBDMA_NOP, 0);
1063 else
1064 tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
1065 DBDMA_NOP | BR_ALWAYS,
1066 virt_to_bus(pb->cap_cmd[fr] + 16));
1067 tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
1068 DBDMA_NOP | BR_ALWAYS,
1069 virt_to_bus(pb->cap_cmd[fr]));
1070 eieio();
1071 }
1072 pb->last_fr = fr;
1073 }
1074 if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
1075
1076 IDEBUG("PlanB: became inactive in the mean time..."
1077 "reactivating\n");
1078
1079 planb_dbdma_stop(&pb->planb_base->ch1);
1080 out_le32 (&pb->planb_base->ch1.cmdptr,
1081 virt_to_bus(pb->cap_cmd[fr]));
1082 planb_dbdma_restart(&pb->planb_base->ch1);
1083 }
1084 }
1085 pb->grabbing++;
1086 planb_unlock(pb);
1087
1088 return 0;
1089}
1090
1091static void planb_pre_capture(int fr, int bpp, struct planb *pb)
1092{
1093 volatile struct dbdma_cmd *c1 = pb->pre_cmd[fr];
1094 int interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0;
1095
1096 tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
1097 if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace,
1098 bpp, 0, pb)) == NULL) {
1099 printk(KERN_WARNING "PlanB: encountered some problems\n");
1100 tab_cmd_dbdma(pb->pre_cmd[fr] + 1, DBDMA_STOP, 0);
1101 return;
1102 }
1103 /* Sync to even field */
1104 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
1105 PLANB_SET(FIELD_SYNC));
1106 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
1107 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1108 PLANB_SET(ODD_FIELD));
1109 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
1110 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
1111 tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0);
1112 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1113 PLANB_SET(DMA_ABORT));
1114 /* For non-interlaced, we use even fields only */
1115 if (pb->gheight[fr] <= pb->maxlines/2)
1116 goto cmd_tab_data_end;
1117 /* Sync to odd field */
1118 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
1119 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1120 PLANB_SET(ODD_FIELD));
1121 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
1122 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
1123 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1124 PLANB_SET(DMA_ABORT));
1125cmd_tab_data_end:
1126 tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->cap_cmd[fr]));
1127
1128 eieio();
1129}
1130
1131static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb)
1132{
1133 int i, bpp, count, nlines, stepsize, interlace;
1134#ifdef PLANB_GSCANLINE
1135 int scanline;
1136#else
1137 int nlpp, leftover1;
1138 unsigned long base;
1139#endif
1140 unsigned long jump;
1141 int pagei;
1142 volatile struct dbdma_cmd *c1;
1143 volatile struct dbdma_cmd *jump_addr;
1144
1145 c1 = pb->cap_cmd[fr];
1146 interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0;
1147 bpp = pb->gfmt[fr]; /* gfmt = bpp */
1148 count = bpp * pb->gwidth[fr];
1149 nlines = pb->gheight[fr];
1150#ifdef PLANB_GSCANLINE
1151 scanline = pb->gbytes_per_line;
1152#else
1153 pb->lsize[fr] = count;
1154 pb->lnum[fr] = 0;
1155#endif
1156
1157 /* Do video in: */
1158
1159 /* Preamble commands: */
1160 tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
1161 tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(c1 + 16)); c1++;
1162 if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace,
1163 bpp, 0, pb)) == NULL) {
1164 printk(KERN_WARNING "PlanB: encountered serious problems\n");
1165 tab_cmd_dbdma(pb->cap_cmd[fr] + 1, DBDMA_STOP, 0);
1166 return (pb->cap_cmd[fr] + 2);
1167 }
1168 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
1169 PLANB_SET(FIELD_SYNC));
1170 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
1171 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1172 PLANB_SET(ODD_FIELD));
1173 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
1174 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
1175 tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0);
1176 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1177 PLANB_SET(DMA_ABORT));
1178
1179 if (interlace) {
1180 stepsize = 2;
1181 jump_addr = c1 + TAB_FACTOR * (nlines + 1) / 2;
1182 } else {
1183 stepsize = 1;
1184 jump_addr = c1 + TAB_FACTOR * nlines;
1185 }
1186 jump = virt_to_bus(jump_addr);
1187
1188 /* even field data: */
1189
1190 pagei = pb->gbuf_idx[fr];
1191#ifdef PLANB_GSCANLINE
1192 for (i = 0; i < nlines; i += stepsize) {
1193 tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
1194 virt_to_bus(pb->rawbuf[pagei
1195 + i * scanline / PAGE_SIZE]), jump);
1196 }
1197#else
1198 i = 0;
1199 leftover1 = 0;
1200 do {
1201 int j;
1202
1203 base = virt_to_bus(pb->rawbuf[pagei]);
1204 nlpp = (PAGE_SIZE - leftover1) / count / stepsize;
1205 for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++)
1206 tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET,
1207 count, base + count * j * stepsize + leftover1, jump);
1208 if(i < nlines) {
1209 int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1;
1210
1211 if(lov0 == 0)
1212 leftover1 = 0;
1213 else {
1214 if(lov0 >= count) {
1215 tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, base
1216 + count * nlpp * stepsize + leftover1, jump);
1217 } else {
1218 pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei]
1219 + count * nlpp * stepsize + leftover1;
1220 pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1;
1221 pb->l_to_next_size[fr][pb->lnum[fr]] = count - lov0;
1222 tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
1223 virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr]
1224 + pb->lnum[fr]]), jump);
1225 if(++pb->lnum[fr] > MAX_LNUM)
1226 pb->lnum[fr]--;
1227 }
1228 leftover1 = count * stepsize - lov0;
1229 i += stepsize;
1230 }
1231 }
1232 pagei++;
1233 } while(i < nlines);
1234 tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump);
1235 c1 = jump_addr;
1236#endif /* PLANB_GSCANLINE */
1237
1238 /* For non-interlaced, we use even fields only */
1239 if (!interlace)
1240 goto cmd_tab_data_end;
1241
1242 /* Sync to odd field */
1243 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
1244 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1245 PLANB_SET(ODD_FIELD));
1246 tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
1247 tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
1248 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
1249 PLANB_SET(DMA_ABORT));
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001250
Linus Torvalds1da177e2005-04-16 15:20:36 -07001251 /* odd field data: */
1252 jump_addr = c1 + TAB_FACTOR * nlines / 2;
1253 jump = virt_to_bus(jump_addr);
1254#ifdef PLANB_GSCANLINE
1255 for (i = 1; i < nlines; i += stepsize) {
1256 tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
1257 virt_to_bus(pb->rawbuf[pagei
1258 + i * scanline / PAGE_SIZE]), jump);
1259 }
1260#else
1261 i = 1;
1262 leftover1 = 0;
1263 pagei = pb->gbuf_idx[fr];
1264 if(nlines <= 1)
1265 goto skip;
1266 do {
1267 int j;
1268
1269 base = virt_to_bus(pb->rawbuf[pagei]);
1270 nlpp = (PAGE_SIZE - leftover1) / count / stepsize;
1271 if(leftover1 >= count) {
1272 tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
1273 base + leftover1 - count, jump);
1274 i += stepsize;
1275 }
1276 for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++)
1277 tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
1278 base + count * (j * stepsize + 1) + leftover1, jump);
1279 if(i < nlines) {
1280 int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1;
1281
1282 if(lov0 == 0)
1283 leftover1 = 0;
1284 else {
1285 if(lov0 > count) {
1286 pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei]
1287 + count * (nlpp * stepsize + 1) + leftover1;
1288 pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1;
1289 pb->l_to_next_size[fr][pb->lnum[fr]] = count * stepsize
1290 - lov0;
1291 tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
1292 virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr]
1293 + pb->lnum[fr]]), jump);
1294 if(++pb->lnum[fr] > MAX_LNUM)
1295 pb->lnum[fr]--;
1296 i += stepsize;
1297 }
1298 leftover1 = count * stepsize - lov0;
1299 }
1300 }
1301 pagei++;
1302 } while(i < nlines);
1303skip:
1304 tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump);
1305 c1 = jump_addr;
1306#endif /* PLANB_GSCANLINE */
1307
1308cmd_tab_data_end:
1309 tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->intr_stat),
1310 (fr << 9) | PLANB_FRM_IRQ | PLANB_GEN_IRQ);
1311 /* stop it */
1312 tab_cmd_dbdma(c1, DBDMA_STOP, 0);
1313
1314 eieio();
1315 return c1;
1316}
1317
David Howells7d12e782006-10-05 14:55:46 +01001318static void planb_irq(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001319{
1320 unsigned int stat, astat;
1321 struct planb *pb = (struct planb *)dev_id;
1322
1323 IDEBUG("PlanB: planb_irq()\n");
1324
1325 /* get/clear interrupt status bits */
1326 eieio();
1327 stat = in_le32(&pb->planb_base->intr_stat);
1328 astat = stat & pb->intr_mask;
1329 out_le32(&pb->planb_base->intr_stat, PLANB_FRM_IRQ
1330 & ~astat & stat & ~PLANB_GEN_IRQ);
1331 IDEBUG("PlanB: stat = %X, astat = %X\n", stat, astat);
1332
1333 if(astat & PLANB_FRM_IRQ) {
1334 unsigned int fr = stat >> 9;
1335#ifndef PLANB_GSCANLINE
1336 int i;
1337#endif
1338 IDEBUG("PlanB: PLANB_FRM_IRQ\n");
1339
1340 pb->gcount++;
1341
1342 IDEBUG("PlanB: grab %d: fr = %d, gcount = %d\n",
1343 pb->grabbing, fr, pb->gcount);
1344#ifndef PLANB_GSCANLINE
1345 IDEBUG("PlanB: %d * %d bytes are being copied over\n",
1346 pb->lnum[fr], pb->lsize[fr]);
1347 for(i = 0; i < pb->lnum[fr]; i++) {
1348 int first = pb->lsize[fr] - pb->l_to_next_size[fr][i];
1349
1350 memcpy(pb->l_to_addr[fr][i],
1351 pb->rawbuf[pb->l_fr_addr_idx[fr] + i],
1352 first);
1353 memcpy(pb->rawbuf[pb->l_to_next_idx[fr][i]],
1354 pb->rawbuf[pb->l_fr_addr_idx[fr] + i] + first,
1355 pb->l_to_next_size[fr][i]);
1356 }
1357#endif
1358 pb->frame_stat[fr] = GBUFFER_DONE;
1359 pb->grabbing--;
1360 wake_up_interruptible(&pb->capq);
1361 return;
1362 }
1363 /* incorrect interrupts? */
1364 pb->intr_mask = PLANB_CLR_IRQ;
1365 out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
1366 printk(KERN_ERR "PlanB: IRQ lockup, cleared intrrupts"
1367 " unconditionally\n");
1368}
1369
1370/*******************************
1371 * Device Operations functions *
1372 *******************************/
1373
1374static int planb_open(struct video_device *dev, int mode)
1375{
1376 struct planb *pb = (struct planb *)dev;
1377
1378 if (pb->user == 0) {
1379 int err;
1380 if((err = planb_prepare_open(pb)) != 0)
1381 return err;
1382 }
1383 pb->user++;
1384
1385 DEBUG("PlanB: device opened\n");
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001386 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001387}
1388
1389static void planb_close(struct video_device *dev)
1390{
1391 struct planb *pb = (struct planb *)dev;
1392
1393 if(pb->user < 1) /* ??? */
1394 return;
1395 planb_lock(pb);
1396 if (pb->user == 1) {
1397 if (pb->overlay) {
1398 planb_dbdma_stop(&pb->planb_base->ch2);
1399 planb_dbdma_stop(&pb->planb_base->ch1);
1400 pb->overlay = 0;
1401 }
1402 planb_prepare_close(pb);
1403 }
1404 pb->user--;
1405 planb_unlock(pb);
1406
1407 DEBUG("PlanB: device closed\n");
1408}
1409
1410static long planb_read(struct video_device *v, char *buf, unsigned long count,
1411 int nonblock)
1412{
1413 DEBUG("planb: read request\n");
1414 return -EINVAL;
1415}
1416
1417static long planb_write(struct video_device *v, const char *buf,
1418 unsigned long count, int nonblock)
1419{
1420 DEBUG("planb: write request\n");
1421 return -EINVAL;
1422}
1423
1424static int planb_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
1425{
1426 struct planb *pb=(struct planb *)dev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001427
Linus Torvalds1da177e2005-04-16 15:20:36 -07001428 switch (cmd)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001429 {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 case VIDIOCGCAP:
1431 {
1432 struct video_capability b;
1433
1434 DEBUG("PlanB: IOCTL VIDIOCGCAP\n");
1435
1436 strcpy (b.name, pb->video_dev.name);
1437 b.type = VID_TYPE_OVERLAY | VID_TYPE_CLIPPING |
1438 VID_TYPE_FRAMERAM | VID_TYPE_SCALES |
1439 VID_TYPE_CAPTURE;
1440 b.channels = 2; /* composite & svhs */
1441 b.audios = 0;
1442 b.maxwidth = PLANB_MAXPIXELS;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001443 b.maxheight = PLANB_MAXLINES;
1444 b.minwidth = 32; /* wild guess */
1445 b.minheight = 32;
1446 if (copy_to_user(arg,&b,sizeof(b)))
1447 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001448 return 0;
1449 }
1450 case VIDIOCSFBUF:
1451 {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001452 struct video_buffer v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 unsigned short bpp;
1454 unsigned int fmt;
1455
1456 DEBUG("PlanB: IOCTL VIDIOCSFBUF\n");
1457
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001458 if (!capable(CAP_SYS_ADMIN)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459 || !capable(CAP_SYS_RAWIO))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001460 return -EPERM;
1461 if (copy_from_user(&v, arg,sizeof(v)))
1462 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463 planb_lock(pb);
1464 switch(v.depth) {
1465 case 8:
1466 bpp = 1;
1467 fmt = PLANB_GRAY;
1468 break;
1469 case 15:
1470 case 16:
1471 bpp = 2;
1472 fmt = PLANB_COLOUR15;
1473 break;
1474 case 24:
1475 case 32:
1476 bpp = 4;
1477 fmt = PLANB_COLOUR32;
1478 break;
1479 default:
1480 planb_unlock(pb);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001481 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001482 }
1483 if (bpp * v.width > v.bytesperline) {
1484 planb_unlock(pb);
1485 return -EINVAL;
1486 }
1487 pb->win.bpp = bpp;
1488 pb->win.color_fmt = fmt;
1489 pb->frame_buffer_phys = (unsigned long) v.base;
1490 pb->win.sheight = v.height;
1491 pb->win.swidth = v.width;
1492 pb->picture.depth = pb->win.depth = v.depth;
1493 pb->win.bpl = pb->win.bpp * pb->win.swidth;
1494 pb->win.pad = v.bytesperline - pb->win.bpl;
1495
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001496 DEBUG("PlanB: Display at %p is %d by %d, bytedepth %d,"
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497 " bpl %d (+ %d)\n", v.base, v.width,v.height,
1498 pb->win.bpp, pb->win.bpl, pb->win.pad);
1499
1500 pb->cmd_buff_inited = 0;
1501 if(pb->overlay) {
1502 suspend_overlay(pb);
1503 fill_cmd_buff(pb);
1504 resume_overlay(pb);
1505 }
1506 planb_unlock(pb);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001507 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001508 }
1509 case VIDIOCGFBUF:
1510 {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001511 struct video_buffer v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512
1513 DEBUG("PlanB: IOCTL VIDIOCGFBUF\n");
1514
1515 v.base = (void *)pb->frame_buffer_phys;
1516 v.height = pb->win.sheight;
1517 v.width = pb->win.swidth;
1518 v.depth = pb->win.depth;
1519 v.bytesperline = pb->win.bpl + pb->win.pad;
1520 if (copy_to_user(arg, &v, sizeof(v)))
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001521 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 return 0;
1523 }
1524 case VIDIOCCAPTURE:
1525 {
1526 int i;
1527
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001528 if(copy_from_user(&i, arg, sizeof(i)))
1529 return -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 if(i==0) {
1531 DEBUG("PlanB: IOCTL VIDIOCCAPTURE Stop\n");
1532
1533 if (!(pb->overlay))
1534 return 0;
1535 planb_lock(pb);
1536 pb->overlay = 0;
1537 overlay_stop(pb);
1538 planb_unlock(pb);
1539 } else {
1540 DEBUG("PlanB: IOCTL VIDIOCCAPTURE Start\n");
1541
1542 if (pb->frame_buffer_phys == 0 ||
1543 pb->win.width == 0 ||
1544 pb->win.height == 0)
1545 return -EINVAL;
1546 if (pb->overlay)
1547 return 0;
1548 planb_lock(pb);
1549 pb->overlay = 1;
1550 if(!(pb->cmd_buff_inited))
1551 fill_cmd_buff(pb);
1552 overlay_start(pb);
1553 planb_unlock(pb);
1554 }
1555 return 0;
1556 }
1557 case VIDIOCGCHAN:
1558 {
1559 struct video_channel v;
1560
1561 DEBUG("PlanB: IOCTL VIDIOCGCHAN\n");
1562
1563 if(copy_from_user(&v, arg,sizeof(v)))
1564 return -EFAULT;
1565 v.flags = 0;
1566 v.tuners = 0;
1567 v.type = VIDEO_TYPE_CAMERA;
1568 v.norm = pb->win.norm;
1569 switch(v.channel)
1570 {
1571 case 0:
1572 strcpy(v.name,"Composite");
1573 break;
1574 case 1:
1575 strcpy(v.name,"SVHS");
1576 break;
1577 default:
1578 return -EINVAL;
1579 break;
1580 }
1581 if(copy_to_user(arg,&v,sizeof(v)))
1582 return -EFAULT;
1583
1584 return 0;
1585 }
1586 case VIDIOCSCHAN:
1587 {
1588 struct video_channel v;
1589
1590 DEBUG("PlanB: IOCTL VIDIOCSCHAN\n");
1591
1592 if(copy_from_user(&v, arg, sizeof(v)))
1593 return -EFAULT;
1594
1595 if (v.norm != pb->win.norm) {
1596 int i, maxlines;
1597
1598 switch (v.norm)
1599 {
1600 case VIDEO_MODE_PAL:
1601 case VIDEO_MODE_SECAM:
1602 maxlines = PLANB_MAXLINES;
1603 break;
1604 case VIDEO_MODE_NTSC:
1605 maxlines = PLANB_NTSC_MAXLINES;
1606 break;
1607 default:
1608 return -EINVAL;
1609 break;
1610 }
1611 planb_lock(pb);
1612 /* empty the grabbing queue */
1613 wait_event(pb->capq, !pb->grabbing);
1614 pb->maxlines = maxlines;
1615 pb->win.norm = v.norm;
1616 /* Stop overlay if running */
1617 suspend_overlay(pb);
1618 for(i = 0; i < MAX_GBUFFERS; i++)
1619 pb->gnorm_switch[i] = 1;
1620 /* I know it's an overkill, but.... */
1621 fill_cmd_buff(pb);
1622 /* ok, now init it accordingly */
1623 saa_init_regs (pb);
1624 /* restart overlay if it was running */
1625 resume_overlay(pb);
1626 planb_unlock(pb);
1627 }
1628
1629 switch(v.channel)
1630 {
1631 case 0: /* Composite */
1632 saa_set (SAA7196_IOCC,
1633 ((saa_regs[pb->win.norm][SAA7196_IOCC] &
1634 ~7) | 3), pb);
1635 break;
1636 case 1: /* SVHS */
1637 saa_set (SAA7196_IOCC,
1638 ((saa_regs[pb->win.norm][SAA7196_IOCC] &
1639 ~7) | 4), pb);
1640 break;
1641 default:
1642 return -EINVAL;
1643 break;
1644 }
1645
1646 return 0;
1647 }
1648 case VIDIOCGPICT:
1649 {
1650 struct video_picture vp = pb->picture;
1651
1652 DEBUG("PlanB: IOCTL VIDIOCGPICT\n");
1653
1654 switch(pb->win.color_fmt) {
1655 case PLANB_GRAY:
1656 vp.palette = VIDEO_PALETTE_GREY;
1657 case PLANB_COLOUR15:
1658 vp.palette = VIDEO_PALETTE_RGB555;
1659 break;
1660 case PLANB_COLOUR32:
1661 vp.palette = VIDEO_PALETTE_RGB32;
1662 break;
1663 default:
1664 vp.palette = 0;
1665 break;
1666 }
1667
1668 if(copy_to_user(arg,&vp,sizeof(vp)))
1669 return -EFAULT;
1670 return 0;
1671 }
1672 case VIDIOCSPICT:
1673 {
1674 struct video_picture vp;
1675
1676 DEBUG("PlanB: IOCTL VIDIOCSPICT\n");
1677
1678 if(copy_from_user(&vp,arg,sizeof(vp)))
1679 return -EFAULT;
1680 pb->picture = vp;
1681 /* Should we do sanity checks here? */
1682 saa_set (SAA7196_BRIG, (unsigned char)
1683 ((pb->picture.brightness) >> 8), pb);
1684 saa_set (SAA7196_HUEC, (unsigned char)
1685 ((pb->picture.hue) >> 8) ^ 0x80, pb);
1686 saa_set (SAA7196_CSAT, (unsigned char)
1687 ((pb->picture.colour) >> 9), pb);
1688 saa_set (SAA7196_CONT, (unsigned char)
1689 ((pb->picture.contrast) >> 9), pb);
1690
1691 return 0;
1692 }
1693 case VIDIOCSWIN:
1694 {
1695 struct video_window vw;
1696 struct video_clip clip;
1697 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001698
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699 DEBUG("PlanB: IOCTL VIDIOCSWIN\n");
1700
1701 if(copy_from_user(&vw,arg,sizeof(vw)))
1702 return -EFAULT;
1703
1704 planb_lock(pb);
1705 /* Stop overlay if running */
1706 suspend_overlay(pb);
1707 pb->win.interlace = (vw.height > pb->maxlines/2)? 1: 0;
1708 if (pb->win.x != vw.x ||
1709 pb->win.y != vw.y ||
1710 pb->win.width != vw.width ||
1711 pb->win.height != vw.height ||
1712 !pb->cmd_buff_inited) {
1713 pb->win.x = vw.x;
1714 pb->win.y = vw.y;
1715 pb->win.width = vw.width;
1716 pb->win.height = vw.height;
1717 fill_cmd_buff(pb);
1718 }
1719 /* Reset clip mask */
1720 memset ((void *) pb->mask, 0xff, (pb->maxlines
1721 * ((PLANB_MAXPIXELS + 7) & ~7)) / 8);
1722 /* Add any clip rects */
1723 for (i = 0; i < vw.clipcount; i++) {
1724 if (copy_from_user(&clip, vw.clips + i,
1725 sizeof(struct video_clip)))
1726 return -EFAULT;
1727 add_clip(pb, &clip);
1728 }
1729 /* restart overlay if it was running */
1730 resume_overlay(pb);
1731 planb_unlock(pb);
1732 return 0;
1733 }
1734 case VIDIOCGWIN:
1735 {
1736 struct video_window vw;
1737
1738 DEBUG("PlanB: IOCTL VIDIOCGWIN\n");
1739
1740 vw.x=pb->win.x;
1741 vw.y=pb->win.y;
1742 vw.width=pb->win.width;
1743 vw.height=pb->win.height;
1744 vw.chromakey=0;
1745 vw.flags=0;
1746 if(pb->win.interlace)
1747 vw.flags|=VIDEO_WINDOW_INTERLACE;
1748 if(copy_to_user(arg,&vw,sizeof(vw)))
1749 return -EFAULT;
1750 return 0;
1751 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001752 case VIDIOCSYNC: {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001753 int i;
1754
1755 IDEBUG("PlanB: IOCTL VIDIOCSYNC\n");
1756
1757 if(copy_from_user((void *)&i,arg,sizeof(int)))
1758 return -EFAULT;
1759
1760 IDEBUG("PlanB: sync to frame %d\n", i);
1761
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001762 if(i > (MAX_GBUFFERS - 1) || i < 0)
1763 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001764chk_grab:
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001765 switch (pb->frame_stat[i]) {
1766 case GBUFFER_UNUSED:
1767 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768 case GBUFFER_GRABBING:
1769 IDEBUG("PlanB: waiting for grab"
1770 " done (%d)\n", i);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001771 interruptible_sleep_on(&pb->capq);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772 if(signal_pending(current))
1773 return -EINTR;
1774 goto chk_grab;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001775 case GBUFFER_DONE:
1776 pb->frame_stat[i] = GBUFFER_UNUSED;
1777 break;
1778 }
1779 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001780 }
1781
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001782 case VIDIOCMCAPTURE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783 {
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001784 struct video_mmap vm;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785 volatile unsigned int status;
1786
1787 IDEBUG("PlanB: IOCTL VIDIOCMCAPTURE\n");
1788
1789 if(copy_from_user((void *) &vm,(void *)arg,sizeof(vm)))
1790 return -EFAULT;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001791 status = pb->frame_stat[vm.frame];
1792 if (status != GBUFFER_UNUSED)
1793 return -EBUSY;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001794
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001795 return vgrab(pb, &vm);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001797
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798 case VIDIOCGMBUF:
1799 {
1800 int i;
1801 struct video_mbuf vm;
1802
1803 DEBUG("PlanB: IOCTL VIDIOCGMBUF\n");
1804
1805 memset(&vm, 0 , sizeof(vm));
1806 vm.size = PLANB_MAX_FBUF * MAX_GBUFFERS;
1807 vm.frames = MAX_GBUFFERS;
1808 for(i = 0; i<MAX_GBUFFERS; i++)
1809 vm.offsets[i] = PLANB_MAX_FBUF * i;
1810 if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
1811 return -EFAULT;
1812 return 0;
1813 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001814
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815 case PLANBIOCGSAAREGS:
1816 {
1817 struct planb_saa_regs preg;
1818
1819 DEBUG("PlanB: IOCTL PLANBIOCGSAAREGS\n");
1820
1821 if(copy_from_user(&preg, arg, sizeof(preg)))
1822 return -EFAULT;
1823 if(preg.addr >= SAA7196_NUMREGS)
1824 return -EINVAL;
1825 preg.val = saa_regs[pb->win.norm][preg.addr];
1826 if(copy_to_user((void *)arg, (void *)&preg,
1827 sizeof(preg)))
1828 return -EFAULT;
1829 return 0;
1830 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001831
Linus Torvalds1da177e2005-04-16 15:20:36 -07001832 case PLANBIOCSSAAREGS:
1833 {
1834 struct planb_saa_regs preg;
1835
1836 DEBUG("PlanB: IOCTL PLANBIOCSSAAREGS\n");
1837
1838 if(copy_from_user(&preg, arg, sizeof(preg)))
1839 return -EFAULT;
1840 if(preg.addr >= SAA7196_NUMREGS)
1841 return -EINVAL;
1842 saa_set (preg.addr, preg.val, pb);
1843 return 0;
1844 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001845
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 case PLANBIOCGSTAT:
1847 {
1848 struct planb_stat_regs pstat;
1849
1850 DEBUG("PlanB: IOCTL PLANBIOCGSTAT\n");
1851
1852 pstat.ch1_stat = in_le32(&pb->planb_base->ch1.status);
1853 pstat.ch2_stat = in_le32(&pb->planb_base->ch2.status);
1854 pstat.saa_stat0 = saa_status(0, pb);
1855 pstat.saa_stat1 = saa_status(1, pb);
1856
1857 if(copy_to_user((void *)arg, (void *)&pstat,
1858 sizeof(pstat)))
1859 return -EFAULT;
1860 return 0;
1861 }
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001862
Linus Torvalds1da177e2005-04-16 15:20:36 -07001863 case PLANBIOCSMODE: {
1864 int v;
1865
1866 DEBUG("PlanB: IOCTL PLANBIOCSMODE\n");
1867
1868 if(copy_from_user(&v, arg, sizeof(v)))
1869 return -EFAULT;
1870
1871 switch(v)
1872 {
1873 case PLANB_TV_MODE:
1874 saa_set (SAA7196_STDC,
1875 (saa_regs[pb->win.norm][SAA7196_STDC] &
1876 0x7f), pb);
1877 break;
1878 case PLANB_VTR_MODE:
1879 saa_set (SAA7196_STDC,
1880 (saa_regs[pb->win.norm][SAA7196_STDC] |
1881 0x80), pb);
1882 break;
1883 default:
1884 return -EINVAL;
1885 break;
1886 }
1887 pb->win.mode = v;
1888 return 0;
1889 }
1890 case PLANBIOCGMODE: {
1891 int v=pb->win.mode;
1892
1893 DEBUG("PlanB: IOCTL PLANBIOCGMODE\n");
1894
1895 if(copy_to_user(arg,&v,sizeof(v)))
1896 return -EFAULT;
1897 return 0;
1898 }
1899#ifdef PLANB_GSCANLINE
1900 case PLANBG_GRAB_BPL: {
1901 int v=pb->gbytes_per_line;
1902
1903 DEBUG("PlanB: IOCTL PLANBG_GRAB_BPL\n");
1904
1905 if(copy_to_user(arg,&v,sizeof(v)))
1906 return -EFAULT;
1907 return 0;
1908 }
1909#endif /* PLANB_GSCANLINE */
1910 case PLANB_INTR_DEBUG: {
1911 int i;
1912
1913 DEBUG("PlanB: IOCTL PLANB_INTR_DEBUG\n");
1914
1915 if(copy_from_user(&i, arg, sizeof(i)))
1916 return -EFAULT;
1917
1918 /* avoid hang ups all together */
1919 for (i = 0; i < MAX_GBUFFERS; i++) {
1920 if(pb->frame_stat[i] == GBUFFER_GRABBING) {
1921 pb->frame_stat[i] = GBUFFER_DONE;
1922 }
1923 }
1924 if(pb->grabbing)
1925 pb->grabbing--;
1926 wake_up_interruptible(&pb->capq);
1927 return 0;
1928 }
1929 case PLANB_INV_REGS: {
1930 int i;
1931 struct planb_any_regs any;
1932
1933 DEBUG("PlanB: IOCTL PLANB_INV_REGS\n");
1934
1935 if(copy_from_user(&any, arg, sizeof(any)))
1936 return -EFAULT;
1937 if(any.offset < 0 || any.offset + any.bytes > 0x400)
1938 return -EINVAL;
1939 if(any.bytes > 128)
1940 return -EINVAL;
1941 for (i = 0; i < any.bytes; i++) {
1942 any.data[i] =
1943 in_8((unsigned char *)pb->planb_base
1944 + any.offset + i);
1945 }
1946 if(copy_to_user(arg,&any,sizeof(any)))
1947 return -EFAULT;
1948 return 0;
1949 }
1950 default:
1951 {
1952 DEBUG("PlanB: Unimplemented IOCTL\n");
1953 return -ENOIOCTLCMD;
1954 }
1955 /* Some IOCTLs are currently unsupported on PlanB */
1956 case VIDIOCGTUNER: {
1957 DEBUG("PlanB: IOCTL VIDIOCGTUNER\n");
1958 goto unimplemented; }
1959 case VIDIOCSTUNER: {
1960 DEBUG("PlanB: IOCTL VIDIOCSTUNER\n");
1961 goto unimplemented; }
1962 case VIDIOCSFREQ: {
1963 DEBUG("PlanB: IOCTL VIDIOCSFREQ\n");
1964 goto unimplemented; }
1965 case VIDIOCGFREQ: {
1966 DEBUG("PlanB: IOCTL VIDIOCGFREQ\n");
1967 goto unimplemented; }
1968 case VIDIOCKEY: {
1969 DEBUG("PlanB: IOCTL VIDIOCKEY\n");
1970 goto unimplemented; }
1971 case VIDIOCSAUDIO: {
1972 DEBUG("PlanB: IOCTL VIDIOCSAUDIO\n");
1973 goto unimplemented; }
1974 case VIDIOCGAUDIO: {
1975 DEBUG("PlanB: IOCTL VIDIOCGAUDIO\n");
1976 goto unimplemented; }
1977unimplemented:
1978 DEBUG(" Unimplemented\n");
1979 return -ENOIOCTLCMD;
1980 }
1981 return 0;
1982}
1983
1984static int planb_mmap(struct vm_area_struct *vma, struct video_device *dev, const char *adr, unsigned long size)
1985{
1986 int i;
1987 struct planb *pb = (struct planb *)dev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001988 unsigned long start = (unsigned long)adr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001989
1990 if (size > MAX_GBUFFERS * PLANB_MAX_FBUF)
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03001991 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992 if (!pb->rawbuf) {
1993 int err;
1994 if((err=grabbuf_alloc(pb)))
1995 return err;
1996 }
1997 for (i = 0; i < pb->rawbuf_size; i++) {
1998 unsigned long pfn;
1999
2000 pfn = virt_to_phys((void *)pb->rawbuf[i]) >> PAGE_SHIFT;
2001 if (remap_pfn_range(vma, start, pfn, PAGE_SIZE, PAGE_SHARED))
2002 return -EAGAIN;
2003 start += PAGE_SIZE;
2004 if (size <= PAGE_SIZE)
2005 break;
2006 size -= PAGE_SIZE;
2007 }
2008 return 0;
2009}
2010
2011static struct video_device planb_template=
2012{
2013 .owner = THIS_MODULE,
2014 .name = PLANB_DEVICE_NAME,
2015 .type = VID_TYPE_OVERLAY,
2016 .hardware = VID_HARDWARE_PLANB,
2017 .open = planb_open,
2018 .close = planb_close,
2019 .read = planb_read,
2020 .write = planb_write,
2021 .ioctl = planb_ioctl,
2022 .mmap = planb_mmap, /* mmap? */
2023};
2024
2025static int init_planb(struct planb *pb)
2026{
2027 unsigned char saa_rev;
2028 int i, result;
2029
2030 memset ((void *) &pb->win, 0, sizeof (struct planb_window));
2031 /* Simple sanity check */
2032 if(def_norm >= NUM_SUPPORTED_NORM || def_norm < 0) {
2033 printk(KERN_ERR "PlanB: Option(s) invalid\n");
2034 return -2;
2035 }
2036 pb->win.norm = def_norm;
2037 pb->win.mode = PLANB_TV_MODE; /* TV mode */
2038 pb->win.interlace=1;
2039 pb->win.x=0;
2040 pb->win.y=0;
2041 pb->win.width=768; /* 640 */
2042 pb->win.height=576; /* 480 */
2043 pb->maxlines=576;
2044#if 0
2045 btv->win.cropwidth=768; /* 640 */
2046 btv->win.cropheight=576; /* 480 */
2047 btv->win.cropx=0;
2048 btv->win.cropy=0;
2049#endif
2050 pb->win.pad=0;
2051 pb->win.bpp=4;
2052 pb->win.depth=32;
2053 pb->win.color_fmt=PLANB_COLOUR32;
2054 pb->win.bpl=1024*pb->win.bpp;
2055 pb->win.swidth=1024;
2056 pb->win.sheight=768;
2057#ifdef PLANB_GSCANLINE
2058 if((pb->gbytes_per_line = PLANB_MAXPIXELS * 4) > PAGE_SIZE
2059 || (pb->gbytes_per_line <= 0))
2060 return -3;
2061 else {
2062 /* page align pb->gbytes_per_line for DMA purpose */
2063 for(i = PAGE_SIZE; pb->gbytes_per_line < (i>>1);)
2064 i>>=1;
2065 pb->gbytes_per_line = i;
2066 }
2067#endif
2068 pb->tab_size = PLANB_MAXLINES + 40;
2069 pb->suspend = 0;
Ingo Molnar3593cab2006-02-07 06:49:14 -02002070 mutex_init(&pb->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071 pb->ch1_cmd = 0;
2072 pb->ch2_cmd = 0;
2073 pb->mask = 0;
2074 pb->priv_space = 0;
2075 pb->offset = 0;
2076 pb->user = 0;
2077 pb->overlay = 0;
2078 init_waitqueue_head(&pb->suspendq);
2079 pb->cmd_buff_inited = 0;
2080 pb->frame_buffer_phys = 0;
2081
2082 /* Reset DMA controllers */
2083 planb_dbdma_stop(&pb->planb_base->ch2);
2084 planb_dbdma_stop(&pb->planb_base->ch1);
2085
2086 saa_rev = (saa_status(0, pb) & 0xf0) >> 4;
2087 printk(KERN_INFO "PlanB: SAA7196 video processor rev. %d\n", saa_rev);
2088 /* Initialize the SAA registers in memory and on chip */
2089 saa_init_regs (pb);
2090
2091 /* clear interrupt mask */
2092 pb->intr_mask = PLANB_CLR_IRQ;
2093
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002094 result = request_irq(pb->irq, planb_irq, 0, "PlanB", (void *)pb);
2095 if (result < 0) {
2096 if (result==-EINVAL)
2097 printk(KERN_ERR "PlanB: Bad irq number (%d) "
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098 "or handler\n", (int)pb->irq);
2099 else if (result==-EBUSY)
2100 printk(KERN_ERR "PlanB: I don't know why, "
2101 "but IRQ %d is busy\n", (int)pb->irq);
2102 return result;
2103 }
2104 disable_irq(pb->irq);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002105
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 /* Now add the template and register the device unit. */
2107 memcpy(&pb->video_dev,&planb_template,sizeof(planb_template));
2108
2109 pb->picture.brightness=0x90<<8;
2110 pb->picture.contrast = 0x70 << 8;
2111 pb->picture.colour = 0x70<<8;
2112 pb->picture.hue = 0x8000;
2113 pb->picture.whiteness = 0;
2114 pb->picture.depth = pb->win.depth;
2115
2116 pb->frame_stat=NULL;
2117 init_waitqueue_head(&pb->capq);
2118 for(i=0; i<MAX_GBUFFERS; i++) {
2119 pb->gbuf_idx[i] = PLANB_MAX_FBUF * i / PAGE_SIZE;
2120 pb->gwidth[i]=0;
2121 pb->gheight[i]=0;
2122 pb->gfmt[i]=0;
2123 pb->cap_cmd[i]=NULL;
2124#ifndef PLANB_GSCANLINE
2125 pb->l_fr_addr_idx[i] = MAX_GBUFFERS * (PLANB_MAX_FBUF
2126 / PAGE_SIZE + 1) + MAX_LNUM * i;
2127 pb->lsize[i] = 0;
2128 pb->lnum[i] = 0;
2129#endif
2130 }
2131 pb->rawbuf=NULL;
2132 pb->grabbing=0;
2133
2134 /* enable interrupts */
2135 out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
2136 pb->intr_mask = PLANB_FRM_IRQ;
2137 enable_irq(pb->irq);
2138
2139 if(video_register_device(&pb->video_dev, VFL_TYPE_GRABBER, video_nr)<0)
2140 return -1;
2141
2142 return 0;
2143}
2144
2145/*
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002146 * Scan for a PlanB controller, request the irq and map the io memory
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147 */
2148
2149static int find_planb(void)
2150{
2151 struct planb *pb;
2152 struct device_node *planb_devices;
2153 unsigned char dev_fn, confreg, bus;
2154 unsigned int old_base, new_base;
2155 unsigned int irq;
2156 struct pci_dev *pdev;
2157 int rc;
2158
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +11002159 if (!machine_is(powermac))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002160 return 0;
2161
Stephen Rothwell30686ba2007-04-24 13:53:04 +10002162 planb_devices = of_find_node_by_name(NULL, "planb");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002163 if (planb_devices == 0) {
2164 planb_num=0;
2165 printk(KERN_WARNING "PlanB: no device found!\n");
2166 return planb_num;
2167 }
2168
2169 if (planb_devices->next != NULL)
2170 printk(KERN_ERR "Warning: only using first PlanB device!\n");
2171 pb = &planbs[0];
2172 planb_num = 1;
2173
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002174 if (planb_devices->n_addrs != 1) {
2175 printk (KERN_WARNING "PlanB: expecting 1 address for planb "
2176 "(got %d)", planb_devices->n_addrs);
Stephen Rothwell30686ba2007-04-24 13:53:04 +10002177 of_node_put(planb_devices);
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);
Stephen Rothwell30686ba2007-04-24 13:53:04 +10002184 of_node_put(planb_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 return 0;
2186 } else {
2187 irq = planb_devices->intrs[0].line;
2188 }
2189
2190 /* Initialize PlanB's PCI registers */
2191
2192 /* There is a bug with the way OF assigns addresses
2193 to the devices behind the chaos bridge.
2194 control needs only 0x1000 of space, but decodes only
2195 the upper 16 bits. It therefore occupies a full 64K.
2196 OF assigns the planb controller memory within this space;
2197 so we need to change that here in order to access planb. */
2198
2199 /* We remap to 0xf1000000 in hope that nobody uses it ! */
2200
2201 bus = (planb_devices->addrs[0].space >> 16) & 0xff;
2202 dev_fn = (planb_devices->addrs[0].space >> 8) & 0xff;
2203 confreg = planb_devices->addrs[0].space & 0xff;
2204 old_base = planb_devices->addrs[0].address;
2205 new_base = 0xf1000000;
Stephen Rothwell30686ba2007-04-24 13:53:04 +10002206 of_node_put(planb_devices);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207
2208 DEBUG("PlanB: Found on bus %d, dev %d, func %d, "
2209 "membase 0x%x (base reg. 0x%x)\n",
2210 bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg);
2211
Alan Cox74ea1c72007-04-23 17:57:28 -03002212 pdev = pci_get_bus_and_slot(bus, dev_fn);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213 if (!pdev) {
2214 printk(KERN_ERR "planb: cannot find slot\n");
2215 goto err_out;
2216 }
2217
2218 /* Enable response in memory space, bus mastering,
2219 use memory write and invalidate */
2220 rc = pci_enable_device(pdev);
2221 if (rc) {
2222 printk(KERN_ERR "planb: cannot enable PCI device %s\n",
2223 pci_name(pdev));
2224 goto err_out;
2225 }
2226 rc = pci_set_mwi(pdev);
2227 if (rc) {
2228 printk(KERN_ERR "planb: cannot enable MWI on PCI device %s\n",
2229 pci_name(pdev));
2230 goto err_out_disable;
2231 }
2232 pci_set_master(pdev);
2233
2234 /* Set the new base address */
2235 pci_write_config_dword (pdev, confreg, new_base);
2236
2237 planb_regs = (volatile struct planb_registers *)
2238 ioremap (new_base, 0x400);
2239 pb->planb_base = planb_regs;
2240 pb->planb_base_phys = (struct planb_registers *)new_base;
2241 pb->irq = irq;
Alan Cox74ea1c72007-04-23 17:57:28 -03002242 pb->dev = pdev;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002243
Linus Torvalds1da177e2005-04-16 15:20:36 -07002244 return planb_num;
2245
2246err_out_disable:
2247 pci_disable_device(pdev);
2248err_out:
2249 /* FIXME handle error */ /* comment moved from pci_find_slot, above */
Alan Cox74ea1c72007-04-23 17:57:28 -03002250 pci_dev_put(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251 return 0;
2252}
2253
2254static void release_planb(void)
2255{
2256 int i;
2257 struct planb *pb;
2258
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002259 for (i=0;i<planb_num; i++)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260 {
2261 pb=&planbs[i];
2262
2263 /* stop and flash DMAs unconditionally */
2264 planb_dbdma_stop(&pb->planb_base->ch2);
2265 planb_dbdma_stop(&pb->planb_base->ch1);
2266
2267 /* clear and free interrupts */
2268 pb->intr_mask = PLANB_CLR_IRQ;
2269 out_le32 (&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
2270 free_irq(pb->irq, pb);
2271
2272 /* make sure all allocated memory are freed */
2273 planb_prepare_close(pb);
2274
2275 printk(KERN_INFO "PlanB: unregistering with v4l\n");
2276 video_unregister_device(&pb->video_dev);
2277
Alan Cox74ea1c72007-04-23 17:57:28 -03002278 pci_dev_put(pb->dev);
2279
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280 /* note that iounmap() does nothing on the PPC right now */
2281 iounmap ((void *)pb->planb_base);
2282 }
2283}
2284
2285static int __init init_planbs(void)
2286{
2287 int i;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002288
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289 if (find_planb()<=0)
2290 return -EIO;
2291
2292 for (i=0; i<planb_num; i++) {
2293 if (init_planb(&planbs[i])<0) {
2294 printk(KERN_ERR "PlanB: error registering device %d"
2295 " with v4l\n", i);
2296 release_planb();
2297 return -EIO;
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002298 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002299 printk(KERN_INFO "PlanB: registered device %d with v4l\n", i);
Mauro Carvalho Chehabd56410e2006-03-25 09:19:53 -03002300 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 return 0;
2302}
2303
2304static void __exit exit_planbs(void)
2305{
2306 release_planb();
2307}
2308
2309module_init(init_planbs);
2310module_exit(exit_planbs);