2 * CORTEX-I Frame Grabber driver V1.0
4 * Copyright (C) 1994, Paul S. LaFollette, Jr. This software may be used,
5 * modified, copied, distributed, and sold, in both source and binary form
6 * provided that the above copyright and these terms are retained. Under
7 * no circumstances is the author responsible for the proper functioning
8 * of this software, nor does the author assume any responsibility
9 * for damages incurred with its use.
11 * $FreeBSD: src/sys/i386/isa/ctx.c,v 1.36 2000/01/29 16:17:31 peter Exp $
12 * $DragonFly: src/sys/dev/video/ctx/ctx.c,v 1.3 2003/07/21 05:50:40 dillon Exp $
19 * Device Driver for CORTEX-I Frame Grabber
20 * Made by ImageNation Corporation
21 * 1200 N.E. Keyues Road
22 * Vancouver, WA 98684 (206) 944-9131
23 * (I have no ties to this company, just thought you might want
24 * to know how to get in touch with them.)
26 * In order to understand this device, you really need to consult the
27 * manual which ImageNation provides when you buy the board. (And
28 * what a pleasure it is to buy something for a PC and actually get
29 * programming information along with it.) I will limit myself here to
30 * a few comments which are specific to this driver. See also the file
31 * ctxreg.h for definitions of registers and control bits.
33 * 1. Although the hardware supports low resolution (256 x 256)
34 * acqusition and display, I have not implemented access to
35 * these modes in this driver. There are some fairly quirky
36 * aspects to the way this board works in low resolution mode,
37 * and I don't want to deal with them. Maybe later.
39 * 2. Choosing the base address for the video memory: This is set
40 * using a combination of hardware and software, using the left
41 * most dip switch on the board, and the AB_SELECT bit of control
42 * port 1, according to the chart below:
44 * Left DIP switch || DOWN | UP |
45 * =================================================
46 * AB_SELECT = 0 || 0xA0000 | 0xB0000 |
47 * -------------------------------------------------
48 * AB_SELECT = 1 || 0xD0000 | 0xE0000 |
49 * ------------------------------------------------
51 * When the RAM_ENABLE bit of control port 1 is clear (0), the
52 * video ram is disconnected from the computer bus. This makes
53 * it possible, in principle, to share memory space with other
54 * devices (such as VGA) which can also disconnect themselves
55 * from the bus. It also means that multiple CORTEX-I boards
56 * can share the same video memory space. Disconnecting from the
57 * bus does not affect the video display of the video ram contents,
58 * so that one needs only set the RAM_ENABLE bit when actually
59 * reading or writing to memory. The cost of this is low,
60 * the benefits to me are great (I need more than one board
61 * in my machine, and 0xE0000 is the only address choice that
62 * doesn't conflict with anything) so I adopt this strategy here.
64 * XXX-Note... this driver has only been tested for the
65 * XXX base = 0xE0000 case!
67 * 3) There is a deficiency in the documentation from ImageNation, I
68 * think. In order to successfully load the lookup table, it is
69 * necessary to clear SEE_STORED_VIDEO in control port 0 as well as
70 * setting LUT_LOAD_ENABLE in control port 1.
72 * 4) This driver accesses video memory through read or write operations.
73 * Other functionality is provided through ioctl's, manifest
74 * constants for which are defined in ioctl_ctx.h. The ioctl's
76 * CTX_LIVE Display live video
77 * CTX_GRAB Grab a frame of video data
78 * CTX_H_ORGANIZE Set things up so that sequential read
79 * operations access horizontal lines of
81 * CTX_V_ORGANIZE Set things up so that sequential read
82 * operations access vertical lines of
84 * CTX_SET_LUT Set the lookup table from an array
85 * of 256 unsigned chars passed as the
86 * third parameter to ioctl.
87 * CTX_GET_LUT Return the current lookup table to
88 * the application as an array of 256
89 * unsigned chars. Again the third
90 * parameter to the ioctl call.
93 * ioctl(fi, CTX_H_ORGANIZE, 0);
94 * lseek(fi, y*512, SEEK_SET);
95 * read(fi, buffer, 512);
97 * will fill buffer with 512 pixels (unsigned chars) which represent
98 * the y-th horizontal line of the image.
100 * ioctl(fi, CTX_V_ORGANIZE, 0:
101 * lseek(fi, x*512+y, SEEK_SET);
102 * read(fi, buffer, 10);
104 * will read 10 a vertical line of 10 pixels starting at (x,y).
106 * Obviously, this sort of ugliness needs to be hidden away from
107 * the casual user, with an appropriate set of higher level
114 #include <sys/param.h>
115 #include <sys/systm.h>
116 #include <sys/conf.h>
118 #include <sys/kernel.h>
119 #include <sys/malloc.h>
120 #include <i386/isa/isa_device.h>
121 #include <i386/isa/ctxreg.h>
122 #include <machine/ioctl_ctx.h>
123 #include <machine/md_var.h>
125 static int waitvb(int port);
128 #define OPEN (0x01) /* device is open */
130 #define UNIT(x) ((x) & 0x07)
132 static int ctxprobe __P((struct isa_device *devp));
133 static int ctxattach __P((struct isa_device *devp));
134 struct isa_driver ctxdriver = {ctxprobe, ctxattach, "ctx"};
136 static d_open_t ctxopen;
137 static d_close_t ctxclose;
138 static d_read_t ctxread;
139 static d_write_t ctxwrite;
140 static d_ioctl_t ctxioctl;
141 #define CDEV_MAJOR 40
143 static struct cdevsw ctx_cdevsw = {
145 /* maj */ CDEV_MAJOR,
151 /* close */ ctxclose,
153 /* write */ ctxwrite,
154 /* ioctl */ ctxioctl,
157 /* strategy */ nostrategy,
163 #define LUTSIZE 256 /* buffer size for Look Up Table (LUT) */
164 #define PAGESIZE 65536 /* size of one video page, 1/4 of the screen */
167 * Per unit shadow registers (because the dumb hardware is RO)
170 static struct ctx_soft_registers {
182 ctxprobe(struct isa_device * devp)
188 cdevsw_add(&ctx_cdevsw);
189 if (inb(devp->id_iobase) == 0xff) /* 0xff only if board absent */
192 status = 1; /*XXX uses only one port? */
198 ctxattach(struct isa_device * devp)
200 struct ctx_soft_registers *sr;
202 sr = &(ctx_sr[devp->id_unit]);
203 sr->cp0 = 0; /* zero out the shadow registers */
204 sr->cp1 = 0; /* and the open flag. wait for */
205 sr->flag = 0; /* open to malloc the LUT space */
206 sr->iobase = devp->id_iobase;
207 sr->maddr = devp->id_maddr;
208 sr->msize = devp->id_msize;
209 make_dev(&ctx_cdevsw, 0, 0, 0, 0600, "ctx%d", devp->id_unit);
214 ctxopen(dev_t dev, int flags, int fmt, struct proc *p)
216 struct ctx_soft_registers *sr;
220 unit = UNIT(minor(dev));
222 /* minor number out of range? */
226 sr = &(ctx_sr[unit]);
228 if (sr->flag != 0) /* someone has already opened us */
231 /* get space for the LUT buffer */
233 sr->lutp = malloc(LUTSIZE, M_DEVBUF, M_WAITOK);
234 if (sr->lutp == NULL)
240 Set up the shadow registers. We don't actually write these
241 values to the control ports until after we finish loading the
244 sr->cp0 |= SEE_STORED_VIDEO;
245 if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000))
246 sr->cp1 |= AB_SELECT; /* map to B or E if necessary */
247 /* but don't enable RAM */
249 Set up the lookup table initially so that it is transparent.
252 outb(sr->iobase + ctx_cp0, (u_char) 0);
253 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
254 for (i = 0; i < LUTSIZE; i++) {
255 outb(sr->iobase + ctx_lutaddr, (u_char) i);
256 sr->lutp[i] = (u_char) i;
257 outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]);
260 Disable LUT loading, and push the data in the shadow
261 registers into the control ports.
263 outb(sr->iobase + ctx_cp0, sr->cp0);
264 outb(sr->iobase + ctx_cp1, sr->cp1);
265 return (0); /* successful open. All ready to go. */
269 ctxclose(dev_t dev, int flags, int fmt, struct proc *p)
273 unit = UNIT(minor(dev));
274 ctx_sr[unit].flag = 0;
275 free(ctx_sr[unit].lutp, M_DEVBUF);
276 ctx_sr[unit].lutp = NULL;
281 ctxwrite(dev_t dev, struct uio * uio, int ioflag)
283 int unit, status = 0;
284 int page, count, offset;
285 struct ctx_soft_registers *sr;
287 unit = UNIT(minor(dev));
288 sr = &(ctx_sr[unit]);
290 if (uio->uio_offset < 0)
292 if (uio->uio_offset >= 4 * PAGESIZE)
295 page = (u_int)uio->uio_offset / PAGESIZE;
296 offset = (u_int)uio->uio_offset % PAGESIZE;
297 count = min(uio->uio_resid, PAGESIZE - offset);
298 while ((page >= 0) && (page <= 3) && (count > 0)) {
301 outb(sr->iobase + ctx_cp0, sr->cp0);
304 Before doing the uiomove, we need to "connect" the frame buffer
305 ram to the machine bus. This is done here so that we can have
306 several different boards installed, all sharing the same memory
307 space... each board is only "connected" to the bus when its memory
308 is actually being read or written. All my instincts tell me that
309 I should disable interrupts here, so I have done so.
313 sr->cp1 |= RAM_ENABLE;
314 outb(sr->iobase + ctx_cp1, sr->cp1);
315 status = uiomove(sr->maddr + offset, count, uio);
316 sr->cp1 &= ~RAM_ENABLE;
317 outb(sr->iobase + ctx_cp1, sr->cp1);
320 page = (u_int)uio->uio_offset / PAGESIZE;
321 offset = (u_int)uio->uio_offset % PAGESIZE;
322 count = min(uio->uio_resid, PAGESIZE - offset);
324 if (uio->uio_resid > 0)
331 ctxread(dev_t dev, struct uio * uio, int ioflag)
333 int unit, status = 0;
334 int page, count, offset;
335 struct ctx_soft_registers *sr;
337 unit = UNIT(minor(dev));
338 sr = &(ctx_sr[unit]);
340 if (uio->uio_offset < 0)
342 if (uio->uio_offset >= 4 * PAGESIZE)
345 page = (u_int)uio->uio_offset / PAGESIZE;
346 offset = (u_int)uio->uio_offset % PAGESIZE;
347 count = min(uio->uio_resid, PAGESIZE - offset);
348 while ((page >= 0) && (page <= 3) && (count > 0)) {
351 outb(sr->iobase + ctx_cp0, sr->cp0);
353 Before doing the uiomove, we need to "connect" the frame buffer
354 ram to the machine bus. This is done here so that we can have
355 several different boards installed, all sharing the same memory
356 space... each board is only "connected" to the bus when its memory
357 is actually being read or written. All my instincts tell me that
358 I should disable interrupts here, so I have done so.
361 sr->cp1 |= RAM_ENABLE;
362 outb(sr->iobase + ctx_cp1, sr->cp1);
363 status = uiomove(sr->maddr + offset, count, uio);
364 sr->cp1 &= ~RAM_ENABLE;
365 outb(sr->iobase + ctx_cp1, sr->cp1);
368 page = (u_int)uio->uio_offset / PAGESIZE;
369 offset = (u_int)uio->uio_offset % PAGESIZE;
370 count = min(uio->uio_resid, PAGESIZE - offset);
372 if (uio->uio_resid > 0)
379 ctxioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
383 struct ctx_soft_registers *sr;
386 unit = UNIT(minor(dev));
387 sr = &(ctx_sr[unit]);
391 sr->cp0 &= ~SEE_STORED_VIDEO;
392 outb(sr->iobase + ctx_cp0, sr->cp0);
395 sr->cp0 &= ~SEE_STORED_VIDEO;
396 outb(sr->iobase + ctx_cp0, sr->cp0);
398 if (waitvb(sr->iobase)) /* wait for vert blank to start
401 outb(sr->iobase + ctx_cp0, sr->cp0);
402 if (waitvb(sr->iobase)) /* wait for two more to finish acquire */
404 if (waitvb(sr->iobase))
406 sr->cp0 &= ~ACQUIRE; /* turn off acquire and turn on
408 sr->cp0 |= SEE_STORED_VIDEO;
409 outb(sr->iobase + ctx_cp0, sr->cp0);
412 sr->cp0 &= ~PAGE_ROTATE;
413 outb(sr->iobase + ctx_cp0, sr->cp0);
416 sr->cp0 |= PAGE_ROTATE;
417 outb(sr->iobase + ctx_cp0, sr->cp0);
420 bcopy((u_char *) data, sr->lutp, LUTSIZE);
421 outb(sr->iobase + ctx_cp0, (u_char) 0);
422 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
423 for (i = 0; i < LUTSIZE; i++) {
424 outb(sr->iobase + ctx_lutaddr, i);
425 outb(sr->iobase + ctx_lutdata, sr->lutp[i]);
427 outb(sr->iobase + ctx_cp0, sr->cp0); /* restore control
429 outb(sr->iobase + ctx_cp1, sr->cp1);
432 bcopy(sr->lutp, (u_char *) data, LUTSIZE);
443 { /* wait for a vertical blank, */
444 if (inb(port) == 0xff) /* 0xff means no board present */
447 while ((inb(port) & VERTICAL_BLANK) != 0) {
449 while ((inb(port) & VERTICAL_BLANK) == 0) {