1*f9228f42Sdholland /* $NetBSD: mlcd.c,v 1.18 2014/07/25 08:10:32 dholland Exp $ */
251cd3ce6Sitohy
351cd3ce6Sitohy /*-
451cd3ce6Sitohy * Copyright (c) 2002 The NetBSD Foundation, Inc.
551cd3ce6Sitohy * All rights reserved.
651cd3ce6Sitohy *
751cd3ce6Sitohy * This code is derived from software contributed to The NetBSD Foundation
851cd3ce6Sitohy * by ITOH Yasufumi.
951cd3ce6Sitohy *
1051cd3ce6Sitohy * Redistribution and use in source and binary forms, with or without
1151cd3ce6Sitohy * modification, are permitted provided that the following conditions
1251cd3ce6Sitohy * are met:
1351cd3ce6Sitohy * 1. Redistributions of source code must retain the above copyright
1451cd3ce6Sitohy * notice, this list of conditions and the following disclaimer.
1551cd3ce6Sitohy * 2. Redistributions in binary form must reproduce the above copyright
1651cd3ce6Sitohy * notice, this list of conditions and the following disclaimer in the
1751cd3ce6Sitohy * documentation and/or other materials provided with the distribution.
1851cd3ce6Sitohy *
1951cd3ce6Sitohy * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2051cd3ce6Sitohy * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2151cd3ce6Sitohy * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2251cd3ce6Sitohy * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2351cd3ce6Sitohy * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2451cd3ce6Sitohy * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2551cd3ce6Sitohy * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2651cd3ce6Sitohy * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2751cd3ce6Sitohy * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2851cd3ce6Sitohy * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2951cd3ce6Sitohy * POSSIBILITY OF SUCH DAMAGE.
3051cd3ce6Sitohy */
3151cd3ce6Sitohy
3214172728Slukem #include <sys/cdefs.h>
33*f9228f42Sdholland __KERNEL_RCSID(0, "$NetBSD: mlcd.c,v 1.18 2014/07/25 08:10:32 dholland Exp $");
3414172728Slukem
3551cd3ce6Sitohy #include <sys/param.h>
3651cd3ce6Sitohy #include <sys/device.h>
3751cd3ce6Sitohy #include <sys/kernel.h>
3851cd3ce6Sitohy #include <sys/malloc.h>
3951cd3ce6Sitohy #include <sys/proc.h>
4051cd3ce6Sitohy #include <sys/systm.h>
4151cd3ce6Sitohy #include <sys/vnode.h>
4251cd3ce6Sitohy #include <sys/conf.h>
4351cd3ce6Sitohy
4451cd3ce6Sitohy #include <dreamcast/dev/maple/maple.h>
4551cd3ce6Sitohy #include <dreamcast/dev/maple/mapleconf.h>
4651cd3ce6Sitohy
470e356e23Stsutsui #include "ioconf.h"
480e356e23Stsutsui
4951cd3ce6Sitohy #define MLCD_MAXACCSIZE 1012 /* (255*4) - 8 = 253*32 / 8 */
5051cd3ce6Sitohy
5151cd3ce6Sitohy struct mlcd_funcdef { /* XXX assuming little-endian structure packing */
5251cd3ce6Sitohy unsigned unused : 6,
5351cd3ce6Sitohy bw : 1, /* 0: normally white, 1: normally black */
5451cd3ce6Sitohy hv : 1, /* 0: horizontal, 1: vertical */
5551cd3ce6Sitohy ra : 4, /* 0 */
5651cd3ce6Sitohy wa : 4, /* number of access / write */
5751cd3ce6Sitohy bb : 8, /* block size / 32 - 1 */
5851cd3ce6Sitohy pt : 8; /* number of partition - 1 */
5951cd3ce6Sitohy };
6051cd3ce6Sitohy
6151cd3ce6Sitohy struct mlcd_request_write_data {
62eff817e3Stsutsui uint32_t func_code;
63eff817e3Stsutsui uint8_t pt;
64eff817e3Stsutsui uint8_t phase; /* 0, 1, 2, 3: for each 128 byte */
65eff817e3Stsutsui uint16_t block;
66eff817e3Stsutsui uint8_t data[MLCD_MAXACCSIZE];
6751cd3ce6Sitohy };
6851cd3ce6Sitohy #define MLCD_SIZE_REQW(sc) ((sc)->sc_waccsz + 8)
6951cd3ce6Sitohy
7051cd3ce6Sitohy struct mlcd_request_get_media_info {
71eff817e3Stsutsui uint32_t func_code;
72eff817e3Stsutsui uint32_t pt; /* pt (1 byte) and unused 3 bytes */
7351cd3ce6Sitohy };
7451cd3ce6Sitohy
7551cd3ce6Sitohy struct mlcd_media_info {
76eff817e3Stsutsui uint8_t width; /* width - 1 */
77eff817e3Stsutsui uint8_t height; /* height - 1 */
78eff817e3Stsutsui uint8_t rsvd[2]; /* ? 0x10 0x02 */
7951cd3ce6Sitohy };
8051cd3ce6Sitohy
8151cd3ce6Sitohy struct mlcd_response_media_info {
82eff817e3Stsutsui uint32_t func_code; /* function code (big endian) */
8351cd3ce6Sitohy struct mlcd_media_info info;
8451cd3ce6Sitohy };
8551cd3ce6Sitohy
860d637417Sitohy struct mlcd_buf {
870d637417Sitohy SIMPLEQ_ENTRY(mlcd_buf) lb_q;
880d637417Sitohy int lb_error;
890d637417Sitohy int lb_partno;
900d637417Sitohy int lb_blkno;
91eff817e3Stsutsui uint32_t lb_data[1]; /* variable length */
920d637417Sitohy };
930d637417Sitohy #define MLCD_BUF_SZ(sc) (offsetof(struct mlcd_buf, lb_data) + (sc)->sc_bsize)
940d637417Sitohy
9551cd3ce6Sitohy struct mlcd_softc {
9607d8ce49Stsutsui device_t sc_dev;
9751cd3ce6Sitohy
9866bdd36fSdyoung device_t sc_parent;
9951cd3ce6Sitohy struct maple_unit *sc_unit;
1000d637417Sitohy int sc_direction;
10151cd3ce6Sitohy enum mlcd_stat {
10251cd3ce6Sitohy MLCD_INIT, /* during initialization */
10351cd3ce6Sitohy MLCD_INIT2, /* during initialization */
10451cd3ce6Sitohy MLCD_IDLE, /* init done, not in I/O */
10551cd3ce6Sitohy MLCD_WRITE, /* in write operation */
10651cd3ce6Sitohy MLCD_DETACH /* detaching */
10751cd3ce6Sitohy } sc_stat;
10851cd3ce6Sitohy
10951cd3ce6Sitohy int sc_npt; /* number of partitions */
11051cd3ce6Sitohy int sc_bsize; /* block size */
11151cd3ce6Sitohy int sc_wacc; /* number of write access per block */
11251cd3ce6Sitohy int sc_waccsz; /* size of a write access */
11351cd3ce6Sitohy
11451cd3ce6Sitohy struct mlcd_pt {
11551cd3ce6Sitohy int pt_flags;
11651cd3ce6Sitohy #define MLCD_PT_OK 1 /* partition is alive */
11751cd3ce6Sitohy #define MLCD_PT_OPEN 2
11851cd3ce6Sitohy struct mlcd_media_info pt_info; /* geometry per part */
11951cd3ce6Sitohy int pt_size; /* partition size in byte */
1200d637417Sitohy int pt_nblk; /* partition size in block */
12151cd3ce6Sitohy
1220d637417Sitohy char pt_name[16 /* see device.h */ + 4 /* ".255" */];
12351cd3ce6Sitohy } *sc_pt;
12451cd3ce6Sitohy
12551cd3ce6Sitohy /* write request buffer (only one is used at a time) */
12651cd3ce6Sitohy union {
12751cd3ce6Sitohy struct mlcd_request_write_data req_write;
12851cd3ce6Sitohy struct mlcd_request_get_media_info req_minfo;
12951cd3ce6Sitohy } sc_req;
13051cd3ce6Sitohy #define sc_reqw sc_req.req_write
13151cd3ce6Sitohy #define sc_reqm sc_req.req_minfo
13251cd3ce6Sitohy
13351cd3ce6Sitohy /* pending buffers */
1340d637417Sitohy SIMPLEQ_HEAD(mlcd_bufq, mlcd_buf) sc_q;
13551cd3ce6Sitohy
13651cd3ce6Sitohy /* current I/O access */
1370d637417Sitohy struct mlcd_buf *sc_bp;
13851cd3ce6Sitohy int sc_retry;
13951cd3ce6Sitohy #define MLCD_MAXRETRY 10
14051cd3ce6Sitohy };
14151cd3ce6Sitohy
14251cd3ce6Sitohy /*
14351cd3ce6Sitohy * minor number layout (mlcddetach() depends on this layout):
14451cd3ce6Sitohy *
14551cd3ce6Sitohy * 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
14651cd3ce6Sitohy * |---------------------------------| |---------------------|
14751cd3ce6Sitohy * unit part
14851cd3ce6Sitohy */
14951cd3ce6Sitohy #define MLCD_PART(dev) (minor(dev) & 0xff)
15051cd3ce6Sitohy #define MLCD_UNIT(dev) (minor(dev) >> 8)
15151cd3ce6Sitohy #define MLCD_MINOR(unit, part) (((unit) << 8) | (part))
15251cd3ce6Sitohy
15307d8ce49Stsutsui static int mlcdmatch(device_t, cfdata_t, void *);
15466bdd36fSdyoung static void mlcdattach(device_t, device_t, void *);
15566bdd36fSdyoung static int mlcddetach(device_t, int);
156eff817e3Stsutsui static void mlcd_intr(void *, struct maple_response *, int, int);
157eff817e3Stsutsui static void mlcd_printerror(const char *, uint32_t);
158eff817e3Stsutsui static struct mlcd_buf *mlcd_buf_alloc(int /*dev*/, int /*flags*/);
159eff817e3Stsutsui static void mlcd_buf_free(struct mlcd_buf *);
1605f1c88d7Sperry static inline uint32_t reverse_32(uint32_t);
161eff817e3Stsutsui static void mlcd_rotate_bitmap(void *, size_t);
162eff817e3Stsutsui static void mlcdstart(struct mlcd_softc *);
163eff817e3Stsutsui static void mlcdstart_bp(struct mlcd_softc *);
164eff817e3Stsutsui static void mlcddone(struct mlcd_softc *);
16551cd3ce6Sitohy
16651cd3ce6Sitohy dev_type_open(mlcdopen);
16751cd3ce6Sitohy dev_type_close(mlcdclose);
16851cd3ce6Sitohy dev_type_write(mlcdwrite);
16951cd3ce6Sitohy dev_type_ioctl(mlcdioctl);
17051cd3ce6Sitohy
17151cd3ce6Sitohy const struct cdevsw mlcd_cdevsw = {
172a68f9396Sdholland .d_open = mlcdopen,
173a68f9396Sdholland .d_close = mlcdclose,
174a68f9396Sdholland .d_read = noread,
175a68f9396Sdholland .d_write = mlcdwrite,
176a68f9396Sdholland .d_ioctl = mlcdioctl,
177a68f9396Sdholland .d_stop = nostop,
178a68f9396Sdholland .d_tty = notty,
179a68f9396Sdholland .d_poll = nopoll,
180a68f9396Sdholland .d_mmap = nommap,
181a68f9396Sdholland .d_kqfilter = nokqfilter,
182*f9228f42Sdholland .d_discard = nodiscard,
183a68f9396Sdholland .d_flag = 0
18451cd3ce6Sitohy };
18551cd3ce6Sitohy
18607d8ce49Stsutsui CFATTACH_DECL_NEW(mlcd, sizeof(struct mlcd_softc),
18751cd3ce6Sitohy mlcdmatch, mlcdattach, mlcddetach, NULL);
18851cd3ce6Sitohy
18951cd3ce6Sitohy /* initial image "NetBSD dreamcast" */
19051cd3ce6Sitohy static const char initimg48x32[192] = {
19151cd3ce6Sitohy 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
19251cd3ce6Sitohy 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
19351cd3ce6Sitohy 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940d637417Sitohy 0x1c, 0x70, 0x00, 0x7e, 0x1c, 0xf0, 0x0c, 0x60, 0x00, 0x33, 0x26, 0x6c,
1950d637417Sitohy 0x0c, 0x60, 0x0c, 0x33, 0x66, 0x66, 0x1e, 0xc7, 0x0c, 0x62, 0x60, 0xc6,
1960d637417Sitohy 0x1a, 0xc9, 0xbe, 0x7c, 0x30, 0xc6, 0x1a, 0xdb, 0x98, 0x66, 0x18, 0xc6,
1970d637417Sitohy 0x1a, 0xdc, 0x18, 0x66, 0x0d, 0x8c, 0x31, 0xb0, 0x32, 0xc6, 0x8d, 0x8c,
1980d637417Sitohy 0x31, 0xb1, 0x36, 0xcd, 0x99, 0x98, 0x71, 0x9e, 0x1d, 0xf9, 0xf3, 0xe0,
1990d637417Sitohy 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2000d637417Sitohy 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x08,
2010d637417Sitohy 0x1d, 0x6c, 0x63, 0xc7, 0x30, 0xde, 0x25, 0x92, 0x12, 0xa8, 0x09, 0x08,
2020d637417Sitohy 0x25, 0x1e, 0x72, 0xa8, 0x38, 0xc8, 0x25, 0x10, 0x92, 0xa8, 0x48, 0x28,
2030d637417Sitohy 0x1d, 0x0e, 0x6a, 0xa7, 0x35, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
20451cd3ce6Sitohy 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
20551cd3ce6Sitohy 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
20651cd3ce6Sitohy 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
20751cd3ce6Sitohy };
20851cd3ce6Sitohy
2090d637417Sitohy /* ARGSUSED */
21051cd3ce6Sitohy static int
mlcdmatch(device_t parent,cfdata_t cf,void * aux)21107d8ce49Stsutsui mlcdmatch(device_t parent, cfdata_t cf, void *aux)
21251cd3ce6Sitohy {
21351cd3ce6Sitohy struct maple_attach_args *ma = aux;
21451cd3ce6Sitohy
21551cd3ce6Sitohy return (ma->ma_function == MAPLE_FN_LCD ? MAPLE_MATCH_FUNC : 0);
21651cd3ce6Sitohy }
21751cd3ce6Sitohy
21851cd3ce6Sitohy static void
mlcdattach(device_t parent,device_t self,void * aux)21966bdd36fSdyoung mlcdattach(device_t parent, device_t self, void *aux)
22051cd3ce6Sitohy {
22166bdd36fSdyoung struct mlcd_softc *sc = device_private(self);
22251cd3ce6Sitohy struct maple_attach_args *ma = aux;
22351cd3ce6Sitohy int i;
22451cd3ce6Sitohy union {
225eff817e3Stsutsui uint32_t v;
22651cd3ce6Sitohy struct mlcd_funcdef s;
22751cd3ce6Sitohy } funcdef;
22851cd3ce6Sitohy
22907d8ce49Stsutsui sc->sc_dev = self;
23051cd3ce6Sitohy sc->sc_parent = parent;
23151cd3ce6Sitohy sc->sc_unit = ma->ma_unit;
2320d637417Sitohy sc->sc_direction = ma->ma_basedevinfo->di_connector_direction;
23351cd3ce6Sitohy
23451cd3ce6Sitohy funcdef.v = maple_get_function_data(ma->ma_devinfo, MAPLE_FN_LCD);
23551cd3ce6Sitohy printf(": LCD display\n");
23651cd3ce6Sitohy printf("%s: %d LCD, %d bytes/block, ",
23707d8ce49Stsutsui device_xname(self),
23851cd3ce6Sitohy sc->sc_npt = funcdef.s.pt + 1,
23951cd3ce6Sitohy sc->sc_bsize = (funcdef.s.bb + 1) << 5);
24051cd3ce6Sitohy if ((sc->sc_wacc = funcdef.s.wa) == 0)
24151cd3ce6Sitohy printf("no ");
24251cd3ce6Sitohy else
24351cd3ce6Sitohy printf("%d acc/", sc->sc_wacc);
2440d637417Sitohy printf("write, %s, norm %s%s\n",
2450d637417Sitohy funcdef.s.hv ? "vert" : "horiz",
2460d637417Sitohy funcdef.s.bw ? "black" : "white",
2470d637417Sitohy sc->sc_direction == MAPLE_CONN_TOP ? ", upside-down" : "");
24851cd3ce6Sitohy
24951cd3ce6Sitohy /*
25051cd3ce6Sitohy * start init sequence
25151cd3ce6Sitohy */
25251cd3ce6Sitohy sc->sc_stat = MLCD_INIT;
2530d637417Sitohy SIMPLEQ_INIT(&sc->sc_q);
25451cd3ce6Sitohy
25551cd3ce6Sitohy /* check consistency */
25651cd3ce6Sitohy if (sc->sc_wacc != 0) {
25751cd3ce6Sitohy sc->sc_waccsz = sc->sc_bsize / sc->sc_wacc;
25851cd3ce6Sitohy if (sc->sc_bsize != sc->sc_waccsz * sc->sc_wacc) {
25951cd3ce6Sitohy printf("%s: write access isn't equally divided\n",
26007d8ce49Stsutsui device_xname(self));
26151cd3ce6Sitohy sc->sc_wacc = 0; /* no write */
26251cd3ce6Sitohy } else if (sc->sc_waccsz > MLCD_MAXACCSIZE) {
26351cd3ce6Sitohy printf("%s: write access size is too large\n",
26407d8ce49Stsutsui device_xname(self));
26551cd3ce6Sitohy sc->sc_wacc = 0; /* no write */
26651cd3ce6Sitohy }
26751cd3ce6Sitohy }
26851cd3ce6Sitohy if (sc->sc_wacc == 0) {
26951cd3ce6Sitohy printf("%s: device doesn't support write\n",
27007d8ce49Stsutsui device_xname(self));
27151cd3ce6Sitohy return;
27251cd3ce6Sitohy }
27351cd3ce6Sitohy
27451cd3ce6Sitohy /* per-part structure */
27551cd3ce6Sitohy sc->sc_pt = malloc(sizeof(struct mlcd_pt) * sc->sc_npt, M_DEVBUF,
27651cd3ce6Sitohy M_WAITOK|M_ZERO);
27751cd3ce6Sitohy
27851cd3ce6Sitohy for (i = 0; i < sc->sc_npt; i++) {
27998335431Schristos snprintf(sc->sc_pt[i].pt_name, sizeof(sc->sc_pt[i].pt_name),
28098335431Schristos "%s.%d", device_xname(self), i);
28151cd3ce6Sitohy }
28251cd3ce6Sitohy
28351cd3ce6Sitohy maple_set_callback(parent, sc->sc_unit, MAPLE_FN_LCD,
28451cd3ce6Sitohy mlcd_intr, sc);
28551cd3ce6Sitohy
28651cd3ce6Sitohy /*
28751cd3ce6Sitohy * get size (start from partition 0)
28851cd3ce6Sitohy */
289eff817e3Stsutsui sc->sc_reqm.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
29051cd3ce6Sitohy sc->sc_reqm.pt = 0;
29151cd3ce6Sitohy maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
29251cd3ce6Sitohy MAPLE_COMMAND_GETMINFO, sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
29351cd3ce6Sitohy }
29451cd3ce6Sitohy
2950d637417Sitohy /* ARGSUSED1 */
29651cd3ce6Sitohy static int
mlcddetach(device_t self,int flags)29766bdd36fSdyoung mlcddetach(device_t self, int flags)
29851cd3ce6Sitohy {
29966bdd36fSdyoung struct mlcd_softc *sc = device_private(self);
3000d637417Sitohy struct mlcd_buf *bp;
30151cd3ce6Sitohy int minor_l, minor_h;
30251cd3ce6Sitohy
30351cd3ce6Sitohy sc->sc_stat = MLCD_DETACH; /* just in case */
30451cd3ce6Sitohy
30551cd3ce6Sitohy /*
30651cd3ce6Sitohy * kill pending I/O
30751cd3ce6Sitohy */
30851cd3ce6Sitohy if ((bp = sc->sc_bp) != NULL) {
3090d637417Sitohy bp->lb_error = EIO;
3100d637417Sitohy wakeup(bp);
31151cd3ce6Sitohy }
3120d637417Sitohy while ((bp = SIMPLEQ_FIRST(&sc->sc_q)) != NULL) {
3130d637417Sitohy SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
3140d637417Sitohy bp->lb_error = EIO;
3150d637417Sitohy wakeup(bp);
31651cd3ce6Sitohy }
31751cd3ce6Sitohy
31851cd3ce6Sitohy /*
31951cd3ce6Sitohy * revoke vnodes
32051cd3ce6Sitohy */
32139cd836eSthorpej minor_l = MLCD_MINOR(device_unit(self), 0);
32239cd836eSthorpej minor_h = MLCD_MINOR(device_unit(self), sc->sc_npt - 1);
32351cd3ce6Sitohy vdevgone(cdevsw_lookup_major(&mlcd_cdevsw), minor_l, minor_h, VCHR);
32451cd3ce6Sitohy
32551cd3ce6Sitohy /*
32651cd3ce6Sitohy * free per-partition structure
32751cd3ce6Sitohy */
32851cd3ce6Sitohy if (sc->sc_pt)
32951cd3ce6Sitohy free(sc->sc_pt, M_DEVBUF);
33051cd3ce6Sitohy
33151cd3ce6Sitohy return 0;
33251cd3ce6Sitohy }
33351cd3ce6Sitohy
33451cd3ce6Sitohy /*
33551cd3ce6Sitohy * called back from maple bus driver
33651cd3ce6Sitohy */
3370d637417Sitohy /* ARGSUSED3 */
33851cd3ce6Sitohy static void
mlcd_intr(void * arg,struct maple_response * response,int sz,int flags)33907d8ce49Stsutsui mlcd_intr(void *arg, struct maple_response *response, int sz, int flags)
34051cd3ce6Sitohy {
34107d8ce49Stsutsui struct mlcd_softc *sc = arg;
34251cd3ce6Sitohy struct mlcd_response_media_info *rm = (void *) response->data;
3430d637417Sitohy struct mlcd_buf *bp;
34451cd3ce6Sitohy int part;
34551cd3ce6Sitohy struct mlcd_pt *pt;
34651cd3ce6Sitohy
34751cd3ce6Sitohy switch (sc->sc_stat) {
34851cd3ce6Sitohy case MLCD_INIT:
34951cd3ce6Sitohy /* checking part geometry */
35051cd3ce6Sitohy part = sc->sc_reqm.pt;
35151cd3ce6Sitohy pt = &sc->sc_pt[part];
35251cd3ce6Sitohy switch ((maple_response_t) response->response_code) {
35351cd3ce6Sitohy case MAPLE_RESPONSE_DATATRF:
35451cd3ce6Sitohy pt->pt_info = rm->info;
35551cd3ce6Sitohy pt->pt_size = ((pt->pt_info.width + 1) *
35651cd3ce6Sitohy (pt->pt_info.height + 1) + 7) / 8;
35751cd3ce6Sitohy pt->pt_nblk = pt->pt_size / sc->sc_bsize;
35851cd3ce6Sitohy printf("%s: %dx%d display, %d bytes\n",
35951cd3ce6Sitohy pt->pt_name,
36051cd3ce6Sitohy pt->pt_info.width + 1, pt->pt_info.height + 1,
36151cd3ce6Sitohy pt->pt_size);
36251cd3ce6Sitohy
36351cd3ce6Sitohy /* this partition is active */
36451cd3ce6Sitohy pt->pt_flags = MLCD_PT_OK;
36551cd3ce6Sitohy
36651cd3ce6Sitohy break;
36751cd3ce6Sitohy default:
36851cd3ce6Sitohy printf("%s: init: unexpected response %#x, sz %d\n",
369eff817e3Stsutsui pt->pt_name, be32toh(response->response_code), sz);
37051cd3ce6Sitohy break;
37151cd3ce6Sitohy }
37251cd3ce6Sitohy if (++part == sc->sc_npt) {
37351cd3ce6Sitohy /* init done */
37451cd3ce6Sitohy
37551cd3ce6Sitohy /* XXX initial image for Visual Memory */
37651cd3ce6Sitohy if (sc->sc_pt[0].pt_size == sizeof initimg48x32 &&
37751cd3ce6Sitohy sc->sc_waccsz == sizeof initimg48x32 &&
37851cd3ce6Sitohy sc->sc_wacc == 1) {
37951cd3ce6Sitohy sc->sc_stat = MLCD_INIT2;
38051cd3ce6Sitohy sc->sc_reqw.func_code =
381eff817e3Stsutsui htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
38251cd3ce6Sitohy sc->sc_reqw.pt = 0; /* part 0 */
38351cd3ce6Sitohy sc->sc_reqw.block = 0;
38451cd3ce6Sitohy sc->sc_reqw.phase = 0;
385eff817e3Stsutsui memcpy(sc->sc_reqw.data, initimg48x32,
38651cd3ce6Sitohy sizeof initimg48x32);
3870d637417Sitohy if (sc->sc_direction == MAPLE_CONN_TOP) {
3880d637417Sitohy /* the LCD is upside-down */
3890d637417Sitohy mlcd_rotate_bitmap(sc->sc_reqw.data,
3900d637417Sitohy sizeof initimg48x32);
3910d637417Sitohy }
39251cd3ce6Sitohy maple_command(sc->sc_parent, sc->sc_unit,
39351cd3ce6Sitohy MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
39451cd3ce6Sitohy MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
39551cd3ce6Sitohy } else
39651cd3ce6Sitohy sc->sc_stat = MLCD_IDLE; /* init done */
39751cd3ce6Sitohy } else {
39851cd3ce6Sitohy sc->sc_reqm.pt = part;
39951cd3ce6Sitohy maple_command(sc->sc_parent, sc->sc_unit,
40051cd3ce6Sitohy MAPLE_FN_LCD, MAPLE_COMMAND_GETMINFO,
40151cd3ce6Sitohy sizeof sc->sc_reqm / 4, &sc->sc_reqm, 0);
40251cd3ce6Sitohy }
40351cd3ce6Sitohy break;
40451cd3ce6Sitohy
40551cd3ce6Sitohy case MLCD_INIT2:
40651cd3ce6Sitohy sc->sc_stat = MLCD_IDLE; /* init done */
40751cd3ce6Sitohy break;
40851cd3ce6Sitohy
40951cd3ce6Sitohy case MLCD_WRITE:
41051cd3ce6Sitohy bp = sc->sc_bp;
41151cd3ce6Sitohy
41251cd3ce6Sitohy switch ((maple_response_t) response->response_code) {
41351cd3ce6Sitohy case MAPLE_RESPONSE_OK: /* write done */
41451cd3ce6Sitohy if (++sc->sc_reqw.phase == sc->sc_wacc) {
41551cd3ce6Sitohy /* all phase done */
41651cd3ce6Sitohy mlcddone(sc);
41751cd3ce6Sitohy } else {
41851cd3ce6Sitohy /* go next phase */
419eff817e3Stsutsui memcpy(sc->sc_reqw.data,
420eff817e3Stsutsui (char *)bp->lb_data +
421eff817e3Stsutsui sc->sc_waccsz * sc->sc_reqw.phase,
422eff817e3Stsutsui sc->sc_waccsz);
42351cd3ce6Sitohy maple_command(sc->sc_parent, sc->sc_unit,
42451cd3ce6Sitohy MAPLE_FN_LCD, MAPLE_COMMAND_BWRITE,
42551cd3ce6Sitohy MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
42651cd3ce6Sitohy }
42751cd3ce6Sitohy break;
42851cd3ce6Sitohy case MAPLE_RESPONSE_LCDERR:
42951cd3ce6Sitohy mlcd_printerror(sc->sc_pt[sc->sc_reqw.pt].pt_name,
43051cd3ce6Sitohy rm->func_code /* XXX */);
4310d637417Sitohy mlcdstart_bp(sc); /* retry */
43251cd3ce6Sitohy break;
43351cd3ce6Sitohy default:
43451cd3ce6Sitohy printf("%s: write: unexpected response %#x, %#x, sz %d\n",
43551cd3ce6Sitohy sc->sc_pt[sc->sc_reqw.pt].pt_name,
436eff817e3Stsutsui be32toh(response->response_code),
437eff817e3Stsutsui be32toh(rm->func_code), sz);
4380d637417Sitohy mlcdstart_bp(sc); /* retry */
43951cd3ce6Sitohy break;
44051cd3ce6Sitohy }
44151cd3ce6Sitohy break;
44251cd3ce6Sitohy
44351cd3ce6Sitohy default:
44451cd3ce6Sitohy break;
44551cd3ce6Sitohy }
44651cd3ce6Sitohy }
44751cd3ce6Sitohy
44851cd3ce6Sitohy static void
mlcd_printerror(const char * head,uint32_t code)449eff817e3Stsutsui mlcd_printerror(const char *head, uint32_t code)
45051cd3ce6Sitohy {
45151cd3ce6Sitohy
45251cd3ce6Sitohy printf("%s:", head);
45351cd3ce6Sitohy NTOHL(code);
45451cd3ce6Sitohy if (code & 1)
45551cd3ce6Sitohy printf(" PT error");
45651cd3ce6Sitohy if (code & 2)
45751cd3ce6Sitohy printf(" Phase error");
45851cd3ce6Sitohy if (code & 4)
45951cd3ce6Sitohy printf(" Block error");
46051cd3ce6Sitohy if (code & 010)
46151cd3ce6Sitohy printf(" Write error");
46251cd3ce6Sitohy if (code & 020)
46351cd3ce6Sitohy printf(" Length error");
46451cd3ce6Sitohy if (code & ~037)
46551cd3ce6Sitohy printf(" Unknown error %#x", code & ~037);
46651cd3ce6Sitohy printf("\n");
46751cd3ce6Sitohy }
46851cd3ce6Sitohy
4690d637417Sitohy /* ARGSUSED */
47051cd3ce6Sitohy int
mlcdopen(dev_t dev,int flags,int devtype,struct lwp * l)47195e1ffb1Schristos mlcdopen(dev_t dev, int flags, int devtype, struct lwp *l)
47251cd3ce6Sitohy {
47351cd3ce6Sitohy int unit, part;
47451cd3ce6Sitohy struct mlcd_softc *sc;
47551cd3ce6Sitohy struct mlcd_pt *pt;
47651cd3ce6Sitohy
47751cd3ce6Sitohy unit = MLCD_UNIT(dev);
47851cd3ce6Sitohy part = MLCD_PART(dev);
479a0e4edbbStsutsui if ((sc = device_lookup_private(&mlcd_cd, unit)) == NULL
48051cd3ce6Sitohy || sc->sc_stat == MLCD_INIT
48151cd3ce6Sitohy || sc->sc_stat == MLCD_INIT2
48251cd3ce6Sitohy || part >= sc->sc_npt || (pt = &sc->sc_pt[part])->pt_flags == 0)
48351cd3ce6Sitohy return ENXIO;
48451cd3ce6Sitohy
48551cd3ce6Sitohy if (pt->pt_flags & MLCD_PT_OPEN)
48651cd3ce6Sitohy return EBUSY;
48751cd3ce6Sitohy
48851cd3ce6Sitohy pt->pt_flags |= MLCD_PT_OPEN;
48951cd3ce6Sitohy
49051cd3ce6Sitohy return 0;
49151cd3ce6Sitohy }
49251cd3ce6Sitohy
4930d637417Sitohy /* ARGSUSED */
49451cd3ce6Sitohy int
mlcdclose(dev_t dev,int flags,int devtype,struct lwp * l)49595e1ffb1Schristos mlcdclose(dev_t dev, int flags, int devtype, struct lwp *l)
49651cd3ce6Sitohy {
49751cd3ce6Sitohy int unit, part;
49851cd3ce6Sitohy struct mlcd_softc *sc;
49951cd3ce6Sitohy struct mlcd_pt *pt;
50051cd3ce6Sitohy
50151cd3ce6Sitohy unit = MLCD_UNIT(dev);
50251cd3ce6Sitohy part = MLCD_PART(dev);
503e6a3083cStsutsui sc = device_lookup_private(&mlcd_cd, unit);
50451cd3ce6Sitohy pt = &sc->sc_pt[part];
50551cd3ce6Sitohy
50651cd3ce6Sitohy pt->pt_flags &= ~MLCD_PT_OPEN;
50751cd3ce6Sitohy
50851cd3ce6Sitohy return 0;
50951cd3ce6Sitohy }
51051cd3ce6Sitohy
51151cd3ce6Sitohy /*
51251cd3ce6Sitohy * start I/O operations
51351cd3ce6Sitohy */
51451cd3ce6Sitohy static void
mlcdstart(struct mlcd_softc * sc)515eff817e3Stsutsui mlcdstart(struct mlcd_softc *sc)
51651cd3ce6Sitohy {
5170d637417Sitohy struct mlcd_buf *bp;
51851cd3ce6Sitohy
5190d637417Sitohy if ((bp = SIMPLEQ_FIRST(&sc->sc_q)) == NULL) {
52051cd3ce6Sitohy sc->sc_stat = MLCD_IDLE;
52151cd3ce6Sitohy maple_enable_unit_ping(sc->sc_parent, sc->sc_unit,
52251cd3ce6Sitohy MAPLE_FN_LCD, 1);
52351cd3ce6Sitohy return;
52451cd3ce6Sitohy }
52551cd3ce6Sitohy
5260d637417Sitohy SIMPLEQ_REMOVE_HEAD(&sc->sc_q, lb_q);
5270d637417Sitohy
5280d637417Sitohy sc->sc_bp = bp;
52951cd3ce6Sitohy sc->sc_retry = 0;
5300d637417Sitohy mlcdstart_bp(sc);
53151cd3ce6Sitohy }
53251cd3ce6Sitohy
53351cd3ce6Sitohy /*
53451cd3ce6Sitohy * start/retry a specified I/O operation
53551cd3ce6Sitohy */
53651cd3ce6Sitohy static void
mlcdstart_bp(struct mlcd_softc * sc)537eff817e3Stsutsui mlcdstart_bp(struct mlcd_softc *sc)
53851cd3ce6Sitohy {
5390d637417Sitohy struct mlcd_buf *bp;
54051cd3ce6Sitohy
5410d637417Sitohy bp = sc->sc_bp;
54251cd3ce6Sitohy
54351cd3ce6Sitohy /* handle retry */
54451cd3ce6Sitohy if (sc->sc_retry++ > MLCD_MAXRETRY) {
54551cd3ce6Sitohy /* retry count exceeded */
5460d637417Sitohy bp->lb_error = EIO;
54751cd3ce6Sitohy mlcddone(sc);
54851cd3ce6Sitohy return;
54951cd3ce6Sitohy }
55051cd3ce6Sitohy
55151cd3ce6Sitohy /*
55251cd3ce6Sitohy * I/O access will fail if the removal detection (by maple driver)
55351cd3ce6Sitohy * occurs before finishing the I/O, so disable it.
55451cd3ce6Sitohy * We are sending commands, and the removal detection is still alive.
55551cd3ce6Sitohy */
55651cd3ce6Sitohy maple_enable_unit_ping(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD, 0);
55751cd3ce6Sitohy
55851cd3ce6Sitohy /*
55951cd3ce6Sitohy * Start the first phase (phase# = 0).
56051cd3ce6Sitohy */
56151cd3ce6Sitohy /* write */
56251cd3ce6Sitohy sc->sc_stat = MLCD_WRITE;
563eff817e3Stsutsui sc->sc_reqw.func_code = htobe32(MAPLE_FUNC(MAPLE_FN_LCD));
5640d637417Sitohy sc->sc_reqw.pt = bp->lb_partno;
565eff817e3Stsutsui sc->sc_reqw.block = htobe16(bp->lb_blkno);
56651cd3ce6Sitohy sc->sc_reqw.phase = 0; /* first phase */
567eff817e3Stsutsui memcpy(sc->sc_reqw.data,
568eff817e3Stsutsui (char *) bp->lb_data /* + sc->sc_waccsz * phase */, sc->sc_waccsz);
56951cd3ce6Sitohy maple_command(sc->sc_parent, sc->sc_unit, MAPLE_FN_LCD,
57051cd3ce6Sitohy MAPLE_COMMAND_BWRITE, MLCD_SIZE_REQW(sc) / 4, &sc->sc_reqw, 0);
57151cd3ce6Sitohy }
57251cd3ce6Sitohy
57351cd3ce6Sitohy static void
mlcddone(struct mlcd_softc * sc)574eff817e3Stsutsui mlcddone(struct mlcd_softc *sc)
57551cd3ce6Sitohy {
5760d637417Sitohy struct mlcd_buf *bp;
57751cd3ce6Sitohy
57851cd3ce6Sitohy /* terminate current transfer */
57951cd3ce6Sitohy bp = sc->sc_bp;
58051cd3ce6Sitohy KASSERT(bp);
58151cd3ce6Sitohy sc->sc_bp = NULL;
5820d637417Sitohy wakeup(bp);
58351cd3ce6Sitohy
58451cd3ce6Sitohy /* go next transfer */
58551cd3ce6Sitohy mlcdstart(sc);
58651cd3ce6Sitohy }
58751cd3ce6Sitohy
5880d637417Sitohy /*
5890d637417Sitohy * allocate a buffer for one block
5900d637417Sitohy *
5910d637417Sitohy * return NULL if
5920d637417Sitohy * [flags == M_NOWAIT] out of buffer space
5930d637417Sitohy * [flags == M_WAITOK] device detach detected
5940d637417Sitohy */
5950d637417Sitohy static struct mlcd_buf *
mlcd_buf_alloc(int dev,int flags)596eff817e3Stsutsui mlcd_buf_alloc(int dev, int flags)
59751cd3ce6Sitohy {
59851cd3ce6Sitohy struct mlcd_softc *sc;
5990d637417Sitohy struct mlcd_pt *pt;
6000d637417Sitohy int unit, part;
6010d637417Sitohy struct mlcd_buf *bp;
60251cd3ce6Sitohy
6030d637417Sitohy unit = MLCD_UNIT(dev);
6040d637417Sitohy part = MLCD_PART(dev);
605e6a3083cStsutsui sc = device_lookup_private(&mlcd_cd, unit);
6060d637417Sitohy KASSERT(sc);
6070d637417Sitohy pt = &sc->sc_pt[part];
6080d637417Sitohy KASSERT(pt);
60951cd3ce6Sitohy
6100d637417Sitohy if ((bp = malloc(MLCD_BUF_SZ(sc), M_DEVBUF, flags)) == NULL)
6110d637417Sitohy return bp;
6120d637417Sitohy
6130d637417Sitohy /*
6140d637417Sitohy * malloc() may sleep, and the device may be detached during sleep.
6150d637417Sitohy * XXX this check is not complete.
6160d637417Sitohy */
617a0e4edbbStsutsui if (sc != device_lookup_private(&mlcd_cd, unit)
6180d637417Sitohy || sc->sc_stat == MLCD_INIT
6190d637417Sitohy || sc->sc_stat == MLCD_INIT2
6200d637417Sitohy || part >= sc->sc_npt || pt != &sc->sc_pt[part]
6210d637417Sitohy || pt->pt_flags == 0) {
6220d637417Sitohy free(bp, M_DEVBUF);
6230d637417Sitohy return NULL;
62451cd3ce6Sitohy }
62551cd3ce6Sitohy
6260d637417Sitohy bp->lb_error = 0;
6270d637417Sitohy
6280d637417Sitohy return bp;
6290d637417Sitohy }
6300d637417Sitohy
6310d637417Sitohy static void
mlcd_buf_free(struct mlcd_buf * bp)632eff817e3Stsutsui mlcd_buf_free(struct mlcd_buf *bp)
6330d637417Sitohy {
6340d637417Sitohy
6350d637417Sitohy free(bp, M_DEVBUF);
6360d637417Sitohy }
6370d637417Sitohy
6380d637417Sitohy /* invert order of bits */
6395f1c88d7Sperry static inline uint32_t
reverse_32(uint32_t b)640eff817e3Stsutsui reverse_32(uint32_t b)
6410d637417Sitohy {
642eff817e3Stsutsui uint32_t b1;
6430d637417Sitohy
6440d637417Sitohy /* invert every 8bit */
6450d637417Sitohy b1 = (b & 0x55555555) << 1; b = (b >> 1) & 0x55555555; b |= b1;
6460d637417Sitohy b1 = (b & 0x33333333) << 2; b = (b >> 2) & 0x33333333; b |= b1;
6470d637417Sitohy b1 = (b & 0x0f0f0f0f) << 4; b = (b >> 4) & 0x0f0f0f0f; b |= b1;
6480d637417Sitohy
6490d637417Sitohy /* invert byte order */
6500d637417Sitohy return bswap32(b);
6510d637417Sitohy }
6520d637417Sitohy
6530d637417Sitohy static void
mlcd_rotate_bitmap(void * ptr,size_t size)654eff817e3Stsutsui mlcd_rotate_bitmap(void *ptr, size_t size)
6550d637417Sitohy {
656eff817e3Stsutsui uint32_t *p, *q, tmp;
6570d637417Sitohy
658eff817e3Stsutsui KDASSERT(size % sizeof(uint32_t) == 0);
6590d637417Sitohy for (p = ptr, q = (void *)((char *)ptr + size); p < q; ) {
6600d637417Sitohy tmp = reverse_32(*p);
6610d637417Sitohy *p++ = reverse_32(*--q);
6620d637417Sitohy *q = tmp;
6630d637417Sitohy }
6640d637417Sitohy }
6650d637417Sitohy
6660d637417Sitohy /* ARGSUSED2 */
66751cd3ce6Sitohy int
mlcdwrite(dev_t dev,struct uio * uio,int flags)668eff817e3Stsutsui mlcdwrite(dev_t dev, struct uio *uio, int flags)
66951cd3ce6Sitohy {
6700d637417Sitohy struct mlcd_softc *sc;
6710d637417Sitohy struct mlcd_pt *pt;
6720d637417Sitohy struct mlcd_buf *bp;
6730d637417Sitohy int part;
6740d637417Sitohy off_t devsize;
6750d637417Sitohy int error = 0;
67651cd3ce6Sitohy
6770d637417Sitohy part = MLCD_PART(dev);
678e6a3083cStsutsui sc = device_lookup_private(&mlcd_cd, MLCD_UNIT(dev));
6790d637417Sitohy pt = &sc->sc_pt[part];
6800d637417Sitohy
6810d637417Sitohy #if 0
6820d637417Sitohy printf("%s: mlcdwrite: offset %ld, size %d\n",
6830d637417Sitohy pt->pt_name, (long) uio->uio_offset, uio->uio_resid);
6840d637417Sitohy #endif
6850d637417Sitohy
6860d637417Sitohy devsize = pt->pt_nblk * sc->sc_bsize;
6870d637417Sitohy if (uio->uio_offset % sc->sc_bsize || uio->uio_offset > devsize)
6880d637417Sitohy return EINVAL;
6890d637417Sitohy
6900d637417Sitohy if ((bp = mlcd_buf_alloc(dev, M_WAITOK)) == NULL)
6910d637417Sitohy return EIO; /* device is detached during allocation */
6920d637417Sitohy
6930d637417Sitohy bp->lb_partno = part;
6940d637417Sitohy
6950d637417Sitohy while (uio->uio_offset < devsize
6960d637417Sitohy && uio->uio_resid >= (size_t) sc->sc_bsize) {
6970d637417Sitohy /* invert block number if upside-down */
6980d637417Sitohy bp->lb_blkno = (sc->sc_direction == MAPLE_CONN_TOP) ?
6990d637417Sitohy pt->pt_nblk - uio->uio_offset / sc->sc_bsize - 1 :
7000d637417Sitohy uio->uio_offset / sc->sc_bsize;
7010d637417Sitohy
7020d637417Sitohy if ((error = uiomove(bp->lb_data, sc->sc_bsize, uio)) != 0)
7030d637417Sitohy break;
7040d637417Sitohy
7050d637417Sitohy if (sc->sc_direction == MAPLE_CONN_TOP) {
7060d637417Sitohy /* the LCD is upside-down */
7070d637417Sitohy mlcd_rotate_bitmap(bp->lb_data, sc->sc_bsize);
7080d637417Sitohy }
7090d637417Sitohy
7100d637417Sitohy /* queue this transfer */
7110d637417Sitohy SIMPLEQ_INSERT_TAIL(&sc->sc_q, bp, lb_q);
7120d637417Sitohy
7130d637417Sitohy if (sc->sc_stat == MLCD_IDLE)
7140d637417Sitohy mlcdstart(sc);
7150d637417Sitohy
7160d637417Sitohy tsleep(bp, PRIBIO + 1, "mlcdbuf", 0);
7170d637417Sitohy
7180d637417Sitohy if ((error = bp->lb_error) != 0) {
7190d637417Sitohy uio->uio_resid += sc->sc_bsize;
7200d637417Sitohy break;
7210d637417Sitohy }
7220d637417Sitohy }
7230d637417Sitohy
7240d637417Sitohy mlcd_buf_free(bp);
7250d637417Sitohy
7260d637417Sitohy return error;
72751cd3ce6Sitohy }
72851cd3ce6Sitohy
72951cd3ce6Sitohy int
mlcdioctl(dev_t dev,u_long cmd,void * data,int flag,struct lwp * l)73053524e44Schristos mlcdioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
73151cd3ce6Sitohy {
73206481cf2Smartin int unit;
73351cd3ce6Sitohy struct mlcd_softc *sc;
73451cd3ce6Sitohy
73551cd3ce6Sitohy unit = MLCD_UNIT(dev);
736e6a3083cStsutsui sc = device_lookup_private(&mlcd_cd, unit);
73751cd3ce6Sitohy
73851cd3ce6Sitohy switch (cmd) {
73951cd3ce6Sitohy
74051cd3ce6Sitohy default:
74151cd3ce6Sitohy /* generic maple ioctl */
74251cd3ce6Sitohy return maple_unit_ioctl(sc->sc_parent, sc->sc_unit, cmd, data,
74395e1ffb1Schristos flag, l);
74451cd3ce6Sitohy }
74551cd3ce6Sitohy
74651cd3ce6Sitohy return 0;
74751cd3ce6Sitohy }
748