File:  [DragonFly] / src / sys / dev / video / ctx / ctx.c
Revision 1.7: download - view: text, annotated - select for diffs
Thu May 13 23:49:22 2004 UTC (10 years, 3 months ago) by dillon
Branches: MAIN
CVS tags: HEAD
device switch 1/many: Remove d_autoq, add d_clone (where d_autoq was).

d_autoq was used to allow the device port dispatch to mix old-style synchronous
calls with new style messaging calls within a particular device.  It was never
used for that purpose.

d_clone will be more fully implemented as work continues.  We are going to
install d_port in the dev_t (struct specinfo) structure itself and d_clone
will be needed to allow devices to 'revector' the port on a minor-number
by minor-number basis, in particular allowing minor numbers to be directly
dispatched to distinct threads.  This is something we will be needing later
on.

    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.7 2004/05/13 23:49:22 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/uio.h>
  118: #include <sys/kernel.h>
  119: #include <sys/malloc.h>
  120: #include <bus/isa/i386/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: #define CDEV_MAJOR 40
  142: 
  143: static struct cdevsw ctx_cdevsw = {
  144: 	/* name */	"ctx",
  145: 	/* maj */	CDEV_MAJOR,
  146: 	/* flags */	0,
  147: 	/* port */	NULL,
  148: 	/* clone */	NULL,
  149: 
  150: 	/* open */	ctxopen,
  151: 	/* close */	ctxclose,
  152: 	/* read */	ctxread,
  153: 	/* write */	ctxwrite,
  154: 	/* ioctl */	ctxioctl,
  155: 	/* poll */	nopoll,
  156: 	/* mmap */	nommap,
  157: 	/* strategy */	nostrategy,
  158: 	/* dump */	nodump,
  159: 	/* psize */	nopsize
  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: 
  170: static 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: 
  181: static int
  182: ctxprobe(struct isa_device * devp)
  183: {
  184: 	int     status;
  185: 	static int once;
  186: 
  187: 	if (!once++)
  188: 		cdevsw_add(&ctx_cdevsw);
  189: 	if (inb(devp->id_iobase) == 0xff)	/* 0xff only if board absent */
  190: 		status = 0;
  191: 	else {
  192: 		status = 1; /*XXX uses only one port? */
  193: 	}
  194: 	return (status);
  195: }
  196: 
  197: static int
  198: ctxattach(struct isa_device * devp)
  199: {
  200: 	struct ctx_soft_registers *sr;
  201: 
  202: 	sr = &(ctx_sr[devp->id_unit]);
  203: 	sr->cp0 = 0;	/* zero out the shadow registers */
  204: 	sr->cp1 = 0;	/* and the open flag.  wait for  */
  205: 	sr->flag = 0;	/* open to malloc the LUT space  */
  206: 	sr->iobase = devp->id_iobase;
  207: 	sr->maddr = devp->id_maddr;
  208: 	sr->msize = devp->id_msize;
  209: 	make_dev(&ctx_cdevsw, 0, 0, 0, 0600, "ctx%d", devp->id_unit);
  210: 	return (1);
  211: }
  212: 
  213: static int
  214: ctxopen(dev_t dev, int flags, int fmt, struct thread *td)
  215: {
  216: 	struct ctx_soft_registers *sr;
  217: 	u_char  unit;
  218: 	int     i;
  219: 
  220: 	unit = UNIT(minor(dev));
  221: 
  222: 	/* minor number out of range? */
  223: 
  224: 	if (unit >= NCTX)
  225: 		return (ENXIO);
  226: 	sr = &(ctx_sr[unit]);
  227: 
  228: 	if (sr->flag != 0)	/* someone has already opened us */
  229: 		return (EBUSY);
  230: 
  231: 	/* get space for the LUT buffer */
  232: 
  233: 	sr->lutp = malloc(LUTSIZE, M_DEVBUF, M_WAITOK);
  234: 	if (sr->lutp == NULL)
  235: 		return (ENOMEM);
  236: 
  237: 	sr->flag = OPEN;
  238: 
  239: /*
  240: 	Set up the shadow registers.  We don't actually write these
  241: 	values to the control ports until after we finish loading the
  242: 	lookup table.
  243: */
  244: 	sr->cp0 |= SEE_STORED_VIDEO;
  245: 	if ((kvtop(sr->maddr) == 0xB0000) || (kvtop(sr->maddr) == 0xE0000))
  246: 		sr->cp1 |= AB_SELECT;	/* map to B or E if necessary */
  247: 	/* but don't enable RAM	  */
  248: /*
  249: 	Set up the lookup table initially so that it is transparent.
  250: */
  251: 
  252: 	outb(sr->iobase + ctx_cp0, (u_char) 0);
  253: 	outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
  254: 	for (i = 0; i < LUTSIZE; i++) {
  255: 		outb(sr->iobase + ctx_lutaddr, (u_char) i);
  256: 		sr->lutp[i] = (u_char) i;
  257: 		outb(sr->iobase + ctx_lutdata, (u_char) sr->lutp[i]);
  258: 	}
  259: /*
  260: 	Disable LUT loading, and push the data in the shadow
  261: 	registers into the control ports.
  262: */
  263: 	outb(sr->iobase + ctx_cp0, sr->cp0);
  264: 	outb(sr->iobase + ctx_cp1, sr->cp1);
  265: 	return (0);	/* successful open.  All ready to go. */
  266: }
  267: 
  268: static int
  269: ctxclose(dev_t dev, int flags, int fmt, struct thread *td)
  270: {
  271: 	int     unit;
  272: 
  273: 	unit = UNIT(minor(dev));
  274: 	ctx_sr[unit].flag = 0;
  275: 	free(ctx_sr[unit].lutp, M_DEVBUF);
  276: 	ctx_sr[unit].lutp = NULL;
  277: 	return (0);
  278: }
  279: 
  280: static int
  281: ctxwrite(dev_t dev, struct uio * uio, int ioflag)
  282: {
  283: 	int     unit, status = 0;
  284: 	int     page, count, offset;
  285: 	struct ctx_soft_registers *sr;
  286: 	u_long	ef;
  287: 
  288: 	unit = UNIT(minor(dev));
  289: 	sr = &(ctx_sr[unit]);
  290: 
  291: 	if (uio->uio_offset < 0)
  292: 		return (EINVAL);
  293: 	if (uio->uio_offset >= 4 * PAGESIZE)
  294: 		page = 4;	/* EOF */
  295: 	else
  296: 		page = (u_int)uio->uio_offset / PAGESIZE;
  297: 	offset = (u_int)uio->uio_offset % PAGESIZE;
  298: 	count = min(uio->uio_resid, PAGESIZE - offset);
  299: 	while ((page >= 0) && (page <= 3) && (count > 0)) {
  300: 		sr->cp0 &= ~3;
  301: 		sr->cp0 |= page;
  302: 		outb(sr->iobase + ctx_cp0, sr->cp0);
  303: 
  304: /*
  305: 	Before doing the uiomove, we need to "connect" the frame buffer
  306: 	ram to the machine bus.  This is done here so that we can have
  307: 	several different boards installed, all sharing the same memory
  308: 	space... each board is only "connected" to the bus when its memory
  309: 	is actually being read or written.  All my instincts tell me that
  310: 	I should disable interrupts here, so I have done so.
  311: */
  312: 
  313: 		ef = read_eflags();
  314: 		cpu_disable_intr();
  315: 		sr->cp1 |= RAM_ENABLE;
  316: 		outb(sr->iobase + ctx_cp1, sr->cp1);
  317: 		status = uiomove(sr->maddr + offset, count, uio);
  318: 		sr->cp1 &= ~RAM_ENABLE;
  319: 		outb(sr->iobase + ctx_cp1, sr->cp1);
  320: 		write_eflags(ef);
  321: 
  322: 		page = (u_int)uio->uio_offset / PAGESIZE;
  323: 		offset = (u_int)uio->uio_offset % PAGESIZE;
  324: 		count = min(uio->uio_resid, PAGESIZE - offset);
  325: 	}
  326: 	if (uio->uio_resid > 0)
  327: 		return (ENOSPC);
  328: 	else
  329: 		return (status);
  330: }
  331: 
  332: static int
  333: ctxread(dev_t dev, struct uio * uio, int ioflag)
  334: {
  335: 	int     unit, status = 0;
  336: 	int     page, count, offset;
  337: 	struct ctx_soft_registers *sr;
  338: 	u_long  ef;
  339: 
  340: 	unit = UNIT(minor(dev));
  341: 	sr = &(ctx_sr[unit]);
  342: 
  343: 	if (uio->uio_offset < 0)
  344: 		return (EINVAL);
  345: 	if (uio->uio_offset >= 4 * PAGESIZE)
  346: 		page = 4;	/* EOF */
  347: 	else
  348: 		page = (u_int)uio->uio_offset / PAGESIZE;
  349: 	offset = (u_int)uio->uio_offset % PAGESIZE;
  350: 	count = min(uio->uio_resid, PAGESIZE - offset);
  351: 	while ((page >= 0) && (page <= 3) && (count > 0)) {
  352: 		sr->cp0 &= ~3;
  353: 		sr->cp0 |= page;
  354: 		outb(sr->iobase + ctx_cp0, sr->cp0);
  355: /*
  356: 	Before doing the uiomove, we need to "connect" the frame buffer
  357: 	ram to the machine bus.  This is done here so that we can have
  358: 	several different boards installed, all sharing the same memory
  359: 	space... each board is only "connected" to the bus when its memory
  360: 	is actually being read or written.  All my instincts tell me that
  361: 	I should disable interrupts here, so I have done so.
  362: */
  363: 		ef = read_eflags();
  364: 		cpu_disable_intr();
  365: 		sr->cp1 |= RAM_ENABLE;
  366: 		outb(sr->iobase + ctx_cp1, sr->cp1);
  367: 		status = uiomove(sr->maddr + offset, count, uio);
  368: 		sr->cp1 &= ~RAM_ENABLE;
  369: 		outb(sr->iobase + ctx_cp1, sr->cp1);
  370: 		write_eflags(ef);
  371: 
  372: 		page = (u_int)uio->uio_offset / PAGESIZE;
  373: 		offset = (u_int)uio->uio_offset % PAGESIZE;
  374: 		count = min(uio->uio_resid, PAGESIZE - offset);
  375: 	}
  376: 	if (uio->uio_resid > 0)
  377: 		return (ENOSPC);
  378: 	else
  379: 		return (status);
  380: }
  381: 
  382: static int
  383: ctxioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct thread *td)
  384: {
  385: 	int     error;
  386: 	int     unit, i;
  387: 	struct ctx_soft_registers *sr;
  388: 
  389: 	error = 0;
  390: 	unit = UNIT(minor(dev));
  391: 	sr = &(ctx_sr[unit]);
  392: 
  393: 	switch (cmd) {
  394: 	case CTX_LIVE:
  395: 		sr->cp0 &= ~SEE_STORED_VIDEO;
  396: 		outb(sr->iobase + ctx_cp0, sr->cp0);
  397: 		break;
  398: 	case CTX_GRAB:
  399: 		sr->cp0 &= ~SEE_STORED_VIDEO;
  400: 		outb(sr->iobase + ctx_cp0, sr->cp0);
  401: 		sr->cp0 |= ACQUIRE;
  402: 		if (waitvb(sr->iobase))	/* wait for vert blank to start
  403: 					 * acquire */
  404: 			error = ENODEV;
  405: 		outb(sr->iobase + ctx_cp0, sr->cp0);
  406: 		if (waitvb(sr->iobase))	/* wait for two more to finish acquire */
  407: 			error = ENODEV;
  408: 		if (waitvb(sr->iobase))
  409: 			error = ENODEV;
  410: 		sr->cp0 &= ~ACQUIRE;	/* turn off acquire and turn on
  411: 					 * display */
  412: 		sr->cp0 |= SEE_STORED_VIDEO;
  413: 		outb(sr->iobase + ctx_cp0, sr->cp0);
  414: 		break;
  415: 	case CTX_H_ORGANIZE:
  416: 		sr->cp0 &= ~PAGE_ROTATE;
  417: 		outb(sr->iobase + ctx_cp0, sr->cp0);
  418: 		break;
  419: 	case CTX_V_ORGANIZE:
  420: 		sr->cp0 |= PAGE_ROTATE;
  421: 		outb(sr->iobase + ctx_cp0, sr->cp0);
  422: 		break;
  423: 	case CTX_SET_LUT:
  424: 		bcopy((u_char *) data, sr->lutp, LUTSIZE);
  425: 		outb(sr->iobase + ctx_cp0, (u_char) 0);
  426: 		outb(sr->iobase + ctx_cp1, (u_char) (LUT_LOAD_ENABLE | BLANK_DISPLAY));
  427: 		for (i = 0; i < LUTSIZE; i++) {
  428: 			outb(sr->iobase + ctx_lutaddr, i);
  429: 			outb(sr->iobase + ctx_lutdata, sr->lutp[i]);
  430: 		}
  431: 		outb(sr->iobase + ctx_cp0, sr->cp0);	/* restore control
  432: 							 * registers */
  433: 		outb(sr->iobase + ctx_cp1, sr->cp1);
  434: 		break;
  435: 	case CTX_GET_LUT:
  436: 		bcopy(sr->lutp, (u_char *) data, LUTSIZE);
  437: 		break;
  438: 	default:
  439: 		error = ENODEV;
  440: 	}
  441: 
  442: 	return (error);
  443: }
  444: 
  445: static int
  446: waitvb(int port)
  447: {				/* wait for a vertical blank,  */
  448: 	if (inb(port) == 0xff)	/* 0xff means no board present */
  449: 		return (1);
  450: 
  451: 	while ((inb(port) & VERTICAL_BLANK) != 0) {
  452: 	}
  453: 	while ((inb(port) & VERTICAL_BLANK) == 0) {
  454: 	}
  455: 
  456: 	return (0);
  457: }