wcfb.c revision 1.3 1 /* $NetBSD: wcfb.c,v 1.3 2010/03/09 22:45:50 macallan Exp $ */
2
3 /*-
4 * Copyright (c) 2010 Michael Lorenz
5 * All rights reserved.
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: wcfb.c,v 1.3 2010/03/09 22:45:50 macallan Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/device.h>
36 #include <sys/proc.h>
37 #include <sys/mutex.h>
38 #include <sys/ioctl.h>
39 #include <sys/kernel.h>
40 #include <sys/systm.h>
41 #include <sys/kauth.h>
42 #include <sys/kmem.h>
43
44 #include <dev/pci/pcidevs.h>
45 #include <dev/pci/pcireg.h>
46 #include <dev/pci/pcivar.h>
47 #include <dev/pci/pciio.h>
48
49 #include <dev/wscons/wsdisplayvar.h>
50 #include <dev/wscons/wsconsio.h>
51 #include <dev/wsfont/wsfont.h>
52 #include <dev/rasops/rasops.h>
53 #include <dev/wscons/wsdisplay_vconsvar.h>
54
55 #include "opt_wsfb.h"
56 #include "opt_wcfb.h"
57
58 #ifdef WCFB_DEBUG
59 # define DPRINTF printf
60 #else
61 # define DPRINTF while (0) printf
62 #endif
63
64 static int wcfb_match(device_t, cfdata_t, void *);
65 static void wcfb_attach(device_t, device_t, void *);
66 static int wcfb_ioctl(void *, void *, u_long, void *, int,
67 struct lwp *);
68 static paddr_t wcfb_mmap(void *, void *, off_t, int);
69
70 struct wcfb_softc {
71 device_t sc_dev;
72
73 pci_chipset_tag_t sc_pc;
74 pcitag_t sc_pcitag;
75
76 bus_space_tag_t sc_memt;
77 bus_space_tag_t sc_regt, sc_wtft;
78 bus_space_tag_t sc_iot;
79
80 bus_space_handle_t sc_fbh, sc_wtfh;
81 bus_space_handle_t sc_regh;
82 bus_addr_t sc_fb, sc_reg, sc_wtf;
83 bus_size_t sc_fbsize, sc_regsize, sc_wtfsize;
84
85 int sc_width, sc_height, sc_stride;
86 int sc_locked;
87 uint8_t *sc_fbaddr, *sc_fb0, *sc_fb1, *sc_shadow;
88 struct vcons_screen sc_console_screen;
89 struct wsscreen_descr sc_defaultscreen_descr;
90 const struct wsscreen_descr *sc_screens[1];
91 struct wsscreen_list sc_screenlist;
92 struct vcons_data vd;
93 int sc_mode;
94 u_char sc_cmap_red[256];
95 u_char sc_cmap_green[256];
96 u_char sc_cmap_blue[256];
97 uint32_t sc_fb0off;
98
99 void (*copycols)(void *, int, int, int, int);
100 void (*erasecols)(void *, int, int, int, long);
101 void (*copyrows)(void *, int, int, int);
102 void (*eraserows)(void *, int, int, long);
103 void (*putchar)(void *, int, int, u_int, long);
104 void (*cursor)(void *, int, int, int);
105
106 };
107
108 static void wcfb_init_screen(void *, struct vcons_screen *, int, long *);
109
110 CFATTACH_DECL_NEW(wcfb, sizeof(struct wcfb_softc),
111 wcfb_match, wcfb_attach, NULL, NULL);
112
113 struct wsdisplay_accessops wcfb_accessops = {
114 wcfb_ioctl,
115 wcfb_mmap,
116 NULL, /* alloc_screen */
117 NULL, /* free_screen */
118 NULL, /* show_screen */
119 NULL, /* load_font */
120 NULL, /* pollc */
121 NULL /* scroll */
122 };
123
124 static void wcfb_putchar(void *, int, int, u_int, long);
125 static void wcfb_cursor(void *, int, int, int);
126 static void wcfb_copycols(void *, int, int, int, int);
127 static void wcfb_erasecols(void *, int, int, int, long);
128 static void wcfb_copyrows(void *, int, int, int);
129 static void wcfb_eraserows(void *, int, int, long);
130
131 static void wcfb_putpalreg(struct wcfb_softc *, int, int, int, int);
132
133 static int
134 wcfb_match(device_t parent, cfdata_t match, void *aux)
135 {
136 struct pci_attach_args *pa = aux;
137
138 if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_3DLABS &&
139 PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_3DLABS_WILDCAT5110)
140 return 100;
141
142 return 0;
143 }
144
145 static void
146 wcfb_attach(device_t parent, device_t self, void *aux)
147 {
148 struct wcfb_softc *sc = device_private(self);
149 struct pci_attach_args *pa = aux;
150 struct rasops_info *ri;
151 prop_dictionary_t dict;
152 struct wsemuldisplaydev_attach_args aa;
153 int i, j;
154 unsigned long defattr;
155 bool is_console;
156 char devinfo[256];
157 void *wtf;
158
159 sc->sc_dev = self;
160 sc->putchar = NULL;
161 pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo));
162 aprint_naive("\n");
163 aprint_normal(": %s\n", devinfo);
164
165 dict = device_properties(self);
166 prop_dictionary_get_bool(dict, "is_console", &is_console);
167 if(!is_console) return;
168
169 sc->sc_memt = pa->pa_memt;
170 sc->sc_iot = pa->pa_iot;
171 sc->sc_pc = pa->pa_pc;
172 sc->sc_pcitag = pa->pa_tag;
173
174 /* fill in parameters from properties */
175 if (!prop_dictionary_get_uint32(dict, "width", &sc->sc_width)) {
176 aprint_error("%s: no width property\n", device_xname(self));
177 return;
178 }
179 if (!prop_dictionary_get_uint32(dict, "height", &sc->sc_height)) {
180 aprint_error("%s: no height property\n", device_xname(self));
181 return;
182 }
183
184 if (pci_mapreg_map(pa, 0x14, PCI_MAPREG_TYPE_MEM, 0,
185 &sc->sc_regt, &sc->sc_regh, &sc->sc_reg, &sc->sc_regsize)) {
186 aprint_error("%s: failed to map registers.\n",
187 device_xname(sc->sc_dev));
188 }
189
190 if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_MEM, BUS_SPACE_MAP_LINEAR,
191 &sc->sc_memt, &sc->sc_fbh, &sc->sc_fb, &sc->sc_fbsize)) {
192 aprint_error("%s: failed to map framebuffer.\n",
193 device_xname(sc->sc_dev));
194 }
195
196 if (pci_mapreg_map(pa, 0x18, PCI_MAPREG_TYPE_MEM, BUS_SPACE_MAP_LINEAR,
197 &sc->sc_wtft, &sc->sc_wtfh, &sc->sc_wtf, &sc->sc_wtfsize)) {
198 aprint_error("%s: failed to map wtf.\n",
199 device_xname(sc->sc_dev));
200 }
201 wtf = bus_space_vaddr(sc->sc_wtft, sc->sc_wtfh);
202 memset(wtf, 0, 0x100000);
203
204 sc->sc_fbaddr = bus_space_vaddr(sc->sc_memt, sc->sc_fbh);
205
206 sc->sc_fb0off = bus_space_read_4(sc->sc_regt, sc->sc_regh, 0x8080) -
207 sc->sc_fb;
208 sc->sc_fb0 = sc->sc_fbaddr + sc->sc_fb0off;
209 sc->sc_fb1 = sc->sc_fb0 + 0x200000;
210
211 sc->sc_stride = 1 <<
212 ((bus_space_read_4(sc->sc_regt, sc->sc_regh, 0x8074) & 0x00ff0000) >> 16);
213 printf("%s: %d x %d, %d\n", device_xname(sc->sc_dev),
214 sc->sc_width, sc->sc_height, sc->sc_stride);
215
216 sc->sc_shadow = kmem_alloc(sc->sc_stride * sc->sc_height, KM_SLEEP);
217 if (sc->sc_shadow == NULL) {
218 printf("%s: failed to allocate shadow buffer\n",
219 device_xname(self));
220 return;
221 }
222
223 for (i = 0x40; i < 0x100; i += 16) {
224 printf("%04x:", i);
225 for (j = 0; j < 16; j += 4) {
226 printf(" %08x", bus_space_read_4(sc->sc_regt,
227 sc->sc_regh, 0x8000 + i + j));
228 }
229 printf("\n");
230 }
231
232 sc->sc_defaultscreen_descr = (struct wsscreen_descr){
233 "default",
234 0, 0,
235 NULL,
236 8, 16,
237 WSSCREEN_WSCOLORS | WSSCREEN_HILIT,
238 NULL
239 };
240 sc->sc_screens[0] = &sc->sc_defaultscreen_descr;
241 sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens};
242 sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
243 sc->sc_locked = 0;
244
245 vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr,
246 &wcfb_accessops);
247 sc->vd.init_screen = wcfb_init_screen;
248
249 /* init engine here */
250 #if 0
251 wcfb_init(sc);
252 #endif
253
254 ri = &sc->sc_console_screen.scr_ri;
255
256 j = 0;
257 for (i = 0; i < 256; i++) {
258
259 sc->sc_cmap_red[i] = rasops_cmap[j];
260 sc->sc_cmap_green[i] = rasops_cmap[j + 1];
261 sc->sc_cmap_blue[i] = rasops_cmap[j + 2];
262 wcfb_putpalreg(sc, i, rasops_cmap[j], rasops_cmap[j + 1],
263 rasops_cmap[j + 2]);
264 j += 3;
265 }
266 wcfb_putpalreg(sc, 0, 0, 0, 0);
267 wcfb_putpalreg(sc, 15, 0xff, 0xff, 0xff);
268
269 if (is_console) {
270 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1,
271 &defattr);
272 sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
273
274 #if 0
275 wcfb_rectfill(sc, 0, 0, sc->sc_width, sc->sc_height,
276 ri->ri_devcmap[(defattr >> 16) & 0xff]);
277 #else
278 /*memset(sc->sc_fbaddr + sc->sc_fb0off, 0, 0x400000);*/
279 #endif
280 sc->sc_defaultscreen_descr.textops = &ri->ri_ops;
281 sc->sc_defaultscreen_descr.capabilities = ri->ri_caps;
282 sc->sc_defaultscreen_descr.nrows = ri->ri_rows;
283 sc->sc_defaultscreen_descr.ncols = ri->ri_cols;
284 wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0,
285 defattr);
286 vcons_replay_msgbuf(&sc->sc_console_screen);
287 } else {
288 /*
289 * since we're not the console we can postpone the rest
290 * until someone actually allocates a screen for us
291 */
292 (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
293 }
294
295 aa.console = is_console;
296 aa.scrdata = &sc->sc_screenlist;
297 aa.accessops = &wcfb_accessops;
298 aa.accesscookie = &sc->vd;
299
300 config_found(sc->sc_dev, &aa, wsemuldisplaydevprint);
301 }
302
303 static int
304 wcfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
305 struct lwp *l)
306 {
307 struct wcfb_softc *sc = v;
308
309 switch (cmd) {
310 case WSDISPLAYIO_GTYPE:
311 *(u_int *)data = WSDISPLAY_TYPE_PCIMISC;
312 return 0;
313
314 /* PCI config read/write passthrough. */
315 case PCI_IOC_CFGREAD:
316 case PCI_IOC_CFGWRITE:
317 return (pci_devioctl(sc->sc_pc, sc->sc_pcitag,
318 cmd, data, flag, l));
319 case WSDISPLAYIO_SMODE:
320 {
321 /*int new_mode = *(int*)data, i;*/
322 }
323 return 0;
324 }
325
326 return EPASSTHROUGH;
327 }
328
329 static paddr_t
330 wcfb_mmap(void *v, void *vs, off_t offset, int prot)
331 {
332 struct wcfb_softc *sc = v;
333
334 /* no point in allowing a wsfb map if we can't provide one */
335 /*
336 * restrict all other mappings to processes with superuser privileges
337 * or the kernel itself
338 */
339 if (kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER,
340 NULL) != 0) {
341 aprint_normal_dev(sc->sc_dev, "mmap() rejected.\n");
342 return -1;
343 }
344
345 #ifdef WSFB_FAKE_VGA_FB
346 if ((offset >= 0xa0000) && (offset < 0xbffff)) {
347
348 return bus_space_mmap(sc->sc_memt, sc->sc_gen.sc_fboffset,
349 offset - 0xa0000, prot, BUS_SPACE_MAP_LINEAR);
350 }
351 #endif
352
353 /*
354 * XXX this should be generalized, let's just
355 * #define PCI_IOAREA_PADDR
356 * #define PCI_IOAREA_OFFSET
357 * #define PCI_IOAREA_SIZE
358 * somewhere in a MD header and compile this code only if all are
359 * present
360 */
361 /*
362 * PCI_IOAREA_PADDR is useless, that's what the IO tag is for
363 * the address isn't guaranteed to be the same on each host bridge
364 * either, never mind the fact that it would be a bus address
365 */
366 #ifdef PCI_MAGIC_IO_RANGE
367 /* allow to map our IO space */
368 if ((offset >= PCI_MAGIC_IO_RANGE) &&
369 (offset < PCI_MAGIC_IO_RANGE + 0x10000)) {
370 return bus_space_mmap(sc->sc_iot, offset - PCI_MAGIC_IO_RANGE,
371 0, prot, BUS_SPACE_MAP_LINEAR);
372 }
373 #endif
374 return -1;
375 }
376
377 static void
378 wcfb_init_screen(void *cookie, struct vcons_screen *scr,
379 int existing, long *defattr)
380 {
381 struct wcfb_softc *sc = cookie;
382 struct rasops_info *ri = &scr->scr_ri;
383
384 ri->ri_depth = 8;
385 ri->ri_width = sc->sc_width;
386 ri->ri_height = sc->sc_height;
387 ri->ri_stride = sc->sc_stride;
388 ri->ri_flg = RI_CENTER /*| RI_FULLCLEAR*/;
389
390 ri->ri_bits = sc->sc_shadow;
391
392 if (existing) {
393 ri->ri_flg |= RI_CLEAR;
394 }
395
396 rasops_init(ri, sc->sc_height / 8, sc->sc_width / 8);
397 ri->ri_caps = WSSCREEN_WSCOLORS;
398
399 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
400 sc->sc_width / ri->ri_font->fontwidth);
401
402 ri->ri_hw = scr;
403 sc->putchar = ri->ri_ops.putchar;
404 sc->copyrows = ri->ri_ops.copyrows;
405 sc->eraserows = ri->ri_ops.eraserows;
406 sc->copycols = ri->ri_ops.copycols;
407 sc->erasecols = ri->ri_ops.erasecols;
408
409 ri->ri_ops.copyrows = wcfb_copyrows;
410 ri->ri_ops.copycols = wcfb_copycols;
411 ri->ri_ops.eraserows = wcfb_eraserows;
412 ri->ri_ops.erasecols = wcfb_erasecols;
413 ri->ri_ops.putchar = wcfb_putchar;
414 ri->ri_ops.cursor = wcfb_cursor;
415 }
416
417 static void
418 wcfb_putchar(void *cookie, int row, int col, u_int c, long attr)
419 {
420 struct rasops_info *ri = cookie;
421 struct vcons_screen *scr = ri->ri_hw;
422 struct wcfb_softc *sc = scr->scr_cookie;
423 int offset = (ri->ri_yorigin + row * ri->ri_font->fontheight) *
424 sc->sc_stride + ri->ri_xorigin + col * ri->ri_font->fontwidth;
425 uint8_t *from, *to0, *to1;
426 int i;
427
428 sc->putchar(ri, row, col, c, attr);
429 from = sc->sc_shadow + offset;
430 to0 = sc->sc_fb0 + offset;
431 to1 = sc->sc_fb1 + offset;
432 for (i = 0; i < ri->ri_font->fontheight; i++) {
433 memcpy(to0, from, ri->ri_font->fontwidth);
434 memcpy(to1, from, ri->ri_font->fontwidth);
435 to0 += sc->sc_stride;
436 to1 += sc->sc_stride;
437 from += sc->sc_stride;
438 }
439 }
440
441 static void
442 wcfb_putpalreg(struct wcfb_softc *sc, int i, int r, int g, int b)
443 {
444 uint32_t rgb;
445
446 bus_space_write_4(sc->sc_regt, sc->sc_regh, 0x80bc, i);
447 rgb = (b << 22) | (g << 12) | (r << 2);
448 bus_space_write_4(sc->sc_regt, sc->sc_regh, 0x80c0, rgb);
449 }
450
451 static void
452 wcfb_cursor(void *cookie, int on, int row, int col)
453 {
454 #if 0
455 struct rasops_info *ri = cookie;
456 struct vcons_screen *scr = ri->ri_hw;
457 struct wcfb_softc *sc = scr->scr_cookie;
458 #endif
459 }
460
461 static void
462 wcfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
463 {
464 struct rasops_info *ri = cookie;
465 struct vcons_screen *scr = ri->ri_hw;
466 struct wcfb_softc *sc = scr->scr_cookie;
467 int offset = (ri->ri_yorigin + row * ri->ri_font->fontheight) *
468 sc->sc_stride + ri->ri_xorigin + dstcol * ri->ri_font->fontwidth;
469 uint8_t *from, *to0, *to1;
470 int i;
471
472 sc->copycols(ri, row, srccol, dstcol, ncols);
473 from = sc->sc_shadow + offset;
474 to0 = sc->sc_fb0 + offset;
475 to1 = sc->sc_fb1 + offset;
476 for (i = 0; i < ri->ri_font->fontheight; i++) {
477 memcpy(to0, from, ri->ri_font->fontwidth * ncols);
478 memcpy(to1, from, ri->ri_font->fontwidth * ncols);
479 to0 += sc->sc_stride;
480 to1 += sc->sc_stride;
481 from += sc->sc_stride;
482 }
483 }
484
485 static void
486 wcfb_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
487 {
488 struct rasops_info *ri = cookie;
489 struct vcons_screen *scr = ri->ri_hw;
490 struct wcfb_softc *sc = scr->scr_cookie;
491 int offset = (ri->ri_yorigin + row * ri->ri_font->fontheight) *
492 sc->sc_stride + ri->ri_xorigin + startcol * ri->ri_font->fontwidth;
493 uint8_t *to0, *to1;
494 int i;
495
496 sc->erasecols(ri, row, startcol, ncols, fillattr);
497
498 to0 = sc->sc_fb0 + offset;
499 to1 = sc->sc_fb1 + offset;
500 for (i = 0; i < ri->ri_font->fontheight; i++) {
501 memset(to0, ri->ri_devcmap[(fillattr >> 16) & 0xff],
502 ri->ri_font->fontwidth * ncols);
503 memset(to1, ri->ri_devcmap[(fillattr >> 16) & 0xff],
504 ri->ri_font->fontwidth * ncols);
505 to0 += sc->sc_stride;
506 to1 += sc->sc_stride;
507 }
508 }
509
510 static void
511 wcfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
512 {
513 struct rasops_info *ri = cookie;
514 struct vcons_screen *scr = ri->ri_hw;
515 struct wcfb_softc *sc = scr->scr_cookie;
516 int offset = (ri->ri_yorigin + dstrow * ri->ri_font->fontheight) *
517 sc->sc_stride + ri->ri_xorigin;
518 uint8_t *from, *to0, *to1;
519 int i;
520
521 sc->copyrows(ri, srcrow, dstrow, nrows);
522
523 from = sc->sc_shadow + offset;
524 to0 = sc->sc_fb0 + offset;
525 to1 = sc->sc_fb1 + offset;
526 for (i = 0; i < ri->ri_font->fontheight * nrows; i++) {
527 memcpy(to0, from, ri->ri_emuwidth);
528 memcpy(to1, from, ri->ri_emuwidth);
529 to0 += sc->sc_stride;
530 to1 += sc->sc_stride;
531 from += sc->sc_stride;
532 }
533 }
534
535 static void
536 wcfb_eraserows(void *cookie, int row, int nrows, long fillattr)
537 {
538 struct rasops_info *ri = cookie;
539 struct vcons_screen *scr = ri->ri_hw;
540 struct wcfb_softc *sc = scr->scr_cookie;
541 int offset = (ri->ri_yorigin + row * ri->ri_font->fontheight) *
542 sc->sc_stride + ri->ri_xorigin;
543 uint8_t *to0, *to1;
544 int i;
545
546 sc->eraserows(ri, row, nrows, fillattr);
547
548 to0 = sc->sc_fb0 + offset;
549 to1 = sc->sc_fb1 + offset;
550 for (i = 0; i < ri->ri_font->fontheight * nrows; i++) {
551 memset(to0, ri->ri_devcmap[(fillattr >> 16) & 0xff],
552 ri->ri_emuwidth);
553 memset(to1, ri->ri_devcmap[(fillattr >> 16) & 0xff],
554 ri->ri_emuwidth);
555 to0 += sc->sc_stride;
556 to1 += sc->sc_stride;
557 }
558 }
559