lets go ahead and commit this before we hit the network interfaces
[dragonfly.git] / sys / dev / video / ctx / ctx.c
1 /*
2  * CORTEX-I Frame Grabber driver V1.0
3  *
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.
10  *
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.6 2003/08/27 06:48:15 rob Exp $
13  */
14
15 /*
16  *
17  *
18  *
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.)
25  *
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.
32  *
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.
38  *
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:
43  *
44  *              Left DIP switch ||      DOWN    |       UP      |
45  *              =================================================
46  *               AB_SELECT =  0 ||    0xA0000   |    0xB0000    |
47  *              -------------------------------------------------
48  *               AB_SELECT = 1  ||    0xD0000   |    0xE0000    |
49  *              ------------------------------------------------
50  *
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.
63  *
64  *          XXX-Note... this driver has only been tested for the
65  *          XXX base = 0xE0000 case!
66  *
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.
71  *
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.
91  *
92  *          Thus,
93  *              ioctl(fi, CTX_H_ORGANIZE, 0);
94  *              lseek(fi, y*512, SEEK_SET);
95  *              read(fi, buffer, 512);
96  *
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);
103  *
104  *          will read 10 a vertical line of 10 pixels starting at (x,y).
105  *
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.
109  *
110  */
111
112 #include "use_ctx.h"
113
114 #include <sys/param.h>
115 #include <sys/systm.h>
116 #include <sys/conf.h>
117 #include <sys/uio.h>
118 #include <sys/kernel.h>
119 #include <sys/malloc.h>
120 #include <bus/isa/i386/isa_device.h>
121 #include "ctxreg.h"
122 #include <machine/ioctl_ctx.h>
123 #include <machine/md_var.h>
124
125 static int     waitvb(int port);
126
127 /* state flags */
128 #define   OPEN        (0x01)    /* device is open */
129
130 #define   UNIT(x) ((x) & 0x07)
131
132 static int      ctxprobe (struct isa_device *devp);
133 static int      ctxattach (struct isa_device *devp);
134 struct isa_driver ctxdriver = {ctxprobe, ctxattach, "ctx"};
135
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
142
143 static struct cdevsw ctx_cdevsw = {
144         /* name */      "ctx",
145         /* maj */       CDEV_MAJOR,
146         /* flags */     0,
147         /* port */      NULL,
148         /* autoq */     0,
149
150         /* open */      ctxopen,
151         /* close */     ctxclose,
152         /* read */      ctxread,
153         /* write */     ctxwrite,
154         /* ioctl */     ctxioctl,
155         /* poll */      nopoll,
156         /* mmap */      nommap,
157         /* strategy */  nostrategy,
158         /* dump */      nodump,
159         /* psize */     nopsize
160 };
161
162
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 */
165
166 /*
167  *  Per unit shadow registers (because the dumb hardware is RO)
168 */
169
170 static struct ctx_soft_registers {
171         u_char *lutp;
172         u_char  cp0;
173         u_char  cp1;
174         u_char  flag;
175         int     iobase;
176         caddr_t maddr;
177         int     msize;
178 }       ctx_sr[NCTX];
179
180
181 static int
182 ctxprobe(struct isa_device * devp)
183 {
184         int     status;
185         static int once;
186
187         if (!once++)
188                 cdevsw_add(&ctx_cdevsw);
189         if (inb(devp->id_iobase) == 0xff)       /* 0xff only if board absent */
190                 status = 0;
191         else {
192                 status = 1; /*XXX uses only one port? */
193         }
194         return (status);
195 }
196
197 static int
198 ctxattach(struct isa_device * devp)
199 {
200         struct ctx_soft_registers *sr;
201
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);
210         return (1);
211 }
212
213 static int
214 ctxopen(dev_t dev, int flags, int fmt, struct thread *td)
215 {
216         struct ctx_soft_registers *sr;
217         u_char  unit;
218         int     i;
219
220         unit = UNIT(minor(dev));
221
222         /* minor number out of range? */
223
224         if (unit >= NCTX)
225                 return (ENXIO);
226         sr = &(ctx_sr[unit]);
227
228         if (sr->flag != 0)      /* someone has already opened us */
229                 return (EBUSY);
230
231         /* get space for the LUT buffer */
232
233         sr->lutp = malloc(LUTSIZE, M_DEVBUF, M_WAITOK);
234         if (sr->lutp == NULL)
235                 return (ENOMEM);
236
237         sr->flag = OPEN;
238
239 /*
240         Set up the shadow registers.  We don't actually write these
241         values to the control ports until after we finish loading the
242         lookup table.
243 */
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   */
248 /*
249         Set up the lookup table initially so that it is transparent.
250 */
251
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]);
258         }
259 /*
260         Disable LUT loading, and push the data in the shadow
261         registers into the control ports.
262 */
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. */
266 }
267
268 static int
269 ctxclose(dev_t dev, int flags, int fmt, struct thread *td)
270 {
271         int     unit;
272
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;
277         return (0);
278 }
279
280 static int
281 ctxwrite(dev_t dev, struct uio * uio, int ioflag)
282 {
283         int     unit, status = 0;
284         int     page, count, offset;
285         struct ctx_soft_registers *sr;
286         u_long  ef;
287
288         unit = UNIT(minor(dev));
289         sr = &(ctx_sr[unit]);
290
291         if (uio->uio_offset < 0)
292                 return (EINVAL);
293         if (uio->uio_offset >= 4 * PAGESIZE)
294                 page = 4;       /* EOF */
295         else
296                 page = (u_int)uio->uio_offset / PAGESIZE;
297         offset = (u_int)uio->uio_offset % PAGESIZE;
298         count = min(uio->uio_resid, PAGESIZE - offset);
299         while ((page >= 0) && (page <= 3) && (count > 0)) {
300                 sr->cp0 &= ~3;
301                 sr->cp0 |= page;
302                 outb(sr->iobase + ctx_cp0, sr->cp0);
303
304 /*
305         Before doing the uiomove, we need to "connect" the frame buffer
306         ram to the machine bus.  This is done here so that we can have
307         several different boards installed, all sharing the same memory
308         space... each board is only "connected" to the bus when its memory
309         is actually being read or written.  All my instincts tell me that
310         I should disable interrupts here, so I have done so.
311 */
312
313                 ef = read_eflags();
314                 cpu_disable_intr();
315                 sr->cp1 |= RAM_ENABLE;
316                 outb(sr->iobase + ctx_cp1, sr->cp1);
317                 status = uiomove(sr->maddr + offset, count, uio);
318                 sr->cp1 &= ~RAM_ENABLE;
319                 outb(sr->iobase + ctx_cp1, sr->cp1);
320                 write_eflags(ef);
321
322                 page = (u_int)uio->uio_offset / PAGESIZE;
323                 offset = (u_int)uio->uio_offset % PAGESIZE;
324                 count = min(uio->uio_resid, PAGESIZE - offset);
325         }
326         if (uio->uio_resid > 0)
327                 return (ENOSPC);
328         else
329                 return (status);
330 }
331
332 static int
333 ctxread(dev_t dev, struct uio * uio, int ioflag)
334 {
335         int     unit, status = 0;
336         int     page, count, offset;
337         struct ctx_soft_registers *sr;
338         u_long  ef;
339
340         unit = UNIT(minor(dev));
341         sr = &(ctx_sr[unit]);
342
343         if (uio->uio_offset < 0)
344                 return (EINVAL);
345         if (uio->uio_offset >= 4 * PAGESIZE)
346                 page = 4;       /* EOF */
347         else
348                 page = (u_int)uio->uio_offset / PAGESIZE;
349         offset = (u_int)uio->uio_offset % PAGESIZE;
350         count = min(uio->uio_resid, PAGESIZE - offset);
351         while ((page >= 0) && (page <= 3) && (count > 0)) {
352                 sr->cp0 &= ~3;
353                 sr->cp0 |= page;
354                 outb(sr->iobase + ctx_cp0, sr->cp0);
355 /*
356         Before doing the uiomove, we need to "connect" the frame buffer
357         ram to the machine bus.  This is done here so that we can have
358         several different boards installed, all sharing the same memory
359         space... each board is only "connected" to the bus when its memory
360         is actually being read or written.  All my instincts tell me that
361         I should disable interrupts here, so I have done so.
362 */
363                 ef = read_eflags();
364                 cpu_disable_intr();
365                 sr->cp1 |= RAM_ENABLE;
366                 outb(sr->iobase + ctx_cp1, sr->cp1);
367                 status = uiomove(sr->maddr + offset, count, uio);
368                 sr->cp1 &= ~RAM_ENABLE;
369                 outb(sr->iobase + ctx_cp1, sr->cp1);
370                 write_eflags(ef);
371
372                 page = (u_int)uio->uio_offset / PAGESIZE;
373                 offset = (u_int)uio->uio_offset % PAGESIZE;
374                 count = min(uio->uio_resid, PAGESIZE - offset);
375         }
376         if (uio->uio_resid > 0)
377                 return (ENOSPC);
378         else
379                 return (status);
380 }
381
382 static int
383 ctxioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
384 {
385         int     error;
386         int     unit, i;
387         struct ctx_soft_registers *sr;
388
389         error = 0;
390         unit = UNIT(minor(dev));
391         sr = &(ctx_sr[unit]);
392
393         switch (cmd) {
394         case CTX_LIVE:
395                 sr->cp0 &= ~SEE_STORED_VIDEO;
396                 outb(sr->iobase + ctx_cp0, sr->cp0);
397                 break;
398         case CTX_GRAB:
399                 sr->cp0 &= ~SEE_STORED_VIDEO;
400                 outb(sr->iobase + ctx_cp0, sr->cp0);
401                 sr->cp0 |= ACQUIRE;
402                 if (waitvb(sr->iobase)) /* wait for vert blank to start
403                                          * acquire */
404                         error = ENODEV;
405                 outb(sr->iobase + ctx_cp0, sr->cp0);
406                 if (waitvb(sr->iobase)) /* wait for two more to finish acquire */
407                         error = ENODEV;
408                 if (waitvb(sr->iobase))
409                         error = ENODEV;
410                 sr->cp0 &= ~ACQUIRE;    /* turn off acquire and turn on
411                                          * display */
412                 sr->cp0 |= SEE_STORED_VIDEO;
413                 outb(sr->iobase + ctx_cp0, sr->cp0);
414                 break;
415         case CTX_H_ORGANIZE:
416                 sr->cp0 &= ~PAGE_ROTATE;
417                 outb(sr->iobase + ctx_cp0, sr->cp0);
418                 break;
419         case CTX_V_ORGANIZE:
420                 sr->cp0 |= PAGE_ROTATE;
421                 outb(sr->iobase + ctx_cp0, sr->cp0);
422                 break;
423         case CTX_SET_LUT:
424                 bcopy((u_char *) data, sr->lutp, LUTSIZE);
425                 outb(sr->iobase + ctx_cp0, (u_char) 0);
426                 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
427                 for (i = 0; i < LUTSIZE; i++) {
428                         outb(sr->iobase + ctx_lutaddr, i);
429                         outb(sr->iobase + ctx_lutdata, sr->lutp[i]);
430                 }
431                 outb(sr->iobase + ctx_cp0, sr->cp0);    /* restore control
432                                                          * registers */
433                 outb(sr->iobase + ctx_cp1, sr->cp1);
434                 break;
435         case CTX_GET_LUT:
436                 bcopy(sr->lutp, (u_char *) data, LUTSIZE);
437                 break;
438         default:
439                 error = ENODEV;
440         }
441
442         return (error);
443 }
444
445 static int
446 waitvb(int port)
447 {                               /* wait for a vertical blank,  */
448         if (inb(port) == 0xff)  /* 0xff means no board present */
449                 return (1);
450
451         while ((inb(port) & VERTICAL_BLANK) != 0) {
452         }
453         while ((inb(port) & VERTICAL_BLANK) == 0) {
454         }
455
456         return (0);
457 }