HAMMER Utilities: MFC work to date.
[dragonfly.git] / sys / dev / video / ctx / ctx.c
blob2921f74617b3d4ae03d7843858f08a21099f09b7
1 /*
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.12 2008/01/06 16:55:51 swildner 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
75 * include:
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
80 * pixels.
81 * CTX_V_ORGANIZE Set things up so that sequential read
82 * operations access vertical lines of
83 * pixels.
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.
92 * Thus,
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.
99 * Similarly,
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
108 * functions.
112 #include "use_ctx.h"
114 #include <sys/param.h>
115 #include <sys/systm.h>
116 #include <sys/conf.h>
117 #include <sys/device.h>
118 #include <sys/uio.h>
119 #include <sys/kernel.h>
120 #include <sys/malloc.h>
121 #include <bus/isa/i386/isa_device.h>
122 #include "ctxreg.h"
123 #include <machine/ioctl_ctx.h>
124 #include <machine/md_var.h>
126 static int waitvb(int port);
128 /* state flags */
129 #define OPEN (0x01) /* device is open */
131 #define UNIT(x) ((x) & 0x07)
133 static int ctxprobe (struct isa_device *devp);
134 static int ctxattach (struct isa_device *devp);
135 struct isa_driver ctxdriver = {ctxprobe, ctxattach, "ctx"};
137 static d_open_t ctxopen;
138 static d_close_t ctxclose;
139 static d_read_t ctxread;
140 static d_write_t ctxwrite;
141 static d_ioctl_t ctxioctl;
142 #define CDEV_MAJOR 40
144 static struct dev_ops ctx_ops = {
145 { "ctx", CDEV_MAJOR, 0 },
146 .d_open = ctxopen,
147 .d_close = ctxclose,
148 .d_read = ctxread,
149 .d_write = ctxwrite,
150 .d_ioctl = ctxioctl,
154 #define LUTSIZE 256 /* buffer size for Look Up Table (LUT) */
155 #define PAGESIZE 65536 /* size of one video page, 1/4 of the screen */
158 * Per unit shadow registers (because the dumb hardware is RO)
161 static struct ctx_soft_registers {
162 u_char *lutp;
163 u_char cp0;
164 u_char cp1;
165 u_char flag;
166 int iobase;
167 caddr_t maddr;
168 int msize;
169 } ctx_sr[NCTX];
172 static int
173 ctxprobe(struct isa_device * devp)
175 int status;
177 if (inb(devp->id_iobase) == 0xff) /* 0xff only if board absent */
178 status = 0;
179 else
180 status = 1; /*XXX uses only one port? */
181 return (status);
184 static int
185 ctxattach(struct isa_device * devp)
187 struct ctx_soft_registers *sr;
189 sr = &(ctx_sr[devp->id_unit]);
190 sr->cp0 = 0; /* zero out the shadow registers */
191 sr->cp1 = 0; /* and the open flag. wait for */
192 sr->flag = 0; /* open to malloc the LUT space */
193 sr->iobase = devp->id_iobase;
194 sr->maddr = devp->id_maddr;
195 sr->msize = devp->id_msize;
196 dev_ops_add(&ctx_ops, -1, devp->id_unit);
197 make_dev(&ctx_ops, devp->id_unit, 0, 0, 0600,
198 "ctx%d", devp->id_unit);
199 return (1);
202 static int
203 ctxopen(struct dev_open_args *ap)
205 cdev_t dev = ap->a_head.a_dev;
206 struct ctx_soft_registers *sr;
207 u_char unit;
208 int i;
210 unit = UNIT(minor(dev));
212 /* minor number out of range? */
214 if (unit >= NCTX)
215 return (ENXIO);
216 sr = &(ctx_sr[unit]);
218 if (sr->flag != 0) /* someone has already opened us */
219 return (EBUSY);
221 /* get space for the LUT buffer */
223 sr->lutp = kmalloc(LUTSIZE, M_DEVBUF, M_WAITOK);
225 sr->flag = OPEN;
228 Set up the shadow registers. We don't actually write these
229 values to the control ports until after we finish loading the
230 lookup table.
232 sr->cp0 |= SEE_STORED_VIDEO;
233 if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000))
234 sr->cp1 |= AB_SELECT; /* map to B or E if necessary */
235 /* but don't enable RAM */
237 Set up the lookup table initially so that it is transparent.
240 outb(sr->iobase + ctx_cp0, (u_char) 0);
241 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
242 for (i = 0; i < LUTSIZE; i++) {
243 outb(sr->iobase + ctx_lutaddr, (u_char) i);
244 sr->lutp[i] = (u_char) i;
245 outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]);
248 Disable LUT loading, and push the data in the shadow
249 registers into the control ports.
251 outb(sr->iobase + ctx_cp0, sr->cp0);
252 outb(sr->iobase + ctx_cp1, sr->cp1);
253 return (0); /* successful open. All ready to go. */
256 static int
257 ctxclose(struct dev_close_args *ap)
259 cdev_t dev = ap->a_head.a_dev;
260 int unit;
262 unit = UNIT(minor(dev));
263 ctx_sr[unit].flag = 0;
264 kfree(ctx_sr[unit].lutp, M_DEVBUF);
265 ctx_sr[unit].lutp = NULL;
266 return (0);
269 static int
270 ctxwrite(struct dev_write_args *ap)
272 cdev_t dev = ap->a_head.a_dev;
273 struct uio *uio = ap->a_uio;
274 int unit, status = 0;
275 int page, count, offset;
276 struct ctx_soft_registers *sr;
277 u_long ef;
279 unit = UNIT(minor(dev));
280 sr = &(ctx_sr[unit]);
282 if (uio->uio_offset < 0)
283 return (EINVAL);
284 if (uio->uio_offset >= 4 * PAGESIZE)
285 page = 4; /* EOF */
286 else
287 page = (u_int)uio->uio_offset / PAGESIZE;
288 offset = (u_int)uio->uio_offset % PAGESIZE;
289 count = min(uio->uio_resid, PAGESIZE - offset);
290 while ((page >= 0) && (page <= 3) && (count > 0)) {
291 sr->cp0 &= ~3;
292 sr->cp0 |= page;
293 outb(sr->iobase + ctx_cp0, sr->cp0);
296 Before doing the uiomove, we need to "connect" the frame buffer
297 ram to the machine bus. This is done here so that we can have
298 several different boards installed, all sharing the same memory
299 space... each board is only "connected" to the bus when its memory
300 is actually being read or written. All my instincts tell me that
301 I should disable interrupts here, so I have done so.
304 ef = read_eflags();
305 cpu_disable_intr();
306 sr->cp1 |= RAM_ENABLE;
307 outb(sr->iobase + ctx_cp1, sr->cp1);
308 status = uiomove(sr->maddr + offset, count, uio);
309 sr->cp1 &= ~RAM_ENABLE;
310 outb(sr->iobase + ctx_cp1, sr->cp1);
311 write_eflags(ef);
313 page = (u_int)uio->uio_offset / PAGESIZE;
314 offset = (u_int)uio->uio_offset % PAGESIZE;
315 count = min(uio->uio_resid, PAGESIZE - offset);
317 if (uio->uio_resid > 0)
318 return (ENOSPC);
319 else
320 return (status);
323 static int
324 ctxread(struct dev_read_args *ap)
326 cdev_t dev = ap->a_head.a_dev;
327 struct uio *uio = ap->a_uio;
328 int unit, status = 0;
329 int page, count, offset;
330 struct ctx_soft_registers *sr;
331 u_long ef;
333 unit = UNIT(minor(dev));
334 sr = &(ctx_sr[unit]);
336 if (uio->uio_offset < 0)
337 return (EINVAL);
338 if (uio->uio_offset >= 4 * PAGESIZE)
339 page = 4; /* EOF */
340 else
341 page = (u_int)uio->uio_offset / PAGESIZE;
342 offset = (u_int)uio->uio_offset % PAGESIZE;
343 count = min(uio->uio_resid, PAGESIZE - offset);
344 while ((page >= 0) && (page <= 3) && (count > 0)) {
345 sr->cp0 &= ~3;
346 sr->cp0 |= page;
347 outb(sr->iobase + ctx_cp0, sr->cp0);
349 Before doing the uiomove, we need to "connect" the frame buffer
350 ram to the machine bus. This is done here so that we can have
351 several different boards installed, all sharing the same memory
352 space... each board is only "connected" to the bus when its memory
353 is actually being read or written. All my instincts tell me that
354 I should disable interrupts here, so I have done so.
356 ef = read_eflags();
357 cpu_disable_intr();
358 sr->cp1 |= RAM_ENABLE;
359 outb(sr->iobase + ctx_cp1, sr->cp1);
360 status = uiomove(sr->maddr + offset, count, uio);
361 sr->cp1 &= ~RAM_ENABLE;
362 outb(sr->iobase + ctx_cp1, sr->cp1);
363 write_eflags(ef);
365 page = (u_int)uio->uio_offset / PAGESIZE;
366 offset = (u_int)uio->uio_offset % PAGESIZE;
367 count = min(uio->uio_resid, PAGESIZE - offset);
369 if (uio->uio_resid > 0)
370 return (ENOSPC);
371 else
372 return (status);
375 static int
376 ctxioctl(struct dev_ioctl_args *ap)
378 cdev_t dev = ap->a_head.a_dev;
379 int error;
380 int unit, i;
381 struct ctx_soft_registers *sr;
383 error = 0;
384 unit = UNIT(minor(dev));
385 sr = &(ctx_sr[unit]);
387 switch (ap->a_cmd) {
388 case CTX_LIVE:
389 sr->cp0 &= ~SEE_STORED_VIDEO;
390 outb(sr->iobase + ctx_cp0, sr->cp0);
391 break;
392 case CTX_GRAB:
393 sr->cp0 &= ~SEE_STORED_VIDEO;
394 outb(sr->iobase + ctx_cp0, sr->cp0);
395 sr->cp0 |= ACQUIRE;
396 if (waitvb(sr->iobase)) /* wait for vert blank to start
397 * acquire */
398 error = ENODEV;
399 outb(sr->iobase + ctx_cp0, sr->cp0);
400 if (waitvb(sr->iobase)) /* wait for two more to finish acquire */
401 error = ENODEV;
402 if (waitvb(sr->iobase))
403 error = ENODEV;
404 sr->cp0 &= ~ACQUIRE; /* turn off acquire and turn on
405 * display */
406 sr->cp0 |= SEE_STORED_VIDEO;
407 outb(sr->iobase + ctx_cp0, sr->cp0);
408 break;
409 case CTX_H_ORGANIZE:
410 sr->cp0 &= ~PAGE_ROTATE;
411 outb(sr->iobase + ctx_cp0, sr->cp0);
412 break;
413 case CTX_V_ORGANIZE:
414 sr->cp0 |= PAGE_ROTATE;
415 outb(sr->iobase + ctx_cp0, sr->cp0);
416 break;
417 case CTX_SET_LUT:
418 bcopy((u_char *) ap->a_data, sr->lutp, LUTSIZE);
419 outb(sr->iobase + ctx_cp0, (u_char) 0);
420 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
421 for (i = 0; i < LUTSIZE; i++) {
422 outb(sr->iobase + ctx_lutaddr, i);
423 outb(sr->iobase + ctx_lutdata, sr->lutp[i]);
425 outb(sr->iobase + ctx_cp0, sr->cp0); /* restore control
426 * registers */
427 outb(sr->iobase + ctx_cp1, sr->cp1);
428 break;
429 case CTX_GET_LUT:
430 bcopy(sr->lutp, (u_char *) ap->a_data, LUTSIZE);
431 break;
432 default:
433 error = ENODEV;
436 return (error);
439 static int
440 waitvb(int port)
441 { /* wait for a vertical blank, */
442 if (inb(port) == 0xff) /* 0xff means no board present */
443 return (1);
445 while ((inb(port) & VERTICAL_BLANK) != 0) {
447 while ((inb(port) & VERTICAL_BLANK) == 0) {
450 return (0);