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