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