xref: /netbsd-src/sys/arch/dreamcast/dev/maple/mlcd.c (revision f9228f42259a421502f04b1ddb2df91c2ecbc519)
1*f9228f42Sdholland /*	$NetBSD: mlcd.c,v 1.18 2014/07/25 08:10:32 dholland Exp $	*/
251cd3ce6Sitohy 
351cd3ce6Sitohy /*-
451cd3ce6Sitohy  * Copyright (c) 2002 The NetBSD Foundation, Inc.
551cd3ce6Sitohy  * All rights reserved.
651cd3ce6Sitohy  *
751cd3ce6Sitohy  * This code is derived from software contributed to The NetBSD Foundation
851cd3ce6Sitohy  * by ITOH Yasufumi.
951cd3ce6Sitohy  *
1051cd3ce6Sitohy  * Redistribution and use in source and binary forms, with or without
1151cd3ce6Sitohy  * modification, are permitted provided that the following conditions
1251cd3ce6Sitohy  * are met:
1351cd3ce6Sitohy  * 1. Redistributions of source code must retain the above copyright
1451cd3ce6Sitohy  *    notice, this list of conditions and the following disclaimer.
1551cd3ce6Sitohy  * 2. Redistributions in binary form must reproduce the above copyright
1651cd3ce6Sitohy  *    notice, this list of conditions and the following disclaimer in the
1751cd3ce6Sitohy  *    documentation and/or other materials provided with the distribution.
1851cd3ce6Sitohy  *
1951cd3ce6Sitohy  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2051cd3ce6Sitohy  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2151cd3ce6Sitohy  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2251cd3ce6Sitohy  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2351cd3ce6Sitohy  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2451cd3ce6Sitohy  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2551cd3ce6Sitohy  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2651cd3ce6Sitohy  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2751cd3ce6Sitohy  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2851cd3ce6Sitohy  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2951cd3ce6Sitohy  * POSSIBILITY OF SUCH DAMAGE.
3051cd3ce6Sitohy  */
3151cd3ce6Sitohy 
3214172728Slukem #include <sys/cdefs.h>
33*f9228f42Sdholland __KERNEL_RCSID(0, "$NetBSD: mlcd.c,v 1.18 2014/07/25 08:10:32 dholland Exp $");
3414172728Slukem 
3551cd3ce6Sitohy #include <sys/param.h>
3651cd3ce6Sitohy #include <sys/device.h>
3751cd3ce6Sitohy #include <sys/kernel.h>
3851cd3ce6Sitohy #include <sys/malloc.h>
3951cd3ce6Sitohy #include <sys/proc.h>
4051cd3ce6Sitohy #include <sys/systm.h>
4151cd3ce6Sitohy #include <sys/vnode.h>
4251cd3ce6Sitohy #include <sys/conf.h>
4351cd3ce6Sitohy 
4451cd3ce6Sitohy #include <dreamcast/dev/maple/maple.h>
4551cd3ce6Sitohy #include <dreamcast/dev/maple/mapleconf.h>
4651cd3ce6Sitohy 
470e356e23Stsutsui #include "ioconf.h"
480e356e23Stsutsui 
4951cd3ce6Sitohy #define MLCD_MAXACCSIZE	1012	/* (255*4) - 8  =  253*32 / 8 */
5051cd3ce6Sitohy 
5151cd3ce6Sitohy struct mlcd_funcdef {	/* XXX assuming little-endian structure packing */
5251cd3ce6Sitohy 	unsigned unused	: 6,
5351cd3ce6Sitohy 		 bw	: 1,	/* 0: normally white, 1: normally black */
5451cd3ce6Sitohy 		 hv	: 1,	/* 0: horizontal, 1: vertical */
5551cd3ce6Sitohy 		 ra	: 4,	/* 0 */
5651cd3ce6Sitohy 		 wa	: 4,	/* number of access / write */
5751cd3ce6Sitohy 		 bb	: 8,	/* block size / 32 - 1 */
5851cd3ce6Sitohy 		 pt	: 8;	/* number of partition - 1 */
5951cd3ce6Sitohy };
6051cd3ce6Sitohy 
6151cd3ce6Sitohy struct mlcd_request_write_data {
62eff817e3Stsutsui 	uint32_t	func_code;
63eff817e3Stsutsui 	uint8_t		pt;
64eff817e3Stsutsui 	uint8_t		phase;		/* 0, 1, 2, 3: for each 128 byte */
65eff817e3Stsutsui 	uint16_t	block;
66eff817e3Stsutsui 	uint8_t		data[MLCD_MAXACCSIZE];
6751cd3ce6Sitohy };
6851cd3ce6Sitohy #define MLCD_SIZE_REQW(sc)	((sc)->sc_waccsz + 8)
6951cd3ce6Sitohy 
7051cd3ce6Sitohy struct mlcd_request_get_media_info {
71eff817e3Stsutsui 	uint32_t	func_code;
72eff817e3Stsutsui 	uint32_t	pt;		/* pt (1 byte) and unused 3 bytes */
7351cd3ce6Sitohy };
7451cd3ce6Sitohy 
7551cd3ce6Sitohy struct mlcd_media_info {
76eff817e3Stsutsui 	uint8_t		width;		/* width - 1 */
77eff817e3Stsutsui 	uint8_t		height;		/* height - 1 */
78eff817e3Stsutsui 	uint8_t		rsvd[2];	/* ? 0x10 0x02 */
7951cd3ce6Sitohy };
8051cd3ce6Sitohy 
8151cd3ce6Sitohy struct mlcd_response_media_info {
82eff817e3Stsutsui 	uint32_t	func_code;	/* function code (big endian) */
8351cd3ce6Sitohy 	struct mlcd_media_info info;
8451cd3ce6Sitohy };
8551cd3ce6Sitohy 
860d637417Sitohy struct mlcd_buf {
870d637417Sitohy 	SIMPLEQ_ENTRY(mlcd_buf)	lb_q;
880d637417Sitohy 	int		lb_error;
890d637417Sitohy 	int		lb_partno;
900d637417Sitohy 	int		lb_blkno;
91eff817e3Stsutsui 	uint32_t	lb_data[1];	/* variable length */
920d637417Sitohy };
930d637417Sitohy #define MLCD_BUF_SZ(sc) (offsetof(struct mlcd_buf, lb_data) + (sc)->sc_bsize)
940d637417Sitohy 
9551cd3ce6Sitohy struct mlcd_softc {
9607d8ce49Stsutsui 	device_t sc_dev;
9751cd3ce6Sitohy 
9866bdd36fSdyoung 	device_t sc_parent;
9951cd3ce6Sitohy 	struct maple_unit *sc_unit;
1000d637417Sitohy 	int		sc_direction;
10151cd3ce6Sitohy 	enum mlcd_stat {
10251cd3ce6Sitohy 		MLCD_INIT,	/* during initialization */
10351cd3ce6Sitohy 		MLCD_INIT2,	/* during initialization */
10451cd3ce6Sitohy 		MLCD_IDLE,	/* init done, not in I/O */
10551cd3ce6Sitohy 		MLCD_WRITE,	/* in write operation */
10651cd3ce6Sitohy 		MLCD_DETACH	/* detaching */
10751cd3ce6Sitohy 	} sc_stat;
10851cd3ce6Sitohy 
10951cd3ce6Sitohy 	int		sc_npt;		/* number of partitions */
11051cd3ce6Sitohy 	int		sc_bsize;	/* block size */
11151cd3ce6Sitohy 	int		sc_wacc;	/* number of write access per block */
11251cd3ce6Sitohy 	int		sc_waccsz;	/* size of a write access */
11351cd3ce6Sitohy 
11451cd3ce6Sitohy 	struct mlcd_pt {
11551cd3ce6Sitohy 		int		pt_flags;
11651cd3ce6Sitohy #define MLCD_PT_OK	1	/* partition is alive */
11751cd3ce6Sitohy #define MLCD_PT_OPEN	2
11851cd3ce6Sitohy 		struct mlcd_media_info pt_info;	/* geometry per part */
11951cd3ce6Sitohy 		int		pt_size;	/* partition size in byte */
1200d637417Sitohy 		int		pt_nblk;	/* partition size in block */
12151cd3ce6Sitohy 
1220d637417Sitohy 		char		pt_name[16 /* see device.h */ + 4 /* ".255" */];
12351cd3ce6Sitohy 	} *sc_pt;
12451cd3ce6Sitohy 
12551cd3ce6Sitohy 	/* write request buffer (only one is used at a time) */
12651cd3ce6Sitohy 	union {
12751cd3ce6Sitohy 		struct mlcd_request_write_data req_write;
12851cd3ce6Sitohy 		struct mlcd_request_get_media_info req_minfo;
12951cd3ce6Sitohy 	} sc_req;
13051cd3ce6Sitohy #define sc_reqw	sc_req.req_write
13151cd3ce6Sitohy #define sc_reqm	sc_req.req_minfo
13251cd3ce6Sitohy 
13351cd3ce6Sitohy 	/* pending buffers */
1340d637417Sitohy 	SIMPLEQ_HEAD(mlcd_bufq, mlcd_buf) sc_q;
13551cd3ce6Sitohy 
13651cd3ce6Sitohy 	/* current I/O access */
1370d637417Sitohy 	struct mlcd_buf	*sc_bp;
13851cd3ce6Sitohy 	int		sc_retry;
13951cd3ce6Sitohy #define MLCD_MAXRETRY	10
14051cd3ce6Sitohy };
14151cd3ce6Sitohy 
14251cd3ce6Sitohy /*
14351cd3ce6Sitohy  * minor number layout (mlcddetach() depends on this layout):
14451cd3ce6Sitohy  *
14551cd3ce6Sitohy  * 19 18 17 16 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
14651cd3ce6Sitohy  * |---------------------------------| |---------------------|
14751cd3ce6Sitohy  *                unit                          part
14851cd3ce6Sitohy  */
14951cd3ce6Sitohy #define MLCD_PART(dev)		(minor(dev) & 0xff)
15051cd3ce6Sitohy #define MLCD_UNIT(dev)		(minor(dev) >> 8)
15151cd3ce6Sitohy #define MLCD_MINOR(unit, part)	(((unit) << 8) | (part))
15251cd3ce6Sitohy 
15307d8ce49Stsutsui static int	mlcdmatch(device_t, cfdata_t, void *);
15466bdd36fSdyoung static void	mlcdattach(device_t, device_t, void *);
15566bdd36fSdyoung static int	mlcddetach(device_t, int);
156eff817e3Stsutsui static void	mlcd_intr(void *, struct maple_response *, int, int);
157eff817e3Stsutsui static void	mlcd_printerror(const char *, uint32_t);
158eff817e3Stsutsui static struct mlcd_buf *mlcd_buf_alloc(int /*dev*/, int /*flags*/);
159eff817e3Stsutsui static void	mlcd_buf_free(struct mlcd_buf *);
1605f1c88d7Sperry static inline uint32_t reverse_32(uint32_t);
161eff817e3Stsutsui static void	mlcd_rotate_bitmap(void *, size_t);
162eff817e3Stsutsui static void	mlcdstart(struct mlcd_softc *);
163eff817e3Stsutsui static void	mlcdstart_bp(struct mlcd_softc *);
164eff817e3Stsutsui static void	mlcddone(struct mlcd_softc *);
16551cd3ce6Sitohy 
16651cd3ce6Sitohy dev_type_open(mlcdopen);
16751cd3ce6Sitohy dev_type_close(mlcdclose);
16851cd3ce6Sitohy dev_type_write(mlcdwrite);
16951cd3ce6Sitohy dev_type_ioctl(mlcdioctl);
17051cd3ce6Sitohy 
17151cd3ce6Sitohy const struct cdevsw mlcd_cdevsw = {
172a68f9396Sdholland 	.d_open = mlcdopen,
173a68f9396Sdholland 	.d_close = mlcdclose,
174a68f9396Sdholland 	.d_read = noread,
175a68f9396Sdholland 	.d_write = mlcdwrite,
176a68f9396Sdholland 	.d_ioctl = mlcdioctl,
177a68f9396Sdholland 	.d_stop = nostop,
178a68f9396Sdholland 	.d_tty = notty,
179a68f9396Sdholland 	.d_poll = nopoll,
180a68f9396Sdholland 	.d_mmap = nommap,
181a68f9396Sdholland 	.d_kqfilter = nokqfilter,
182*f9228f42Sdholland 	.d_discard = nodiscard,
183a68f9396Sdholland 	.d_flag = 0
18451cd3ce6Sitohy };
18551cd3ce6Sitohy 
18607d8ce49Stsutsui CFATTACH_DECL_NEW(mlcd, sizeof(struct mlcd_softc),
18751cd3ce6Sitohy     mlcdmatch, mlcdattach, mlcddetach, NULL);
18851cd3ce6Sitohy 
18951cd3ce6Sitohy /* initial image "NetBSD dreamcast" */
19051cd3ce6Sitohy static const char initimg48x32[192] = {
19151cd3ce6Sitohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
19251cd3ce6Sitohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
19351cd3ce6Sitohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940d637417Sitohy 	0x1c, 0x70, 0x00, 0x7e, 0x1c, 0xf0, 0x0c, 0x60, 0x00, 0x33, 0x26, 0x6c,
1950d637417Sitohy 	0x0c, 0x60, 0x0c, 0x33, 0x66, 0x66, 0x1e, 0xc7, 0x0c, 0x62, 0x60, 0xc6,
1960d637417Sitohy 	0x1a, 0xc9, 0xbe, 0x7c, 0x30, 0xc6, 0x1a, 0xdb, 0x98, 0x66, 0x18, 0xc6,
1970d637417Sitohy 	0x1a, 0xdc, 0x18, 0x66, 0x0d, 0x8c, 0x31, 0xb0, 0x32, 0xc6, 0x8d, 0x8c,
1980d637417Sitohy 	0x31, 0xb1, 0x36, 0xcd, 0x99, 0x98, 0x71, 0x9e, 0x1d, 0xf9, 0xf3, 0xe0,
1990d637417Sitohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2000d637417Sitohy 	0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08,
2010d637417Sitohy 	0x1d, 0x6c, 0x63, 0xc7, 0x30, 0xde, 0x25, 0x92, 0x12, 0xa8, 0x09, 0x08,
2020d637417Sitohy 	0x25, 0x1e, 0x72, 0xa8, 0x38, 0xc8, 0x25, 0x10, 0x92, 0xa8, 0x48, 0x28,
2030d637417Sitohy 	0x1d, 0x0e, 0x6a, 0xa7, 0x35, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
20451cd3ce6Sitohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
20551cd3ce6Sitohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
20651cd3ce6Sitohy 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
20751cd3ce6Sitohy };
20851cd3ce6Sitohy 
2090d637417Sitohy /* ARGSUSED */
21051cd3ce6Sitohy static int
mlcdmatch(device_t parent,cfdata_t cf,void * aux)21107d8ce49Stsutsui mlcdmatch(device_t parent, cfdata_t cf, void *aux)
21251cd3ce6Sitohy {
21351cd3ce6Sitohy 	struct maple_attach_args *ma = aux;
21451cd3ce6Sitohy 
21551cd3ce6Sitohy 	return (ma->ma_function == MAPLE_FN_LCD ? MAPLE_MATCH_FUNC : 0);
21651cd3ce6Sitohy }
21751cd3ce6Sitohy 
21851cd3ce6Sitohy static void
mlcdattach(device_t parent,device_t self,void * aux)21966bdd36fSdyoung mlcdattach(device_t parent, device_t self, void *aux)
22051cd3ce6Sitohy {
22166bdd36fSdyoung 	struct mlcd_softc *sc = device_private(self);
22251cd3ce6Sitohy 	struct maple_attach_args *ma = aux;
22351cd3ce6Sitohy 	int i;
22451cd3ce6Sitohy 	union {
225eff817e3Stsutsui 		uint32_t v;
22651cd3ce6Sitohy 		struct mlcd_funcdef s;
22751cd3ce6Sitohy 	} funcdef;
22851cd3ce6Sitohy 
22907d8ce49Stsutsui 	sc->sc_dev = self;
23051cd3ce6Sitohy 	sc->sc_parent = parent;
23151cd3ce6Sitohy 	sc->sc_unit = ma->ma_unit;
2320d637417Sitohy 	sc->sc_direction = ma->ma_basedevinfo->di_connector_direction;
23351cd3ce6Sitohy 
23451cd3ce6Sitohy 	funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_LCD);
23551cd3ce6Sitohy 	printf(": LCD display\n");
23651cd3ce6Sitohy 	printf("%s: %d LCD, %d bytes/block, ",
23707d8ce49Stsutsui 	    device_xname(self),
23851cd3ce6Sitohy 	    sc->sc_npt = funcdef.s.pt + 1,
23951cd3ce6Sitohy 	    sc->sc_bsize = (funcdef.s.bb + 1) << 5);
24051cd3ce6Sitohy 	if ((sc->sc_wacc = funcdef.s.wa) == 0)
24151cd3ce6Sitohy 		printf("no ");
24251cd3ce6Sitohy 	else
24351cd3ce6Sitohy 		printf("%d acc/", sc->sc_wacc);
2440d637417Sitohy 	printf("write, %s, norm %s%s\n",
2450d637417Sitohy 	    funcdef.s.hv ? "vert" : "horiz",
2460d637417Sitohy 	    funcdef.s.bw ? "black" : "white",
2470d637417Sitohy 	    sc->sc_direction == MAPLE_CONN_TOP ? ", upside-down" : "");
24851cd3ce6Sitohy 
24951cd3ce6Sitohy 	/*
25051cd3ce6Sitohy 	 * start init sequence
25151cd3ce6Sitohy 	 */
25251cd3ce6Sitohy 	sc->sc_stat = MLCD_INIT;
2530d637417Sitohy 	SIMPLEQ_INIT(&sc->sc_q);
25451cd3ce6Sitohy 
25551cd3ce6Sitohy 	/* check consistency */
25651cd3ce6Sitohy 	if (sc->sc_wacc != 0) {
25751cd3ce6Sitohy 		sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc;
25851cd3ce6Sitohy 		if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) {
25951cd3ce6Sitohy 			printf("%s: write access isn't equally divided\n",
26007d8ce49Stsutsui 			    device_xname(self));
26151cd3ce6Sitohy 			sc->sc_wacc = 0;	/* no write */
26251cd3ce6Sitohy 		} else if (sc->sc_waccsz > MLCD_MAXACCSIZE) {
26351cd3ce6Sitohy 			printf("%s: write access size is too large\n",
26407d8ce49Stsutsui 			    device_xname(self));
26551cd3ce6Sitohy 			sc->sc_wacc = 0;	/* no write */
26651cd3ce6Sitohy 		}
26751cd3ce6Sitohy 	}
26851cd3ce6Sitohy 	if (sc->sc_wacc == 0) {
26951cd3ce6Sitohy 		printf("%s: device doesn't support write\n",
27007d8ce49Stsutsui 		    device_xname(self));
27151cd3ce6Sitohy 		return;
27251cd3ce6Sitohy 	}
27351cd3ce6Sitohy 
27451cd3ce6Sitohy 	/* per-part structure */
27551cd3ce6Sitohy 	sc->sc_pt = malloc(sizeof(struct mlcd_pt) * sc->sc_npt, M_DEVBUF,
27651cd3ce6Sitohy 	    M_WAITOK|M_ZERO);
27751cd3ce6Sitohy 
27851cd3ce6Sitohy 	for (i = 0; i < sc->sc_npt; i++) {
27998335431Schristos 		snprintf(sc->sc_pt[i].pt_name, sizeof(sc->sc_pt[i].pt_name),
28098335431Schristos 		    "%s.%d", device_xname(self), i);
28151cd3ce6Sitohy 	}
28251cd3ce6Sitohy 
28351cd3ce6Sitohy 	maple_set_callback(parent, sc->sc_unit, MAPLE_FN_LCD,
28451cd3ce6Sitohy 	    mlcd_intr, sc);
28551cd3ce6Sitohy 
28651cd3ce6Sitohy 	/*
28751cd3ce6Sitohy 	 * get size (start from partition 0)
28851cd3ce6Sitohy 	 */
289eff817e3Stsutsui 	sc->sc_reqm.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
29051cd3ce6Sitohy 	sc->sc_reqm.pt = 0;
29151cd3ce6Sitohy 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
29251cd3ce6Sitohy 	    MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
29351cd3ce6Sitohy }
29451cd3ce6Sitohy 
2950d637417Sitohy /* ARGSUSED1 */
29651cd3ce6Sitohy static int
mlcddetach(device_t self,int flags)29766bdd36fSdyoung mlcddetach(device_t self, int flags)
29851cd3ce6Sitohy {
29966bdd36fSdyoung 	struct mlcd_softc *sc = device_private(self);
3000d637417Sitohy 	struct mlcd_buf *bp;
30151cd3ce6Sitohy 	int minor_l, minor_h;
30251cd3ce6Sitohy 
30351cd3ce6Sitohy 	sc->sc_stat = MLCD_DETACH;	/* just in case */
30451cd3ce6Sitohy 
30551cd3ce6Sitohy 	/*
30651cd3ce6Sitohy 	 * kill pending I/O
30751cd3ce6Sitohy 	 */
30851cd3ce6Sitohy 	if ((bp = sc->sc_bp) != NULL) {
3090d637417Sitohy 		bp->lb_error = EIO;
3100d637417Sitohy 		wakeup(bp);
31151cd3ce6Sitohy 	}
3120d637417Sitohy 	while ((bp = SIMPLEQ_FIRST(&sc->sc_q)) != NULL) {
3130d637417Sitohy 		SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
3140d637417Sitohy 		bp->lb_error = EIO;
3150d637417Sitohy 		wakeup(bp);
31651cd3ce6Sitohy 	}
31751cd3ce6Sitohy 
31851cd3ce6Sitohy 	/*
31951cd3ce6Sitohy 	 * revoke vnodes
32051cd3ce6Sitohy 	 */
32139cd836eSthorpej 	minor_l = MLCD_MINOR(device_unit(self), 0);
32239cd836eSthorpej 	minor_h = MLCD_MINOR(device_unit(self), sc->sc_npt - 1);
32351cd3ce6Sitohy 	vdevgone(cdevsw_lookup_major(&mlcd_cdevsw), minor_l, minor_h, VCHR);
32451cd3ce6Sitohy 
32551cd3ce6Sitohy 	/*
32651cd3ce6Sitohy 	 * free per-partition structure
32751cd3ce6Sitohy 	 */
32851cd3ce6Sitohy 	if (sc->sc_pt)
32951cd3ce6Sitohy 		free(sc->sc_pt, M_DEVBUF);
33051cd3ce6Sitohy 
33151cd3ce6Sitohy 	return 0;
33251cd3ce6Sitohy }
33351cd3ce6Sitohy 
33451cd3ce6Sitohy /*
33551cd3ce6Sitohy  * called back from maple bus driver
33651cd3ce6Sitohy  */
3370d637417Sitohy /* ARGSUSED3 */
33851cd3ce6Sitohy static void
mlcd_intr(void * arg,struct maple_response * response,int sz,int flags)33907d8ce49Stsutsui mlcd_intr(void *arg, struct maple_response *response, int sz, int flags)
34051cd3ce6Sitohy {
34107d8ce49Stsutsui 	struct mlcd_softc *sc = arg;
34251cd3ce6Sitohy 	struct mlcd_response_media_info *rm = (void *) response->data;
3430d637417Sitohy 	struct mlcd_buf *bp;
34451cd3ce6Sitohy 	int part;
34551cd3ce6Sitohy 	struct mlcd_pt *pt;
34651cd3ce6Sitohy 
34751cd3ce6Sitohy 	switch (sc->sc_stat) {
34851cd3ce6Sitohy 	case MLCD_INIT:
34951cd3ce6Sitohy 		/* checking part geometry */
35051cd3ce6Sitohy 		part = sc->sc_reqm.pt;
35151cd3ce6Sitohy 		pt = &sc->sc_pt[part];
35251cd3ce6Sitohy 		switch ((maple_response_t) response->response_code) {
35351cd3ce6Sitohy 		case MAPLE_RESPONSE_DATATRF:
35451cd3ce6Sitohy 			pt->pt_info = rm->info;
35551cd3ce6Sitohy 			pt->pt_size = ((pt->pt_info.width + 1) *
35651cd3ce6Sitohy 			    (pt->pt_info.height + 1) + 7) / 8;
35751cd3ce6Sitohy 			pt->pt_nblk = pt->pt_size / sc->sc_bsize;
35851cd3ce6Sitohy 			printf("%s: %dx%d display, %d bytes\n",
35951cd3ce6Sitohy 			    pt->pt_name,
36051cd3ce6Sitohy 			    pt->pt_info.width + 1, pt->pt_info.height + 1,
36151cd3ce6Sitohy 			    pt->pt_size);
36251cd3ce6Sitohy 
36351cd3ce6Sitohy 			/* this partition is active */
36451cd3ce6Sitohy 			pt->pt_flags = MLCD_PT_OK;
36551cd3ce6Sitohy 
36651cd3ce6Sitohy 			break;
36751cd3ce6Sitohy 		default:
36851cd3ce6Sitohy 			printf("%s: init: unexpected response %#x, sz %d\n",
369eff817e3Stsutsui 			    pt->pt_name, be32toh(response->response_code), sz);
37051cd3ce6Sitohy 			break;
37151cd3ce6Sitohy 		}
37251cd3ce6Sitohy 		if (++part == sc->sc_npt) {
37351cd3ce6Sitohy 			/* init done */
37451cd3ce6Sitohy 
37551cd3ce6Sitohy 			/* XXX initial image for Visual Memory */
37651cd3ce6Sitohy 			if (sc->sc_pt[0].pt_size == sizeof initimg48x32 &&
37751cd3ce6Sitohy 			    sc->sc_waccsz == sizeof initimg48x32 &&
37851cd3ce6Sitohy 			    sc->sc_wacc == 1) {
37951cd3ce6Sitohy 				sc->sc_stat = MLCD_INIT2;
38051cd3ce6Sitohy 				sc->sc_reqw.func_code =
381eff817e3Stsutsui 				    htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
38251cd3ce6Sitohy 				sc->sc_reqw.pt = 0;	/* part 0 */
38351cd3ce6Sitohy 				sc->sc_reqw.block = 0;
38451cd3ce6Sitohy 				sc->sc_reqw.phase = 0;
385eff817e3Stsutsui 				memcpy(sc->sc_reqw.data, initimg48x32,
38651cd3ce6Sitohy 				    sizeof initimg48x32);
3870d637417Sitohy 				if (sc->sc_direction == MAPLE_CONN_TOP) {
3880d637417Sitohy 					/* the LCD is upside-down */
3890d637417Sitohy 					mlcd_rotate_bitmap(sc->sc_reqw.data,
3900d637417Sitohy 					    sizeof initimg48x32);
3910d637417Sitohy 				}
39251cd3ce6Sitohy 				maple_command(sc->sc_parent, sc->sc_unit,
39351cd3ce6Sitohy 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
39451cd3ce6Sitohy 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
39551cd3ce6Sitohy 			} else
39651cd3ce6Sitohy 				sc->sc_stat = MLCD_IDLE;	/* init done */
39751cd3ce6Sitohy 		} else {
39851cd3ce6Sitohy 			sc->sc_reqm.pt = part;
39951cd3ce6Sitohy 			maple_command(sc->sc_parent, sc->sc_unit,
40051cd3ce6Sitohy 			    MAPLE_FN_LCD, MAPLE_COMMAND_GETMINFO,
40151cd3ce6Sitohy 			    sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
40251cd3ce6Sitohy 		}
40351cd3ce6Sitohy 		break;
40451cd3ce6Sitohy 
40551cd3ce6Sitohy 	case MLCD_INIT2:
40651cd3ce6Sitohy 		sc->sc_stat = MLCD_IDLE;	/* init done */
40751cd3ce6Sitohy 		break;
40851cd3ce6Sitohy 
40951cd3ce6Sitohy 	case MLCD_WRITE:
41051cd3ce6Sitohy 		bp = sc->sc_bp;
41151cd3ce6Sitohy 
41251cd3ce6Sitohy 		switch ((maple_response_t) response->response_code) {
41351cd3ce6Sitohy 		case MAPLE_RESPONSE_OK:			/* write done */
41451cd3ce6Sitohy 			if (++sc->sc_reqw.phase == sc->sc_wacc) {
41551cd3ce6Sitohy 				/* all phase done */
41651cd3ce6Sitohy 				mlcddone(sc);
41751cd3ce6Sitohy 			} else {
41851cd3ce6Sitohy 				/* go next phase */
419eff817e3Stsutsui 				memcpy(sc->sc_reqw.data,
420eff817e3Stsutsui 				    (char *)bp->lb_data +
421eff817e3Stsutsui 				    sc->sc_waccsz * sc->sc_reqw.phase,
422eff817e3Stsutsui 				    sc->sc_waccsz);
42351cd3ce6Sitohy 				maple_command(sc->sc_parent, sc->sc_unit,
42451cd3ce6Sitohy 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
42551cd3ce6Sitohy 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
42651cd3ce6Sitohy 			}
42751cd3ce6Sitohy 			break;
42851cd3ce6Sitohy 		case MAPLE_RESPONSE_LCDERR:
42951cd3ce6Sitohy 			mlcd_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name,
43051cd3ce6Sitohy 			    rm->func_code /* XXX */);
4310d637417Sitohy 			mlcdstart_bp(sc);		/* retry */
43251cd3ce6Sitohy 			break;
43351cd3ce6Sitohy 		default:
43451cd3ce6Sitohy 			printf("%s: write: unexpected response %#x, %#x, sz %d\n",
43551cd3ce6Sitohy 			    sc->sc_pt[sc->sc_reqw.pt].pt_name,
436eff817e3Stsutsui 			    be32toh(response->response_code),
437eff817e3Stsutsui 			    be32toh(rm->func_code), sz);
4380d637417Sitohy 			mlcdstart_bp(sc);		/* retry */
43951cd3ce6Sitohy 			break;
44051cd3ce6Sitohy 		}
44151cd3ce6Sitohy 		break;
44251cd3ce6Sitohy 
44351cd3ce6Sitohy 	default:
44451cd3ce6Sitohy 		break;
44551cd3ce6Sitohy 	}
44651cd3ce6Sitohy }
44751cd3ce6Sitohy 
44851cd3ce6Sitohy static void
mlcd_printerror(const char * head,uint32_t code)449eff817e3Stsutsui mlcd_printerror(const char *head, uint32_t code)
45051cd3ce6Sitohy {
45151cd3ce6Sitohy 
45251cd3ce6Sitohy 	printf("%s:", head);
45351cd3ce6Sitohy 	NTOHL(code);
45451cd3ce6Sitohy 	if (code & 1)
45551cd3ce6Sitohy 		printf(" PT error");
45651cd3ce6Sitohy 	if (code & 2)
45751cd3ce6Sitohy 		printf(" Phase error");
45851cd3ce6Sitohy 	if (code & 4)
45951cd3ce6Sitohy 		printf(" Block error");
46051cd3ce6Sitohy 	if (code & 010)
46151cd3ce6Sitohy 		printf(" Write error");
46251cd3ce6Sitohy 	if (code & 020)
46351cd3ce6Sitohy 		printf(" Length error");
46451cd3ce6Sitohy 	if (code & ~037)
46551cd3ce6Sitohy 		printf(" Unknown error %#x", code & ~037);
46651cd3ce6Sitohy 	printf("\n");
46751cd3ce6Sitohy }
46851cd3ce6Sitohy 
4690d637417Sitohy /* ARGSUSED */
47051cd3ce6Sitohy int
mlcdopen(dev_t dev,int flags,int devtype,struct lwp * l)47195e1ffb1Schristos mlcdopen(dev_t dev, int flags, int devtype, struct lwp *l)
47251cd3ce6Sitohy {
47351cd3ce6Sitohy 	int unit, part;
47451cd3ce6Sitohy 	struct mlcd_softc *sc;
47551cd3ce6Sitohy 	struct mlcd_pt *pt;
47651cd3ce6Sitohy 
47751cd3ce6Sitohy 	unit = MLCD_UNIT(dev);
47851cd3ce6Sitohy 	part = MLCD_PART(dev);
479a0e4edbbStsutsui 	if ((sc = device_lookup_private(&mlcd_cd, unit)) == NULL
48051cd3ce6Sitohy 	    || sc->sc_stat == MLCD_INIT
48151cd3ce6Sitohy 	    || sc->sc_stat == MLCD_INIT2
48251cd3ce6Sitohy 	    || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0)
48351cd3ce6Sitohy 		return ENXIO;
48451cd3ce6Sitohy 
48551cd3ce6Sitohy 	if (pt->pt_flags & MLCD_PT_OPEN)
48651cd3ce6Sitohy 		return EBUSY;
48751cd3ce6Sitohy 
48851cd3ce6Sitohy 	pt->pt_flags |= MLCD_PT_OPEN;
48951cd3ce6Sitohy 
49051cd3ce6Sitohy 	return 0;
49151cd3ce6Sitohy }
49251cd3ce6Sitohy 
4930d637417Sitohy /* ARGSUSED */
49451cd3ce6Sitohy int
mlcdclose(dev_t dev,int flags,int devtype,struct lwp * l)49595e1ffb1Schristos mlcdclose(dev_t dev, int flags, int devtype, struct lwp *l)
49651cd3ce6Sitohy {
49751cd3ce6Sitohy 	int unit, part;
49851cd3ce6Sitohy 	struct mlcd_softc *sc;
49951cd3ce6Sitohy 	struct mlcd_pt *pt;
50051cd3ce6Sitohy 
50151cd3ce6Sitohy 	unit = MLCD_UNIT(dev);
50251cd3ce6Sitohy 	part = MLCD_PART(dev);
503e6a3083cStsutsui 	sc = device_lookup_private(&mlcd_cd, unit);
50451cd3ce6Sitohy 	pt = &sc->sc_pt[part];
50551cd3ce6Sitohy 
50651cd3ce6Sitohy 	pt->pt_flags &= ~MLCD_PT_OPEN;
50751cd3ce6Sitohy 
50851cd3ce6Sitohy 	return 0;
50951cd3ce6Sitohy }
51051cd3ce6Sitohy 
51151cd3ce6Sitohy /*
51251cd3ce6Sitohy  * start I/O operations
51351cd3ce6Sitohy  */
51451cd3ce6Sitohy static void
mlcdstart(struct mlcd_softc * sc)515eff817e3Stsutsui mlcdstart(struct mlcd_softc *sc)
51651cd3ce6Sitohy {
5170d637417Sitohy 	struct mlcd_buf *bp;
51851cd3ce6Sitohy 
5190d637417Sitohy 	if ((bp = SIMPLEQ_FIRST(&sc->sc_q)) == NULL) {
52051cd3ce6Sitohy 		sc->sc_stat = MLCD_IDLE;
52151cd3ce6Sitohy 		maple_enable_unit_ping(sc->sc_parent, sc->sc_unit,
52251cd3ce6Sitohy 		    MAPLE_FN_LCD, 1);
52351cd3ce6Sitohy 		return;
52451cd3ce6Sitohy 	}
52551cd3ce6Sitohy 
5260d637417Sitohy 	SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
5270d637417Sitohy 
5280d637417Sitohy 	sc->sc_bp = bp;
52951cd3ce6Sitohy 	sc->sc_retry = 0;
5300d637417Sitohy 	mlcdstart_bp(sc);
53151cd3ce6Sitohy }
53251cd3ce6Sitohy 
53351cd3ce6Sitohy /*
53451cd3ce6Sitohy  * start/retry a specified I/O operation
53551cd3ce6Sitohy  */
53651cd3ce6Sitohy static void
mlcdstart_bp(struct mlcd_softc * sc)537eff817e3Stsutsui mlcdstart_bp(struct mlcd_softc *sc)
53851cd3ce6Sitohy {
5390d637417Sitohy 	struct mlcd_buf *bp;
54051cd3ce6Sitohy 
5410d637417Sitohy 	bp = sc->sc_bp;
54251cd3ce6Sitohy 
54351cd3ce6Sitohy 	/* handle retry */
54451cd3ce6Sitohy 	if (sc->sc_retry++ > MLCD_MAXRETRY) {
54551cd3ce6Sitohy 		/* retry count exceeded */
5460d637417Sitohy 		bp->lb_error = EIO;
54751cd3ce6Sitohy 		mlcddone(sc);
54851cd3ce6Sitohy 		return;
54951cd3ce6Sitohy 	}
55051cd3ce6Sitohy 
55151cd3ce6Sitohy 	/*
55251cd3ce6Sitohy 	 * I/O access will fail if the removal detection (by maple driver)
55351cd3ce6Sitohy 	 * occurs before finishing the I/O, so disable it.
55451cd3ce6Sitohy 	 * We are sending commands, and the removal detection is still alive.
55551cd3ce6Sitohy 	 */
55651cd3ce6Sitohy 	maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
55751cd3ce6Sitohy 
55851cd3ce6Sitohy 	/*
55951cd3ce6Sitohy 	 * Start the first phase (phase# = 0).
56051cd3ce6Sitohy 	 */
56151cd3ce6Sitohy 	/* write */
56251cd3ce6Sitohy 	sc->sc_stat = MLCD_WRITE;
563eff817e3Stsutsui 	sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
5640d637417Sitohy 	sc->sc_reqw.pt = bp->lb_partno;
565eff817e3Stsutsui 	sc->sc_reqw.block = htobe16(bp->lb_blkno);
56651cd3ce6Sitohy 	sc->sc_reqw.phase = 0;		/* first phase */
567eff817e3Stsutsui 	memcpy(sc->sc_reqw.data,
568eff817e3Stsutsui 	    (char *) bp->lb_data /* + sc->sc_waccsz * phase */, sc->sc_waccsz);
56951cd3ce6Sitohy 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
57051cd3ce6Sitohy 	    MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
57151cd3ce6Sitohy }
57251cd3ce6Sitohy 
57351cd3ce6Sitohy static void
mlcddone(struct mlcd_softc * sc)574eff817e3Stsutsui mlcddone(struct mlcd_softc *sc)
57551cd3ce6Sitohy {
5760d637417Sitohy 	struct mlcd_buf *bp;
57751cd3ce6Sitohy 
57851cd3ce6Sitohy 	/* terminate current transfer */
57951cd3ce6Sitohy 	bp = sc->sc_bp;
58051cd3ce6Sitohy 	KASSERT(bp);
58151cd3ce6Sitohy 	sc->sc_bp = NULL;
5820d637417Sitohy 	wakeup(bp);
58351cd3ce6Sitohy 
58451cd3ce6Sitohy 	/* go next transfer */
58551cd3ce6Sitohy 	mlcdstart(sc);
58651cd3ce6Sitohy }
58751cd3ce6Sitohy 
5880d637417Sitohy /*
5890d637417Sitohy  * allocate a buffer for one block
5900d637417Sitohy  *
5910d637417Sitohy  * return NULL if
5920d637417Sitohy  *	[flags == M_NOWAIT] out of buffer space
5930d637417Sitohy  *	[flags == M_WAITOK] device detach detected
5940d637417Sitohy  */
5950d637417Sitohy static struct mlcd_buf *
mlcd_buf_alloc(int dev,int flags)596eff817e3Stsutsui mlcd_buf_alloc(int dev, int flags)
59751cd3ce6Sitohy {
59851cd3ce6Sitohy 	struct mlcd_softc *sc;
5990d637417Sitohy 	struct mlcd_pt *pt;
6000d637417Sitohy 	int unit, part;
6010d637417Sitohy 	struct mlcd_buf *bp;
60251cd3ce6Sitohy 
6030d637417Sitohy 	unit = MLCD_UNIT(dev);
6040d637417Sitohy 	part = MLCD_PART(dev);
605e6a3083cStsutsui 	sc = device_lookup_private(&mlcd_cd, unit);
6060d637417Sitohy 	KASSERT(sc);
6070d637417Sitohy 	pt = &sc->sc_pt[part];
6080d637417Sitohy 	KASSERT(pt);
60951cd3ce6Sitohy 
6100d637417Sitohy 	if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
6110d637417Sitohy 		return bp;
6120d637417Sitohy 
6130d637417Sitohy 	/*
6140d637417Sitohy 	 * malloc() may sleep, and the device may be detached during sleep.
6150d637417Sitohy 	 * XXX this check is not complete.
6160d637417Sitohy 	 */
617a0e4edbbStsutsui 	if (sc != device_lookup_private(&mlcd_cd, unit)
6180d637417Sitohy 	    || sc->sc_stat == MLCD_INIT
6190d637417Sitohy 	    || sc->sc_stat == MLCD_INIT2
6200d637417Sitohy 	    || part >= sc->sc_npt || pt != &sc->sc_pt[part]
6210d637417Sitohy 	    || pt->pt_flags == 0) {
6220d637417Sitohy 		free(bp, M_DEVBUF);
6230d637417Sitohy 		return NULL;
62451cd3ce6Sitohy 	}
62551cd3ce6Sitohy 
6260d637417Sitohy 	bp->lb_error = 0;
6270d637417Sitohy 
6280d637417Sitohy 	return bp;
6290d637417Sitohy }
6300d637417Sitohy 
6310d637417Sitohy static void
mlcd_buf_free(struct mlcd_buf * bp)632eff817e3Stsutsui mlcd_buf_free(struct mlcd_buf *bp)
6330d637417Sitohy {
6340d637417Sitohy 
6350d637417Sitohy 	free(bp, M_DEVBUF);
6360d637417Sitohy }
6370d637417Sitohy 
6380d637417Sitohy /* invert order of bits */
6395f1c88d7Sperry static inline uint32_t
reverse_32(uint32_t b)640eff817e3Stsutsui reverse_32(uint32_t b)
6410d637417Sitohy {
642eff817e3Stsutsui 	uint32_t b1;
6430d637417Sitohy 
6440d637417Sitohy 	/* invert every 8bit */
6450d637417Sitohy 	b1 = (b & 0x55555555) << 1;  b = (b >> 1) & 0x55555555;  b |= b1;
6460d637417Sitohy 	b1 = (b & 0x33333333) << 2;  b = (b >> 2) & 0x33333333;  b |= b1;
6470d637417Sitohy 	b1 = (b & 0x0f0f0f0f) << 4;  b = (b >> 4) & 0x0f0f0f0f;  b |= b1;
6480d637417Sitohy 
6490d637417Sitohy 	/* invert byte order */
6500d637417Sitohy 	return bswap32(b);
6510d637417Sitohy }
6520d637417Sitohy 
6530d637417Sitohy static void
mlcd_rotate_bitmap(void * ptr,size_t size)654eff817e3Stsutsui mlcd_rotate_bitmap(void *ptr, size_t size)
6550d637417Sitohy {
656eff817e3Stsutsui 	uint32_t *p, *q, tmp;
6570d637417Sitohy 
658eff817e3Stsutsui 	KDASSERT(size % sizeof(uint32_t) == 0);
6590d637417Sitohy 	for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
6600d637417Sitohy 		tmp = reverse_32(*p);
6610d637417Sitohy 		*p++ = reverse_32(*--q);
6620d637417Sitohy 		*q = tmp;
6630d637417Sitohy 	}
6640d637417Sitohy }
6650d637417Sitohy 
6660d637417Sitohy /* ARGSUSED2 */
66751cd3ce6Sitohy int
mlcdwrite(dev_t dev,struct uio * uio,int flags)668eff817e3Stsutsui mlcdwrite(dev_t dev, struct uio *uio, int flags)
66951cd3ce6Sitohy {
6700d637417Sitohy 	struct mlcd_softc *sc;
6710d637417Sitohy 	struct mlcd_pt *pt;
6720d637417Sitohy 	struct mlcd_buf *bp;
6730d637417Sitohy 	int part;
6740d637417Sitohy 	off_t devsize;
6750d637417Sitohy 	int error = 0;
67651cd3ce6Sitohy 
6770d637417Sitohy 	part = MLCD_PART(dev);
678e6a3083cStsutsui 	sc = device_lookup_private(&mlcd_cd, MLCD_UNIT(dev));
6790d637417Sitohy 	pt = &sc->sc_pt[part];
6800d637417Sitohy 
6810d637417Sitohy #if 0
6820d637417Sitohy 	printf("%s: mlcdwrite: offset %ld, size %d\n",
6830d637417Sitohy 	    pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
6840d637417Sitohy #endif
6850d637417Sitohy 
6860d637417Sitohy 	devsize = pt->pt_nblk * sc->sc_bsize;
6870d637417Sitohy 	if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
6880d637417Sitohy 		return EINVAL;
6890d637417Sitohy 
6900d637417Sitohy 	if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
6910d637417Sitohy 		return EIO;	/* device is detached during allocation */
6920d637417Sitohy 
6930d637417Sitohy 	bp->lb_partno = part;
6940d637417Sitohy 
6950d637417Sitohy 	while (uio->uio_offset < devsize
6960d637417Sitohy 	    && uio->uio_resid >= (size_t) sc->sc_bsize) {
6970d637417Sitohy 		/* invert block number if upside-down */
6980d637417Sitohy 		bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
6990d637417Sitohy 		    pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
7000d637417Sitohy 		    uio->uio_offset / sc->sc_bsize;
7010d637417Sitohy 
7020d637417Sitohy 		if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
7030d637417Sitohy 			break;
7040d637417Sitohy 
7050d637417Sitohy 		if (sc->sc_direction == MAPLE_CONN_TOP) {
7060d637417Sitohy 			/* the LCD is upside-down */
7070d637417Sitohy 			mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
7080d637417Sitohy 		}
7090d637417Sitohy 
7100d637417Sitohy 		/* queue this transfer */
7110d637417Sitohy 		SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
7120d637417Sitohy 
7130d637417Sitohy 		if (sc->sc_stat == MLCD_IDLE)
7140d637417Sitohy 			mlcdstart(sc);
7150d637417Sitohy 
7160d637417Sitohy 		tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
7170d637417Sitohy 
7180d637417Sitohy 		if ((error = bp->lb_error) != 0) {
7190d637417Sitohy 			uio->uio_resid += sc->sc_bsize;
7200d637417Sitohy 			break;
7210d637417Sitohy 		}
7220d637417Sitohy 	}
7230d637417Sitohy 
7240d637417Sitohy 	mlcd_buf_free(bp);
7250d637417Sitohy 
7260d637417Sitohy 	return error;
72751cd3ce6Sitohy }
72851cd3ce6Sitohy 
72951cd3ce6Sitohy int
mlcdioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)73053524e44Schristos mlcdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
73151cd3ce6Sitohy {
73206481cf2Smartin 	int unit;
73351cd3ce6Sitohy 	struct mlcd_softc *sc;
73451cd3ce6Sitohy 
73551cd3ce6Sitohy 	unit = MLCD_UNIT(dev);
736e6a3083cStsutsui 	sc = device_lookup_private(&mlcd_cd, unit);
73751cd3ce6Sitohy 
73851cd3ce6Sitohy 	switch (cmd) {
73951cd3ce6Sitohy 
74051cd3ce6Sitohy 	default:
74151cd3ce6Sitohy 		/* generic maple ioctl */
74251cd3ce6Sitohy 		return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
74395e1ffb1Schristos 		    flag, l);
74451cd3ce6Sitohy 	}
74551cd3ce6Sitohy 
74651cd3ce6Sitohy 	return 0;
74751cd3ce6Sitohy }
748