DEVFS - remove dev_ops_add(), dev_ops_get(), and get_dev()
[dragonfly.git] / sys / dev / video / ctx / ctx.c
... / ...
CommitLineData
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
126static int waitvb(int port);
127
128/* state flags */
129#define OPEN (0x01) /* device is open */
130
131#define UNIT(x) ((x) & 0x07)
132
133static int ctxprobe (struct isa_device *devp);
134static int ctxattach (struct isa_device *devp);
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
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,
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;
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
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;
196 make_dev(&ctx_ops, devp->id_unit, 0, 0, 0600,
197 "ctx%d", devp->id_unit);
198 return (1);
199}
200
201static int
202ctxopen(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
255static int
256ctxclose(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
268static int
269ctxwrite(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
322static int
323ctxread(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
374static int
375ctxioctl(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
438static int
439waitvb(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}