xref: /netbsd-src/sys/dev/ic/sti.c (revision ccd9df534e375a4366c5b55f23782053c7a98d82)
1 /*	$NetBSD: sti.c,v 1.37 2024/07/03 13:08:36 macallan Exp $	*/
2 
3 /*	$OpenBSD: sti.c,v 1.61 2009/09/05 14:09:35 miod Exp $	*/
4 
5 /*
6  * Copyright (c) 2000-2003 Michael Shalayeff
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
22  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
27  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 /*
31  * TODO:
32  *	call sti procs asynchronously;
33  *	implement console scroll-back;
34  *	X11 support on more models.
35  */
36 
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: sti.c,v 1.37 2024/07/03 13:08:36 macallan Exp $");
39 
40 #include "wsdisplay.h"
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/device.h>
45 #include <sys/malloc.h>
46 
47 #include <uvm/uvm_extern.h>
48 
49 #include <sys/bus.h>
50 
51 #include <dev/wscons/wsdisplayvar.h>
52 #include <dev/wscons/wsconsio.h>
53 
54 #include <dev/ic/stireg.h>
55 #include <dev/ic/stivar.h>
56 
57 #ifdef STIDEBUG
58 
59 #define	DPRINTF(s)	do {	\
60 	if (stidebug)		\
61 		printf s;	\
62 } while(0)
63 
64 int stidebug = 1;
65 #else
66 #define	DPRINTF(s)	/* */
67 #endif
68 
69 void sti_cursor(void *, int, int, int);
70 int  sti_mapchar(void *, int, u_int *);
71 void sti_putchar(void *, int, int, u_int, long);
72 void sti_copycols(void *, int, int, int, int);
73 void sti_erasecols(void *, int, int, int, long);
74 void sti_copyrows(void *, int, int, int);
75 void sti_eraserows(void *, int, int, long);
76 int  sti_alloc_attr(void *, int, int, int, long *);
77 
78 /* pseudo attribute ops for sti ROM putchar function */
79 #define WSATTR_FG_SHIFT	24
80 #define WSATTR_BG_SHIFT	16
81 #define WSATTR_UNPACK_FG(attr)	(((attr) >> WSATTR_FG_SHIFT) & 0xff)
82 #define WSATTR_UNPACK_BG(attr)	(((attr) >> WSATTR_BG_SHIFT) & 0xff)
83 #define WSATTR_UNPACK_FLAG(attr) ((attr) & WSATTR_USERMASK)
84 #define WSATTR_PACK_FG(fg)	((fg) << WSATTR_FG_SHIFT)
85 #define WSATTR_PACK_BG(bg)	((bg) << WSATTR_BG_SHIFT)
86 #define WSATTR_PACK_FLAG(flag)	((flag))
87 #define WSATTR_PACK(fg, bg, flag)	\
88     (WSATTR_PACK_FG(fg) | WSATTR_PACK_BG(bg) | WSATTR_PACK_FLAG(flag))
89 
90 struct wsdisplay_emulops sti_emulops = {
91 	.cursor = sti_cursor,
92 	.mapchar = sti_mapchar,
93 	.putchar = sti_putchar,
94 	.copycols = sti_copycols,
95 	.erasecols = sti_erasecols,
96 	.copyrows = sti_copyrows,
97 	.eraserows = sti_eraserows,
98 	.allocattr = sti_alloc_attr
99 };
100 
101 const struct wsdisplay_accessops sti_accessops = {
102 	.ioctl = sti_ioctl,
103 	.mmap = sti_mmap,
104 	.alloc_screen = sti_alloc_screen,
105 	.free_screen = sti_free_screen,
106 	.show_screen = sti_show_screen,
107 	.load_font = sti_load_font
108 };
109 
110 enum sti_bmove_funcs {
111 	bmf_clear, bmf_copy, bmf_invert, bmf_underline
112 };
113 
114 void	sti_bmove(struct sti_screen *, int, int, int, int, int, int,
115 	    enum sti_bmove_funcs);
116 int	sti_inqcfg(struct sti_screen *, struct sti_inqconfout *);
117 int	sti_setcment(struct sti_screen *, u_int, u_char, u_char, u_char);
118 
119 struct sti_screen *sti_attach_screen(struct sti_softc *, int);
120 void	sti_describe_screen(struct sti_softc *, struct sti_screen *);
121 
122 int	sti_fetchfonts(struct sti_screen *, struct sti_inqconfout *, uint32_t,
123 	    u_int);
124 void	sti_region_setup(struct sti_screen *);
125 int	sti_rom_setup(struct sti_rom *, bus_space_tag_t, bus_space_tag_t,
126 	    bus_space_handle_t, bus_addr_t *, u_int);
127 int	sti_screen_setup(struct sti_screen *, int);
128 
129 int	ngle_default_putcmap(struct sti_screen *, u_int, u_int);
130 
131 #ifndef SMALL_KERNEL
132 void	ngle_artist_setupfb(struct sti_screen *);
133 void	ngle_elk_setupfb(struct sti_screen *);
134 void	ngle_timber_setupfb(struct sti_screen *);
135 int	ngle_putcmap(struct sti_screen *, u_int, u_int);
136 int	ngle_hcrx_putcmap(struct sti_screen *, u_int, u_int);
137 #endif
138 
139 #define	STI_ENABLE_ROM(sc) \
140 do { \
141 	if ((sc) != NULL && (sc)->sc_enable_rom != NULL) \
142 		(*(sc)->sc_enable_rom)(sc); \
143 } while (0)
144 #define	STI_DISABLE_ROM(sc) \
145 do { \
146 	if ((sc) != NULL && (sc)->sc_disable_rom != NULL) \
147 		(*(sc)->sc_disable_rom)(sc); \
148 } while (0)
149 
150 /* Macros to read larger than 8 bit values from byte roms */
151 #define	parseshort(o) \
152 	((bus_space_read_1(memt, romh, (o) + 3) <<  8) | \
153 	 (bus_space_read_1(memt, romh, (o) + 7)))
154 #define	parseword(o) \
155 	((bus_space_read_1(memt, romh, (o) +  3) << 24) | \
156 	 (bus_space_read_1(memt, romh, (o) +  7) << 16) | \
157 	 (bus_space_read_1(memt, romh, (o) + 11) <<  8) | \
158 	 (bus_space_read_1(memt, romh, (o) + 15)))
159 
160 int
161 sti_attach_common(struct sti_softc *sc, bus_space_tag_t iot,
162     bus_space_tag_t memt, bus_space_handle_t romh, u_int codebase)
163 {
164 	struct sti_rom *rom;
165 	int rc;
166 
167 	rom = (struct sti_rom *)malloc(sizeof(*rom), M_DEVBUF,
168 	    M_WAITOK | M_ZERO);
169 	rom->rom_softc = sc;
170 	rc = sti_rom_setup(rom, iot, memt, romh, sc->bases, codebase);
171 	if (rc != 0) {
172 		free(rom, M_DEVBUF);
173 		return rc;
174 	}
175 
176 	sc->sc_rom = rom;
177 
178 	sti_describe(sc);
179 
180 	sc->sc_scr = sti_attach_screen(sc,
181 	    sc->sc_flags & STI_CONSOLE ? 0 : STI_CLEARSCR);
182 	if (sc->sc_scr == NULL)
183 		rc = ENOMEM;
184 
185 	return rc;
186 }
187 
188 struct sti_screen *
189 sti_attach_screen(struct sti_softc *sc, int flags)
190 {
191 	struct sti_screen *scr;
192 	int rc;
193 
194 	scr = (struct sti_screen *)malloc(sizeof(*scr), M_DEVBUF,
195 	    M_WAITOK | M_ZERO);
196 	scr->scr_rom = sc->sc_rom;
197 	rc = sti_screen_setup(scr, flags);
198 	if (rc != 0) {
199 		free(scr, M_DEVBUF);
200 		return NULL;
201 	}
202 
203 	sti_describe_screen(sc, scr);
204 
205 	return scr;
206 }
207 
208 int
209 sti_rom_setup(struct sti_rom *rom, bus_space_tag_t iot, bus_space_tag_t memt,
210     bus_space_handle_t romh, bus_addr_t *bases, u_int codebase)
211 {
212 	struct sti_dd *dd;
213 	int error, size, i;
214 
215 	KASSERT(rom != NULL);
216 	STI_ENABLE_ROM(rom->rom_softc);
217 
218 	rom->iot = iot;
219 	rom->memt = memt;
220 	rom->romh = romh;
221 	rom->bases = bases;
222 
223 	/*
224 	 * Get ROM header and code function pointers.
225 	 */
226 
227 	dd = &rom->rom_dd;
228 	rom->rom_devtype = bus_space_read_1(memt, romh, 3);
229 	if (rom->rom_devtype == STI_DEVTYPE1) {
230 		dd->dd_type      = bus_space_read_1(memt, romh, 0x03);
231 		dd->dd_nmon      = bus_space_read_1(memt, romh, 0x07);
232 		dd->dd_grrev     = bus_space_read_1(memt, romh, 0x0b);
233 		dd->dd_lrrev     = bus_space_read_1(memt, romh, 0x0f);
234 		dd->dd_grid[0]   = parseword(0x10);
235 		dd->dd_grid[1]   = parseword(0x20);
236 		dd->dd_fntaddr   = parseword(0x30) & ~3;
237 		dd->dd_maxst     = parseword(0x40);
238 		dd->dd_romend    = parseword(0x50) & ~3;
239 		dd->dd_reglst    = parseword(0x60) & ~3;
240 		dd->dd_maxreent  = parseshort(0x70);
241 		dd->dd_maxtimo   = parseshort(0x78);
242 		dd->dd_montbl    = parseword(0x80) & ~3;
243 		dd->dd_udaddr    = parseword(0x90) & ~3;
244 		dd->dd_stimemreq = parseword(0xa0);
245 		dd->dd_udsize    = parseword(0xb0);
246 		dd->dd_pwruse    = parseshort(0xc0);
247 		dd->dd_bussup    = bus_space_read_1(memt, romh, 0xcb);
248 		dd->dd_ebussup   = bus_space_read_1(memt, romh, 0xcf);
249 		dd->dd_altcodet  = bus_space_read_1(memt, romh, 0xd3);
250 		dd->dd_eddst[0]  = bus_space_read_1(memt, romh, 0xd7);
251 		dd->dd_eddst[1]  = bus_space_read_1(memt, romh, 0xdb);
252 		dd->dd_eddst[2]  = bus_space_read_1(memt, romh, 0xdf);
253 		dd->dd_cfbaddr   = parseword(0xe0) & ~3;
254 
255 		codebase <<= 2;
256 		dd->dd_pacode[0x0] = parseword(codebase + 0x000) & ~3;
257 		dd->dd_pacode[0x1] = parseword(codebase + 0x010) & ~3;
258 		dd->dd_pacode[0x2] = parseword(codebase + 0x020) & ~3;
259 		dd->dd_pacode[0x3] = parseword(codebase + 0x030) & ~3;
260 		dd->dd_pacode[0x4] = parseword(codebase + 0x040) & ~3;
261 		dd->dd_pacode[0x5] = parseword(codebase + 0x050) & ~3;
262 		dd->dd_pacode[0x6] = parseword(codebase + 0x060) & ~3;
263 		dd->dd_pacode[0x7] = parseword(codebase + 0x070) & ~3;
264 		dd->dd_pacode[0x8] = parseword(codebase + 0x080) & ~3;
265 		dd->dd_pacode[0x9] = parseword(codebase + 0x090) & ~3;
266 		dd->dd_pacode[0xa] = parseword(codebase + 0x0a0) & ~3;
267 		dd->dd_pacode[0xb] = parseword(codebase + 0x0b0) & ~3;
268 		dd->dd_pacode[0xc] = parseword(codebase + 0x0c0) & ~3;
269 		dd->dd_pacode[0xd] = parseword(codebase + 0x0d0) & ~3;
270 		dd->dd_pacode[0xe] = parseword(codebase + 0x0e0) & ~3;
271 		dd->dd_pacode[0xf] = parseword(codebase + 0x0f0) & ~3;
272 	} else {	/* STI_DEVTYPE4 */
273 		bus_space_read_region_stream_4(memt, romh, 0, (uint32_t *)dd,
274 		    sizeof(*dd) / 4);
275 		/* fix pacode... */
276 		bus_space_read_region_stream_4(memt, romh, codebase,
277 		    (uint32_t *)dd->dd_pacode, sizeof(dd->dd_pacode) / 4);
278 	}
279 
280 	STI_DISABLE_ROM(rom->rom_softc);
281 
282 	DPRINTF(("dd:\n"
283 	    "devtype=%x, rev=%x;%d, altt=%x, gid=%08x%08x, font=%x, mss=%x\n"
284 	    "end=%x, regions=%x, msto=%x, timo=%d, mont=%x, user=%x[%x]\n"
285 	    "memrq=%x, pwr=%d, bus=%x, ebus=%x, cfb=%x\n"
286 	    "code=",
287 	    dd->dd_type & 0xff, dd->dd_grrev, dd->dd_lrrev, dd->dd_altcodet,
288 	    dd->dd_grid[0], dd->dd_grid[1], dd->dd_fntaddr, dd->dd_maxst,
289 	    dd->dd_romend, dd->dd_reglst, dd->dd_maxreent, dd->dd_maxtimo,
290 	    dd->dd_montbl, dd->dd_udaddr, dd->dd_udsize, dd->dd_stimemreq,
291 	    dd->dd_pwruse, dd->dd_bussup, dd->dd_ebussup, dd->dd_cfbaddr));
292 	DPRINTF(("%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x,%x\n",
293 	    dd->dd_pacode[0x0], dd->dd_pacode[0x1], dd->dd_pacode[0x2],
294 	    dd->dd_pacode[0x3], dd->dd_pacode[0x4], dd->dd_pacode[0x5],
295 	    dd->dd_pacode[0x6], dd->dd_pacode[0x7], dd->dd_pacode[0x8],
296 	    dd->dd_pacode[0x9], dd->dd_pacode[0xa], dd->dd_pacode[0xb],
297 	    dd->dd_pacode[0xc], dd->dd_pacode[0xd], dd->dd_pacode[0xe],
298 	    dd->dd_pacode[0xf]));
299 
300 	/*
301 	 * Figure out how many bytes we need for the STI code.
302 	 * Note there could be fewer than STI_END pointer entries
303 	 * populated, especially on older devices.
304 	 */
305 	for (i = STI_END; dd->dd_pacode[i] == 0; i--)
306 		;
307 
308 	size = dd->dd_pacode[i] - dd->dd_pacode[STI_BEGIN];
309 
310 	if (rom->rom_devtype == STI_DEVTYPE1)
311 		size = (size + 3) / 4;
312 	if (size == 0) {
313 		aprint_error(": no code for the requested platform\n");
314 		return EINVAL;
315 	}
316 
317 	if (!(rom->rom_code = uvm_km_alloc(kernel_map, round_page(size), 0,
318 	    UVM_KMF_WIRED))) {
319 		aprint_error(": cannot allocate %u bytes for code\n", size);
320 		return ENOMEM;
321 	}
322 	DPRINTF(("code=0x%lx[%x]\n", rom->rom_code, size));
323 
324 	/*
325 	 * Copy code into memory and make it executable.
326 	 */
327 
328 	STI_ENABLE_ROM(rom->rom_softc);
329 
330 	if (rom->rom_devtype == STI_DEVTYPE1) {
331 		uint8_t *p;
332 		uint32_t addr, eaddr;
333 
334 		p = (uint8_t *)rom->rom_code;
335 
336 		for (addr = dd->dd_pacode[STI_BEGIN], eaddr = addr + size * 4;
337 		    addr < eaddr; addr += 4 ) {
338 			*p++ = bus_space_read_4(memt, romh, addr) & 0xff;
339 		}
340 	} else {	/* STI_DEVTYPE4 */
341 		bus_space_read_region_stream_4(memt, romh,
342 		    dd->dd_pacode[STI_BEGIN], (uint32_t *)rom->rom_code,
343 		    size / 4);
344 	}
345 
346 	STI_DISABLE_ROM(rom->rom_softc);
347 
348 	if ((error = uvm_map_protect(kernel_map, rom->rom_code,
349 	    rom->rom_code + round_page(size), UVM_PROT_RX, FALSE))) {
350 		aprint_error(": uvm_map_protect failed (%d)\n", error);
351 		uvm_km_free(kernel_map, rom->rom_code, round_page(size),
352 		    UVM_KMF_WIRED);
353 		return error;
354 	}
355 
356 	/*
357 	 * Setup code function pointers.
358 	 */
359 
360 #define	O(i) \
361 	(dd->dd_pacode[(i)] == 0 ? 0 : \
362 	    (rom->rom_code + (dd->dd_pacode[(i)] - dd->dd_pacode[0]) /	\
363 	    (rom->rom_devtype == STI_DEVTYPE1 ? 4 : 1)))
364 
365 	rom->init	= (sti_init_t)O(STI_INIT_GRAPH);
366 	rom->mgmt	= (sti_mgmt_t)O(STI_STATE_MGMT);
367 	rom->unpmv	= (sti_unpmv_t)O(STI_FONT_UNPMV);
368 	rom->blkmv	= (sti_blkmv_t)O(STI_BLOCK_MOVE);
369 	rom->test	= (sti_test_t)O(STI_SELF_TEST);
370 	rom->exhdl	= (sti_exhdl_t)O(STI_EXCEP_HDLR);
371 	rom->inqconf	= (sti_inqconf_t)O(STI_INQ_CONF);
372 	rom->scment	= (sti_scment_t)O(STI_SCM_ENT);
373 	rom->dmac	= (sti_dmac_t)O(STI_DMA_CTRL);
374 	rom->flowc	= (sti_flowc_t)O(STI_FLOW_CTRL);
375 	rom->utiming	= (sti_utiming_t)O(STI_UTIMING);
376 	rom->pmgr	= (sti_pmgr_t)O(STI_PROC_MGR);
377 	rom->util	= (sti_util_t)O(STI_UTIL);
378 
379 #undef O
380 
381 	/*
382 	 * Set colormap entry is not implemented until 8.04, so force
383 	 * a NULL pointer here.
384 	 */
385 	if (dd->dd_grrev < STI_REVISION(8, 4)) {
386 		rom->scment = NULL;
387 	}
388 
389 	return 0;
390 }
391 
392 /*
393  * Map all regions.
394  */
395 void
396 sti_region_setup(struct sti_screen *scr)
397 {
398 	struct sti_rom *rom = scr->scr_rom;
399 	bus_space_tag_t memt = rom->memt;
400 	bus_space_handle_t romh = rom->romh;
401 	bus_addr_t *bases = rom->bases;
402 	struct sti_dd *dd = &rom->rom_dd;
403 	struct sti_cfg *cc = &scr->scr_cfg;
404 	struct sti_region regions[STI_REGION_MAX], *r;
405 	u_int regno, regcnt;
406 	bus_addr_t addr;
407 
408 	DPRINTF(("stiregions @ %x:\n", dd->dd_reglst));
409 
410 	/*
411 	 * Read the region information.
412 	 */
413 
414 	STI_ENABLE_ROM(rom->rom_softc);
415 
416 	if (rom->rom_devtype == STI_DEVTYPE1) {
417 		for (regno = 0; regno < STI_REGION_MAX; regno++)
418 			*(u_int *)(regions + regno) =
419 			    parseword(dd->dd_reglst + regno * 0x10);
420 	} else {
421 		bus_space_read_region_stream_4(memt, romh, dd->dd_reglst,
422 		    (uint32_t *)regions, sizeof(regions) / 4);
423 	}
424 
425 	STI_DISABLE_ROM(rom->rom_softc);
426 
427 	/*
428 	 * Count them.
429 	 */
430 
431 	for (regcnt = 0, r = regions; regcnt < STI_REGION_MAX; regcnt++, r++)
432 		if (r->last)
433 			break;
434 	regcnt++;
435 
436 	/*
437 	 * Map them.
438 	 */
439 
440 	for (regno = 0, r = regions; regno < regcnt; regno++, r++) {
441 		if (r->length == 0)
442 			continue;
443 
444 		/*
445 		 * Assume an existing mapping exists.
446 		 */
447 		addr = bases[regno] + (r->offset << PGSHIFT);
448 		DPRINTF(("%08x @ 0x%08x%s%s%s%s",
449 		    r->length << PGSHIFT, (int)addr, r->sys_only ? " sys" : "",
450 		    r->cache ? " cache" : "", r->btlb ? " btlb" : "",
451 		    r->last ? " last" : ""));
452 
453 		/*
454 		 * Region #0 is always the rom, and it should have been
455 		 * mapped already.
456 		 * XXX This expects a 1:1 mapping...
457 		 */
458 		if (regno == 0 && romh == bases[0]) {
459 			cc->regions[0] = addr;
460 			DPRINTF(("\n"));
461 			continue;
462 		}
463 
464 		if (bus_space_map(memt, addr, r->length << PGSHIFT,
465 		    BUS_SPACE_MAP_LINEAR | (r->cache ?
466 		    BUS_SPACE_MAP_CACHEABLE : 0), &rom->regh[regno]) != 0) {
467 			rom->regh[regno] = romh;	/* XXX */
468 			DPRINTF((" - already mapped region\n"));
469 		} else {
470 			addr = (bus_addr_t)
471 			    bus_space_vaddr(memt, rom->regh[regno]);
472 			if (regno == 1) {
473 				DPRINTF((" - fb"));
474 				scr->fbaddr = addr;
475 				scr->fblen = r->length << PGSHIFT;
476 			}
477 			DPRINTF(("\n"));
478 		}
479 
480 		cc->regions[regno] = addr;
481 	}
482 
483 #ifdef STIDEBUG
484 	/*
485 	 * Make sure we'll trap accessing unmapped regions
486 	 */
487 	for (regno = 0; regno < STI_REGION_MAX; regno++)
488 		if (cc->regions[regno] == 0)
489 		    cc->regions[regno] = 0x81234567;
490 #endif
491 }
492 
493 int
494 sti_screen_setup(struct sti_screen *scr, int flags)
495 {
496 	struct sti_rom *rom = scr->scr_rom;
497 	bus_space_tag_t memt = rom->memt;
498 	bus_space_handle_t romh = rom->romh;
499 	struct sti_dd *dd = &rom->rom_dd;
500 	struct sti_cfg *cc = &scr->scr_cfg;
501 	struct sti_inqconfout cfg;
502 	struct sti_einqconfout ecfg;
503 #ifdef STIDEBUG
504 	char buf[256];
505 #endif
506 	int error, i;
507 	int geometry_kluge = 0;
508 	u_int fontindex = 0;
509 
510 	KASSERT(scr != NULL);
511 	memset(cc, 0, sizeof(*cc));
512 	cc->ext_cfg = &scr->scr_ecfg;
513 	memset(cc->ext_cfg, 0, sizeof(*cc->ext_cfg));
514 
515 	if (dd->dd_stimemreq) {
516 		scr->scr_ecfg.addr =
517 		    malloc(dd->dd_stimemreq, M_DEVBUF, M_WAITOK);
518 	}
519 
520 	sti_region_setup(scr);
521 
522 	if ((error = sti_init(scr, 0))) {
523 		aprint_error(": cannot initialize (%d)\n", error);
524 		goto fail;
525 	}
526 
527 	memset(&cfg, 0, sizeof(cfg));
528 	memset(&ecfg, 0, sizeof(ecfg));
529 	cfg.ext = &ecfg;
530 	if ((error = sti_inqcfg(scr, &cfg))) {
531 		aprint_error(": error %d inquiring config\n", error);
532 		goto fail;
533 	}
534 
535 	/*
536 	 * Older (rev 8.02) boards report wrong offset values,
537 	 * similar to the displayable area size, at least in m68k mode.
538 	 * Attempt to detect this and adjust here.
539 	 */
540 	if (cfg.owidth == cfg.width &&
541 	    cfg.oheight == cfg.height)
542 		geometry_kluge = 1;
543 
544 	if (geometry_kluge) {
545 		scr->scr_cfg.oscr_width = cfg.owidth =
546 		    cfg.fbwidth - cfg.width;
547 		scr->scr_cfg.oscr_height = cfg.oheight =
548 		    cfg.fbheight - cfg.height;
549 	}
550 
551 	/*
552 	 * Save a few fields for sti_describe_screen() later
553 	 */
554 	scr->fbheight = cfg.fbheight;
555 	scr->fbwidth = cfg.fbwidth;
556 	scr->oheight = cfg.oheight;
557 	scr->owidth = cfg.owidth;
558 	memcpy(scr->name, cfg.name, sizeof(scr->name));
559 
560 	if (flags & STI_FBMODE) {
561 		/* we're done here */
562 		sti_init(scr, STI_FBMODE);
563 		return 0;
564 	}
565 
566 	if ((error = sti_init(scr, STI_TEXTMODE | flags))) {
567 		aprint_error(": cannot initialize (%d)\n", error);
568 		goto fail;
569 	}
570 #ifdef STIDEBUG
571 	snprintb(buf, sizeof(buf), STI_INQCONF_BITS, cfg.attributes);
572 	DPRINTF(("conf: bpp=%d planes=%d attr=%s\n"
573 	    "crt=0x%x:0x%x:0x%x hw=0x%x:0x%x:0x%x\n", cfg.bpp,
574 	    cfg.planes, buf,
575 	    ecfg.crt_config[0], ecfg.crt_config[1], ecfg.crt_config[2],
576 	    ecfg.crt_hw[0], ecfg.crt_hw[1], ecfg.crt_hw[2]));
577 #endif
578 	scr->scr_bpp = cfg.bppu;
579 
580 	/*
581 	 * Although scr->scr_ecfg.current_monitor is not filled by
582 	 * sti_init() as expected, we can nevertheless walk the monitor
583 	 * list, if there is any, and if we find a mode matching our
584 	 * resolution, pick its font index.
585 	 */
586 	if (dd->dd_montbl != 0) {
587 		STI_ENABLE_ROM(rom->rom_softc);
588 
589 		for (i = 0; i < dd->dd_nmon; i++) {
590 			u_int offs = dd->dd_montbl + 8 * i;
591 			uint32_t m[2];
592 			sti_mon_t mon = (void *)m;
593 			if (rom->rom_devtype == STI_DEVTYPE1) {
594 				m[0] = parseword(4 * offs);
595 				m[1] = parseword(4 * (offs + 4));
596 			} else {
597 				bus_space_read_region_stream_4(memt, romh, offs,
598 				    (uint32_t *)mon, sizeof(*mon) / 4);
599 			}
600 
601 			if (mon->width == scr->scr_cfg.scr_width &&
602 			    mon->height == scr->scr_cfg.scr_height) {
603 				fontindex = mon->font;
604 				break;
605 			}
606 		}
607 
608 		STI_DISABLE_ROM(rom->rom_softc);
609 
610 		DPRINTF(("font index: %d\n", fontindex));
611 	}
612 
613 	if ((error = sti_fetchfonts(scr, &cfg, dd->dd_fntaddr, fontindex))) {
614 		aprint_error(": cannot fetch fonts (%d)\n", error);
615 		goto fail;
616 	}
617 
618 	/*
619 	 * setup screen descriptions:
620 	 *	figure number of fonts supported;
621 	 *	allocate wscons structures;
622 	 *	calculate dimensions.
623 	 */
624 
625 	scr->scr_wsd.name = "std";
626 	scr->scr_wsd.ncols = cfg.width / scr->scr_curfont.width;
627 	scr->scr_wsd.nrows = cfg.height / scr->scr_curfont.height;
628 	scr->scr_wsd.textops = &sti_emulops;
629 	scr->scr_wsd.fontwidth = scr->scr_curfont.width;
630 	scr->scr_wsd.fontheight = scr->scr_curfont.height;
631 	scr->scr_wsd.capabilities = WSSCREEN_REVERSE;
632 
633 	scr->scr_scrlist[0] = &scr->scr_wsd;
634 	scr->scr_screenlist.nscreens = 1;
635 	scr->scr_screenlist.screens = scr->scr_scrlist;
636 
637 #ifndef SMALL_KERNEL
638 	/*
639 	 * Decide which board-specific routines to use.
640 	 */
641 
642 	switch (dd->dd_grid[0]) {
643 	case STI_DD_CRX:
644 		scr->setupfb = ngle_elk_setupfb;
645 		scr->putcmap = ngle_putcmap;
646 
647 		scr->reg10_value = 0x13601000;
648 		if (scr->scr_bpp > 8)
649 			scr->reg12_value = NGLE_BUFF1_CMAP3;
650 		else
651 			scr->reg12_value = NGLE_BUFF1_CMAP0;
652 		scr->cmap_finish_register = NGLE_REG_1;
653 		break;
654 
655 	case STI_DD_TIMBER:
656 		scr->setupfb = ngle_timber_setupfb;
657 		scr->putcmap = ngle_putcmap;
658 
659 		scr->reg10_value = 0x13602000;
660 		scr->reg12_value = NGLE_BUFF1_CMAP0;
661 		scr->cmap_finish_register = NGLE_REG_1;
662 		break;
663 
664 	case STI_DD_ARTIST:
665 		scr->setupfb = ngle_artist_setupfb;
666 		scr->putcmap = ngle_putcmap;
667 
668 		scr->reg10_value = 0x13601000;
669 		scr->reg12_value = NGLE_ARTIST_CMAP0;
670 		scr->cmap_finish_register = NGLE_REG_26;
671 		break;
672 
673 	case STI_DD_EG:
674 		scr->setupfb = ngle_artist_setupfb;
675 		scr->putcmap = ngle_putcmap;
676 
677 		scr->reg10_value = 0x13601000;
678 		if (scr->scr_bpp > 8) {
679 			scr->reg12_value = NGLE_BUFF1_CMAP3;
680 			scr->cmap_finish_register = NGLE_REG_1;
681 		} else {
682 			scr->reg12_value = NGLE_ARTIST_CMAP0;
683 			scr->cmap_finish_register = NGLE_REG_26;
684 		}
685 		break;
686 
687 	case STI_DD_HCRX:
688 		scr->setupfb = ngle_elk_setupfb;
689 		scr->putcmap = ngle_hcrx_putcmap;
690 
691 		if (scr->scr_bpp > 8) {
692 			scr->reg12_value = NGLE_BUFF1_CMAP3;
693 			scr->reg10_value = 0xBBA0A000;
694 		} else {
695 			scr->reg12_value = NGLE_BUFF1_CMAP0;
696 			scr->reg10_value = 0x13602000;
697 		}
698 		scr->cmap_finish_register = NGLE_REG_38;
699 		break;
700 
701 	case STI_DD_GRX:
702 	case STI_DD_CRX24:
703 	case STI_DD_EVRX:
704 	case STI_DD_3X2V:
705 	case STI_DD_DUAL_CRX:
706 	case STI_DD_LEGO:
707 	case STI_DD_SUMMIT:
708 	case STI_DD_PINNACLE:
709 	default:
710 		scr->setupfb = NULL;
711 		scr->putcmap =
712 		    rom->scment == NULL ? NULL : ngle_default_putcmap;
713 		break;
714 	}
715 #endif
716 
717 	return 0;
718 
719 fail:
720 	/* XXX free resources */
721 	if (scr->scr_ecfg.addr != NULL) {
722 		free(scr->scr_ecfg.addr, M_DEVBUF);
723 		scr->scr_ecfg.addr = NULL;
724 	}
725 
726 	return ENXIO;
727 }
728 
729 void
730 sti_describe_screen(struct sti_softc *sc, struct sti_screen *scr)
731 {
732 	struct sti_font *fp = &scr->scr_curfont;
733 
734 	aprint_normal("%s: %s, %dx%d frame buffer, %dx%dx%d display\n",
735 	    device_xname(sc->sc_dev), scr->name, scr->fbwidth, scr->fbheight,
736 	    scr->scr_cfg.scr_width, scr->scr_cfg.scr_height, scr->scr_bpp);
737 
738 	aprint_normal("%s: %dx%d font type %d, %d bpc, charset %d-%d\n",
739 	    device_xname(sc->sc_dev), fp->width, fp->height,
740 	    fp->type, fp->bpc, fp->first, fp->last);
741 }
742 
743 void
744 sti_describe(struct sti_softc *sc)
745 {
746 	struct sti_rom *rom = sc->sc_rom;
747 	struct sti_dd *dd = &rom->rom_dd;
748 
749 	aprint_normal(": rev %d.%02d;%d, ID 0x%08X%08X\n",
750 	    dd->dd_grrev >> 4, dd->dd_grrev & 0xf,
751 	    dd->dd_lrrev, dd->dd_grid[0], dd->dd_grid[1]);
752 
753 	if (sc->sc_scr != NULL)
754 		sti_describe_screen(sc, sc->sc_scr);
755 }
756 
757 /*
758  * Final part of attachment. On hppa where we use the PDC console
759  * during autoconf, this has to be postponed until autoconf has
760  * completed.
761  */
762 void
763 sti_end_attach(struct sti_softc *sc)
764 {
765 	struct sti_screen *scr = sc->sc_scr;
766 
767 	if (scr == NULL)
768 		return;
769 #if NWSDISPLAY > 0
770 	else {
771 		struct wsemuldisplaydev_attach_args waa;
772 		scr->scr_wsmode = WSDISPLAYIO_MODE_EMUL;
773 
774 		waa.console = sc->sc_flags & STI_CONSOLE ? 1 : 0;
775 		waa.scrdata = &scr->scr_screenlist;
776 		waa.accessops = &sti_accessops;
777 		waa.accesscookie = scr;
778 
779 		/* attach as console if required */
780 		if (waa.console && !ISSET(sc->sc_flags, STI_ATTACHED)) {
781 			long defattr;
782 
783 			sti_alloc_attr(scr, 0, 0, 0, &defattr);
784 			wsdisplay_cnattach(&scr->scr_wsd, scr,
785 			    0, scr->scr_wsd.nrows - 1, defattr);
786 			sc->sc_flags |= STI_ATTACHED;
787 		}
788 
789 		config_found(sc->sc_dev, &waa, wsemuldisplaydevprint,
790 		    CFARGS_NONE);
791 	}
792 #endif
793 }
794 
795 u_int
796 sti_rom_size(bus_space_tag_t memt, bus_space_handle_t romh)
797 {
798 	int devtype;
799 	u_int romend;
800 
801 	devtype = bus_space_read_1(memt, romh, 3);
802 	if (devtype == STI_DEVTYPE4) {
803 		bus_space_read_region_stream_4(memt, romh, STI_DEV4_DD_ROMEND,
804 		    (uint32_t *)&romend, 1);
805 	} else {
806 		romend = parseword(STI_DEV1_DD_ROMEND);
807 	}
808 
809 	DPRINTF(("%s: %08x (%08x)\n", __func__, romend, round_page(romend)));
810 
811 	return round_page(romend);
812 }
813 
814 int
815 sti_fetchfonts(struct sti_screen *scr, struct sti_inqconfout *cfg,
816     uint32_t baseaddr, u_int fontindex)
817 {
818 	struct sti_rom *rom = scr->scr_rom;
819 	bus_space_tag_t memt = rom->memt;
820 	bus_space_handle_t romh = rom->romh;
821 	struct sti_font *fp = &scr->scr_curfont;
822 	uint32_t addr;
823 	int size;
824 #ifdef notyet
825 	int uc;
826 	struct {
827 		struct sti_unpmvflags flags;
828 		struct sti_unpmvin in;
829 		struct sti_unpmvout out;
830 	} a;
831 #endif
832 
833 	/*
834 	 * Get the first PROM font in memory
835 	 */
836 
837 	STI_ENABLE_ROM(rom->rom_softc);
838 
839 rescan:
840 	addr = baseaddr;
841 	do {
842 		if (rom->rom_devtype == STI_DEVTYPE1) {
843 			fp->first  = parseshort(addr + 0x00);
844 			fp->last   = parseshort(addr + 0x08);
845 			fp->width  = bus_space_read_1(memt, romh, addr + 0x13);
846 			fp->height = bus_space_read_1(memt, romh, addr + 0x17);
847 			fp->type   = bus_space_read_1(memt, romh, addr + 0x1b);
848 			fp->bpc    = bus_space_read_1(memt, romh, addr + 0x1f);
849 			fp->next   = parseword(addr + 0x20);
850 			fp->uheight= bus_space_read_1(memt, romh, addr + 0x33);
851 			fp->uoffset= bus_space_read_1(memt, romh, addr + 0x37);
852 		} else {	/* STI_DEVTYPE4 */
853 			bus_space_read_region_stream_4(memt, romh, addr,
854 			    (uint32_t *)fp, sizeof(struct sti_font) / 4);
855 		}
856 
857 #ifdef STIDEBUG
858 		STI_DISABLE_ROM(rom->rom_softc);
859 		DPRINTF(("%s: %dx%d font type %d, %d bpc, charset %d-%d\n",
860 		    device_xname(scr->scr_rom->rom_softc->sc_dev), fp->width,
861 		    fp->height, fp->type, fp->bpc, fp->first, fp->last));
862 		STI_ENABLE_ROM(rom->rom_softc);
863 #endif
864 
865 		if (fontindex == 0) {
866 			size = sizeof(struct sti_font) +
867 			    (fp->last - fp->first + 1) * fp->bpc;
868 			if (rom->rom_devtype == STI_DEVTYPE1)
869 				size *= 4;
870 			scr->scr_romfont = malloc(size, M_DEVBUF, M_WAITOK);
871 
872 			bus_space_read_region_stream_4(memt, romh, addr,
873 			    (uint32_t *)scr->scr_romfont, size / 4);
874 			break;
875 		}
876 
877 		addr = baseaddr + fp->next;
878 		fontindex--;
879 	} while (fp->next != 0);
880 
881 	/*
882 	 * If our font index was bogus, we did not find the expected font.
883 	 * In this case, pick the first one and be done with it.
884 	 */
885 	if (fp->next == 0 && scr->scr_romfont == NULL) {
886 		fontindex = 0;
887 		goto rescan;
888 	}
889 
890 	STI_DISABLE_ROM(rom->rom_softc);
891 
892 #ifdef notyet
893 	/*
894 	 * If there is enough room in the off-screen framebuffer memory,
895 	 * display all the characters there in order to display them
896 	 * faster with blkmv operations rather than unpmv later on.
897 	 */
898 	if (size <= cfg->fbheight *
899 	    (cfg->fbwidth - cfg->width - cfg->owidth)) {
900 		memset(&a, 0, sizeof(a));
901 		a.flags.flags = STI_UNPMVF_WAIT;
902 		a.in.fg_colour = STI_COLOUR_WHITE;
903 		a.in.bg_colour = STI_COLOUR_BLACK;
904 		a.in.font_addr = scr->scr_romfont;
905 
906 		scr->scr_fontmaxcol = cfg->fbheight / fp->height;
907 		scr->scr_fontbase = cfg->width + cfg->owidth;
908 		for (uc = fp->first; uc <= fp->last; uc++) {
909 			a.in.x = ((uc - fp->first) / scr->scr_fontmaxcol) *
910 			    fp->width + scr->scr_fontbase;
911 			a.in.y = ((uc - fp->first) % scr->scr_fontmaxcol) *
912 			    fp->height;
913 			a.in.index = uc;
914 
915 			(*scr->unpmv)(&a.flags, &a.in, &a.out, &scr->scr_cfg);
916 			if (a.out.errno) {
917 				aprint_error_dev(sc->sc_dev, "unpmv %d "
918 				    "returned %d\n", uc, a.out.errno);
919 				return 0;
920 			}
921 		}
922 
923 		free(scr->scr_romfont, M_DEVBUF);
924 		scr->scr_romfont = NULL;
925 	}
926 #endif
927 
928 	return 0;
929 }
930 
931 /*
932  * Wrappers around STI code pointers
933  */
934 
935 int
936 sti_init(struct sti_screen *scr, int mode)
937 {
938 	struct sti_rom *rom = scr->scr_rom;
939 	struct {
940 		struct sti_initflags flags;
941 		struct sti_initin in;
942 		struct sti_einitin ein;
943 		struct sti_initout out;
944 	} a;
945 
946 	KASSERT(rom != NULL);
947 	memset(&a, 0, sizeof(a));
948 
949 	a.flags.flags = STI_INITF_WAIT | STI_INITF_PBET | STI_INITF_PBETI;
950 	if ((mode & STI_TEXTMODE) != 0) {
951 		a.flags.flags |= STI_INITF_TEXT | STI_INITF_CMB |
952 		    STI_INITF_PBET | STI_INITF_PBETI | STI_INITF_ICMT;
953 		a.in.text_planes = 1;
954 	} else {
955 		a.flags.flags |= STI_INITF_TEXT | STI_INITF_NTEXT;
956 		/*
957 		 * Request as many text planes as STI will allow.
958 		 * The reason to do this - when switching to framebuffer mode
959 		 * for X we need access to all planes. In theory STI should do
960 		 * just that when we request access to both text and non-text
961 		 * planes as above.
962 		 * In reality though, at least on my PCI Visualize EG, some
963 		 * planes and/or colour registers remain inaccessible if we
964 		 * request only one text plane.
965 		 * Clearly we're missing a register write or two here, but so
966 		 * far I haven't found it.
967 		 */
968 		a.in.text_planes = 3;
969 	}
970 	if ((mode & STI_CLEARSCR) != 0)
971 		a.flags.flags |= STI_INITF_CLEAR;
972 
973 	a.in.ext_in = &a.ein;
974 
975 	DPRINTF(("%s: init,%p(%x, %p, %p, %p)\n",
976 	    device_xname(rom->rom_softc->sc_dev), rom->init, a.flags.flags,
977 	    &a.in, &a.out, &scr->scr_cfg));
978 
979 	(*rom->init)(&a.flags, &a.in, &a.out, &scr->scr_cfg);
980 
981 	if (a.out.text_planes != a.in.text_planes)
982 		return -1;	/* not colliding with sti errno values */
983 	return a.out.errno;
984 }
985 
986 int
987 sti_inqcfg(struct sti_screen *scr, struct sti_inqconfout *out)
988 {
989 	struct sti_rom *rom = scr->scr_rom;
990 	struct {
991 		struct sti_inqconfflags flags;
992 		struct sti_inqconfin in;
993 	} a;
994 
995 	memset(&a, 0, sizeof(a));
996 
997 	a.flags.flags = STI_INQCONFF_WAIT;
998 	(*rom->inqconf)(&a.flags, &a.in, out, &scr->scr_cfg);
999 
1000 	return out->errno;
1001 }
1002 
1003 void
1004 sti_bmove(struct sti_screen *scr, int x1, int y1, int x2, int y2, int h, int w,
1005     enum sti_bmove_funcs f)
1006 {
1007 	struct sti_rom *rom = scr->scr_rom;
1008 	struct {
1009 		struct sti_blkmvflags flags;
1010 		struct sti_blkmvin in;
1011 		struct sti_blkmvout out;
1012 	} a;
1013 
1014 	memset(&a, 0, sizeof(a));
1015 
1016 	a.flags.flags = STI_BLKMVF_WAIT;
1017 	switch (f) {
1018 	case bmf_clear:
1019 		a.flags.flags |= STI_BLKMVF_CLR;
1020 		a.in.bg_colour = STI_COLOUR_BLACK;
1021 		break;
1022 	case bmf_underline:
1023 	case bmf_copy:
1024 		a.in.fg_colour = STI_COLOUR_WHITE;
1025 		a.in.bg_colour = STI_COLOUR_BLACK;
1026 		break;
1027 	case bmf_invert:
1028 		a.flags.flags |= STI_BLKMVF_COLR;
1029 		a.in.fg_colour = STI_COLOUR_BLACK;
1030 		a.in.bg_colour = STI_COLOUR_WHITE;
1031 		break;
1032 	}
1033 	a.in.srcx = x1;
1034 	a.in.srcy = y1;
1035 	a.in.dstx = x2;
1036 	a.in.dsty = y2;
1037 	a.in.height = h;
1038 	a.in.width = w;
1039 
1040 	(*rom->blkmv)(&a.flags, &a.in, &a.out, &scr->scr_cfg);
1041 #ifdef STIDEBUG
1042 	if (a.out.errno)
1043 		printf("%s: blkmv returned %d\n",
1044 		    device_xname(rom->rom_softc->sc_dev), a.out.errno);
1045 #endif
1046 }
1047 
1048 int
1049 sti_setcment(struct sti_screen *scr, u_int i, u_char r, u_char g, u_char b)
1050 {
1051 	struct sti_rom *rom = scr->scr_rom;
1052 	struct {
1053 		struct sti_scmentflags flags;
1054 		struct sti_scmentin in;
1055 		struct sti_scmentout out;
1056 	} a;
1057 
1058 	memset(&a, 0, sizeof(a));
1059 
1060 	a.flags.flags = STI_SCMENTF_WAIT;
1061 	a.in.entry = i;
1062 	a.in.value = (r << 16) | (g << 8) | b;
1063 
1064 	(*rom->scment)(&a.flags, &a.in, &a.out, &scr->scr_cfg);
1065 
1066 	return a.out.errno;
1067 }
1068 
1069 /*
1070  * wsdisplay accessops
1071  */
1072 int
1073 sti_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
1074 {
1075 	struct sti_screen *scr = (struct sti_screen *)v;
1076 	struct wsdisplay_fbinfo *wdf;
1077 	struct wsdisplay_cmap *cmapp;
1078 	u_int mode, idx, count;
1079 	int ret;
1080 
1081 	ret = 0;
1082 	switch (cmd) {
1083 	case WSDISPLAYIO_GMODE:
1084 		*(u_int *)data = scr->scr_wsmode;
1085 		break;
1086 
1087 	case WSDISPLAYIO_SMODE:
1088 		mode = *(u_int *)data;
1089 		switch (mode) {
1090 		case WSDISPLAYIO_MODE_EMUL:
1091 			if (scr->scr_wsmode != WSDISPLAYIO_MODE_EMUL)
1092 				ret = sti_init(scr, STI_TEXTMODE);
1093 			break;
1094 		case WSDISPLAYIO_MODE_DUMBFB:
1095 			if (scr->scr_wsmode != WSDISPLAYIO_MODE_DUMBFB) {
1096 				ret = sti_init(scr, 0);
1097 				if (scr->setupfb != NULL)
1098 					scr->setupfb(scr);
1099 				else
1100 #if 0
1101 					ret = sti_init(scr, STI_FBMODE);
1102 #else
1103 					ret = EINVAL;
1104 #endif
1105 			}
1106 			break;
1107 		case WSDISPLAYIO_MODE_MAPPED:
1108 		default:
1109 			ret = EINVAL;
1110 			break;
1111 		}
1112 		if (ret == 0)
1113 			scr->scr_wsmode = mode;
1114 		break;
1115 
1116 	case WSDISPLAYIO_GTYPE:
1117 		*(u_int *)data = WSDISPLAY_TYPE_STI;
1118 		break;
1119 
1120 	case WSDISPLAYIO_GINFO:
1121 		wdf = (struct wsdisplay_fbinfo *)data;
1122 		wdf->height = scr->scr_cfg.scr_height;
1123 		wdf->width  = scr->scr_cfg.scr_width;
1124 		wdf->depth  = scr->scr_bpp;
1125 		if (scr->putcmap == NULL || scr->scr_bpp > 8)
1126 			wdf->cmsize = 0;
1127 		else
1128 			wdf->cmsize = STI_NCMAP;
1129 		break;
1130 
1131 	case WSDISPLAYIO_LINEBYTES:
1132 		if (scr->scr_bpp > 8)
1133 			*(u_int *)data = scr->scr_cfg.fb_width * 4;
1134 		else
1135 			*(u_int *)data = scr->scr_cfg.fb_width;
1136 		break;
1137 
1138 	case WSDISPLAYIO_GETCMAP:
1139 		if (scr->putcmap == NULL || scr->scr_bpp > 8)
1140 			return ENODEV;
1141 		cmapp = (struct wsdisplay_cmap *)data;
1142 		idx = cmapp->index;
1143 		count = cmapp->count;
1144 		if (idx >= STI_NCMAP || count > STI_NCMAP - idx)
1145 			return EINVAL;
1146 		if ((ret = copyout(&scr->scr_rcmap[idx], cmapp->red, count)))
1147 			break;
1148 		if ((ret = copyout(&scr->scr_gcmap[idx], cmapp->green, count)))
1149 			break;
1150 		if ((ret = copyout(&scr->scr_bcmap[idx], cmapp->blue, count)))
1151 			break;
1152 		break;
1153 
1154 	case WSDISPLAYIO_PUTCMAP:
1155 		if (scr->putcmap == NULL || scr->scr_bpp > 8)
1156 			return ENODEV;
1157 		if (scr->scr_wsmode == WSDISPLAYIO_MODE_EMUL) {
1158 			/*
1159 			 * The hardware palette settings are handled by
1160 			 * the STI ROM in STI_TEXTMODE and changing cmap
1161 			 * could cause mangled text colors at least on CRX.
1162 			 * Updating CMAP in EMUL mode isn't expected anyway
1163 			 * so just ignore it.
1164 			 */
1165 			return 0;
1166 		}
1167 		cmapp = (struct wsdisplay_cmap *)data;
1168 		idx = cmapp->index;
1169 		count = cmapp->count;
1170 		if (idx >= STI_NCMAP || count > STI_NCMAP - idx)
1171 			return EINVAL;
1172 		if ((ret = copyin(cmapp->red, &scr->scr_rcmap[idx], count)))
1173 			break;
1174 		if ((ret = copyin(cmapp->green, &scr->scr_gcmap[idx], count)))
1175 			break;
1176 		if ((ret = copyin(cmapp->blue, &scr->scr_bcmap[idx], count)))
1177 			break;
1178 		ret = scr->putcmap(scr, idx, count);
1179 		break;
1180 
1181 	case WSDISPLAYIO_SVIDEO:
1182 	case WSDISPLAYIO_GVIDEO:
1183 	case WSDISPLAYIO_GCURPOS:
1184 	case WSDISPLAYIO_SCURPOS:
1185 	case WSDISPLAYIO_GCURMAX:
1186 	case WSDISPLAYIO_GCURSOR:
1187 	case WSDISPLAYIO_SCURSOR:
1188 	default:
1189 		return ENOTTY;	/* not supported yet */
1190 	}
1191 
1192 	return ret;
1193 }
1194 
1195 paddr_t
1196 sti_mmap(void *v, void *vs, off_t offset, int prot)
1197 {
1198 	struct sti_screen *scr = (struct sti_screen *)v;
1199 	struct sti_rom *rom = scr->scr_rom;
1200 	paddr_t pa;
1201 
1202 	if ((offset & PAGE_MASK) != 0)
1203 		return -1;
1204 
1205 	if (offset < 0 || offset >= scr->fblen)
1206 		return -1;
1207 
1208 	if (scr->scr_wsmode != WSDISPLAYIO_MODE_DUMBFB)
1209 		return -1;
1210 
1211 	pa = bus_space_mmap(rom->memt, scr->fbaddr, offset, prot,
1212 	    BUS_SPACE_MAP_LINEAR);
1213 
1214 	if (pa == -1)
1215 		pa = scr->fbaddr + offset;
1216 
1217 	return pa;
1218 }
1219 
1220 int
1221 sti_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
1222     int *cxp, int *cyp, long *defattr)
1223 {
1224 	struct sti_screen *scr = (struct sti_screen *)v;
1225 
1226 	if (scr->scr_nscreens > 0)
1227 		return ENOMEM;
1228 
1229 	*cookiep = scr;
1230 	*cxp = 0;
1231 	*cyp = 0;
1232 	sti_alloc_attr(scr, 0, 0, 0, defattr);
1233 	scr->scr_nscreens++;
1234 	return 0;
1235 }
1236 
1237 void
1238 sti_free_screen(void *v, void *cookie)
1239 {
1240 	struct sti_screen *scr = (struct sti_screen *)v;
1241 
1242 	scr->scr_nscreens--;
1243 }
1244 
1245 int
1246 sti_show_screen(void *v, void *cookie, int waitok, void (*cb)(void *, int, int),
1247     void *cbarg)
1248 {
1249 #if 0
1250 	struct sti_screen *scr = (struct sti_screen *)v;
1251 #endif
1252 
1253 	return 0;
1254 }
1255 
1256 int
1257 sti_load_font(void *v, void *cookie, struct wsdisplay_font *font)
1258 {
1259 #if 0
1260 	struct sti_screen *scr = (struct sti_screen *)v;
1261 #endif
1262 
1263 	return -1;
1264 }
1265 
1266 /*
1267  * wsdisplay emulops
1268  */
1269 void
1270 sti_cursor(void *v, int on, int row, int col)
1271 {
1272 	struct sti_screen *scr = (struct sti_screen *)v;
1273 	struct sti_font *fp = &scr->scr_curfont;
1274 
1275 	sti_bmove(scr,
1276 	    col * fp->width, row * fp->height,
1277 	    col * fp->width, row * fp->height,
1278 	    fp->height, fp->width, bmf_invert);
1279 }
1280 
1281 /*
1282  * ISO 8859-1 part of Unicode to HP Roman font index conversion array.
1283  */
1284 static const uint8_t
1285 sti_unitoroman[0x100 - 0xa0] = {
1286 	0xa0, 0xb8, 0xbf, 0xbb, 0xba, 0xbc,    0, 0xbd,
1287 	0xab,    0, 0xf9, 0xfb,    0, 0xf6,    0, 0xb0,
1288 
1289 	0xb3, 0xfe,    0,    0, 0xa8, 0xf3, 0xf4, 0xf2,
1290 	   0,    0, 0xfa, 0xfd, 0xf7, 0xf8,    0, 0xb9,
1291 
1292 	0xa1, 0xe0, 0xa2, 0xe1, 0xd8, 0xd0, 0xd3, 0xb4,
1293 	0xa3, 0xdc, 0xa4, 0xa5, 0xe6, 0xe5, 0xa6, 0xa7,
1294 
1295 	0xe3, 0xb6, 0xe8, 0xe7, 0xdf, 0xe9, 0xda,    0,
1296 	0xd2, 0xad, 0xed, 0xae, 0xdb, 0xb1, 0xf0, 0xde,
1297 
1298 	0xc8, 0xc4, 0xc0, 0xe2, 0xcc, 0xd4, 0xd7, 0xb5,
1299 	0xc9, 0xc5, 0xc1, 0xcd, 0xd9, 0xd5, 0xd1, 0xdd,
1300 
1301 	0xe4, 0xb7, 0xca, 0xc6, 0xc2, 0xea, 0xce,    0,
1302 	0xd6, 0xcb, 0xc7, 0xc3, 0xcf, 0xb2, 0xf1, 0xef
1303 };
1304 
1305 int
1306 sti_mapchar(void *v, int uni, u_int *index)
1307 {
1308 	struct sti_screen *scr = (struct sti_screen *)v;
1309 	struct sti_font *fp = &scr->scr_curfont;
1310 	int c;
1311 
1312 	switch (fp->type) {
1313 	case STI_FONT_HPROMAN8:
1314 		if (uni >= 0x80 && uni < 0xa0)
1315 			c = -1;
1316 		else if (uni >= 0xa0 && uni < 0x100) {
1317 			c = (int)sti_unitoroman[uni - 0xa0];
1318 			if (c == 0)
1319 				c = -1;
1320 		} else
1321 			c = uni;
1322 		break;
1323 	default:
1324 		c = uni;
1325 		break;
1326 	}
1327 
1328 	if (c == -1 || c < fp->first || c > fp->last) {
1329 		*index = ' ';
1330 		return 0;
1331 	}
1332 
1333 	*index = c;
1334 	return 5;
1335 }
1336 
1337 void
1338 sti_putchar(void *v, int row, int col, u_int uc, long attr)
1339 {
1340 	struct sti_screen *scr = (struct sti_screen *)v;
1341 	struct sti_rom *rom = scr->scr_rom;
1342 	struct sti_font *fp = &scr->scr_curfont;
1343 	int bg, fg;
1344 
1345 	fg = WSATTR_UNPACK_FG(attr);
1346 	bg = WSATTR_UNPACK_BG(attr);
1347 
1348 	if (scr->scr_romfont != NULL) {
1349 		/*
1350 		 * Font is in memory, use unpmv
1351 		 */
1352 		struct {
1353 			struct sti_unpmvflags flags;
1354 			struct sti_unpmvin in;
1355 			struct sti_unpmvout out;
1356 		} a;
1357 
1358 		memset(&a, 0, sizeof(a));
1359 
1360 		a.flags.flags = STI_UNPMVF_WAIT;
1361 		a.in.fg_colour = fg;
1362 		a.in.bg_colour = bg;
1363 		a.in.x = col * fp->width;
1364 		a.in.y = row * fp->height;
1365 		a.in.font_addr = scr->scr_romfont;
1366 		a.in.index = uc;
1367 
1368 		(*rom->unpmv)(&a.flags, &a.in, &a.out, &scr->scr_cfg);
1369 	} else {
1370 		/*
1371 		 * Font is in frame buffer, use blkmv
1372 		 */
1373 		struct {
1374 			struct sti_blkmvflags flags;
1375 			struct sti_blkmvin in;
1376 			struct sti_blkmvout out;
1377 		} a;
1378 
1379 		memset(&a, 0, sizeof(a));
1380 
1381 		a.flags.flags = STI_BLKMVF_WAIT;
1382 		a.in.fg_colour = fg;
1383 		a.in.bg_colour = bg;
1384 
1385 		a.in.srcx = ((uc - fp->first) / scr->scr_fontmaxcol) *
1386 		    fp->width + scr->scr_fontbase;
1387 		a.in.srcy = ((uc - fp->first) % scr->scr_fontmaxcol) *
1388 		    fp->height;
1389 		a.in.dstx = col * fp->width;
1390 		a.in.dsty = row * fp->height;
1391 		a.in.height = fp->height;
1392 		a.in.width = fp->width;
1393 
1394 		(*rom->blkmv)(&a.flags, &a.in, &a.out, &scr->scr_cfg);
1395 	}
1396 }
1397 
1398 void
1399 sti_copycols(void *v, int row, int srccol, int dstcol, int ncols)
1400 {
1401 	struct sti_screen *scr = (struct sti_screen *)v;
1402 	struct sti_font *fp = &scr->scr_curfont;
1403 
1404 	sti_bmove(scr,
1405 	    srccol * fp->width, row * fp->height,
1406 	    dstcol * fp->width, row * fp->height,
1407 	    fp->height, ncols * fp->width, bmf_copy);
1408 }
1409 
1410 void
1411 sti_erasecols(void *v, int row, int startcol, int ncols, long attr)
1412 {
1413 	struct sti_screen *scr = (struct sti_screen *)v;
1414 	struct sti_font *fp = &scr->scr_curfont;
1415 
1416 	sti_bmove(scr,
1417 	    startcol * fp->width, row * fp->height,
1418 	    startcol * fp->width, row * fp->height,
1419 	    fp->height, ncols * fp->width, bmf_clear);
1420 }
1421 
1422 void
1423 sti_copyrows(void *v, int srcrow, int dstrow, int nrows)
1424 {
1425 	struct sti_screen *scr = (struct sti_screen *)v;
1426 	struct sti_font *fp = &scr->scr_curfont;
1427 
1428 	sti_bmove(scr, 0, srcrow * fp->height, 0, dstrow * fp->height,
1429 	    nrows * fp->height, scr->scr_cfg.scr_width, bmf_copy);
1430 }
1431 
1432 void
1433 sti_eraserows(void *v, int srcrow, int nrows, long attr)
1434 {
1435 	struct sti_screen *scr = (struct sti_screen *)v;
1436 	struct sti_font *fp = &scr->scr_curfont;
1437 
1438 	sti_bmove(scr, 0, srcrow * fp->height, 0, srcrow * fp->height,
1439 	    nrows * fp->height, scr->scr_cfg.scr_width, bmf_clear);
1440 }
1441 
1442 int
1443 sti_alloc_attr(void *v, int fg, int bg, int flags, long *pattr)
1444 {
1445 #if 0
1446 	struct sti_screen *scr = (struct sti_screen *)v;
1447 #endif
1448 
1449 	if ((flags & (WSATTR_HILIT | WSATTR_BLINK |
1450 	    WSATTR_UNDERLINE | WSATTR_WSCOLORS)) != 0)
1451 		return EINVAL;
1452 	if ((flags & WSATTR_REVERSE) != 0) {
1453 		fg = STI_COLOUR_BLACK;
1454 		bg = STI_COLOUR_WHITE;
1455 	} else {
1456 		fg = STI_COLOUR_WHITE;
1457 		bg = STI_COLOUR_BLACK;
1458 	}
1459 
1460 	*pattr = WSATTR_PACK(fg, bg, flags);
1461 	return 0;
1462 }
1463 
1464 /*
1465  * Early console support.  Only used on hp300, currently
1466  */
1467 int
1468 sti_cnattach(struct sti_rom *rom, struct sti_screen *scr, bus_space_tag_t memt,
1469     bus_addr_t *bases, u_int codebase)
1470 {
1471 	bus_space_handle_t romh;
1472 	u_int romend;
1473 	int error;
1474 	long defattr;
1475 
1476 	if ((error = bus_space_map(memt, bases[0], PAGE_SIZE, 0, &romh)) != 0)
1477 		return error;
1478 
1479 	/*
1480 	 * Compute real PROM size
1481 	 */
1482 	romend = sti_rom_size(memt, romh);
1483 
1484 	bus_space_unmap(memt, romh, PAGE_SIZE);
1485 
1486 	if ((error = bus_space_map(memt, bases[0], romend, 0, &romh)) != 0)
1487 		return error;
1488 
1489 	bases[0] = romh;
1490 	if (sti_rom_setup(rom, memt, memt, romh, bases, codebase) != 0)
1491 		return -1;
1492 	scr->scr_rom = rom;
1493 	if (sti_screen_setup(scr, STI_CLEARSCR) != 0)
1494 		return -1;
1495 
1496 	sti_alloc_attr(scr, 0, 0, 0, &defattr);
1497 	wsdisplay_cnattach(&scr->scr_wsd, scr, 0, 0, defattr);
1498 
1499 	return 0;
1500 }
1501 
1502 int
1503 ngle_default_putcmap(struct sti_screen *scr, u_int idx, u_int count)
1504 {
1505 	int i, ret;
1506 
1507 	for (i = idx + count - 1; i >= (int)idx; i--)
1508 		if ((ret = sti_setcment(scr, i, scr->scr_rcmap[i],
1509 		    scr->scr_gcmap[i], scr->scr_bcmap[i])))
1510 			return EINVAL;
1511 
1512 	return 0;
1513 }
1514 
1515 #ifndef SMALL_KERNEL
1516 
1517 void	ngle_setup_hw(bus_space_tag_t, bus_space_handle_t);
1518 void	ngle_setup_fb(bus_space_tag_t, bus_space_handle_t, uint32_t);
1519 void	ngle_setup_attr_planes(struct sti_screen *scr);
1520 void	ngle_setup_bt458(struct sti_screen *scr);
1521 
1522 #define	ngle_bt458_write(memt, memh, r, v) \
1523 	bus_space_write_stream_4(memt, memh, NGLE_REG_RAMDAC + ((r) << 2), (v) << 24)
1524 
1525 void
1526 ngle_artist_setupfb(struct sti_screen *scr)
1527 {
1528 	struct sti_rom *rom = scr->scr_rom;
1529 	bus_space_tag_t memt = rom->memt;
1530 	bus_space_handle_t memh = rom->regh[2];
1531 
1532 	ngle_setup_bt458(scr);
1533 
1534 	ngle_setup_hw(memt, memh);
1535 	ngle_setup_fb(memt, memh, scr->reg10_value);
1536 
1537 	ngle_setup_attr_planes(scr);
1538 
1539 	ngle_setup_hw(memt, memh);
1540 	bus_space_write_stream_4(memt, memh, NGLE_REG_21,
1541 	    bus_space_read_stream_4(memt, memh, NGLE_REG_21) | 0x0a000000);
1542 	bus_space_write_stream_4(memt, memh, NGLE_REG_27,
1543 	    bus_space_read_stream_4(memt, memh, NGLE_REG_27) | 0x00800000);
1544 }
1545 
1546 void
1547 ngle_elk_setupfb(struct sti_screen *scr)
1548 {
1549 	struct sti_rom *rom = scr->scr_rom;
1550 	bus_space_tag_t memt = rom->memt;
1551 	bus_space_handle_t memh = rom->regh[2];
1552 
1553 	ngle_setup_bt458(scr);
1554 
1555 	ngle_setup_hw(memt, memh);
1556 	ngle_setup_fb(memt, memh, scr->reg10_value);
1557 
1558 	ngle_setup_attr_planes(scr);
1559 
1560 	ngle_setup_hw(memt, memh);
1561 	/* enable overlay planes in Bt458 command register */
1562 	ngle_bt458_write(memt, memh, 0x0c, 0x06);
1563 	ngle_bt458_write(memt, memh, 0x0e, 0x43);
1564 }
1565 
1566 void
1567 ngle_timber_setupfb(struct sti_screen *scr)
1568 {
1569 	struct sti_rom *rom = scr->scr_rom;
1570 	bus_space_tag_t memt = rom->memt;
1571 	bus_space_handle_t memh = rom->regh[2];
1572 
1573 	ngle_setup_bt458(scr);
1574 
1575 	ngle_setup_hw(memt, memh);
1576 	/* enable overlay planes in Bt458 command register */
1577 	ngle_bt458_write(memt, memh, 0x0c, 0x06);
1578 	ngle_bt458_write(memt, memh, 0x0e, 0x43);
1579 }
1580 
1581 void
1582 ngle_setup_bt458(struct sti_screen *scr)
1583 {
1584 	struct sti_rom *rom = scr->scr_rom;
1585 	bus_space_tag_t memt = rom->memt;
1586 	bus_space_handle_t memh = rom->regh[2];
1587 
1588 	ngle_setup_hw(memt, memh);
1589 	/* set Bt458 read mask register to all planes */
1590 	ngle_bt458_write(memt, memh, 0x08, 0x04);
1591 	ngle_bt458_write(memt, memh, 0x0a, 0xff);
1592 }
1593 
1594 void
1595 ngle_setup_attr_planes(struct sti_screen *scr)
1596 {
1597 	struct sti_rom *rom = scr->scr_rom;
1598 	bus_space_tag_t memt = rom->memt;
1599 	bus_space_handle_t memh = rom->regh[2];
1600 
1601 	ngle_setup_hw(memt, memh);
1602 	bus_space_write_stream_4(memt, memh, NGLE_REG_11, 0x2ea0d000);
1603 	bus_space_write_stream_4(memt, memh, NGLE_REG_14, 0x23000302);
1604 	bus_space_write_stream_4(memt, memh, NGLE_REG_12, scr->reg12_value);
1605 	bus_space_write_stream_4(memt, memh, NGLE_REG_8, 0xffffffff);
1606 
1607 	bus_space_write_stream_4(memt, memh, NGLE_REG_6, 0x00000000);
1608 	bus_space_write_stream_4(memt, memh, NGLE_REG_9,
1609 	    (scr->scr_cfg.scr_width << 16) | scr->scr_cfg.scr_height);
1610 	bus_space_write_stream_4(memt, memh, NGLE_REG_6, 0x05000000);
1611 	bus_space_write_stream_4(memt, memh, NGLE_REG_9, 0x00040001);
1612 
1613 	ngle_setup_hw(memt, memh);
1614 	bus_space_write_stream_4(memt, memh, NGLE_REG_12, 0x00000000);
1615 
1616 	ngle_setup_fb(memt, memh, scr->reg10_value);
1617 }
1618 
1619 int
1620 ngle_putcmap(struct sti_screen *scr, u_int idx, u_int count)
1621 {
1622 	struct sti_rom *rom = scr->scr_rom;
1623 	bus_space_tag_t memt = rom->memt;
1624 	bus_space_handle_t memh = rom->regh[2];
1625 	uint8_t *r, *g, *b;
1626 	uint32_t cmap_finish;
1627 
1628 	if (scr->scr_bpp > 8)
1629 		cmap_finish = 0x83000100;
1630 	else
1631 		cmap_finish = 0x80000100;
1632 
1633 	r = scr->scr_rcmap + idx;
1634 	g = scr->scr_gcmap + idx;
1635 	b = scr->scr_bcmap + idx;
1636 
1637 	ngle_setup_hw(memt, memh);
1638 	bus_space_write_stream_4(memt, memh, NGLE_REG_10, 0xbbe0f000);
1639 	bus_space_write_stream_4(memt, memh, NGLE_REG_14, 0x03000300);
1640 	bus_space_write_stream_4(memt, memh, NGLE_REG_13, 0xffffffff);
1641 
1642 	while (count-- != 0) {
1643 		ngle_setup_hw(memt, memh);
1644 		bus_space_write_stream_4(memt, memh, NGLE_REG_3,
1645 		    0x400 | (idx << 2));
1646 		bus_space_write_stream_4(memt, memh, NGLE_REG_4,
1647 		    (*r << 16) | (*g << 8) | *b);
1648 
1649 		idx++;
1650 		r++, g++, b++;
1651 	}
1652 
1653 
1654 	bus_space_write_stream_4(memt, memh, NGLE_REG_2, 0x400);
1655 	bus_space_write_stream_4(memt, memh, scr->cmap_finish_register,
1656 	    cmap_finish);
1657 	ngle_setup_fb(memt, memh, scr->reg10_value);
1658 
1659 
1660 	return 0;
1661 }
1662 
1663 int
1664 ngle_hcrx_putcmap(struct sti_screen *scr, u_int idx, u_int count)
1665 {
1666 	struct sti_rom *rom = scr->scr_rom;
1667 	bus_space_tag_t memt = rom->memt;
1668 	bus_space_handle_t memh = rom->regh[2];
1669 	uint8_t *r, *g, *b;
1670 	uint32_t cmap_finish;
1671 
1672 	if (scr->scr_bpp > 8)
1673 		cmap_finish = 0x80000100;
1674 	else
1675 		cmap_finish = 0x82000100;
1676 
1677 	r = scr->scr_rcmap + idx;
1678 	g = scr->scr_gcmap + idx;
1679 	b = scr->scr_bcmap + idx;
1680 
1681 	ngle_setup_hw(memt, memh);
1682 	bus_space_write_stream_4(memt, memh, NGLE_REG_10, 0xbbe0f000);
1683 	bus_space_write_stream_4(memt, memh, NGLE_REG_14, 0x03000300);
1684 	bus_space_write_stream_4(memt, memh, NGLE_REG_13, 0xffffffff);
1685 
1686 	while (count-- != 0) {
1687 		ngle_setup_hw(memt, memh);
1688 		bus_space_write_stream_4(memt, memh, NGLE_REG_3,
1689 		    0x400 | (idx << 2));
1690 		bus_space_write_stream_4(memt, memh, NGLE_REG_4,
1691 		    (*r << 16) | (*g << 8) | *b);
1692 
1693 		idx++;
1694 		r++, g++, b++;
1695 	}
1696 
1697 
1698 	bus_space_write_stream_4(memt, memh, NGLE_REG_2, 0x400);
1699 	bus_space_write_stream_4(memt, memh, NGLE_REG_38, cmap_finish);
1700 	ngle_setup_fb(memt, memh, scr->reg10_value);
1701 
1702 
1703 	return 0;
1704 }
1705 
1706 void
1707 ngle_setup_hw(bus_space_tag_t memt, bus_space_handle_t memh)
1708 {
1709 	uint8_t stat;
1710 
1711 	do {
1712 		stat = bus_space_read_1(memt, memh, NGLE_REG_15b0);
1713 		if (stat == 0)
1714 			stat = bus_space_read_1(memt, memh, NGLE_REG_15b0);
1715 	} while (stat != 0);
1716 }
1717 
1718 void
1719 ngle_setup_fb(bus_space_tag_t memt, bus_space_handle_t memh, uint32_t reg10)
1720 {
1721 
1722 	ngle_setup_hw(memt, memh);
1723 	bus_space_write_stream_4(memt, memh, NGLE_REG_10, reg10);
1724 	bus_space_write_stream_4(memt, memh, NGLE_REG_14, 0x83000300);
1725 	ngle_setup_hw(memt, memh);
1726 	bus_space_write_1(memt, memh, NGLE_REG_16b1, 1);
1727 }
1728 #endif	/* SMALL_KERNEL */
1729