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