Home | History | Annotate | Line # | Download | only in maple
mlcd.c revision 1.7
      1  1.7   thorpej /*	$NetBSD: mlcd.c,v 1.7 2006/03/28 17:38:24 thorpej Exp $	*/
      2  1.1     itohy 
      3  1.1     itohy /*-
      4  1.1     itohy  * Copyright (c) 2002 The NetBSD Foundation, Inc.
      5  1.1     itohy  * All rights reserved.
      6  1.1     itohy  *
      7  1.1     itohy  * This code is derived from software contributed to The NetBSD Foundation
      8  1.1     itohy  * by ITOH Yasufumi.
      9  1.1     itohy  *
     10  1.1     itohy  * Redistribution and use in source and binary forms, with or without
     11  1.1     itohy  * modification, are permitted provided that the following conditions
     12  1.1     itohy  * are met:
     13  1.1     itohy  * 1. Redistributions of source code must retain the above copyright
     14  1.1     itohy  *    notice, this list of conditions and the following disclaimer.
     15  1.1     itohy  * 2. Redistributions in binary form must reproduce the above copyright
     16  1.1     itohy  *    notice, this list of conditions and the following disclaimer in the
     17  1.1     itohy  *    documentation and/or other materials provided with the distribution.
     18  1.1     itohy  * 3. All advertising materials mentioning features or use of this software
     19  1.1     itohy  *    must display the following acknowledgement:
     20  1.1     itohy  *	This product includes software developed by the NetBSD
     21  1.1     itohy  *	Foundation, Inc. and its contributors.
     22  1.1     itohy  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  1.1     itohy  *    contributors may be used to endorse or promote products derived
     24  1.1     itohy  *    from this software without specific prior written permission.
     25  1.1     itohy  *
     26  1.1     itohy  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  1.1     itohy  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  1.1     itohy  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  1.1     itohy  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  1.1     itohy  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  1.1     itohy  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  1.1     itohy  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  1.1     itohy  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  1.1     itohy  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  1.1     itohy  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  1.1     itohy  * POSSIBILITY OF SUCH DAMAGE.
     37  1.1     itohy  */
     38  1.3     lukem 
     39  1.3     lukem #include <sys/cdefs.h>
     40  1.7   thorpej __KERNEL_RCSID(0, "$NetBSD: mlcd.c,v 1.7 2006/03/28 17:38:24 thorpej Exp $");
     41  1.1     itohy 
     42  1.1     itohy #include <sys/param.h>
     43  1.1     itohy #include <sys/device.h>
     44  1.1     itohy #include <sys/kernel.h>
     45  1.1     itohy #include <sys/malloc.h>
     46  1.1     itohy #include <sys/proc.h>
     47  1.1     itohy #include <sys/systm.h>
     48  1.1     itohy #include <sys/vnode.h>
     49  1.1     itohy #include <sys/conf.h>
     50  1.1     itohy 
     51  1.1     itohy #include <dreamcast/dev/maple/maple.h>
     52  1.1     itohy #include <dreamcast/dev/maple/mapleconf.h>
     53  1.1     itohy 
     54  1.1     itohy #define MLCD_MAXACCSIZE	1012	/* (255*4) - 8  =  253*32 / 8 */
     55  1.1     itohy 
     56  1.1     itohy struct mlcd_funcdef {	/* XXX assuming little-endian structure packing */
     57  1.1     itohy 	unsigned unused	: 6,
     58  1.1     itohy 		 bw	: 1,	/* 0: normally white, 1: normally black */
     59  1.1     itohy 		 hv	: 1,	/* 0: horizontal, 1: vertical */
     60  1.1     itohy 		 ra	: 4,	/* 0 */
     61  1.1     itohy 		 wa	: 4,	/* number of access / write */
     62  1.1     itohy 		 bb	: 8,	/* block size / 32 - 1 */
     63  1.1     itohy 		 pt	: 8;	/* number of partition - 1 */
     64  1.1     itohy };
     65  1.1     itohy 
     66  1.1     itohy struct mlcd_request_write_data {
     67  1.4   tsutsui 	uint32_t	func_code;
     68  1.4   tsutsui 	uint8_t		pt;
     69  1.4   tsutsui 	uint8_t		phase;		/* 0, 1, 2, 3: for each 128 byte */
     70  1.4   tsutsui 	uint16_t	block;
     71  1.4   tsutsui 	uint8_t		data[MLCD_MAXACCSIZE];
     72  1.1     itohy };
     73  1.1     itohy #define MLCD_SIZE_REQW(sc)	((sc)->sc_waccsz + 8)
     74  1.1     itohy 
     75  1.1     itohy struct mlcd_request_get_media_info {
     76  1.4   tsutsui 	uint32_t	func_code;
     77  1.4   tsutsui 	uint32_t	pt;		/* pt (1 byte) and unused 3 bytes */
     78  1.1     itohy };
     79  1.1     itohy 
     80  1.1     itohy struct mlcd_media_info {
     81  1.4   tsutsui 	uint8_t		width;		/* width - 1 */
     82  1.4   tsutsui 	uint8_t		height;		/* height - 1 */
     83  1.4   tsutsui 	uint8_t		rsvd[2];	/* ? 0x10 0x02 */
     84  1.1     itohy };
     85  1.1     itohy 
     86  1.1     itohy struct mlcd_response_media_info {
     87  1.4   tsutsui 	uint32_t	func_code;	/* function code (big endian) */
     88  1.1     itohy 	struct mlcd_media_info info;
     89  1.1     itohy };
     90  1.1     itohy 
     91  1.2     itohy struct mlcd_buf {
     92  1.2     itohy 	SIMPLEQ_ENTRY(mlcd_buf)	lb_q;
     93  1.2     itohy 	int		lb_error;
     94  1.2     itohy 	int		lb_partno;
     95  1.2     itohy 	int		lb_blkno;
     96  1.4   tsutsui 	uint32_t	lb_data[1];	/* variable length */
     97  1.2     itohy };
     98  1.2     itohy #define MLCD_BUF_SZ(sc) (offsetof(struct mlcd_buf, lb_data) + (sc)->sc_bsize)
     99  1.2     itohy 
    100  1.1     itohy struct mlcd_softc {
    101  1.1     itohy 	struct device	sc_dev;
    102  1.1     itohy 
    103  1.1     itohy 	struct device	*sc_parent;
    104  1.1     itohy 	struct maple_unit *sc_unit;
    105  1.2     itohy 	int		sc_direction;
    106  1.1     itohy 	enum mlcd_stat {
    107  1.1     itohy 		MLCD_INIT,	/* during initialization */
    108  1.1     itohy 		MLCD_INIT2,	/* during initialization */
    109  1.1     itohy 		MLCD_IDLE,	/* init done, not in I/O */
    110  1.1     itohy 		MLCD_WRITE,	/* in write operation */
    111  1.1     itohy 		MLCD_DETACH	/* detaching */
    112  1.1     itohy 	} sc_stat;
    113  1.1     itohy 
    114  1.1     itohy 	int		sc_npt;		/* number of partitions */
    115  1.1     itohy 	int		sc_bsize;	/* block size */
    116  1.1     itohy 	int		sc_wacc;	/* number of write access per block */
    117  1.1     itohy 	int		sc_waccsz;	/* size of a write access */
    118  1.1     itohy 
    119  1.1     itohy 	struct mlcd_pt {
    120  1.1     itohy 		int		pt_flags;
    121  1.1     itohy #define MLCD_PT_OK	1	/* partition is alive */
    122  1.1     itohy #define MLCD_PT_OPEN	2
    123  1.1     itohy 		struct mlcd_media_info pt_info;	/* geometry per part */
    124  1.1     itohy 		int		pt_size;	/* partition size in byte */
    125  1.2     itohy 		int		pt_nblk;	/* partition size in block */
    126  1.1     itohy 
    127  1.2     itohy 		char		pt_name[16 /* see device.h */ + 4 /* ".255" */];
    128  1.1     itohy 	} *sc_pt;
    129  1.1     itohy 
    130  1.1     itohy 	/* write request buffer (only one is used at a time) */
    131  1.1     itohy 	union {
    132  1.1     itohy 		struct mlcd_request_write_data req_write;
    133  1.1     itohy 		struct mlcd_request_get_media_info req_minfo;
    134  1.1     itohy 	} sc_req;
    135  1.1     itohy #define sc_reqw	sc_req.req_write
    136  1.1     itohy #define sc_reqm	sc_req.req_minfo
    137  1.1     itohy 
    138  1.1     itohy 	/* pending buffers */
    139  1.2     itohy 	SIMPLEQ_HEAD(mlcd_bufq, mlcd_buf) sc_q;
    140  1.1     itohy 
    141  1.1     itohy 	/* current I/O access */
    142  1.2     itohy 	struct mlcd_buf	*sc_bp;
    143  1.1     itohy 	int		sc_retry;
    144  1.1     itohy #define MLCD_MAXRETRY	10
    145  1.1     itohy };
    146  1.1     itohy 
    147  1.1     itohy /*
    148  1.1     itohy  * minor number layout (mlcddetach() depends on this layout):
    149  1.1     itohy  *
    150  1.1     itohy  * 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
    151  1.1     itohy  * |---------------------------------| |---------------------|
    152  1.1     itohy  *                unit                          part
    153  1.1     itohy  */
    154  1.1     itohy #define MLCD_PART(dev)		(minor(dev) & 0xff)
    155  1.1     itohy #define MLCD_UNIT(dev)		(minor(dev) >> 8)
    156  1.1     itohy #define MLCD_MINOR(unit, part)	(((unit) << 8) | (part))
    157  1.1     itohy 
    158  1.4   tsutsui static int	mlcdmatch(struct device *, struct cfdata *, void *);
    159  1.4   tsutsui static void	mlcdattach(struct device *, struct device *, void *);
    160  1.4   tsutsui static int	mlcddetach(struct device *, int);
    161  1.4   tsutsui static void	mlcd_intr(void *, struct maple_response *, int, int);
    162  1.4   tsutsui static void	mlcd_printerror(const char *, uint32_t);
    163  1.4   tsutsui static struct mlcd_buf *mlcd_buf_alloc(int /*dev*/, int /*flags*/);
    164  1.4   tsutsui static void	mlcd_buf_free(struct mlcd_buf *);
    165  1.6     perry static inline uint32_t reverse_32(uint32_t);
    166  1.4   tsutsui static void	mlcd_rotate_bitmap(void *, size_t);
    167  1.4   tsutsui static void	mlcdstart(struct mlcd_softc *);
    168  1.4   tsutsui static void	mlcdstart_bp(struct mlcd_softc *);
    169  1.4   tsutsui static void	mlcddone(struct mlcd_softc *);
    170  1.1     itohy 
    171  1.1     itohy dev_type_open(mlcdopen);
    172  1.1     itohy dev_type_close(mlcdclose);
    173  1.1     itohy dev_type_write(mlcdwrite);
    174  1.1     itohy dev_type_ioctl(mlcdioctl);
    175  1.1     itohy 
    176  1.1     itohy const struct cdevsw mlcd_cdevsw = {
    177  1.1     itohy 	mlcdopen, mlcdclose, noread, mlcdwrite, mlcdioctl,
    178  1.1     itohy 	nostop, notty, nopoll, nommap, nokqfilter
    179  1.1     itohy };
    180  1.1     itohy 
    181  1.1     itohy CFATTACH_DECL(mlcd, sizeof(struct mlcd_softc),
    182  1.1     itohy     mlcdmatch, mlcdattach, mlcddetach, NULL);
    183  1.1     itohy 
    184  1.1     itohy extern struct cfdriver mlcd_cd;
    185  1.1     itohy 
    186  1.1     itohy /* initial image "NetBSD dreamcast" */
    187  1.1     itohy static const char initimg48x32[192] = {
    188  1.1     itohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    189  1.1     itohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    190  1.1     itohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    191  1.2     itohy 	0x1c, 0x70, 0x00, 0x7e, 0x1c, 0xf0, 0x0c, 0x60, 0x00, 0x33, 0x26, 0x6c,
    192  1.2     itohy 	0x0c, 0x60, 0x0c, 0x33, 0x66, 0x66, 0x1e, 0xc7, 0x0c, 0x62, 0x60, 0xc6,
    193  1.2     itohy 	0x1a, 0xc9, 0xbe, 0x7c, 0x30, 0xc6, 0x1a, 0xdb, 0x98, 0x66, 0x18, 0xc6,
    194  1.2     itohy 	0x1a, 0xdc, 0x18, 0x66, 0x0d, 0x8c, 0x31, 0xb0, 0x32, 0xc6, 0x8d, 0x8c,
    195  1.2     itohy 	0x31, 0xb1, 0x36, 0xcd, 0x99, 0x98, 0x71, 0x9e, 0x1d, 0xf9, 0xf3, 0xe0,
    196  1.1     itohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    197  1.2     itohy 	0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08,
    198  1.2     itohy 	0x1d, 0x6c, 0x63, 0xc7, 0x30, 0xde, 0x25, 0x92, 0x12, 0xa8, 0x09, 0x08,
    199  1.2     itohy 	0x25, 0x1e, 0x72, 0xa8, 0x38, 0xc8, 0x25, 0x10, 0x92, 0xa8, 0x48, 0x28,
    200  1.2     itohy 	0x1d, 0x0e, 0x6a, 0xa7, 0x35, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    201  1.1     itohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    202  1.1     itohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    203  1.1     itohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
    204  1.1     itohy };
    205  1.1     itohy 
    206  1.2     itohy /* ARGSUSED */
    207  1.1     itohy static int
    208  1.4   tsutsui mlcdmatch(struct device *parent, struct cfdata *cf, void *aux)
    209  1.1     itohy {
    210  1.1     itohy 	struct maple_attach_args *ma = aux;
    211  1.1     itohy 
    212  1.1     itohy 	return (ma->ma_function == MAPLE_FN_LCD ? MAPLE_MATCH_FUNC : 0);
    213  1.1     itohy }
    214  1.1     itohy 
    215  1.1     itohy static void
    216  1.4   tsutsui mlcdattach(struct device *parent, struct device *self, void *aux)
    217  1.1     itohy {
    218  1.1     itohy 	struct mlcd_softc *sc = (void *) self;
    219  1.1     itohy 	struct maple_attach_args *ma = aux;
    220  1.1     itohy 	int i;
    221  1.1     itohy 	union {
    222  1.4   tsutsui 		uint32_t v;
    223  1.1     itohy 		struct mlcd_funcdef s;
    224  1.1     itohy 	} funcdef;
    225  1.1     itohy 
    226  1.1     itohy 	sc->sc_parent = parent;
    227  1.1     itohy 	sc->sc_unit = ma->ma_unit;
    228  1.2     itohy 	sc->sc_direction = ma->ma_basedevinfo->di_connector_direction;
    229  1.1     itohy 
    230  1.1     itohy 	funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_LCD);
    231  1.1     itohy 	printf(": LCD display\n");
    232  1.1     itohy 	printf("%s: %d LCD, %d bytes/block, ",
    233  1.1     itohy 	    sc->sc_dev.dv_xname,
    234  1.1     itohy 	    sc->sc_npt = funcdef.s.pt + 1,
    235  1.1     itohy 	    sc->sc_bsize = (funcdef.s.bb + 1) << 5);
    236  1.1     itohy 	if ((sc->sc_wacc = funcdef.s.wa) == 0)
    237  1.1     itohy 		printf("no ");
    238  1.1     itohy 	else
    239  1.1     itohy 		printf("%d acc/", sc->sc_wacc);
    240  1.2     itohy 	printf("write, %s, norm %s%s\n",
    241  1.2     itohy 	    funcdef.s.hv ? "vert" : "horiz",
    242  1.2     itohy 	    funcdef.s.bw ? "black" : "white",
    243  1.2     itohy 	    sc->sc_direction == MAPLE_CONN_TOP ? ", upside-down" : "");
    244  1.1     itohy 
    245  1.1     itohy 	/*
    246  1.1     itohy 	 * start init sequence
    247  1.1     itohy 	 */
    248  1.1     itohy 	sc->sc_stat = MLCD_INIT;
    249  1.2     itohy 	SIMPLEQ_INIT(&sc->sc_q);
    250  1.1     itohy 
    251  1.1     itohy 	/* check consistency */
    252  1.1     itohy 	if (sc->sc_wacc != 0) {
    253  1.1     itohy 		sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc;
    254  1.1     itohy 		if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) {
    255  1.1     itohy 			printf("%s: write access isn't equally divided\n",
    256  1.1     itohy 			    sc->sc_dev.dv_xname);
    257  1.1     itohy 			sc->sc_wacc = 0;	/* no write */
    258  1.1     itohy 		} else if (sc->sc_waccsz > MLCD_MAXACCSIZE) {
    259  1.1     itohy 			printf("%s: write access size is too large\n",
    260  1.1     itohy 			    sc->sc_dev.dv_xname);
    261  1.1     itohy 			sc->sc_wacc = 0;	/* no write */
    262  1.1     itohy 		}
    263  1.1     itohy 	}
    264  1.1     itohy 	if (sc->sc_wacc == 0) {
    265  1.1     itohy 		printf("%s: device doesn't support write\n",
    266  1.1     itohy 		    sc->sc_dev.dv_xname);
    267  1.1     itohy 		return;
    268  1.1     itohy 	}
    269  1.1     itohy 
    270  1.1     itohy 	/* per-part structure */
    271  1.1     itohy 	sc->sc_pt = malloc(sizeof(struct mlcd_pt) * sc->sc_npt, M_DEVBUF,
    272  1.1     itohy 	    M_WAITOK|M_ZERO);
    273  1.1     itohy 
    274  1.1     itohy 	for (i = 0; i < sc->sc_npt; i++) {
    275  1.1     itohy 		sprintf(sc->sc_pt[i].pt_name, "%s.%d", sc->sc_dev.dv_xname, i);
    276  1.1     itohy 	}
    277  1.1     itohy 
    278  1.1     itohy 	maple_set_callback(parent, sc->sc_unit, MAPLE_FN_LCD,
    279  1.1     itohy 	    mlcd_intr, sc);
    280  1.1     itohy 
    281  1.1     itohy 	/*
    282  1.1     itohy 	 * get size (start from partition 0)
    283  1.1     itohy 	 */
    284  1.4   tsutsui 	sc->sc_reqm.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
    285  1.1     itohy 	sc->sc_reqm.pt = 0;
    286  1.1     itohy 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
    287  1.1     itohy 	    MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
    288  1.1     itohy }
    289  1.1     itohy 
    290  1.2     itohy /* ARGSUSED1 */
    291  1.1     itohy static int
    292  1.4   tsutsui mlcddetach(struct device *self, int flags)
    293  1.1     itohy {
    294  1.1     itohy 	struct mlcd_softc *sc = (struct mlcd_softc *) self;
    295  1.2     itohy 	struct mlcd_buf *bp;
    296  1.1     itohy 	int minor_l, minor_h;
    297  1.1     itohy 
    298  1.1     itohy 	sc->sc_stat = MLCD_DETACH;	/* just in case */
    299  1.1     itohy 
    300  1.1     itohy 	/*
    301  1.1     itohy 	 * kill pending I/O
    302  1.1     itohy 	 */
    303  1.1     itohy 	if ((bp = sc->sc_bp) != NULL) {
    304  1.2     itohy 		bp->lb_error = EIO;
    305  1.2     itohy 		wakeup(bp);
    306  1.2     itohy 	}
    307  1.2     itohy 	while ((bp = SIMPLEQ_FIRST(&sc->sc_q)) != NULL) {
    308  1.2     itohy 		SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
    309  1.2     itohy 		bp->lb_error = EIO;
    310  1.2     itohy 		wakeup(bp);
    311  1.1     itohy 	}
    312  1.1     itohy 
    313  1.1     itohy 	/*
    314  1.1     itohy 	 * revoke vnodes
    315  1.1     itohy 	 */
    316  1.7   thorpej 	minor_l = MLCD_MINOR(device_unit(self), 0);
    317  1.7   thorpej 	minor_h = MLCD_MINOR(device_unit(self), sc->sc_npt - 1);
    318  1.1     itohy 	vdevgone(cdevsw_lookup_major(&mlcd_cdevsw), minor_l, minor_h, VCHR);
    319  1.1     itohy 
    320  1.1     itohy 	/*
    321  1.1     itohy 	 * free per-partition structure
    322  1.1     itohy 	 */
    323  1.1     itohy 	if (sc->sc_pt)
    324  1.1     itohy 		free(sc->sc_pt, M_DEVBUF);
    325  1.1     itohy 
    326  1.1     itohy 	return 0;
    327  1.1     itohy }
    328  1.1     itohy 
    329  1.1     itohy /*
    330  1.1     itohy  * called back from maple bus driver
    331  1.1     itohy  */
    332  1.2     itohy /* ARGSUSED3 */
    333  1.1     itohy static void
    334  1.4   tsutsui mlcd_intr(void *dev, struct maple_response *response, int sz, int flags)
    335  1.1     itohy {
    336  1.1     itohy 	struct mlcd_softc *sc = dev;
    337  1.1     itohy 	struct mlcd_response_media_info *rm = (void *) response->data;
    338  1.2     itohy 	struct mlcd_buf *bp;
    339  1.1     itohy 	int part;
    340  1.1     itohy 	struct mlcd_pt *pt;
    341  1.1     itohy 
    342  1.1     itohy 	switch (sc->sc_stat) {
    343  1.1     itohy 	case MLCD_INIT:
    344  1.1     itohy 		/* checking part geometry */
    345  1.1     itohy 		part = sc->sc_reqm.pt;
    346  1.1     itohy 		pt = &sc->sc_pt[part];
    347  1.1     itohy 		switch ((maple_response_t) response->response_code) {
    348  1.1     itohy 		case MAPLE_RESPONSE_DATATRF:
    349  1.1     itohy 			pt->pt_info = rm->info;
    350  1.1     itohy 			pt->pt_size = ((pt->pt_info.width + 1) *
    351  1.1     itohy 			    (pt->pt_info.height + 1) + 7) / 8;
    352  1.1     itohy 			pt->pt_nblk = pt->pt_size / sc->sc_bsize;
    353  1.1     itohy 			printf("%s: %dx%d display, %d bytes\n",
    354  1.1     itohy 			    pt->pt_name,
    355  1.1     itohy 			    pt->pt_info.width + 1, pt->pt_info.height + 1,
    356  1.1     itohy 			    pt->pt_size);
    357  1.1     itohy 
    358  1.1     itohy 			/* this partition is active */
    359  1.1     itohy 			pt->pt_flags = MLCD_PT_OK;
    360  1.1     itohy 
    361  1.1     itohy 			break;
    362  1.1     itohy 		default:
    363  1.1     itohy 			printf("%s: init: unexpected response %#x, sz %d\n",
    364  1.4   tsutsui 			    pt->pt_name, be32toh(response->response_code), sz);
    365  1.1     itohy 			break;
    366  1.1     itohy 		}
    367  1.1     itohy 		if (++part == sc->sc_npt) {
    368  1.1     itohy 			/* init done */
    369  1.1     itohy 
    370  1.1     itohy 			/* XXX initial image for Visual Memory */
    371  1.1     itohy 			if (sc->sc_pt[0].pt_size == sizeof initimg48x32 &&
    372  1.1     itohy 			    sc->sc_waccsz == sizeof initimg48x32 &&
    373  1.1     itohy 			    sc->sc_wacc == 1) {
    374  1.1     itohy 				sc->sc_stat = MLCD_INIT2;
    375  1.1     itohy 				sc->sc_reqw.func_code =
    376  1.4   tsutsui 				    htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
    377  1.1     itohy 				sc->sc_reqw.pt = 0;	/* part 0 */
    378  1.1     itohy 				sc->sc_reqw.block = 0;
    379  1.1     itohy 				sc->sc_reqw.phase = 0;
    380  1.4   tsutsui 				memcpy(sc->sc_reqw.data, initimg48x32,
    381  1.1     itohy 				    sizeof initimg48x32);
    382  1.2     itohy 				if (sc->sc_direction == MAPLE_CONN_TOP) {
    383  1.2     itohy 					/* the LCD is upside-down */
    384  1.2     itohy 					mlcd_rotate_bitmap(sc->sc_reqw.data,
    385  1.2     itohy 					    sizeof initimg48x32);
    386  1.2     itohy 				}
    387  1.1     itohy 				maple_command(sc->sc_parent, sc->sc_unit,
    388  1.1     itohy 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
    389  1.1     itohy 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    390  1.1     itohy 			} else
    391  1.1     itohy 				sc->sc_stat = MLCD_IDLE;	/* init done */
    392  1.1     itohy 		} else {
    393  1.1     itohy 			sc->sc_reqm.pt = part;
    394  1.1     itohy 			maple_command(sc->sc_parent, sc->sc_unit,
    395  1.1     itohy 			    MAPLE_FN_LCD, MAPLE_COMMAND_GETMINFO,
    396  1.1     itohy 			    sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
    397  1.1     itohy 		}
    398  1.1     itohy 		break;
    399  1.1     itohy 
    400  1.1     itohy 	case MLCD_INIT2:
    401  1.1     itohy 		sc->sc_stat = MLCD_IDLE;	/* init done */
    402  1.1     itohy 		break;
    403  1.1     itohy 
    404  1.1     itohy 	case MLCD_WRITE:
    405  1.1     itohy 		bp = sc->sc_bp;
    406  1.1     itohy 
    407  1.1     itohy 		switch ((maple_response_t) response->response_code) {
    408  1.1     itohy 		case MAPLE_RESPONSE_OK:			/* write done */
    409  1.1     itohy 			if (++sc->sc_reqw.phase == sc->sc_wacc) {
    410  1.1     itohy 				/* all phase done */
    411  1.1     itohy 				mlcddone(sc);
    412  1.1     itohy 			} else {
    413  1.1     itohy 				/* go next phase */
    414  1.4   tsutsui 				memcpy(sc->sc_reqw.data,
    415  1.4   tsutsui 				    (char *)bp->lb_data +
    416  1.4   tsutsui 				    sc->sc_waccsz * sc->sc_reqw.phase,
    417  1.4   tsutsui 				    sc->sc_waccsz);
    418  1.1     itohy 				maple_command(sc->sc_parent, sc->sc_unit,
    419  1.1     itohy 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
    420  1.1     itohy 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    421  1.1     itohy 			}
    422  1.1     itohy 			break;
    423  1.1     itohy 		case MAPLE_RESPONSE_LCDERR:
    424  1.1     itohy 			mlcd_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name,
    425  1.1     itohy 			    rm->func_code /* XXX */);
    426  1.2     itohy 			mlcdstart_bp(sc);		/* retry */
    427  1.1     itohy 			break;
    428  1.1     itohy 		default:
    429  1.1     itohy 			printf("%s: write: unexpected response %#x, %#x, sz %d\n",
    430  1.1     itohy 			    sc->sc_pt[sc->sc_reqw.pt].pt_name,
    431  1.4   tsutsui 			    be32toh(response->response_code),
    432  1.4   tsutsui 			    be32toh(rm->func_code), sz);
    433  1.2     itohy 			mlcdstart_bp(sc);		/* retry */
    434  1.1     itohy 			break;
    435  1.1     itohy 		}
    436  1.1     itohy 		break;
    437  1.1     itohy 
    438  1.1     itohy 	default:
    439  1.1     itohy 		break;
    440  1.1     itohy 	}
    441  1.1     itohy }
    442  1.1     itohy 
    443  1.1     itohy static void
    444  1.4   tsutsui mlcd_printerror(const char *head, uint32_t code)
    445  1.1     itohy {
    446  1.1     itohy 
    447  1.1     itohy 	printf("%s:", head);
    448  1.1     itohy 	NTOHL(code);
    449  1.1     itohy 	if (code & 1)
    450  1.1     itohy 		printf(" PT error");
    451  1.1     itohy 	if (code & 2)
    452  1.1     itohy 		printf(" Phase error");
    453  1.1     itohy 	if (code & 4)
    454  1.1     itohy 		printf(" Block error");
    455  1.1     itohy 	if (code & 010)
    456  1.1     itohy 		printf(" Write error");
    457  1.1     itohy 	if (code & 020)
    458  1.1     itohy 		printf(" Length error");
    459  1.1     itohy 	if (code & ~037)
    460  1.1     itohy 		printf(" Unknown error %#x", code & ~037);
    461  1.1     itohy 	printf("\n");
    462  1.1     itohy }
    463  1.1     itohy 
    464  1.2     itohy /* ARGSUSED */
    465  1.1     itohy int
    466  1.5  christos mlcdopen(dev_t dev, int flags, int devtype, struct lwp *l)
    467  1.1     itohy {
    468  1.1     itohy 	int unit, part;
    469  1.1     itohy 	struct mlcd_softc *sc;
    470  1.1     itohy 	struct mlcd_pt *pt;
    471  1.1     itohy 
    472  1.1     itohy 	unit = MLCD_UNIT(dev);
    473  1.1     itohy 	part = MLCD_PART(dev);
    474  1.1     itohy 	if ((sc = device_lookup(&mlcd_cd, unit)) == NULL
    475  1.1     itohy 	    || sc->sc_stat == MLCD_INIT
    476  1.1     itohy 	    || sc->sc_stat == MLCD_INIT2
    477  1.1     itohy 	    || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0)
    478  1.1     itohy 		return ENXIO;
    479  1.1     itohy 
    480  1.1     itohy 	if (pt->pt_flags & MLCD_PT_OPEN)
    481  1.1     itohy 		return EBUSY;
    482  1.1     itohy 
    483  1.1     itohy 	pt->pt_flags |= MLCD_PT_OPEN;
    484  1.1     itohy 
    485  1.1     itohy 	return 0;
    486  1.1     itohy }
    487  1.1     itohy 
    488  1.2     itohy /* ARGSUSED */
    489  1.1     itohy int
    490  1.5  christos mlcdclose(dev_t dev, int flags, int devtype, struct lwp *l)
    491  1.1     itohy {
    492  1.1     itohy 	int unit, part;
    493  1.1     itohy 	struct mlcd_softc *sc;
    494  1.1     itohy 	struct mlcd_pt *pt;
    495  1.1     itohy 
    496  1.1     itohy 	unit = MLCD_UNIT(dev);
    497  1.1     itohy 	part = MLCD_PART(dev);
    498  1.1     itohy 	sc = mlcd_cd.cd_devs[unit];
    499  1.1     itohy 	pt = &sc->sc_pt[part];
    500  1.1     itohy 
    501  1.1     itohy 	pt->pt_flags &= ~MLCD_PT_OPEN;
    502  1.1     itohy 
    503  1.1     itohy 	return 0;
    504  1.1     itohy }
    505  1.1     itohy 
    506  1.1     itohy /*
    507  1.1     itohy  * start I/O operations
    508  1.1     itohy  */
    509  1.1     itohy static void
    510  1.4   tsutsui mlcdstart(struct mlcd_softc *sc)
    511  1.1     itohy {
    512  1.2     itohy 	struct mlcd_buf *bp;
    513  1.1     itohy 
    514  1.2     itohy 	if ((bp = SIMPLEQ_FIRST(&sc->sc_q)) == NULL) {
    515  1.1     itohy 		sc->sc_stat = MLCD_IDLE;
    516  1.1     itohy 		maple_enable_unit_ping(sc->sc_parent, sc->sc_unit,
    517  1.1     itohy 		    MAPLE_FN_LCD, 1);
    518  1.1     itohy 		return;
    519  1.1     itohy 	}
    520  1.1     itohy 
    521  1.2     itohy 	SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
    522  1.2     itohy 
    523  1.2     itohy 	sc->sc_bp = bp;
    524  1.1     itohy 	sc->sc_retry = 0;
    525  1.2     itohy 	mlcdstart_bp(sc);
    526  1.1     itohy }
    527  1.1     itohy 
    528  1.1     itohy /*
    529  1.1     itohy  * start/retry a specified I/O operation
    530  1.1     itohy  */
    531  1.1     itohy static void
    532  1.4   tsutsui mlcdstart_bp(struct mlcd_softc *sc)
    533  1.1     itohy {
    534  1.2     itohy 	struct mlcd_buf *bp;
    535  1.1     itohy 	struct mlcd_pt *pt;
    536  1.1     itohy 
    537  1.2     itohy 	bp = sc->sc_bp;
    538  1.2     itohy 	pt = &sc->sc_pt[bp->lb_partno];
    539  1.1     itohy 
    540  1.1     itohy 	/* handle retry */
    541  1.1     itohy 	if (sc->sc_retry++ > MLCD_MAXRETRY) {
    542  1.1     itohy 		/* retry count exceeded */
    543  1.2     itohy 		bp->lb_error = EIO;
    544  1.1     itohy 		mlcddone(sc);
    545  1.1     itohy 		return;
    546  1.1     itohy 	}
    547  1.1     itohy 
    548  1.1     itohy 	/*
    549  1.1     itohy 	 * I/O access will fail if the removal detection (by maple driver)
    550  1.1     itohy 	 * occurs before finishing the I/O, so disable it.
    551  1.1     itohy 	 * We are sending commands, and the removal detection is still alive.
    552  1.1     itohy 	 */
    553  1.1     itohy 	maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
    554  1.1     itohy 
    555  1.1     itohy 	/*
    556  1.1     itohy 	 * Start the first phase (phase# = 0).
    557  1.1     itohy 	 */
    558  1.1     itohy 	/* write */
    559  1.1     itohy 	sc->sc_stat = MLCD_WRITE;
    560  1.4   tsutsui 	sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
    561  1.2     itohy 	sc->sc_reqw.pt = bp->lb_partno;
    562  1.4   tsutsui 	sc->sc_reqw.block = htobe16(bp->lb_blkno);
    563  1.1     itohy 	sc->sc_reqw.phase = 0;		/* first phase */
    564  1.4   tsutsui 	memcpy(sc->sc_reqw.data,
    565  1.4   tsutsui 	    (char *) bp->lb_data /* + sc->sc_waccsz * phase */, sc->sc_waccsz);
    566  1.1     itohy 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
    567  1.1     itohy 	    MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
    568  1.1     itohy }
    569  1.1     itohy 
    570  1.1     itohy static void
    571  1.4   tsutsui mlcddone(struct mlcd_softc *sc)
    572  1.1     itohy {
    573  1.2     itohy 	struct mlcd_buf *bp;
    574  1.1     itohy 
    575  1.1     itohy 	/* terminate current transfer */
    576  1.1     itohy 	bp = sc->sc_bp;
    577  1.1     itohy 	KASSERT(bp);
    578  1.1     itohy 	sc->sc_bp = NULL;
    579  1.2     itohy 	wakeup(bp);
    580  1.1     itohy 
    581  1.1     itohy 	/* go next transfer */
    582  1.1     itohy 	mlcdstart(sc);
    583  1.1     itohy }
    584  1.1     itohy 
    585  1.2     itohy /*
    586  1.2     itohy  * allocate a buffer for one block
    587  1.2     itohy  *
    588  1.2     itohy  * return NULL if
    589  1.2     itohy  *	[flags == M_NOWAIT] out of buffer space
    590  1.2     itohy  *	[flags == M_WAITOK] device detach detected
    591  1.2     itohy  */
    592  1.2     itohy static struct mlcd_buf *
    593  1.4   tsutsui mlcd_buf_alloc(int dev, int flags)
    594  1.2     itohy {
    595  1.2     itohy 	struct mlcd_softc *sc;
    596  1.2     itohy 	struct mlcd_pt *pt;
    597  1.2     itohy 	int unit, part;
    598  1.2     itohy 	struct mlcd_buf *bp;
    599  1.2     itohy 
    600  1.2     itohy 	unit = MLCD_UNIT(dev);
    601  1.2     itohy 	part = MLCD_PART(dev);
    602  1.2     itohy 	sc = mlcd_cd.cd_devs[unit];
    603  1.2     itohy 	KASSERT(sc);
    604  1.2     itohy 	pt = &sc->sc_pt[part];
    605  1.2     itohy 	KASSERT(pt);
    606  1.2     itohy 
    607  1.2     itohy 	if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
    608  1.2     itohy 		return bp;
    609  1.2     itohy 
    610  1.2     itohy 	/*
    611  1.2     itohy 	 * malloc() may sleep, and the device may be detached during sleep.
    612  1.2     itohy 	 * XXX this check is not complete.
    613  1.2     itohy 	 */
    614  1.2     itohy 	if (sc != device_lookup(&mlcd_cd, unit)
    615  1.2     itohy 	    || sc->sc_stat == MLCD_INIT
    616  1.2     itohy 	    || sc->sc_stat == MLCD_INIT2
    617  1.2     itohy 	    || part >= sc->sc_npt || pt != &sc->sc_pt[part]
    618  1.2     itohy 	    || pt->pt_flags == 0) {
    619  1.2     itohy 		free(bp, M_DEVBUF);
    620  1.2     itohy 		return NULL;
    621  1.2     itohy 	}
    622  1.2     itohy 
    623  1.2     itohy 	bp->lb_error = 0;
    624  1.2     itohy 
    625  1.2     itohy 	return bp;
    626  1.2     itohy }
    627  1.1     itohy 
    628  1.1     itohy static void
    629  1.4   tsutsui mlcd_buf_free(struct mlcd_buf *bp)
    630  1.1     itohy {
    631  1.1     itohy 
    632  1.2     itohy 	free(bp, M_DEVBUF);
    633  1.2     itohy }
    634  1.2     itohy 
    635  1.2     itohy /* invert order of bits */
    636  1.6     perry static inline uint32_t
    637  1.4   tsutsui reverse_32(uint32_t b)
    638  1.2     itohy {
    639  1.4   tsutsui 	uint32_t b1;
    640  1.2     itohy 
    641  1.2     itohy 	/* invert every 8bit */
    642  1.2     itohy 	b1 = (b & 0x55555555) << 1;  b = (b >> 1) & 0x55555555;  b |= b1;
    643  1.2     itohy 	b1 = (b & 0x33333333) << 2;  b = (b >> 2) & 0x33333333;  b |= b1;
    644  1.2     itohy 	b1 = (b & 0x0f0f0f0f) << 4;  b = (b >> 4) & 0x0f0f0f0f;  b |= b1;
    645  1.2     itohy 
    646  1.2     itohy 	/* invert byte order */
    647  1.2     itohy 	return bswap32(b);
    648  1.2     itohy }
    649  1.1     itohy 
    650  1.2     itohy static void
    651  1.4   tsutsui mlcd_rotate_bitmap(void *ptr, size_t size)
    652  1.2     itohy {
    653  1.4   tsutsui 	uint32_t *p, *q, tmp;
    654  1.2     itohy 
    655  1.4   tsutsui 	KDASSERT(size % sizeof(uint32_t) == 0);
    656  1.2     itohy 	for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
    657  1.2     itohy 		tmp = reverse_32(*p);
    658  1.2     itohy 		*p++ = reverse_32(*--q);
    659  1.2     itohy 		*q = tmp;
    660  1.2     itohy 	}
    661  1.1     itohy }
    662  1.1     itohy 
    663  1.2     itohy /* ARGSUSED2 */
    664  1.1     itohy int
    665  1.4   tsutsui mlcdwrite(dev_t dev, struct uio *uio, int flags)
    666  1.1     itohy {
    667  1.2     itohy 	struct mlcd_softc *sc;
    668  1.2     itohy 	struct mlcd_pt *pt;
    669  1.2     itohy 	struct mlcd_buf *bp;
    670  1.2     itohy 	int part;
    671  1.2     itohy 	off_t devsize;
    672  1.2     itohy 	int error = 0;
    673  1.2     itohy 
    674  1.2     itohy 	part = MLCD_PART(dev);
    675  1.2     itohy 	sc = mlcd_cd.cd_devs[MLCD_UNIT(dev)];
    676  1.2     itohy 	pt = &sc->sc_pt[part];
    677  1.2     itohy 
    678  1.2     itohy #if 0
    679  1.2     itohy 	printf("%s: mlcdwrite: offset %ld, size %d\n",
    680  1.2     itohy 	    pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
    681  1.2     itohy #endif
    682  1.2     itohy 
    683  1.2     itohy 	devsize = pt->pt_nblk * sc->sc_bsize;
    684  1.2     itohy 	if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
    685  1.2     itohy 		return EINVAL;
    686  1.2     itohy 
    687  1.2     itohy 	if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
    688  1.2     itohy 		return EIO;	/* device is detached during allocation */
    689  1.2     itohy 
    690  1.2     itohy 	bp->lb_partno = part;
    691  1.2     itohy 
    692  1.2     itohy 	while (uio->uio_offset < devsize
    693  1.2     itohy 	    && uio->uio_resid >= (size_t) sc->sc_bsize) {
    694  1.2     itohy 		/* invert block number if upside-down */
    695  1.2     itohy 		bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
    696  1.2     itohy 		    pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
    697  1.2     itohy 		    uio->uio_offset / sc->sc_bsize;
    698  1.2     itohy 
    699  1.2     itohy 		if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
    700  1.2     itohy 			break;
    701  1.2     itohy 
    702  1.2     itohy 		if (sc->sc_direction == MAPLE_CONN_TOP) {
    703  1.2     itohy 			/* the LCD is upside-down */
    704  1.2     itohy 			mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
    705  1.2     itohy 		}
    706  1.2     itohy 
    707  1.2     itohy 		/* queue this transfer */
    708  1.2     itohy 		SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
    709  1.2     itohy 
    710  1.2     itohy 		if (sc->sc_stat == MLCD_IDLE)
    711  1.2     itohy 			mlcdstart(sc);
    712  1.2     itohy 
    713  1.2     itohy 		tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
    714  1.2     itohy 
    715  1.2     itohy 		if ((error = bp->lb_error) != 0) {
    716  1.2     itohy 			uio->uio_resid += sc->sc_bsize;
    717  1.2     itohy 			break;
    718  1.2     itohy 		}
    719  1.2     itohy 	}
    720  1.2     itohy 
    721  1.2     itohy 	mlcd_buf_free(bp);
    722  1.1     itohy 
    723  1.2     itohy 	return error;
    724  1.1     itohy }
    725  1.1     itohy 
    726  1.1     itohy int
    727  1.5  christos mlcdioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct lwp *l)
    728  1.1     itohy {
    729  1.1     itohy 	int unit, part;
    730  1.1     itohy 	struct mlcd_softc *sc;
    731  1.1     itohy 	struct mlcd_pt *pt;
    732  1.1     itohy 
    733  1.1     itohy 	unit = MLCD_UNIT(dev);
    734  1.1     itohy 	part = MLCD_PART(dev);
    735  1.1     itohy 	sc = mlcd_cd.cd_devs[unit];
    736  1.1     itohy 	pt = &sc->sc_pt[part];
    737  1.1     itohy 
    738  1.1     itohy 	switch (cmd) {
    739  1.1     itohy 
    740  1.1     itohy 	default:
    741  1.1     itohy 		/* generic maple ioctl */
    742  1.1     itohy 		return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
    743  1.5  christos 		    flag, l);
    744  1.1     itohy 	}
    745  1.1     itohy 
    746  1.1     itohy 	return 0;
    747  1.1     itohy }
    748