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