xref: /netbsd-src/sys/arch/dreamcast/dev/maple/mlcd.c (revision c2f76ff004a2cb67efe5b12d97bd3ef7fe89e18d)
1 /*	$NetBSD: mlcd.c,v 1.14 2010/10/17 14:17:49 tsutsui 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.14 2010/10/17 14:17:49 tsutsui 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 	mlcdopen, mlcdclose, noread, mlcdwrite, mlcdioctl,
173 	nostop, notty, nopoll, nommap, nokqfilter
174 };
175 
176 CFATTACH_DECL_NEW(mlcd, sizeof(struct mlcd_softc),
177     mlcdmatch, mlcdattach, mlcddetach, NULL);
178 
179 /* initial image "NetBSD dreamcast" */
180 static const char initimg48x32[192] = {
181 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
182 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
183 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
184 	0x1c, 0x70, 0x00, 0x7e, 0x1c, 0xf0, 0x0c, 0x60, 0x00, 0x33, 0x26, 0x6c,
185 	0x0c, 0x60, 0x0c, 0x33, 0x66, 0x66, 0x1e, 0xc7, 0x0c, 0x62, 0x60, 0xc6,
186 	0x1a, 0xc9, 0xbe, 0x7c, 0x30, 0xc6, 0x1a, 0xdb, 0x98, 0x66, 0x18, 0xc6,
187 	0x1a, 0xdc, 0x18, 0x66, 0x0d, 0x8c, 0x31, 0xb0, 0x32, 0xc6, 0x8d, 0x8c,
188 	0x31, 0xb1, 0x36, 0xcd, 0x99, 0x98, 0x71, 0x9e, 0x1d, 0xf9, 0xf3, 0xe0,
189 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
190 	0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08,
191 	0x1d, 0x6c, 0x63, 0xc7, 0x30, 0xde, 0x25, 0x92, 0x12, 0xa8, 0x09, 0x08,
192 	0x25, 0x1e, 0x72, 0xa8, 0x38, 0xc8, 0x25, 0x10, 0x92, 0xa8, 0x48, 0x28,
193 	0x1d, 0x0e, 0x6a, 0xa7, 0x35, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
194 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
195 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
196 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
197 };
198 
199 /* ARGSUSED */
200 static int
201 mlcdmatch(device_t parent, cfdata_t cf, void *aux)
202 {
203 	struct maple_attach_args *ma = aux;
204 
205 	return (ma->ma_function == MAPLE_FN_LCD ? MAPLE_MATCH_FUNC : 0);
206 }
207 
208 static void
209 mlcdattach(device_t parent, device_t self, void *aux)
210 {
211 	struct mlcd_softc *sc = device_private(self);
212 	struct maple_attach_args *ma = aux;
213 	int i;
214 	union {
215 		uint32_t v;
216 		struct mlcd_funcdef s;
217 	} funcdef;
218 
219 	sc->sc_dev = self;
220 	sc->sc_parent = parent;
221 	sc->sc_unit = ma->ma_unit;
222 	sc->sc_direction = ma->ma_basedevinfo->di_connector_direction;
223 
224 	funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_LCD);
225 	printf(": LCD display\n");
226 	printf("%s: %d LCD, %d bytes/block, ",
227 	    device_xname(self),
228 	    sc->sc_npt = funcdef.s.pt + 1,
229 	    sc->sc_bsize = (funcdef.s.bb + 1) << 5);
230 	if ((sc->sc_wacc = funcdef.s.wa) == 0)
231 		printf("no ");
232 	else
233 		printf("%d acc/", sc->sc_wacc);
234 	printf("write, %s, norm %s%s\n",
235 	    funcdef.s.hv ? "vert" : "horiz",
236 	    funcdef.s.bw ? "black" : "white",
237 	    sc->sc_direction == MAPLE_CONN_TOP ? ", upside-down" : "");
238 
239 	/*
240 	 * start init sequence
241 	 */
242 	sc->sc_stat = MLCD_INIT;
243 	SIMPLEQ_INIT(&sc->sc_q);
244 
245 	/* check consistency */
246 	if (sc->sc_wacc != 0) {
247 		sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc;
248 		if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) {
249 			printf("%s: write access isn't equally divided\n",
250 			    device_xname(self));
251 			sc->sc_wacc = 0;	/* no write */
252 		} else if (sc->sc_waccsz > MLCD_MAXACCSIZE) {
253 			printf("%s: write access size is too large\n",
254 			    device_xname(self));
255 			sc->sc_wacc = 0;	/* no write */
256 		}
257 	}
258 	if (sc->sc_wacc == 0) {
259 		printf("%s: device doesn't support write\n",
260 		    device_xname(self));
261 		return;
262 	}
263 
264 	/* per-part structure */
265 	sc->sc_pt = malloc(sizeof(struct mlcd_pt) * sc->sc_npt, M_DEVBUF,
266 	    M_WAITOK|M_ZERO);
267 
268 	for (i = 0; i < sc->sc_npt; i++) {
269 		sprintf(sc->sc_pt[i].pt_name, "%s.%d", device_xname(self), i);
270 	}
271 
272 	maple_set_callback(parent, sc->sc_unit, MAPLE_FN_LCD,
273 	    mlcd_intr, sc);
274 
275 	/*
276 	 * get size (start from partition 0)
277 	 */
278 	sc->sc_reqm.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
279 	sc->sc_reqm.pt = 0;
280 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
281 	    MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
282 }
283 
284 /* ARGSUSED1 */
285 static int
286 mlcddetach(device_t self, int flags)
287 {
288 	struct mlcd_softc *sc = device_private(self);
289 	struct mlcd_buf *bp;
290 	int minor_l, minor_h;
291 
292 	sc->sc_stat = MLCD_DETACH;	/* just in case */
293 
294 	/*
295 	 * kill pending I/O
296 	 */
297 	if ((bp = sc->sc_bp) != NULL) {
298 		bp->lb_error = EIO;
299 		wakeup(bp);
300 	}
301 	while ((bp = SIMPLEQ_FIRST(&sc->sc_q)) != NULL) {
302 		SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
303 		bp->lb_error = EIO;
304 		wakeup(bp);
305 	}
306 
307 	/*
308 	 * revoke vnodes
309 	 */
310 	minor_l = MLCD_MINOR(device_unit(self), 0);
311 	minor_h = MLCD_MINOR(device_unit(self), sc->sc_npt - 1);
312 	vdevgone(cdevsw_lookup_major(&mlcd_cdevsw), minor_l, minor_h, VCHR);
313 
314 	/*
315 	 * free per-partition structure
316 	 */
317 	if (sc->sc_pt)
318 		free(sc->sc_pt, M_DEVBUF);
319 
320 	return 0;
321 }
322 
323 /*
324  * called back from maple bus driver
325  */
326 /* ARGSUSED3 */
327 static void
328 mlcd_intr(void *arg, struct maple_response *response, int sz, int flags)
329 {
330 	struct mlcd_softc *sc = arg;
331 	struct mlcd_response_media_info *rm = (void *) response->data;
332 	struct mlcd_buf *bp;
333 	int part;
334 	struct mlcd_pt *pt;
335 
336 	switch (sc->sc_stat) {
337 	case MLCD_INIT:
338 		/* checking part geometry */
339 		part = sc->sc_reqm.pt;
340 		pt = &sc->sc_pt[part];
341 		switch ((maple_response_t) response->response_code) {
342 		case MAPLE_RESPONSE_DATATRF:
343 			pt->pt_info = rm->info;
344 			pt->pt_size = ((pt->pt_info.width + 1) *
345 			    (pt->pt_info.height + 1) + 7) / 8;
346 			pt->pt_nblk = pt->pt_size / sc->sc_bsize;
347 			printf("%s: %dx%d display, %d bytes\n",
348 			    pt->pt_name,
349 			    pt->pt_info.width + 1, pt->pt_info.height + 1,
350 			    pt->pt_size);
351 
352 			/* this partition is active */
353 			pt->pt_flags = MLCD_PT_OK;
354 
355 			break;
356 		default:
357 			printf("%s: init: unexpected response %#x, sz %d\n",
358 			    pt->pt_name, be32toh(response->response_code), sz);
359 			break;
360 		}
361 		if (++part == sc->sc_npt) {
362 			/* init done */
363 
364 			/* XXX initial image for Visual Memory */
365 			if (sc->sc_pt[0].pt_size == sizeof initimg48x32 &&
366 			    sc->sc_waccsz == sizeof initimg48x32 &&
367 			    sc->sc_wacc == 1) {
368 				sc->sc_stat = MLCD_INIT2;
369 				sc->sc_reqw.func_code =
370 				    htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
371 				sc->sc_reqw.pt = 0;	/* part 0 */
372 				sc->sc_reqw.block = 0;
373 				sc->sc_reqw.phase = 0;
374 				memcpy(sc->sc_reqw.data, initimg48x32,
375 				    sizeof initimg48x32);
376 				if (sc->sc_direction == MAPLE_CONN_TOP) {
377 					/* the LCD is upside-down */
378 					mlcd_rotate_bitmap(sc->sc_reqw.data,
379 					    sizeof initimg48x32);
380 				}
381 				maple_command(sc->sc_parent, sc->sc_unit,
382 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
383 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
384 			} else
385 				sc->sc_stat = MLCD_IDLE;	/* init done */
386 		} else {
387 			sc->sc_reqm.pt = part;
388 			maple_command(sc->sc_parent, sc->sc_unit,
389 			    MAPLE_FN_LCD, MAPLE_COMMAND_GETMINFO,
390 			    sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
391 		}
392 		break;
393 
394 	case MLCD_INIT2:
395 		sc->sc_stat = MLCD_IDLE;	/* init done */
396 		break;
397 
398 	case MLCD_WRITE:
399 		bp = sc->sc_bp;
400 
401 		switch ((maple_response_t) response->response_code) {
402 		case MAPLE_RESPONSE_OK:			/* write done */
403 			if (++sc->sc_reqw.phase == sc->sc_wacc) {
404 				/* all phase done */
405 				mlcddone(sc);
406 			} else {
407 				/* go next phase */
408 				memcpy(sc->sc_reqw.data,
409 				    (char *)bp->lb_data +
410 				    sc->sc_waccsz * sc->sc_reqw.phase,
411 				    sc->sc_waccsz);
412 				maple_command(sc->sc_parent, sc->sc_unit,
413 				    MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
414 				    MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
415 			}
416 			break;
417 		case MAPLE_RESPONSE_LCDERR:
418 			mlcd_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name,
419 			    rm->func_code /* XXX */);
420 			mlcdstart_bp(sc);		/* retry */
421 			break;
422 		default:
423 			printf("%s: write: unexpected response %#x, %#x, sz %d\n",
424 			    sc->sc_pt[sc->sc_reqw.pt].pt_name,
425 			    be32toh(response->response_code),
426 			    be32toh(rm->func_code), sz);
427 			mlcdstart_bp(sc);		/* retry */
428 			break;
429 		}
430 		break;
431 
432 	default:
433 		break;
434 	}
435 }
436 
437 static void
438 mlcd_printerror(const char *head, uint32_t code)
439 {
440 
441 	printf("%s:", head);
442 	NTOHL(code);
443 	if (code & 1)
444 		printf(" PT error");
445 	if (code & 2)
446 		printf(" Phase error");
447 	if (code & 4)
448 		printf(" Block error");
449 	if (code & 010)
450 		printf(" Write error");
451 	if (code & 020)
452 		printf(" Length error");
453 	if (code & ~037)
454 		printf(" Unknown error %#x", code & ~037);
455 	printf("\n");
456 }
457 
458 /* ARGSUSED */
459 int
460 mlcdopen(dev_t dev, int flags, int devtype, struct lwp *l)
461 {
462 	int unit, part;
463 	struct mlcd_softc *sc;
464 	struct mlcd_pt *pt;
465 
466 	unit = MLCD_UNIT(dev);
467 	part = MLCD_PART(dev);
468 	if ((sc = device_lookup_private(&mlcd_cd, unit)) == NULL
469 	    || sc->sc_stat == MLCD_INIT
470 	    || sc->sc_stat == MLCD_INIT2
471 	    || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0)
472 		return ENXIO;
473 
474 	if (pt->pt_flags & MLCD_PT_OPEN)
475 		return EBUSY;
476 
477 	pt->pt_flags |= MLCD_PT_OPEN;
478 
479 	return 0;
480 }
481 
482 /* ARGSUSED */
483 int
484 mlcdclose(dev_t dev, int flags, int devtype, struct lwp *l)
485 {
486 	int unit, part;
487 	struct mlcd_softc *sc;
488 	struct mlcd_pt *pt;
489 
490 	unit = MLCD_UNIT(dev);
491 	part = MLCD_PART(dev);
492 	sc = device_lookup_private(&mlcd_cd, unit);
493 	pt = &sc->sc_pt[part];
494 
495 	pt->pt_flags &= ~MLCD_PT_OPEN;
496 
497 	return 0;
498 }
499 
500 /*
501  * start I/O operations
502  */
503 static void
504 mlcdstart(struct mlcd_softc *sc)
505 {
506 	struct mlcd_buf *bp;
507 
508 	if ((bp = SIMPLEQ_FIRST(&sc->sc_q)) == NULL) {
509 		sc->sc_stat = MLCD_IDLE;
510 		maple_enable_unit_ping(sc->sc_parent, sc->sc_unit,
511 		    MAPLE_FN_LCD, 1);
512 		return;
513 	}
514 
515 	SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
516 
517 	sc->sc_bp = bp;
518 	sc->sc_retry = 0;
519 	mlcdstart_bp(sc);
520 }
521 
522 /*
523  * start/retry a specified I/O operation
524  */
525 static void
526 mlcdstart_bp(struct mlcd_softc *sc)
527 {
528 	struct mlcd_buf *bp;
529 	struct mlcd_pt *pt;
530 
531 	bp = sc->sc_bp;
532 	pt = &sc->sc_pt[bp->lb_partno];
533 
534 	/* handle retry */
535 	if (sc->sc_retry++ > MLCD_MAXRETRY) {
536 		/* retry count exceeded */
537 		bp->lb_error = EIO;
538 		mlcddone(sc);
539 		return;
540 	}
541 
542 	/*
543 	 * I/O access will fail if the removal detection (by maple driver)
544 	 * occurs before finishing the I/O, so disable it.
545 	 * We are sending commands, and the removal detection is still alive.
546 	 */
547 	maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
548 
549 	/*
550 	 * Start the first phase (phase# = 0).
551 	 */
552 	/* write */
553 	sc->sc_stat = MLCD_WRITE;
554 	sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
555 	sc->sc_reqw.pt = bp->lb_partno;
556 	sc->sc_reqw.block = htobe16(bp->lb_blkno);
557 	sc->sc_reqw.phase = 0;		/* first phase */
558 	memcpy(sc->sc_reqw.data,
559 	    (char *) bp->lb_data /* + sc->sc_waccsz * phase */, sc->sc_waccsz);
560 	maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
561 	    MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
562 }
563 
564 static void
565 mlcddone(struct mlcd_softc *sc)
566 {
567 	struct mlcd_buf *bp;
568 
569 	/* terminate current transfer */
570 	bp = sc->sc_bp;
571 	KASSERT(bp);
572 	sc->sc_bp = NULL;
573 	wakeup(bp);
574 
575 	/* go next transfer */
576 	mlcdstart(sc);
577 }
578 
579 /*
580  * allocate a buffer for one block
581  *
582  * return NULL if
583  *	[flags == M_NOWAIT] out of buffer space
584  *	[flags == M_WAITOK] device detach detected
585  */
586 static struct mlcd_buf *
587 mlcd_buf_alloc(int dev, int flags)
588 {
589 	struct mlcd_softc *sc;
590 	struct mlcd_pt *pt;
591 	int unit, part;
592 	struct mlcd_buf *bp;
593 
594 	unit = MLCD_UNIT(dev);
595 	part = MLCD_PART(dev);
596 	sc = device_lookup_private(&mlcd_cd, unit);
597 	KASSERT(sc);
598 	pt = &sc->sc_pt[part];
599 	KASSERT(pt);
600 
601 	if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
602 		return bp;
603 
604 	/*
605 	 * malloc() may sleep, and the device may be detached during sleep.
606 	 * XXX this check is not complete.
607 	 */
608 	if (sc != device_lookup_private(&mlcd_cd, unit)
609 	    || sc->sc_stat == MLCD_INIT
610 	    || sc->sc_stat == MLCD_INIT2
611 	    || part >= sc->sc_npt || pt != &sc->sc_pt[part]
612 	    || pt->pt_flags == 0) {
613 		free(bp, M_DEVBUF);
614 		return NULL;
615 	}
616 
617 	bp->lb_error = 0;
618 
619 	return bp;
620 }
621 
622 static void
623 mlcd_buf_free(struct mlcd_buf *bp)
624 {
625 
626 	free(bp, M_DEVBUF);
627 }
628 
629 /* invert order of bits */
630 static inline uint32_t
631 reverse_32(uint32_t b)
632 {
633 	uint32_t b1;
634 
635 	/* invert every 8bit */
636 	b1 = (b & 0x55555555) << 1;  b = (b >> 1) & 0x55555555;  b |= b1;
637 	b1 = (b & 0x33333333) << 2;  b = (b >> 2) & 0x33333333;  b |= b1;
638 	b1 = (b & 0x0f0f0f0f) << 4;  b = (b >> 4) & 0x0f0f0f0f;  b |= b1;
639 
640 	/* invert byte order */
641 	return bswap32(b);
642 }
643 
644 static void
645 mlcd_rotate_bitmap(void *ptr, size_t size)
646 {
647 	uint32_t *p, *q, tmp;
648 
649 	KDASSERT(size % sizeof(uint32_t) == 0);
650 	for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
651 		tmp = reverse_32(*p);
652 		*p++ = reverse_32(*--q);
653 		*q = tmp;
654 	}
655 }
656 
657 /* ARGSUSED2 */
658 int
659 mlcdwrite(dev_t dev, struct uio *uio, int flags)
660 {
661 	struct mlcd_softc *sc;
662 	struct mlcd_pt *pt;
663 	struct mlcd_buf *bp;
664 	int part;
665 	off_t devsize;
666 	int error = 0;
667 
668 	part = MLCD_PART(dev);
669 	sc = device_lookup_private(&mlcd_cd, MLCD_UNIT(dev));
670 	pt = &sc->sc_pt[part];
671 
672 #if 0
673 	printf("%s: mlcdwrite: offset %ld, size %d\n",
674 	    pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
675 #endif
676 
677 	devsize = pt->pt_nblk * sc->sc_bsize;
678 	if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
679 		return EINVAL;
680 
681 	if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
682 		return EIO;	/* device is detached during allocation */
683 
684 	bp->lb_partno = part;
685 
686 	while (uio->uio_offset < devsize
687 	    && uio->uio_resid >= (size_t) sc->sc_bsize) {
688 		/* invert block number if upside-down */
689 		bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
690 		    pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
691 		    uio->uio_offset / sc->sc_bsize;
692 
693 		if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
694 			break;
695 
696 		if (sc->sc_direction == MAPLE_CONN_TOP) {
697 			/* the LCD is upside-down */
698 			mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
699 		}
700 
701 		/* queue this transfer */
702 		SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
703 
704 		if (sc->sc_stat == MLCD_IDLE)
705 			mlcdstart(sc);
706 
707 		tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
708 
709 		if ((error = bp->lb_error) != 0) {
710 			uio->uio_resid += sc->sc_bsize;
711 			break;
712 		}
713 	}
714 
715 	mlcd_buf_free(bp);
716 
717 	return error;
718 }
719 
720 int
721 mlcdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
722 {
723 	int unit, part;
724 	struct mlcd_softc *sc;
725 	struct mlcd_pt *pt;
726 
727 	unit = MLCD_UNIT(dev);
728 	part = MLCD_PART(dev);
729 	sc = device_lookup_private(&mlcd_cd, unit);
730 	pt = &sc->sc_pt[part];
731 
732 	switch (cmd) {
733 
734 	default:
735 		/* generic maple ioctl */
736 		return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
737 		    flag, l);
738 	}
739 
740 	return 0;
741 }
742