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 $
18 * Device Driver for CORTEX-I Frame Grabber
19 * Made by ImageNation Corporation
20 * 1200 N.E. Keyues Road
21 * Vancouver, WA 98684 (206) 944-9131
22 * (I have no ties to this company, just thought you might want
23 * to know how to get in touch with them.)
25 * In order to understand this device, you really need to consult the
26 * manual which ImageNation provides when you buy the board. (And
27 * what a pleasure it is to buy something for a PC and actually get
28 * programming information along with it.) I will limit myself here to
29 * a few comments which are specific to this driver. See also the file
30 * ctxreg.h for definitions of registers and control bits.
32 * 1. Although the hardware supports low resolution (256 x 256)
33 * acqusition and display, I have not implemented access to
34 * these modes in this driver. There are some fairly quirky
35 * aspects to the way this board works in low resolution mode,
36 * and I don't want to deal with them. Maybe later.
38 * 2. Choosing the base address for the video memory: This is set
39 * using a combination of hardware and software, using the left
40 * most dip switch on the board, and the AB_SELECT bit of control
41 * port 1, according to the chart below:
43 * Left DIP switch || DOWN | UP |
44 * =================================================
45 * AB_SELECT = 0 || 0xA0000 | 0xB0000 |
46 * -------------------------------------------------
47 * AB_SELECT = 1 || 0xD0000 | 0xE0000 |
48 * ------------------------------------------------
50 * When the RAM_ENABLE bit of control port 1 is clear (0), the
51 * video ram is disconnected from the computer bus. This makes
52 * it possible, in principle, to share memory space with other
53 * devices (such as VGA) which can also disconnect themselves
54 * from the bus. It also means that multiple CORTEX-I boards
55 * can share the same video memory space. Disconnecting from the
56 * bus does not affect the video display of the video ram contents,
57 * so that one needs only set the RAM_ENABLE bit when actually
58 * reading or writing to memory. The cost of this is low,
59 * the benefits to me are great (I need more than one board
60 * in my machine, and 0xE0000 is the only address choice that
61 * doesn't conflict with anything) so I adopt this strategy here.
63 * XXX-Note... this driver has only been tested for the
64 * XXX base = 0xE0000 case!
66 * 3) There is a deficiency in the documentation from ImageNation, I
67 * think. In order to successfully load the lookup table, it is
68 * necessary to clear SEE_STORED_VIDEO in control port 0 as well as
69 * setting LUT_LOAD_ENABLE in control port 1.
71 * 4) This driver accesses video memory through read or write operations.
72 * Other functionality is provided through ioctl's, manifest
73 * constants for which are defined in ioctl_ctx.h. The ioctl's
75 * CTX_LIVE Display live video
76 * CTX_GRAB Grab a frame of video data
77 * CTX_H_ORGANIZE Set things up so that sequential read
78 * operations access horizontal lines of
80 * CTX_V_ORGANIZE Set things up so that sequential read
81 * operations access vertical lines of
83 * CTX_SET_LUT Set the lookup table from an array
84 * of 256 unsigned chars passed as the
85 * third parameter to ioctl.
86 * CTX_GET_LUT Return the current lookup table to
87 * the application as an array of 256
88 * unsigned chars. Again the third
89 * parameter to the ioctl call.
92 * ioctl(fi, CTX_H_ORGANIZE, 0);
93 * lseek(fi, y*512, SEEK_SET);
94 * read(fi, buffer, 512);
96 * will fill buffer with 512 pixels (unsigned chars) which represent
97 * the y-th horizontal line of the image.
99 * ioctl(fi, CTX_V_ORGANIZE, 0:
100 * lseek(fi, x*512+y, SEEK_SET);
101 * read(fi, buffer, 10);
103 * will read 10 a vertical line of 10 pixels starting at (x,y).
105 * Obviously, this sort of ugliness needs to be hidden away from
106 * the casual user, with an appropriate set of higher level
113 #include <sys/param.h>
114 #include <sys/systm.h>
115 #include <sys/conf.h>
116 #include <sys/device.h>
118 #include <sys/kernel.h>
119 #include <sys/malloc.h>
120 #include <bus/isa/isa_device.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 (struct isa_device *devp);
133 static int ctxattach (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;
142 static struct dev_ops ctx_ops = {
152 #define LUTSIZE 256 /* buffer size for Look Up Table (LUT) */
153 #define PAGESIZE 65536 /* size of one video page, 1/4 of the screen */
156 * Per unit shadow registers (because the dumb hardware is RO)
159 static struct ctx_soft_registers {
171 ctxprobe(struct isa_device * devp)
175 if (inb(devp->id_iobase) == 0xff) /* 0xff only if board absent */
178 status = 1; /*XXX uses only one port? */
183 ctxattach(struct isa_device * devp)
185 struct ctx_soft_registers *sr;
187 sr = &(ctx_sr[devp->id_unit]);
188 sr->cp0 = 0; /* zero out the shadow registers */
189 sr->cp1 = 0; /* and the open flag. wait for */
190 sr->flag = 0; /* open to malloc the LUT space */
191 sr->iobase = devp->id_iobase;
192 sr->maddr = devp->id_maddr;
193 sr->msize = devp->id_msize;
194 make_dev(&ctx_ops, devp->id_unit, 0, 0, 0600,
195 "ctx%d", devp->id_unit);
200 ctxopen(struct dev_open_args *ap)
202 cdev_t dev = ap->a_head.a_dev;
203 struct ctx_soft_registers *sr;
207 unit = UNIT(minor(dev));
209 /* minor number out of range? */
213 sr = &(ctx_sr[unit]);
215 if (sr->flag != 0) /* someone has already opened us */
218 /* get space for the LUT buffer */
220 sr->lutp = kmalloc(LUTSIZE, M_DEVBUF, M_WAITOK);
225 Set up the shadow registers. We don't actually write these
226 values to the control ports until after we finish loading the
229 sr->cp0 |= SEE_STORED_VIDEO;
230 if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000))
231 sr->cp1 |= AB_SELECT; /* map to B or E if necessary */
232 /* but don't enable RAM */
234 Set up the lookup table initially so that it is transparent.
237 outb(sr->iobase + ctx_cp0, (u_char) 0);
238 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
239 for (i = 0; i < LUTSIZE; i++) {
240 outb(sr->iobase + ctx_lutaddr, (u_char) i);
241 sr->lutp[i] = (u_char) i;
242 outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]);
245 Disable LUT loading, and push the data in the shadow
246 registers into the control ports.
248 outb(sr->iobase + ctx_cp0, sr->cp0);
249 outb(sr->iobase + ctx_cp1, sr->cp1);
250 return (0); /* successful open. All ready to go. */
254 ctxclose(struct dev_close_args *ap)
256 cdev_t dev = ap->a_head.a_dev;
259 unit = UNIT(minor(dev));
260 ctx_sr[unit].flag = 0;
261 kfree(ctx_sr[unit].lutp, M_DEVBUF);
262 ctx_sr[unit].lutp = NULL;
267 ctxwrite(struct dev_write_args *ap)
269 cdev_t dev = ap->a_head.a_dev;
270 struct uio *uio = ap->a_uio;
271 int unit, status = 0;
272 int page, count, offset;
273 struct ctx_soft_registers *sr;
276 unit = UNIT(minor(dev));
277 sr = &(ctx_sr[unit]);
279 if (uio->uio_offset < 0)
281 if (uio->uio_offset >= 4 * PAGESIZE)
284 page = (u_int)uio->uio_offset / PAGESIZE;
285 offset = (u_int)uio->uio_offset % PAGESIZE;
286 count = (int)szmin(uio->uio_resid, PAGESIZE - offset);
287 while ((page >= 0) && (page <= 3) && (count > 0)) {
290 outb(sr->iobase + ctx_cp0, sr->cp0);
293 Before doing the uiomove, we need to "connect" the frame buffer
294 ram to the machine bus. This is done here so that we can have
295 several different boards installed, all sharing the same memory
296 space... each board is only "connected" to the bus when its memory
297 is actually being read or written. All my instincts tell me that
298 I should disable interrupts here, so I have done so.
303 sr->cp1 |= RAM_ENABLE;
304 outb(sr->iobase + ctx_cp1, sr->cp1);
305 status = uiomove(sr->maddr + offset, count, uio);
306 sr->cp1 &= ~RAM_ENABLE;
307 outb(sr->iobase + ctx_cp1, sr->cp1);
310 page = (u_int)uio->uio_offset / PAGESIZE;
311 offset = (u_int)uio->uio_offset % PAGESIZE;
312 count = (int)szmin(uio->uio_resid, PAGESIZE - offset);
314 if (uio->uio_resid > 0)
321 ctxread(struct dev_read_args *ap)
323 cdev_t dev = ap->a_head.a_dev;
324 struct uio *uio = ap->a_uio;
325 int unit, status = 0;
326 int page, count, offset;
327 struct ctx_soft_registers *sr;
330 unit = UNIT(minor(dev));
331 sr = &(ctx_sr[unit]);
333 if (uio->uio_offset < 0)
335 if (uio->uio_offset >= 4 * PAGESIZE)
338 page = (u_int)uio->uio_offset / PAGESIZE;
339 offset = (u_int)uio->uio_offset % PAGESIZE;
340 count = (int)szmin(uio->uio_resid, PAGESIZE - offset);
341 while ((page >= 0) && (page <= 3) && (count > 0)) {
344 outb(sr->iobase + ctx_cp0, sr->cp0);
346 Before doing the uiomove, we need to "connect" the frame buffer
347 ram to the machine bus. This is done here so that we can have
348 several different boards installed, all sharing the same memory
349 space... each board is only "connected" to the bus when its memory
350 is actually being read or written. All my instincts tell me that
351 I should disable interrupts here, so I have done so.
355 sr->cp1 |= RAM_ENABLE;
356 outb(sr->iobase + ctx_cp1, sr->cp1);
357 status = uiomove(sr->maddr + offset, count, uio);
358 sr->cp1 &= ~RAM_ENABLE;
359 outb(sr->iobase + ctx_cp1, sr->cp1);
362 page = (u_int)uio->uio_offset / PAGESIZE;
363 offset = (u_int)uio->uio_offset % PAGESIZE;
364 count = (int)szmin(uio->uio_resid, PAGESIZE - offset);
366 if (uio->uio_resid > 0)
373 ctxioctl(struct dev_ioctl_args *ap)
375 cdev_t dev = ap->a_head.a_dev;
378 struct ctx_soft_registers *sr;
381 unit = UNIT(minor(dev));
382 sr = &(ctx_sr[unit]);
386 sr->cp0 &= ~SEE_STORED_VIDEO;
387 outb(sr->iobase + ctx_cp0, sr->cp0);
390 sr->cp0 &= ~SEE_STORED_VIDEO;
391 outb(sr->iobase + ctx_cp0, sr->cp0);
393 if (waitvb(sr->iobase)) /* wait for vert blank to start
396 outb(sr->iobase + ctx_cp0, sr->cp0);
397 if (waitvb(sr->iobase)) /* wait for two more to finish acquire */
399 if (waitvb(sr->iobase))
401 sr->cp0 &= ~ACQUIRE; /* turn off acquire and turn on
403 sr->cp0 |= SEE_STORED_VIDEO;
404 outb(sr->iobase + ctx_cp0, sr->cp0);
407 sr->cp0 &= ~PAGE_ROTATE;
408 outb(sr->iobase + ctx_cp0, sr->cp0);
411 sr->cp0 |= PAGE_ROTATE;
412 outb(sr->iobase + ctx_cp0, sr->cp0);
415 bcopy((u_char *) ap->a_data, sr->lutp, LUTSIZE);
416 outb(sr->iobase + ctx_cp0, (u_char) 0);
417 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
418 for (i = 0; i < LUTSIZE; i++) {
419 outb(sr->iobase + ctx_lutaddr, i);
420 outb(sr->iobase + ctx_lutdata, sr->lutp[i]);
422 outb(sr->iobase + ctx_cp0, sr->cp0); /* restore control
424 outb(sr->iobase + ctx_cp1, sr->cp1);
427 bcopy(sr->lutp, (u_char *) ap->a_data, LUTSIZE);
438 { /* wait for a vertical blank, */
439 if (inb(port) == 0xff) /* 0xff means no board present */
442 while ((inb(port) & VERTICAL_BLANK) != 0) {
444 while ((inb(port) & VERTICAL_BLANK) == 0) {