DEVFS - remove dev_ops_add(), dev_ops_get(), and get_dev()
[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.13 2008/08/02 01:14:43 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/device.h>
118 #include <sys/uio.h>
119 #include <sys/kernel.h>
120 #include <sys/malloc.h>
121 #include <bus/isa/isa_device.h>
122 #include "ctxreg.h"
123 #include <machine/ioctl_ctx.h>
124 #include <machine/md_var.h>
125
126 static int     waitvb(int port);
127
128 /* state flags */
129 #define   OPEN        (0x01)    /* device is open */
130
131 #define   UNIT(x) ((x) & 0x07)
132
133 static int      ctxprobe (struct isa_device *devp);
134 static int      ctxattach (struct isa_device *devp);
135 struct isa_driver ctxdriver = {ctxprobe, ctxattach, "ctx"};
136
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
143
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,
151 };
152
153
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 */
156
157 /*
158  *  Per unit shadow registers (because the dumb hardware is RO)
159 */
160
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];
170
171
172 static int
173 ctxprobe(struct isa_device * devp)
174 {
175         int     status;
176
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);
182 }
183
184 static int
185 ctxattach(struct isa_device * devp)
186 {
187         struct ctx_soft_registers *sr;
188
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         make_dev(&ctx_ops, devp->id_unit, 0, 0, 0600, 
197                  "ctx%d", devp->id_unit);
198         return (1);
199 }
200
201 static int
202 ctxopen(struct dev_open_args *ap)
203 {
204         cdev_t dev = ap->a_head.a_dev;
205         struct ctx_soft_registers *sr;
206         u_char  unit;
207         int     i;
208
209         unit = UNIT(minor(dev));
210
211         /* minor number out of range? */
212
213         if (unit >= NCTX)
214                 return (ENXIO);
215         sr = &(ctx_sr[unit]);
216
217         if (sr->flag != 0)      /* someone has already opened us */
218                 return (EBUSY);
219
220         /* get space for the LUT buffer */
221
222         sr->lutp = kmalloc(LUTSIZE, M_DEVBUF, M_WAITOK);
223
224         sr->flag = OPEN;
225
226 /*
227         Set up the shadow registers.  We don't actually write these
228         values to the control ports until after we finish loading the
229         lookup table.
230 */
231         sr->cp0 |= SEE_STORED_VIDEO;
232         if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000))
233                 sr->cp1 |= AB_SELECT;   /* map to B or E if necessary */
234         /* but don't enable RAM   */
235 /*
236         Set up the lookup table initially so that it is transparent.
237 */
238
239         outb(sr->iobase + ctx_cp0, (u_char) 0);
240         outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
241         for (i = 0; i < LUTSIZE; i++) {
242                 outb(sr->iobase + ctx_lutaddr, (u_char) i);
243                 sr->lutp[i] = (u_char) i;
244                 outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]);
245         }
246 /*
247         Disable LUT loading, and push the data in the shadow
248         registers into the control ports.
249 */
250         outb(sr->iobase + ctx_cp0, sr->cp0);
251         outb(sr->iobase + ctx_cp1, sr->cp1);
252         return (0);     /* successful open.  All ready to go. */
253 }
254
255 static int
256 ctxclose(struct dev_close_args *ap)
257 {
258         cdev_t dev = ap->a_head.a_dev;
259         int     unit;
260
261         unit = UNIT(minor(dev));
262         ctx_sr[unit].flag = 0;
263         kfree(ctx_sr[unit].lutp, M_DEVBUF);
264         ctx_sr[unit].lutp = NULL;
265         return (0);
266 }
267
268 static int
269 ctxwrite(struct dev_write_args *ap)
270 {
271         cdev_t dev = ap->a_head.a_dev;
272         struct uio *uio = ap->a_uio;
273         int     unit, status = 0;
274         int     page, count, offset;
275         struct ctx_soft_registers *sr;
276         u_long  ef;
277
278         unit = UNIT(minor(dev));
279         sr = &(ctx_sr[unit]);
280
281         if (uio->uio_offset < 0)
282                 return (EINVAL);
283         if (uio->uio_offset >= 4 * PAGESIZE)
284                 page = 4;       /* EOF */
285         else
286                 page = (u_int)uio->uio_offset / PAGESIZE;
287         offset = (u_int)uio->uio_offset % PAGESIZE;
288         count = min(uio->uio_resid, PAGESIZE - offset);
289         while ((page >= 0) && (page <= 3) && (count > 0)) {
290                 sr->cp0 &= ~3;
291                 sr->cp0 |= page;
292                 outb(sr->iobase + ctx_cp0, sr->cp0);
293
294 /*
295         Before doing the uiomove, we need to "connect" the frame buffer
296         ram to the machine bus.  This is done here so that we can have
297         several different boards installed, all sharing the same memory
298         space... each board is only "connected" to the bus when its memory
299         is actually being read or written.  All my instincts tell me that
300         I should disable interrupts here, so I have done so.
301 */
302
303                 ef = read_eflags();
304                 cpu_disable_intr();
305                 sr->cp1 |= RAM_ENABLE;
306                 outb(sr->iobase + ctx_cp1, sr->cp1);
307                 status = uiomove(sr->maddr + offset, count, uio);
308                 sr->cp1 &= ~RAM_ENABLE;
309                 outb(sr->iobase + ctx_cp1, sr->cp1);
310                 write_eflags(ef);
311
312                 page = (u_int)uio->uio_offset / PAGESIZE;
313                 offset = (u_int)uio->uio_offset % PAGESIZE;
314                 count = min(uio->uio_resid, PAGESIZE - offset);
315         }
316         if (uio->uio_resid > 0)
317                 return (ENOSPC);
318         else
319                 return (status);
320 }
321
322 static int
323 ctxread(struct dev_read_args *ap)
324 {
325         cdev_t dev = ap->a_head.a_dev;
326         struct uio *uio = ap->a_uio;
327         int     unit, status = 0;
328         int     page, count, offset;
329         struct ctx_soft_registers *sr;
330         u_long  ef;
331
332         unit = UNIT(minor(dev));
333         sr = &(ctx_sr[unit]);
334
335         if (uio->uio_offset < 0)
336                 return (EINVAL);
337         if (uio->uio_offset >= 4 * PAGESIZE)
338                 page = 4;       /* EOF */
339         else
340                 page = (u_int)uio->uio_offset / PAGESIZE;
341         offset = (u_int)uio->uio_offset % PAGESIZE;
342         count = min(uio->uio_resid, PAGESIZE - offset);
343         while ((page >= 0) && (page <= 3) && (count > 0)) {
344                 sr->cp0 &= ~3;
345                 sr->cp0 |= page;
346                 outb(sr->iobase + ctx_cp0, sr->cp0);
347 /*
348         Before doing the uiomove, we need to "connect" the frame buffer
349         ram to the machine bus.  This is done here so that we can have
350         several different boards installed, all sharing the same memory
351         space... each board is only "connected" to the bus when its memory
352         is actually being read or written.  All my instincts tell me that
353         I should disable interrupts here, so I have done so.
354 */
355                 ef = read_eflags();
356                 cpu_disable_intr();
357                 sr->cp1 |= RAM_ENABLE;
358                 outb(sr->iobase + ctx_cp1, sr->cp1);
359                 status = uiomove(sr->maddr + offset, count, uio);
360                 sr->cp1 &= ~RAM_ENABLE;
361                 outb(sr->iobase + ctx_cp1, sr->cp1);
362                 write_eflags(ef);
363
364                 page = (u_int)uio->uio_offset / PAGESIZE;
365                 offset = (u_int)uio->uio_offset % PAGESIZE;
366                 count = min(uio->uio_resid, PAGESIZE - offset);
367         }
368         if (uio->uio_resid > 0)
369                 return (ENOSPC);
370         else
371                 return (status);
372 }
373
374 static int
375 ctxioctl(struct dev_ioctl_args *ap)
376 {
377         cdev_t dev = ap->a_head.a_dev;
378         int     error;
379         int     unit, i;
380         struct ctx_soft_registers *sr;
381
382         error = 0;
383         unit = UNIT(minor(dev));
384         sr = &(ctx_sr[unit]);
385
386         switch (ap->a_cmd) {
387         case CTX_LIVE:
388                 sr->cp0 &= ~SEE_STORED_VIDEO;
389                 outb(sr->iobase + ctx_cp0, sr->cp0);
390                 break;
391         case CTX_GRAB:
392                 sr->cp0 &= ~SEE_STORED_VIDEO;
393                 outb(sr->iobase + ctx_cp0, sr->cp0);
394                 sr->cp0 |= ACQUIRE;
395                 if (waitvb(sr->iobase)) /* wait for vert blank to start
396                                          * acquire */
397                         error = ENODEV;
398                 outb(sr->iobase + ctx_cp0, sr->cp0);
399                 if (waitvb(sr->iobase)) /* wait for two more to finish acquire */
400                         error = ENODEV;
401                 if (waitvb(sr->iobase))
402                         error = ENODEV;
403                 sr->cp0 &= ~ACQUIRE;    /* turn off acquire and turn on
404                                          * display */
405                 sr->cp0 |= SEE_STORED_VIDEO;
406                 outb(sr->iobase + ctx_cp0, sr->cp0);
407                 break;
408         case CTX_H_ORGANIZE:
409                 sr->cp0 &= ~PAGE_ROTATE;
410                 outb(sr->iobase + ctx_cp0, sr->cp0);
411                 break;
412         case CTX_V_ORGANIZE:
413                 sr->cp0 |= PAGE_ROTATE;
414                 outb(sr->iobase + ctx_cp0, sr->cp0);
415                 break;
416         case CTX_SET_LUT:
417                 bcopy((u_char *) ap->a_data, sr->lutp, LUTSIZE);
418                 outb(sr->iobase + ctx_cp0, (u_char) 0);
419                 outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
420                 for (i = 0; i < LUTSIZE; i++) {
421                         outb(sr->iobase + ctx_lutaddr, i);
422                         outb(sr->iobase + ctx_lutdata, sr->lutp[i]);
423                 }
424                 outb(sr->iobase + ctx_cp0, sr->cp0);    /* restore control
425                                                          * registers */
426                 outb(sr->iobase + ctx_cp1, sr->cp1);
427                 break;
428         case CTX_GET_LUT:
429                 bcopy(sr->lutp, (u_char *) ap->a_data, LUTSIZE);
430                 break;
431         default:
432                 error = ENODEV;
433         }
434
435         return (error);
436 }
437
438 static int
439 waitvb(int port)
440 {                               /* wait for a vertical blank,  */
441         if (inb(port) == 0xff)  /* 0xff means no board present */
442                 return (1);
443
444         while ((inb(port) & VERTICAL_BLANK) != 0) {
445         }
446         while ((inb(port) & VERTICAL_BLANK) == 0) {
447         }
448
449         return (0);
450 }