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