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