Merge branch 'vendor/DIFFUTILS'
[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  */
13
14 /*
15  *
16  *
17  *
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.)
24  *
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.
31  *
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.
37  *
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:
42  *
43  *              Left DIP switch ||      DOWN    |       UP      |
44  *              =================================================
45  *               AB_SELECT =  0 ||    0xA0000   |    0xB0000    |
46  *              -------------------------------------------------
47  *               AB_SELECT = 1  ||    0xD0000   |    0xE0000    |
48  *              ------------------------------------------------
49  *
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.
62  *
63  *          XXX-Note... this driver has only been tested for the
64  *          XXX base = 0xE0000 case!
65  *
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.
70  *
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
74  *          include:
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
79  *                                      pixels.
80  *                      CTX_V_ORGANIZE  Set things up so that sequential read
81  *                                      operations access vertical lines of
82  *                                      pixels.
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.
90  *
91  *          Thus,
92  *              ioctl(fi, CTX_H_ORGANIZE, 0);
93  *              lseek(fi, y*512, SEEK_SET);
94  *              read(fi, buffer, 512);
95  *
96  *          will fill buffer with 512 pixels (unsigned chars) which represent
97  *          the y-th horizontal line of the image.
98  *          Similarly,
99  *              ioctl(fi, CTX_V_ORGANIZE, 0:
100  *              lseek(fi, x*512+y, SEEK_SET);
101  *              read(fi, buffer, 10);
102  *
103  *          will read 10 a vertical line of 10 pixels starting at (x,y).
104  *
105  *          Obviously, this sort of ugliness needs to be hidden away from
106  *          the casual user, with an appropriate set of higher level
107  *          functions.
108  *
109  */
110
111 #include "use_ctx.h"
112
113 #include <sys/param.h>
114 #include <sys/systm.h>
115 #include <sys/conf.h>
116 #include <sys/device.h>
117 #include <sys/uio.h>
118 #include <sys/kernel.h>
119 #include <sys/malloc.h>
120 #include <bus/isa/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
142 static struct dev_ops ctx_ops = {
143         { "ctx", 0, 0 },
144         .d_open =       ctxopen,
145         .d_close =      ctxclose,
146         .d_read =       ctxread,
147         .d_write =      ctxwrite,
148         .d_ioctl =      ctxioctl,
149 };
150
151
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 */
154
155 /*
156  *  Per unit shadow registers (because the dumb hardware is RO)
157 */
158
159 static struct ctx_soft_registers {
160         u_char *lutp;
161         u_char  cp0;
162         u_char  cp1;
163         u_char  flag;
164         int     iobase;
165         caddr_t maddr;
166         int     msize;
167 }       ctx_sr[NCTX];
168
169
170 static int
171 ctxprobe(struct isa_device * devp)
172 {
173         int     status;
174
175         if (inb(devp->id_iobase) == 0xff)       /* 0xff only if board absent */
176                 status = 0;
177         else
178                 status = 1;                     /*XXX uses only one port? */
179         return (status);
180 }
181
182 static int
183 ctxattach(struct isa_device * devp)
184 {
185         struct ctx_soft_registers *sr;
186
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);
196         return (1);
197 }
198
199 static int
200 ctxopen(struct dev_open_args *ap)
201 {
202         cdev_t dev = ap->a_head.a_dev;
203         struct ctx_soft_registers *sr;
204         u_char  unit;
205         int     i;
206
207         unit = UNIT(minor(dev));
208
209         /* minor number out of range? */
210
211         if (unit >= NCTX)
212                 return (ENXIO);
213         sr = &(ctx_sr[unit]);
214
215         if (sr->flag != 0)      /* someone has already opened us */
216                 return (EBUSY);
217
218         /* get space for the LUT buffer */
219
220         sr->lutp = kmalloc(LUTSIZE, M_DEVBUF, M_WAITOK);
221
222         sr->flag = OPEN;
223
224 /*
225         Set up the shadow registers.  We don't actually write these
226         values to the control ports until after we finish loading the
227         lookup table.
228 */
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   */
233 /*
234         Set up the lookup table initially so that it is transparent.
235 */
236
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]);
243         }
244 /*
245         Disable LUT loading, and push the data in the shadow
246         registers into the control ports.
247 */
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. */
251 }
252
253 static int
254 ctxclose(struct dev_close_args *ap)
255 {
256         cdev_t dev = ap->a_head.a_dev;
257         int     unit;
258
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;
263         return (0);
264 }
265
266 static int
267 ctxwrite(struct dev_write_args *ap)
268 {
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;
274         u_long  ef;
275
276         unit = UNIT(minor(dev));
277         sr = &(ctx_sr[unit]);
278
279         if (uio->uio_offset < 0)
280                 return (EINVAL);
281         if (uio->uio_offset >= 4 * PAGESIZE)
282                 page = 4;       /* EOF */
283         else
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)) {
288                 sr->cp0 &= ~3;
289                 sr->cp0 |= page;
290                 outb(sr->iobase + ctx_cp0, sr->cp0);
291
292 /*
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.
299 */
300
301                 ef = read_eflags();
302                 cpu_disable_intr();
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);
308                 write_eflags(ef);
309
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);
313         }
314         if (uio->uio_resid > 0)
315                 return (ENOSPC);
316         else
317                 return (status);
318 }
319
320 static int
321 ctxread(struct dev_read_args *ap)
322 {
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;
328         u_long  ef;
329
330         unit = UNIT(minor(dev));
331         sr = &(ctx_sr[unit]);
332
333         if (uio->uio_offset < 0)
334                 return (EINVAL);
335         if (uio->uio_offset >= 4 * PAGESIZE)
336                 page = 4;       /* EOF */
337         else
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)) {
342                 sr->cp0 &= ~3;
343                 sr->cp0 |= page;
344                 outb(sr->iobase + ctx_cp0, sr->cp0);
345 /*
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.
352 */
353                 ef = read_eflags();
354                 cpu_disable_intr();
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);
360                 write_eflags(ef);
361
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);
365         }
366         if (uio->uio_resid > 0)
367                 return (ENOSPC);
368         else
369                 return (status);
370 }
371
372 static int
373 ctxioctl(struct dev_ioctl_args *ap)
374 {
375         cdev_t dev = ap->a_head.a_dev;
376         int     error;
377         int     unit, i;
378         struct ctx_soft_registers *sr;
379
380         error = 0;
381         unit = UNIT(minor(dev));
382         sr = &(ctx_sr[unit]);
383
384         switch (ap->a_cmd) {
385         case CTX_LIVE:
386                 sr->cp0 &= ~SEE_STORED_VIDEO;
387                 outb(sr->iobase + ctx_cp0, sr->cp0);
388                 break;
389         case CTX_GRAB:
390                 sr->cp0 &= ~SEE_STORED_VIDEO;
391                 outb(sr->iobase + ctx_cp0, sr->cp0);
392                 sr->cp0 |= ACQUIRE;
393                 if (waitvb(sr->iobase)) /* wait for vert blank to start
394                                          * acquire */
395                         error = ENODEV;
396                 outb(sr->iobase + ctx_cp0, sr->cp0);
397                 if (waitvb(sr->iobase)) /* wait for two more to finish acquire */
398                         error = ENODEV;
399                 if (waitvb(sr->iobase))
400                         error = ENODEV;
401                 sr->cp0 &= ~ACQUIRE;    /* turn off acquire and turn on
402                                          * display */
403                 sr->cp0 |= SEE_STORED_VIDEO;
404                 outb(sr->iobase + ctx_cp0, sr->cp0);
405                 break;
406         case CTX_H_ORGANIZE:
407                 sr->cp0 &= ~PAGE_ROTATE;
408                 outb(sr->iobase + ctx_cp0, sr->cp0);
409                 break;
410         case CTX_V_ORGANIZE:
411                 sr->cp0 |= PAGE_ROTATE;
412                 outb(sr->iobase + ctx_cp0, sr->cp0);
413                 break;
414         case CTX_SET_LUT:
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]);
421                 }
422                 outb(sr->iobase + ctx_cp0, sr->cp0);    /* restore control
423                                                          * registers */
424                 outb(sr->iobase + ctx_cp1, sr->cp1);
425                 break;
426         case CTX_GET_LUT:
427                 bcopy(sr->lutp, (u_char *) ap->a_data, LUTSIZE);
428                 break;
429         default:
430                 error = ENODEV;
431         }
432
433         return (error);
434 }
435
436 static int
437 waitvb(int port)
438 {                               /* wait for a vertical blank,  */
439         if (inb(port) == 0xff)  /* 0xff means no board present */
440                 return (1);
441
442         while ((inb(port) & VERTICAL_BLANK) != 0) {
443         }
444         while ((inb(port) & VERTICAL_BLANK) == 0) {
445         }
446
447         return (0);
448 }