1/*	$NetBSD: diofb.c,v 1.2 2011/02/12 16:40:29 tsutsui Exp $	*/
2/*	$OpenBSD: diofb.c,v 1.18 2010/12/26 15:40:59 miod Exp $	*/
3
4/*
5 * Copyright (c) 2005, Miodrag Vallat
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28/*
29 * Copyright (c) 1988 University of Utah.
30 * Copyright (c) 1990, 1993
31 *	The Regents of the University of California.  All rights reserved.
32 *
33 * This code is derived from software contributed to Berkeley by
34 * the Systems Programming Group of the University of Utah Computer
35 * Science Department.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 *    notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 *    notice, this list of conditions and the following disclaimer in the
44 *    documentation and/or other materials provided with the distribution.
45 * 3. Neither the name of the University nor the names of its contributors
46 *    may be used to endorse or promote products derived from this software
47 *    without specific prior written permission.
48 *
49 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
50 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
51 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
52 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
53 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
54 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
55 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
56 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
57 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
58 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
59 * SUCH DAMAGE.
60 */
61
62#include <sys/param.h>
63#include <sys/conf.h>
64#include <sys/proc.h>
65#include <sys/ioctl.h>
66#include <sys/tty.h>
67#include <sys/systm.h>
68#include <sys/device.h>
69#include <sys/bus.h>
70#include <sys/cpu.h>
71
72#include <machine/autoconf.h>
73
74#include <dev/wscons/wsconsio.h>
75#include <dev/wscons/wsdisplayvar.h>
76#include <dev/rasops/rasops.h>
77
78#include <hp300/dev/dioreg.h>
79#include <hp300/dev/diovar.h>
80#include <hp300/dev/diofbreg.h>
81#include <hp300/dev/diofbvar.h>
82
83static void	diofb_do_cursor(struct rasops_info *);
84static void	diofb_copycols(void *, int, int, int, int);
85static void	diofb_erasecols(void *, int, int, int, long);
86static void	diofb_copyrows(void *, int, int, int);
87static void	diofb_eraserows(void *, int, int, long);
88static int	diofb_allocattr(void *, int, int, int, long *);
89
90struct diofb diofb_cn;
91
92/*
93 * Frame buffer geometry initialization
94 */
95
96int
97diofb_fbinquire(struct diofb *fb, int scode, struct diofbreg *fbr)
98{
99	int fboff, regsize;
100
101	if (ISIIOVA(fbr))
102		fb->regaddr = (uint8_t *)IIOP(fbr);
103	else
104		fb->regaddr = dio_scodetopa(scode);
105
106	if (fb->fbwidth == 0 || fb->fbheight == 0) {
107		fb->fbwidth = (fbr->fbwmsb << 8) | fbr->fbwlsb;
108		fb->fbheight = (fbr->fbhmsb << 8) | fbr->fbhlsb;
109	}
110	fb->fbsize = fb->fbwidth * fb->fbheight;
111
112	fb->regkva = (uint8_t *)fbr;
113	fboff = (fbr->fbomsb << 8) | fbr->fbolsb;
114	fb->fbaddr = (uint8_t *) (*((uint8_t *)fbr + fboff) << 16);
115
116	if (fb->regaddr >= (uint8_t *)DIOII_BASE) {
117		/*
118		 * For DIO-II space the fbaddr just computed is
119		 * the offset from the select code base (regaddr)
120		 * of the framebuffer.  Hence it is also implicitly
121		 * the size of the set.
122		 */
123		regsize = (uintptr_t)fb->fbaddr;
124		fb->fbaddr = fb->regaddr + (uintptr_t)fb->fbaddr;
125		fb->fbkva = (uint8_t *)fbr + regsize;
126	} else {
127		/*
128		 * For internal or DIO-I space we need to map the separate
129		 * framebuffer.
130		 */
131		fb->fbkva = iomap(fb->fbaddr, fb->fbsize);
132		if (fb->fbkva == NULL)
133			return ENOMEM;
134	}
135	if (fb->dwidth == 0 || fb->dheight == 0) {
136		fb->dwidth = (fbr->dwmsb << 8) | fbr->dwlsb;
137		fb->dheight = (fbr->dhmsb << 8) | fbr->dhlsb;
138	}
139
140	/*
141	 * Some displays, such as the DaVinci, appear to return a display
142	 * height larger than the frame buffer height.
143	 */
144	if (fb->dwidth > fb->fbwidth)
145		fb->dwidth = fb->fbwidth;
146	if (fb->dheight > fb->fbheight)
147		fb->dheight = fb->fbheight;
148
149	fb->planes = fbr->num_planes;
150	if (fb->planes > 8)
151		fb->planes = 8;
152	fb->planemask = (1 << fb->planes) - 1;
153
154	fb->mapmode = WSDISPLAYIO_MODE_DUMBFB;
155
156	return 0;
157}
158
159/*
160 * Frame buffer rasops and colormap setup
161 */
162
163void
164diofb_fbsetup(struct diofb *fb)
165{
166	struct rasops_info *ri = &fb->ri;
167
168	/*
169	 * Pretend we are an 8bpp frame buffer, unless ri_depth is already
170	 * initialized, since this is how it is supposed to be addressed.
171	 * (Hyperion forces 1bpp because it is really 1bpp addressed).
172	 */
173	if (ri->ri_depth == 0)
174		ri->ri_depth = 8;
175	ri->ri_stride = (fb->fbwidth * ri->ri_depth) / 8;
176
177	ri->ri_flg = RI_CENTER | RI_FULLCLEAR;
178	/* We don't really support colors on less than 4bpp frame buffers */
179	if (fb->planes < 4)
180		ri->ri_flg |= RI_FORCEMONO;
181	if (fb == &diofb_cn)
182		ri->ri_flg |= RI_NO_AUTO;
183	ri->ri_bits = fb->fbkva;
184	ri->ri_width = fb->dwidth;
185	ri->ri_height = fb->dheight;
186	ri->ri_hw = fb;
187
188	/*
189	 * Ask for an unholy big display, rasops will trim this to more
190	 * reasonable values.
191	 */
192	rasops_init(ri, 160, 160);
193
194	diofb_resetcmap(fb);
195
196	/*
197	 * For low depth frame buffers, since we have faked a 8bpp frame buffer
198	 * to rasops, we actually have to remove capabilities.
199	 */
200	if (fb->planes == 4) {
201		ri->ri_ops.allocattr = diofb_allocattr;
202		ri->ri_caps &= ~WSSCREEN_HILIT;
203	}
204
205	ri->ri_ops.copycols = diofb_copycols;
206	ri->ri_ops.erasecols = diofb_erasecols;
207	if (ri->ri_depth != 1) {
208		ri->ri_ops.copyrows = diofb_copyrows;
209		ri->ri_ops.eraserows = diofb_eraserows;
210		ri->ri_do_cursor = diofb_do_cursor;
211	}
212
213	/* Clear entire display, including non visible areas */
214	(*fb->bmv)(fb, 0, 0, 0, 0, fb->fbwidth, fb->fbheight, RR_CLEAR, 0xff);
215
216	fb->wsd.name = fb->wsdname;
217	fb->wsd.ncols = ri->ri_cols;
218	fb->wsd.nrows = ri->ri_rows;
219	fb->wsd.textops = &ri->ri_ops;
220	fb->wsd.fontwidth = ri->ri_font->fontwidth;
221	fb->wsd.fontheight = ri->ri_font->fontheight;
222	fb->wsd.capabilities = ri->ri_caps;
223	strlcpy(fb->wsdname, "std", sizeof(fb->wsdname));
224}
225
226/*
227 * Setup default emulation mode colormap
228 */
229void
230diofb_resetcmap(struct diofb *fb)
231{
232	const u_char *color;
233	u_int i;
234
235	/* start with the rasops colormap */
236	color = (const u_char *)rasops_cmap;
237	for (i = 0; i < 256; i++) {
238		fb->cmap.r[i] = *color++;
239		fb->cmap.g[i] = *color++;
240		fb->cmap.b[i] = *color++;
241	}
242
243	/*
244	 * Tweak colormap
245	 *
246	 * Due to the way rasops cursor work, we need to provide
247	 * copies of the 8 or 16 basic colors at extra locations
248	 * in 4bpp and 6bpp mode. This is because missing planes
249	 * accept writes but read back as zero.
250	 *
251	 * So, in 6bpp mode:
252	 *   00 gets inverted to ff, read back as 3f
253	 *   3f gets inverted to c0, read back as 00
254	 * and in 4bpp mode:
255	 *   00 gets inverted to ff, read back as 0f
256	 *   0f gets inverted to f0, read back as 00
257	 */
258
259	switch (fb->planes) {
260	case 6:
261		/*
262		 * 00-0f normal colors
263		 * 30-3f inverted colors
264		 * c0-cf normal colors
265		 * f0-ff inverted colors
266		 */
267		memcpy(fb->cmap.r + 0xc0, fb->cmap.r + 0x00, 0x10);
268		memcpy(fb->cmap.g + 0xc0, fb->cmap.g + 0x00, 0x10);
269		memcpy(fb->cmap.b + 0xc0, fb->cmap.b + 0x00, 0x10);
270		memcpy(fb->cmap.r + 0x30, fb->cmap.r + 0xf0, 0x10);
271		memcpy(fb->cmap.g + 0x30, fb->cmap.g + 0xf0, 0x10);
272		memcpy(fb->cmap.b + 0x30, fb->cmap.b + 0xf0, 0x10);
273		break;
274	case 4:
275		/*
276		 * 00-07 normal colors
277		 * 08-0f inverted colors
278		 * highlighted colors are not available.
279		 */
280		memcpy(fb->cmap.r + 0x08, fb->cmap.r + 0xf8, 0x08);
281		memcpy(fb->cmap.g + 0x08, fb->cmap.g + 0xf8, 0x08);
282		memcpy(fb->cmap.b + 0x08, fb->cmap.b + 0xf8, 0x08);
283		break;
284	}
285}
286
287/*
288 * Attachment helpers
289 */
290
291void
292diofb_cnattach(struct diofb *fb)
293{
294	long defattr;
295	struct rasops_info *ri;
296
297	ri = &fb->ri;
298	ri->ri_ops.allocattr(ri, 0, 0, 0, &defattr);
299	wsdisplay_cnattach(&fb->wsd, ri, 0, 0, defattr);
300}
301
302void
303diofb_end_attach(device_t self, struct wsdisplay_accessops *accessops,
304    struct diofb *fb, int console, const char *descr)
305{
306	struct wsemuldisplaydev_attach_args waa;
307
308	aprint_normal(": %dx%d", fb->dwidth, fb->dheight);
309
310	if (fb->planes == 1)
311		aprint_normal(" monochrome");
312	else
313		aprint_normal("x%d", fb->planes);
314
315	if (descr != NULL)
316		aprint_normal(" %s", descr);
317	aprint_normal(" frame buffer\n");
318
319	fb->scrlist[0] = &fb->wsd;
320	fb->wsl.nscreens = 1;
321	fb->wsl.screens = (const struct wsscreen_descr **)fb->scrlist;
322
323	waa.console = console;
324	waa.scrdata = &fb->wsl;
325	waa.accessops = accessops;
326	waa.accesscookie = fb;
327
328	config_found(self, &waa, wsemuldisplaydevprint);
329}
330
331/*
332 * Common wsdisplay emulops for DIO frame buffers
333 */
334
335int
336diofb_allocattr(void *cookie, int fg, int bg, int flg, long *attr)
337{
338
339	if ((flg & (WSATTR_BLINK | WSATTR_HILIT)) != 0)
340		return EINVAL;
341
342	if ((flg & WSATTR_WSCOLORS) == 0) {
343		fg = WSCOL_WHITE;
344		bg = WSCOL_BLACK;
345	}
346
347	if ((flg & WSATTR_REVERSE) != 0) {
348		int swap;
349		swap = fg;
350		fg = bg;
351		bg = swap;
352	}
353
354	flg = ((flg & WSATTR_UNDERLINE) ? 1 : 0);
355
356	*attr = (bg << 16) | (fg << 24) | flg;
357
358	return 0;
359}
360
361void
362diofb_copycols(void *cookie, int row, int src, int dst, int n)
363{
364	struct rasops_info *ri = cookie;
365	struct diofb *fb = ri->ri_hw;
366
367	n *= ri->ri_font->fontwidth;
368	src *= ri->ri_font->fontwidth;
369	dst *= ri->ri_font->fontwidth;
370	row *= ri->ri_font->fontheight;
371
372	(*fb->bmv)(fb, ri->ri_xorigin + src, ri->ri_yorigin + row,
373	    ri->ri_xorigin + dst, ri->ri_yorigin + row,
374	    n, ri->ri_font->fontheight, RR_COPY, 0xff);
375}
376
377void
378diofb_copyrows(void *cookie, int src, int dst, int n)
379{
380	struct rasops_info *ri = cookie;
381	struct diofb *fb = ri->ri_hw;
382
383	n *= ri->ri_font->fontheight;
384	src *= ri->ri_font->fontheight;
385	dst *= ri->ri_font->fontheight;
386
387	(*fb->bmv)(fb, ri->ri_xorigin, ri->ri_yorigin + src,
388	    ri->ri_xorigin, ri->ri_yorigin + dst,
389	    ri->ri_emuwidth, n, RR_COPY, 0xff);
390}
391
392void
393diofb_erasecols(void *cookie, int row, int col, int num, long attr)
394{
395	struct rasops_info *ri = cookie;
396	struct diofb *fb = ri->ri_hw;
397	int fg, bg;
398	int snum, scol, srow;
399
400	rasops_unpack_attr(attr, &fg, &bg, NULL);
401
402	snum = num * ri->ri_font->fontwidth;
403	scol = col * ri->ri_font->fontwidth + ri->ri_xorigin;
404	srow = row * ri->ri_font->fontheight + ri->ri_yorigin;
405
406	/*
407	 * If this is too tricky for the simple raster ops engine,
408	 * pass the fun to rasops.
409	 */
410	if ((*fb->bmv)(fb, scol, srow, scol, srow, snum,
411	    ri->ri_font->fontheight, RR_CLEAR, 0xff ^ bg) != 0)
412		rasops_erasecols(cookie, row, col, num, attr);
413}
414
415void
416diofb_eraserows(void *cookie, int row, int num, long attr)
417{
418	struct rasops_info *ri = cookie;
419	struct diofb *fb = ri->ri_hw;
420	int fg, bg;
421	int srow, snum;
422	int rc;
423
424	rasops_unpack_attr(attr, &fg, &bg, NULL);
425	bg ^= 0xff;
426
427	if (num == ri->ri_rows && (ri->ri_flg & RI_FULLCLEAR)) {
428		rc = (*fb->bmv)(fb, 0, 0, 0, 0, ri->ri_width, ri->ri_height,
429		    RR_CLEAR, bg);
430	} else {
431		srow = row * ri->ri_font->fontheight + ri->ri_yorigin;
432		snum = num * ri->ri_font->fontheight;
433		rc = (*fb->bmv)(fb, ri->ri_xorigin, srow, ri->ri_xorigin,
434		    srow, ri->ri_emuwidth, snum, RR_CLEAR, bg);
435	}
436	if (rc != 0)
437		rasops_eraserows(cookie, row, num, attr);
438}
439
440void
441diofb_do_cursor(struct rasops_info *ri)
442{
443	struct diofb *fb = ri->ri_hw;
444	int x, y;
445
446	x = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin;
447	y = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin;
448	(*fb->bmv)(fb, x, y, x, y, ri->ri_font->fontwidth,
449	    ri->ri_font->fontheight, RR_INVERT, 0xff);
450}
451
452/*
453 * Common wsdisplay accessops for DIO frame buffers
454 */
455
456int
457diofb_alloc_screen(void *v, const struct wsscreen_descr *type,
458    void **cookiep, int *curxp, int *curyp, long *attrp)
459{
460	struct diofb *fb = v;
461	struct rasops_info *ri = &fb->ri;
462
463	if (fb->nscreens > 0)
464		return ENOMEM;
465
466	*cookiep = ri;
467	*curxp = *curyp = 0;
468	ri->ri_ops.allocattr(ri, 0, 0, 0, attrp);
469	fb->nscreens++;
470
471	return 0;
472}
473
474void
475diofb_free_screen(void *v, void *cookie)
476{
477	struct diofb *fb = v;
478
479	fb->nscreens--;
480}
481
482int
483diofb_show_screen(void *v, void *cookie, int waitok,
484    void (*cb)(void *, int, int), void *cbarg)
485{
486
487	return 0;
488}
489
490paddr_t
491diofb_mmap(void *v, void *vs, off_t offset, int prot)
492{
493	struct diofb *fb = v;
494
495	if ((offset & PAGE_MASK) != 0)
496		return -1;
497
498	switch (fb->mapmode) {
499	case WSDISPLAYIO_MODE_MAPPED:
500		if (offset >= 0 && offset < DIOFB_REGSPACE)
501			return m68k_btop(fb->regaddr + offset);
502		offset -= DIOFB_REGSPACE;
503		/* FALLTHROUGH */
504	case WSDISPLAYIO_MODE_DUMBFB:
505		if (offset >= 0 && offset < fb->fbsize)
506			return m68k_btop(fb->fbaddr + offset);
507		break;
508	}
509
510	return -1;
511}
512
513int
514diofb_getcmap(struct diofb *fb, struct wsdisplay_cmap *cm)
515{
516	u_int index = cm->index, count = cm->count;
517	u_int colcount = 1 << fb->planes;
518	int error;
519
520	if (index >= colcount || count > colcount - index)
521		return EINVAL;
522
523	if ((error = copyout(fb->cmap.r + index, cm->red, count)) != 0)
524		return error;
525	if ((error = copyout(fb->cmap.g + index, cm->green, count)) != 0)
526		return error;
527	if ((error = copyout(fb->cmap.b + index, cm->blue, count)) != 0)
528		return error;
529
530	return 0;
531}
532