timeout/untimeout ==> callout_*
[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.8 2004/05/19 22:52:53 dillon 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         /* clone */     NULL,
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
186         if (inb(devp->id_iobase) == 0xff)       /* 0xff only if board absent */
187                 status = 0;
188         else
189                 status = 1;                     /*XXX uses only one port? */
190         return (status);
191 }
192
193 static int
194 ctxattach(struct isa_device * devp)
195 {
196         struct ctx_soft_registers *sr;
197
198         sr = &(ctx_sr[devp->id_unit]);
199         sr->cp0 = 0;    /* zero out the shadow registers */
200         sr->cp1 = 0;    /* and the open flag.  wait for  */
201         sr->flag = 0;   /* open to malloc the LUT space  */
202         sr->iobase = devp->id_iobase;
203         sr->maddr = devp->id_maddr;
204         sr->msize = devp->id_msize;
205         cdevsw_add(&ctx_cdevsw, -1, devp->id_unit);
206         make_dev(&ctx_cdevsw, devp->id_unit, 0, 0, 0600, 
207                 "ctx%d", devp->id_unit);
208         return (1);
209 }
210
211 static int
212 ctxopen(dev_t dev, int flags, int fmt, struct thread *td)
213 {
214         struct ctx_soft_registers *sr;
215         u_char  unit;
216         int     i;
217
218         unit = UNIT(minor(dev));
219
220         /* minor number out of range? */
221
222         if (unit >= NCTX)
223                 return (ENXIO);
224         sr = &(ctx_sr[unit]);
225
226         if (sr->flag != 0)      /* someone has already opened us */
227                 return (EBUSY);
228
229         /* get space for the LUT buffer */
230
231         sr->lutp = malloc(LUTSIZE, M_DEVBUF, M_WAITOK);
232         if (sr->lutp == NULL)
233                 return (ENOMEM);
234
235         sr->flag = OPEN;
236
237 /*
238         Set up the shadow registers.  We don't actually write these
239         values to the control ports until after we finish loading the
240         lookup table.
241 */
242         sr->cp0 |= SEE_STORED_VIDEO;
243         if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000))
244                 sr->cp1 |= AB_SELECT;   /* map to B or E if necessary */
245         /* but don't enable RAM   */
246 /*
247         Set up the lookup table initially so that it is transparent.
248 */
249
250         outb(sr->iobase + ctx_cp0, (u_char) 0);
251         outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
252         for (i = 0; i < LUTSIZE; i++) {
253                 outb(sr->iobase + ctx_lutaddr, (u_char) i);
254                 sr->lutp[i] = (u_char) i;
255                 outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]);
256         }
257 /*
258         Disable LUT loading, and push the data in the shadow
259         registers into the control ports.
260 */
261         outb(sr->iobase + ctx_cp0, sr->cp0);
262         outb(sr->iobase + ctx_cp1, sr->cp1);
263         return (0);     /* successful open.  All ready to go. */
264 }
265
266 static int
267 ctxclose(dev_t dev, int flags, int fmt, struct thread *td)
268 {
269         int     unit;
270
271         unit = UNIT(minor(dev));
272         ctx_sr[unit].flag = 0;
273         free(ctx_sr[unit].lutp, M_DEVBUF);
274         ctx_sr[unit].lutp = NULL;
275         return (0);
276 }
277
278 static int
279 ctxwrite(dev_t dev, struct uio * uio, int ioflag)
280 {
281         int     unit, status = 0;
282         int     page, count, offset;
283         struct ctx_soft_registers *sr;
284         u_long  ef;
285
286         unit = UNIT(minor(dev));
287         sr = &(ctx_sr[unit]);
288
289         if (uio->uio_offset < 0)
290                 return (EINVAL);
291         if (uio->uio_offset >= 4 * PAGESIZE)
292                 page = 4;       /* EOF */
293         else
294                 page = (u_int)uio->uio_offset / PAGESIZE;
295         offset = (u_int)uio->uio_offset % PAGESIZE;
296         count = min(uio->uio_resid, PAGESIZE - offset);
297         while ((page >= 0) && (page <= 3) && (count > 0)) {
298                 sr->cp0 &= ~3;
299                 sr->cp0 |= page;
300                 outb(sr->iobase + ctx_cp0, sr->cp0);
301
302 /*
303         Before doing the uiomove, we need to "connect" the frame buffer
304         ram to the machine bus.  This is done here so that we can have
305         several different boards installed, all sharing the same memory
306         space... each board is only "connected" to the bus when its memory
307         is actually being read or written.  All my instincts tell me that
308         I should disable interrupts here, so I have done so.
309 */
310
311                 ef = read_eflags();
312                 cpu_disable_intr();
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);
318                 write_eflags(ef);
319
320                 page = (u_int)uio->uio_offset / PAGESIZE;
321                 offset = (u_int)uio->uio_offset % PAGESIZE;
322                 count = min(uio->uio_resid, PAGESIZE - offset);
323         }
324         if (uio->uio_resid > 0)
325                 return (ENOSPC);
326         else
327                 return (status);
328 }
329
330 static int
331 ctxread(dev_t dev, struct uio * uio, int ioflag)
332 {
333         int     unit, status = 0;
334         int     page, count, offset;
335         struct ctx_soft_registers *sr;
336         u_long  ef;
337
338         unit = UNIT(minor(dev));
339         sr = &(ctx_sr[unit]);
340
341         if (uio->uio_offset < 0)
342                 return (EINVAL);
343         if (uio->uio_offset >= 4 * PAGESIZE)
344                 page = 4;       /* EOF */
345         else
346                 page = (u_int)uio->uio_offset / PAGESIZE;
347         offset = (u_int)uio->uio_offset % PAGESIZE;
348         count = min(uio->uio_resid, PAGESIZE - offset);
349         while ((page >= 0) && (page <= 3) && (count > 0)) {
350                 sr->cp0 &= ~3;
351                 sr->cp0 |= page;
352                 outb(sr->iobase + ctx_cp0, sr->cp0);
353 /*
354         Before doing the uiomove, we need to "connect" the frame buffer
355         ram to the machine bus.  This is done here so that we can have
356         several different boards installed, all sharing the same memory
357         space... each board is only "connected" to the bus when its memory
358         is actually being read or written.  All my instincts tell me that
359         I should disable interrupts here, so I have done so.
360 */
361                 ef = read_eflags();
362                 cpu_disable_intr();
363                 sr->cp1 |= RAM_ENABLE;
364                 outb(sr->iobase + ctx_cp1, sr->cp1);
365                 status = uiomove(sr->maddr + offset, count, uio);
366                 sr->cp1 &= ~RAM_ENABLE;
367                 outb(sr->iobase + ctx_cp1, sr->cp1);
368                 write_eflags(ef);
369
370                 page = (u_int)uio->uio_offset / PAGESIZE;
371                 offset = (u_int)uio->uio_offset % PAGESIZE;
372                 count = min(uio->uio_resid, PAGESIZE - offset);
373         }
374         if (uio->uio_resid > 0)
375                 return (ENOSPC);
376         else
377                 return (status);
378 }
379
380 static int
381 ctxioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
382 {
383         int     error;
384         int     unit, i;
385         struct ctx_soft_registers *sr;
386
387         error = 0;
388         unit = UNIT(minor(dev));
389         sr = &(ctx_sr[unit]);
390
391         switch (cmd) {
392         case CTX_LIVE:
393                 sr->cp0 &= ~SEE_STORED_VIDEO;
394                 outb(sr->iobase + ctx_cp0, sr->cp0);
395                 break;
396         case CTX_GRAB:
397                 sr->cp0 &= ~SEE_STORED_VIDEO;
398                 outb(sr->iobase + ctx_cp0, sr->cp0);
399                 sr->cp0 |= ACQUIRE;
400                 if (waitvb(sr->iobase)) /* wait for vert blank to start
401                                          * acquire */
402                         error = ENODEV;
403                 outb(sr->iobase + ctx_cp0, sr->cp0);
404                 if (waitvb(sr->iobase)) /* wait for two more to finish acquire */
405                         error = ENODEV;
406                 if (waitvb(sr->iobase))
407                         error = ENODEV;
408                 sr->cp0 &= ~ACQUIRE;    /* turn off acquire and turn on
409                                          * display */
410                 sr->cp0 |= SEE_STORED_VIDEO;
411                 outb(sr->iobase + ctx_cp0, sr->cp0);
412                 break;
413         case CTX_H_ORGANIZE:
414                 sr->cp0 &= ~PAGE_ROTATE;
415                 outb(sr->iobase + ctx_cp0, sr->cp0);
416                 break;
417         case CTX_V_ORGANIZE:
418                 sr->cp0 |= PAGE_ROTATE;
419                 outb(sr->iobase + ctx_cp0, sr->cp0);
420                 break;
421         case CTX_SET_LUT:
422                 bcopy((u_char *) data, sr->lutp, LUTSIZE);
423                 outb(sr->iobase + ctx_cp0, (u_char) 0);
424                 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
425                 for (i = 0; i < LUTSIZE; i++) {
426                         outb(sr->iobase + ctx_lutaddr, i);
427                         outb(sr->iobase + ctx_lutdata, sr->lutp[i]);
428                 }
429                 outb(sr->iobase + ctx_cp0, sr->cp0);    /* restore control
430                                                          * registers */
431                 outb(sr->iobase + ctx_cp1, sr->cp1);
432                 break;
433         case CTX_GET_LUT:
434                 bcopy(sr->lutp, (u_char *) data, LUTSIZE);
435                 break;
436         default:
437                 error = ENODEV;
438         }
439
440         return (error);
441 }
442
443 static int
444 waitvb(int port)
445 {                               /* wait for a vertical blank,  */
446         if (inb(port) == 0xff)  /* 0xff means no board present */
447                 return (1);
448
449         while ((inb(port) & VERTICAL_BLANK) != 0) {
450         }
451         while ((inb(port) & VERTICAL_BLANK) == 0) {
452         }
453
454         return (0);
455 }