Device layer rollup commit.
[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 $
e4c9c0c8 12 * $DragonFly: src/sys/dev/video/ctx/ctx.c,v 1.8 2004/05/19 22:52:53 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>
117#include <sys/uio.h>
118#include <sys/kernel.h>
119#include <sys/malloc.h>
1f2de5d4
MD
120#include <bus/isa/i386/isa_device.h>
121#include "ctxreg.h"
984263bc
MD
122#include <machine/ioctl_ctx.h>
123#include <machine/md_var.h>
124
125static int waitvb(int port);
126
127/* state flags */
128#define OPEN (0x01) /* device is open */
129
130#define UNIT(x) ((x) & 0x07)
131
5ca58d54
RG
132static int ctxprobe (struct isa_device *devp);
133static int ctxattach (struct isa_device *devp);
984263bc
MD
134struct isa_driver ctxdriver = {ctxprobe, ctxattach, "ctx"};
135
136static d_open_t ctxopen;
137static d_close_t ctxclose;
138static d_read_t ctxread;
139static d_write_t ctxwrite;
140static d_ioctl_t ctxioctl;
141#define CDEV_MAJOR 40
142
143static struct cdevsw ctx_cdevsw = {
fabb8ceb
MD
144 /* name */ "ctx",
145 /* maj */ CDEV_MAJOR,
146 /* flags */ 0,
147 /* port */ NULL,
455fcd7e 148 /* clone */ NULL,
fabb8ceb 149
984263bc
MD
150 /* open */ ctxopen,
151 /* close */ ctxclose,
152 /* read */ ctxread,
153 /* write */ ctxwrite,
154 /* ioctl */ ctxioctl,
155 /* poll */ nopoll,
156 /* mmap */ nommap,
157 /* strategy */ nostrategy,
984263bc 158 /* dump */ nodump,
fabb8ceb 159 /* psize */ nopsize
984263bc
MD
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
170static 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
181static int
182ctxprobe(struct isa_device * devp)
183{
184 int status;
984263bc 185
984263bc
MD
186 if (inb(devp->id_iobase) == 0xff) /* 0xff only if board absent */
187 status = 0;
e4c9c0c8
MD
188 else
189 status = 1; /*XXX uses only one port? */
984263bc
MD
190 return (status);
191}
192
193static int
194ctxattach(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;
e4c9c0c8
MD
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);
984263bc
MD
208 return (1);
209}
210
211static int
7b95be2a 212ctxopen(dev_t dev, int flags, int fmt, struct thread *td)
984263bc
MD
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
266static int
7b95be2a 267ctxclose(dev_t dev, int flags, int fmt, struct thread *td)
984263bc
MD
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
278static int
279ctxwrite(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;
7b95be2a 284 u_long ef;
984263bc
MD
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
7b95be2a
MD
311 ef = read_eflags();
312 cpu_disable_intr();
984263bc
MD
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);
7b95be2a 318 write_eflags(ef);
984263bc
MD
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
330static int
331ctxread(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;
7b95be2a 336 u_long ef;
984263bc
MD
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*/
7b95be2a
MD
361 ef = read_eflags();
362 cpu_disable_intr();
984263bc
MD
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);
7b95be2a 368 write_eflags(ef);
984263bc
MD
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
380static int
7b95be2a 381ctxioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
984263bc
MD
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
443static int
444waitvb(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}