crmfb.c revision 1.7 1 /* $NetBSD: crmfb.c,v 1.7 2007/07/26 02:25:14 macallan Exp $ */
2
3 /*-
4 * Copyright (c) 2007 Jared D. McNeill <jmcneill (at) invisible.ca>
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 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Jared D. McNeill.
18 * 4. Neither the name of The NetBSD Foundation nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 /*
36 * SGI-CRM (O2) Framebuffer driver
37 */
38
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: crmfb.c,v 1.7 2007/07/26 02:25:14 macallan Exp $");
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/device.h>
45 #include <sys/malloc.h>
46
47 #define _SGIMIPS_BUS_DMA_PRIVATE
48 #include <machine/autoconf.h>
49 #include <machine/bus.h>
50 #include <machine/machtype.h>
51 #include <machine/vmparam.h>
52
53 #include <dev/arcbios/arcbios.h>
54 #include <dev/arcbios/arcbiosvar.h>
55
56 #include <dev/wscons/wsdisplayvar.h>
57 #include <dev/wscons/wsconsio.h>
58 #include <dev/wsfont/wsfont.h>
59 #include <dev/rasops/rasops.h>
60 #include <dev/wscons/wsdisplay_vconsvar.h>
61
62 #include <arch/sgimips/dev/crmfbreg.h>
63
64 #define CRMFB_SHADOWFB
65
66 struct wsscreen_descr crmfb_defaultscreen = {
67 "default",
68 0, 0,
69 NULL,
70 8, 16,
71 WSSCREEN_WSCOLORS,
72 NULL,
73 };
74
75 const struct wsscreen_descr *_crmfb_scrlist[] = {
76 &crmfb_defaultscreen,
77 };
78
79 struct wsscreen_list crmfb_screenlist = {
80 sizeof(_crmfb_scrlist) / sizeof(struct wsscreen_descr *),
81 _crmfb_scrlist
82 };
83
84 static struct vcons_screen crmfb_console_screen;
85
86 static int crmfb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
87 static paddr_t crmfb_mmap(void *, void *, off_t, int);
88 static void crmfb_init_screen(void *, struct vcons_screen *, int, long *);
89
90 struct wsdisplay_accessops crmfb_accessops = {
91 crmfb_ioctl,
92 crmfb_mmap,
93 NULL, /* alloc_screen */
94 NULL, /* free_screen */
95 NULL, /* show_screen */
96 NULL, /* load_font */
97 NULL, /* pollc */
98 NULL, /* scroll */
99 };
100
101 /* Memory to allocate to SGI-CRM -- remember, this is stolen from
102 * host memory!
103 */
104 #define CRMFB_TILESIZE (512*128)
105
106 static int crmfb_match(struct device *, struct cfdata *, void *);
107 static void crmfb_attach(struct device *, struct device *, void *);
108 int crmfb_probe(void);
109
110 #define KERNADDR(p) ((void *)((p).addr))
111 #define DMAADDR(p) ((p).map->dm_segs[0].ds_addr)
112
113 struct crmfb_dma {
114 bus_dmamap_t map;
115 void *addr;
116 bus_dma_segment_t segs[1];
117 int nsegs;
118 size_t size;
119 };
120
121 struct crmfb_softc {
122 struct device sc_dev;
123 struct vcons_data sc_vd;
124
125 bus_space_tag_t sc_iot;
126 bus_space_handle_t sc_ioh;
127
128 bus_dma_tag_t sc_dmat;
129
130 struct crmfb_dma sc_dma;
131 struct crmfb_dma sc_dmai;
132
133 int sc_width;
134 int sc_height;
135 int sc_depth;
136 uint32_t sc_fbsize;
137 uint8_t *sc_shadowfb;
138
139 int sc_wsmode;
140
141 /* cursor stuff */
142 int sc_cur_x;
143 int sc_cur_y;
144 int sc_hot_x;
145 int sc_hot_y;
146
147 u_char sc_cmap_red[256];
148 u_char sc_cmap_green[256];
149 u_char sc_cmap_blue[256];
150 };
151
152 static int crmfb_putcmap(struct crmfb_softc *, struct wsdisplay_cmap *);
153 static int crmfb_getcmap(struct crmfb_softc *, struct wsdisplay_cmap *);
154 static void crmfb_set_palette(struct crmfb_softc *,
155 int, uint8_t, uint8_t, uint8_t);
156 static int crmfb_set_curpos(struct crmfb_softc *, int, int);
157 static int crmfb_gcursor(struct crmfb_softc *, struct wsdisplay_cursor *);
158 static int crmfb_scursor(struct crmfb_softc *, struct wsdisplay_cursor *);
159 static inline void crmfb_write_reg(struct crmfb_softc *, int, uint32_t);
160
161 /* setup video hw in given colour depth */
162 static int crmfb_setup_video(struct crmfb_softc *, int);
163 static void crmfb_setup_palette(struct crmfb_softc *);
164
165 CFATTACH_DECL(crmfb, sizeof(struct crmfb_softc),
166 crmfb_match, crmfb_attach, NULL, NULL);
167
168 static int
169 crmfb_match(struct device *parent, struct cfdata *cf, void *opaque)
170 {
171 return crmfb_probe();
172 }
173
174 static void
175 crmfb_attach(struct device *parent, struct device *self, void *opaque)
176 {
177 struct mainbus_attach_args *ma;
178 struct crmfb_softc *sc;
179 struct rasops_info *ri;
180 struct wsemuldisplaydev_attach_args aa;
181 uint32_t d, h;
182 uint16_t *p;
183 unsigned long v;
184 long defattr;
185 const char *consdev;
186 int rv, i, tiles_x, tiles_y;
187
188 sc = (struct crmfb_softc *)self;
189 ma = (struct mainbus_attach_args *)opaque;
190
191 sc->sc_iot = SGIMIPS_BUS_SPACE_CRIME;
192 sc->sc_dmat = &sgimips_default_bus_dma_tag;
193 sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL;
194
195 aprint_normal(": SGI CRIME Graphics Display Engine\n");
196 rv = bus_space_map(sc->sc_iot, ma->ma_addr, 0 /* XXX */,
197 BUS_SPACE_MAP_LINEAR, &sc->sc_ioh);
198 if (rv)
199 panic("crmfb_attach: can't map I/O space");
200
201 /* determine mode configured by firmware */
202 d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_VT_VCMAP);
203 sc->sc_width = (d >> CRMFB_VT_VCMAP_ON_SHIFT) & 0xfff;
204 d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_VT_HCMAP);
205 sc->sc_height = (d >> CRMFB_VT_HCMAP_ON_SHIFT) & 0xfff;
206 d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_TILESIZE);
207 h = (d >> CRMFB_FRM_TILESIZE_DEPTH_SHIFT) & 0x3;
208 if (h == 0)
209 sc->sc_depth = 8;
210 else if (h == 1)
211 sc->sc_depth = 16;
212 else
213 sc->sc_depth = 32;
214
215 if (sc->sc_width == 0 || sc->sc_height == 0) {
216 printf("%s: device unusable if not setup by firmware\n",
217 sc->sc_dev.dv_xname);
218 bus_space_unmap(sc->sc_iot, sc->sc_ioh, 0 /* XXX */);
219 return;
220 }
221
222 printf("%s: initial resolution %dx%d\n",
223 sc->sc_dev.dv_xname, sc->sc_width, sc->sc_height);
224
225 /*
226 * first determine how many tiles we need
227 * in 32bit each tile is 128x128 pixels
228 */
229 tiles_x = (sc->sc_width + 127) >> 7;
230 tiles_y = (sc->sc_height + 127) >> 7;
231 sc->sc_fbsize = 0x10000 * tiles_x * tiles_y;
232 printf("so we need %d x %d tiles -> %08x\n", tiles_x, tiles_y,
233 sc->sc_fbsize);
234
235 sc->sc_dmai.size = 256 * sizeof(uint16_t);
236 rv = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmai.size, 65536, 0,
237 sc->sc_dmai.segs,
238 sizeof(sc->sc_dmai.segs) / sizeof(sc->sc_dmai.segs[0]),
239 &sc->sc_dmai.nsegs, BUS_DMA_NOWAIT);
240 if (rv)
241 panic("crmfb_attach: can't allocate DMA memory");
242 rv = bus_dmamem_map(sc->sc_dmat, sc->sc_dmai.segs, sc->sc_dmai.nsegs,
243 sc->sc_dmai.size, &sc->sc_dmai.addr,
244 BUS_DMA_NOWAIT);
245 if (rv)
246 panic("crmfb_attach: can't map DMA memory");
247 rv = bus_dmamap_create(sc->sc_dmat, sc->sc_dmai.size, 1,
248 sc->sc_dmai.size, 0, BUS_DMA_NOWAIT, &sc->sc_dmai.map);
249 if (rv)
250 panic("crmfb_attach: can't create DMA map");
251 rv = bus_dmamap_load(sc->sc_dmat, sc->sc_dmai.map, sc->sc_dmai.addr,
252 sc->sc_dmai.size, NULL, BUS_DMA_NOWAIT);
253 if (rv)
254 panic("crmfb_attach: can't load DMA map");
255
256 sc->sc_dma.size = sc->sc_fbsize;
257 rv = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dma.size, 65536, 0,
258 sc->sc_dma.segs,
259 sizeof(sc->sc_dma.segs) / sizeof(sc->sc_dma.segs[0]),
260 &sc->sc_dma.nsegs, BUS_DMA_NOWAIT);
261 if (rv)
262 panic("crmfb_attach: can't allocate DMA memory");
263 rv = bus_dmamem_map(sc->sc_dmat, sc->sc_dma.segs, sc->sc_dma.nsegs,
264 sc->sc_dma.size, &sc->sc_dma.addr,
265 BUS_DMA_NOWAIT | BUS_DMA_COHERENT);
266 if (rv)
267 panic("crmfb_attach: can't map DMA memory");
268 rv = bus_dmamap_create(sc->sc_dmat, sc->sc_dma.size, 1,
269 sc->sc_dma.size, 0, BUS_DMA_NOWAIT, &sc->sc_dma.map);
270 if (rv)
271 panic("crmfb_attach: can't create DMA map");
272 rv = bus_dmamap_load(sc->sc_dmat, sc->sc_dma.map, sc->sc_dma.addr,
273 sc->sc_dma.size, NULL, BUS_DMA_NOWAIT);
274 if (rv)
275 panic("crmfb_attach: can't load DMA map");
276
277 p = KERNADDR(sc->sc_dmai);
278 v = (unsigned long)DMAADDR(sc->sc_dma);
279 for (i = 0; i < (sc->sc_fbsize >> 16); i++) {
280 p[i] = ((uint32_t)v >> 16) + i;
281 }
282
283 memset(KERNADDR(sc->sc_dma), 0x00, sc->sc_fbsize);
284
285 printf("%s: allocated %d byte fb @ %p (%p)\n", sc->sc_dev.dv_xname,
286 sc->sc_fbsize, KERNADDR(sc->sc_dmai), KERNADDR(sc->sc_dma));
287
288 #ifdef CRMFB_SHADOWFB
289 /* setup shadow framebuffer */
290 sc->sc_shadowfb = malloc(sc->sc_fbsize, M_DEVBUF, M_NOWAIT | M_ZERO);
291 if (sc->sc_shadowfb == NULL)
292 aprint_error("%s: WARNING: couldn't allocate shadow fb\n",
293 sc->sc_dev.dv_xname);
294 #endif
295
296 ri = &crmfb_console_screen.scr_ri;
297 memset(ri, 0, sizeof(struct rasops_info));
298
299 vcons_init(&sc->sc_vd, sc, &crmfb_defaultscreen, &crmfb_accessops);
300 sc->sc_vd.init_screen = crmfb_init_screen;
301 crmfb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
302 vcons_init_screen(&sc->sc_vd, &crmfb_console_screen, 1, &defattr);
303
304 crmfb_defaultscreen.ncols = ri->ri_cols;
305 crmfb_defaultscreen.nrows = ri->ri_rows;
306 crmfb_defaultscreen.textops = &ri->ri_ops;
307 crmfb_defaultscreen.capabilities = ri->ri_caps;
308 crmfb_defaultscreen.modecookie = NULL;
309
310 crmfb_setup_video(sc, 8);
311 crmfb_setup_palette(sc);
312
313 consdev = ARCBIOS->GetEnvironmentVariable("ConsoleOut");
314 if (consdev != NULL && strcmp(consdev, "video()") == 0) {
315 wsdisplay_cnattach(&crmfb_defaultscreen, ri, 0, 0, defattr);
316 aa.console = 1;
317 } else
318 aa.console = 0;
319 aa.scrdata = &crmfb_screenlist;
320 aa.accessops = &crmfb_accessops;
321 aa.accesscookie = &sc->sc_vd;
322
323 config_found(self, &aa, wsemuldisplaydevprint);
324
325 sc->sc_cur_x = 0;
326 sc->sc_cur_y = 0;
327 sc->sc_hot_x = 0;
328 sc->sc_hot_y = 0;
329
330 return;
331 }
332
333 int
334 crmfb_probe(void)
335 {
336
337 if (mach_type != MACH_SGI_IP32)
338 return 0;
339
340 return 1;
341 }
342
343 static int
344 crmfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
345 {
346 struct vcons_data *vd;
347 struct crmfb_softc *sc;
348 struct vcons_screen *ms;
349 struct wsdisplay_fbinfo *wdf;
350 int nmode;
351
352 vd = (struct vcons_data *)v;
353 sc = (struct crmfb_softc *)vd->cookie;
354 ms = (struct vcons_screen *)vd->active;
355
356 switch (cmd) {
357 case WSDISPLAYIO_GTYPE:
358 /* not really, but who cares? */
359 /* wsfb does */
360 *(u_int *)data = WSDISPLAY_TYPE_CRIME;
361 return 0;
362 case WSDISPLAYIO_GINFO:
363 if (vd->active != NULL) {
364 wdf = (void *)data;
365 wdf->height = sc->sc_height;
366 wdf->width = sc->sc_width;
367 wdf->depth = 32;
368 wdf->cmsize = 256;
369 return 0;
370 } else
371 return ENODEV;
372 case WSDISPLAYIO_GETCMAP:
373 if (sc->sc_depth == 8)
374 return crmfb_getcmap(sc, (struct wsdisplay_cmap *)data);
375 else
376 return EINVAL;
377 case WSDISPLAYIO_PUTCMAP:
378 if (sc->sc_depth == 8)
379 return crmfb_putcmap(sc, (struct wsdisplay_cmap *)data);
380 else
381 return EINVAL;
382 case WSDISPLAYIO_LINEBYTES:
383 *(u_int *)data = sc->sc_width * sc->sc_depth / 8;
384 return 0;
385 case WSDISPLAYIO_SMODE:
386 nmode = *(int *)data;
387 if (nmode != sc->sc_wsmode) {
388 sc->sc_wsmode = nmode;
389 if (nmode == WSDISPLAYIO_MODE_EMUL) {
390 crmfb_setup_video(sc, 8);
391 crmfb_setup_palette(sc);
392 vcons_redraw_screen(vd->active);
393 } else {
394 crmfb_setup_video(sc, 32);
395 }
396 }
397 return 0;
398 case WSDISPLAYIO_SVIDEO:
399 case WSDISPLAYIO_GVIDEO:
400 return ENODEV; /* not supported yet */
401
402 case WSDISPLAYIO_GCURPOS:
403 {
404 struct wsdisplay_curpos *pos;
405
406 pos = (struct wsdisplay_curpos *)data;
407 pos->x = sc->sc_cur_x;
408 pos->y = sc->sc_cur_y;
409 }
410 return 0;
411 case WSDISPLAYIO_SCURPOS:
412 {
413 struct wsdisplay_curpos *pos;
414
415 pos = (struct wsdisplay_curpos *)data;
416 crmfb_set_curpos(sc, pos->x, pos->y);
417 }
418 return 0;
419 case WSDISPLAYIO_GCURMAX:
420 {
421 struct wsdisplay_curpos *pos;
422
423 pos = (struct wsdisplay_curpos *)data;
424 pos->x = 32;
425 pos->y = 32;
426 }
427 return 0;
428 case WSDISPLAYIO_GCURSOR:
429 {
430 struct wsdisplay_cursor *cu;
431
432 cu = (struct wsdisplay_cursor *)data;
433 return crmfb_gcursor(sc, cu);
434 }
435 case WSDISPLAYIO_SCURSOR:
436 {
437 struct wsdisplay_cursor *cu;
438
439 cu = (struct wsdisplay_cursor *)data;
440 return crmfb_scursor(sc, cu);
441 }
442 }
443 return EPASSTHROUGH;
444 }
445
446 static paddr_t
447 crmfb_mmap(void *v, void *vs, off_t offset, int prot)
448 {
449 struct vcons_data *vd;
450 struct crmfb_softc *sc;
451 paddr_t pa;
452
453 vd = (struct vcons_data *)v;
454 sc = (struct crmfb_softc *)vd->cookie;
455
456 #if 1
457 if (offset >= 0 && offset < sc->sc_fbsize) {
458 pa = bus_dmamem_mmap(sc->sc_dmat, sc->sc_dma.segs,
459 sc->sc_dma.nsegs, offset, prot,
460 BUS_DMA_WAITOK | BUS_DMA_COHERENT);
461 return pa;
462 }
463 #else
464 if (offset >= 0 && offset < sc->sc_fbsize) {
465 pa = bus_space_mmap(SGIMIPS_BUS_SPACE_NORMAL, sc->sc_fbh,
466 offset, prot, SGIMIPS_BUS_SPACE_NORMAL);
467 printf("%s: %08llx -> %llx\n", __func__, offset, pa);
468 return pa;
469 }
470 #endif
471 return -1;
472 }
473
474 static void
475 crmfb_init_screen(void *c, struct vcons_screen *scr, int existing,
476 long *defattr)
477 {
478 struct crmfb_softc *sc;
479 struct rasops_info *ri;
480
481 sc = (struct crmfb_softc *)c;
482 ri = &scr->scr_ri;
483
484 ri->ri_flg = RI_CENTER;
485 ri->ri_depth = sc->sc_depth;
486 ri->ri_width = sc->sc_width;
487 ri->ri_height = sc->sc_height;
488 ri->ri_stride = ri->ri_width * (ri->ri_depth / 8);
489
490 switch (ri->ri_depth) {
491 case 16:
492 ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 5;
493 ri->ri_rpos = 10;
494 ri->ri_gpos = 5;
495 ri->ri_bpos = 0;
496 break;
497 case 32:
498 ri->ri_rnum = ri->ri_gnum = ri->ri_bnum = 8;
499 ri->ri_rpos = 8;
500 ri->ri_gpos = 16;
501 ri->ri_bpos = 24;
502 break;
503 }
504
505 if (sc->sc_shadowfb == NULL)
506 ri->ri_bits = KERNADDR(sc->sc_dma);
507 else {
508 ri->ri_bits = sc->sc_shadowfb;
509 ri->ri_hwbits = KERNADDR(sc->sc_dma);
510 }
511
512 if (existing)
513 ri->ri_flg |= RI_CLEAR;
514
515 rasops_init(ri, ri->ri_height / 16, ri->ri_width / 8);
516 ri->ri_caps = WSSCREEN_WSCOLORS;
517 rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
518 ri->ri_width / ri->ri_font->fontwidth);
519 ri->ri_hw = scr;
520
521 return;
522 }
523
524 static int
525 crmfb_putcmap(struct crmfb_softc *sc, struct wsdisplay_cmap *cm)
526 {
527 u_int idx, cnt;
528 u_char r[256], g[256], b[256];
529 u_char *rp, *gp, *bp;
530 int rv, i;
531
532 idx = cm->index;
533 cnt = cm->count;
534
535 if (idx >= 255 || cnt > 256 || idx + cnt > 256)
536 return EINVAL;
537
538 rv = copyin(cm->red, &r[idx], cnt);
539 if (rv)
540 return rv;
541 rv = copyin(cm->green, &g[idx], cnt);
542 if (rv)
543 return rv;
544 rv = copyin(cm->blue, &b[idx], cnt);
545 if (rv)
546 return rv;
547
548 memcpy(&sc->sc_cmap_red[idx], &r[idx], cnt);
549 memcpy(&sc->sc_cmap_green[idx], &g[idx], cnt);
550 memcpy(&sc->sc_cmap_blue[idx], &b[idx], cnt);
551
552 rp = &sc->sc_cmap_red[idx];
553 gp = &sc->sc_cmap_green[idx];
554 bp = &sc->sc_cmap_blue[idx];
555
556 for (i = 0; i < cnt; i++) {
557 crmfb_set_palette(sc, idx, *rp, *gp, *bp);
558 idx++;
559 rp++, gp++, bp++;
560 }
561
562 return 0;
563 }
564
565 static int
566 crmfb_getcmap(struct crmfb_softc *sc, struct wsdisplay_cmap *cm)
567 {
568 u_int idx, cnt;
569 int rv;
570
571 idx = cm->index;
572 cnt = cm->count;
573
574 if (idx >= 255 || cnt > 256 || idx + cnt > 256)
575 return EINVAL;
576
577 rv = copyout(&sc->sc_cmap_red[idx], cm->red, cnt);
578 if (rv)
579 return rv;
580 rv = copyout(&sc->sc_cmap_green[idx], cm->green, cnt);
581 if (rv)
582 return rv;
583 rv = copyout(&sc->sc_cmap_blue[idx], cm->blue, cnt);
584 if (rv)
585 return rv;
586
587 return 0;
588 }
589
590 static void
591 crmfb_set_palette(struct crmfb_softc *sc, int reg, uint8_t r, uint8_t g,
592 uint8_t b)
593 {
594 uint32_t val;
595
596 if (reg > 255 || sc->sc_depth != 8)
597 return;
598
599 while (bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_CMAP_FIFO) >= 63)
600 DELAY(10);
601
602 val = (r << 24) | (g << 16) | (b << 8);
603 crmfb_write_reg(sc, CRMFB_CMAP + (reg * 4), val);
604
605 return;
606 }
607
608 static int
609 crmfb_set_curpos(struct crmfb_softc *sc, int x, int y)
610 {
611 uint32_t val;
612
613 sc->sc_cur_x = x;
614 sc->sc_cur_y = y;
615
616 val = ((x - sc->sc_hot_x) & 0xffff) | ((y - sc->sc_hot_y) << 16);
617 crmfb_write_reg(sc, CRMFB_CURSOR_POS, val);
618
619 return 0;
620 }
621
622 static int
623 crmfb_gcursor(struct crmfb_softc *sc, struct wsdisplay_cursor *cur)
624 {
625 /* do nothing for now */
626 return 0;
627 }
628
629 static int
630 crmfb_scursor(struct crmfb_softc *sc, struct wsdisplay_cursor *cur)
631 {
632 if (cur->which & WSDISPLAY_CURSOR_DOCUR) {
633
634 crmfb_write_reg(sc, CRMFB_CURSOR_CONTROL, cur->enable ? 1 : 0);
635 }
636 if (cur->which & WSDISPLAY_CURSOR_DOHOT) {
637
638 sc->sc_hot_x = cur->hot.x;
639 sc->sc_hot_y = cur->hot.y;
640 }
641 if (cur->which & WSDISPLAY_CURSOR_DOPOS) {
642
643 crmfb_set_curpos(sc, cur->pos.x, cur->pos.y);
644 }
645 if (cur->which & WSDISPLAY_CURSOR_DOCMAP) {
646 int i;
647 uint32_t val;
648
649 for (i = 0; i < cur->cmap.count; i++) {
650 val = (cur->cmap.red[i] << 24) |
651 (cur->cmap.green[i] << 16) |
652 (cur->cmap.blue[i] << 8);
653 crmfb_write_reg(sc, CRMFB_CURSOR_CMAP0 +
654 ((i + cur->cmap.index) << 2), val);
655 }
656 }
657 if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) {
658
659 int i, j, cnt = 0;
660 uint32_t latch = 0, omask;
661 uint8_t imask;
662 for (i = 0; i < 64; i++) {
663 omask = 0x80000000;
664 imask = 0x01;
665 cur->image[cnt] &= cur->mask[cnt];
666 for (j = 0; j < 8; j++) {
667 if (cur->image[cnt] & imask)
668 latch |= omask;
669 omask >>= 1;
670 if (cur->mask[cnt] & imask)
671 latch |= omask;
672 omask >>= 1;
673 imask <<= 1;
674 }
675 cnt++;
676 imask = 0x01;
677 cur->image[cnt] &= cur->mask[cnt];
678 for (j = 0; j < 8; j++) {
679 if (cur->image[cnt] & imask)
680 latch |= omask;
681 omask >>= 1;
682 if (cur->mask[cnt] & imask)
683 latch |= omask;
684 omask >>= 1;
685 imask <<= 1;
686 }
687 cnt++;
688 crmfb_write_reg(sc, CRMFB_CURSOR_BITMAP + (i << 2),
689 latch);
690 latch = 0;
691 }
692 }
693 return 0;
694 }
695
696 static inline void
697 crmfb_write_reg(struct crmfb_softc *sc, int offset, uint32_t val)
698 {
699
700 bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, val);
701 wbflush();
702 }
703
704 static int
705 crmfb_setup_video(struct crmfb_softc *sc, int depth)
706 {
707 uint32_t d, h;
708 int i;
709
710 /* disable DMA */
711 d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_OVR_CONTROL);
712 d &= ~(1 << CRMFB_OVR_CONTROL_DMAEN_SHIFT);
713 crmfb_write_reg(sc, CRMFB_OVR_CONTROL, d);
714 DELAY(50000);
715 d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_CONTROL);
716 d &= ~(1 << CRMFB_FRM_CONTROL_DMAEN_SHIFT);
717 crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
718 DELAY(50000);
719 crmfb_write_reg(sc, CRMFB_DID_CONTROL, 0);
720 DELAY(50000);
721
722 /* ensure that CRM starts drawing at the top left of the screen
723 * when we re-enable DMA later
724 */
725 d = (1 << CRMFB_VT_XY_FREEZE_SHIFT);
726 crmfb_write_reg(sc, CRMFB_VT_XY, d);
727 delay(1000);
728 d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_DOTCLOCK);
729 d &= ~(1 << CRMFB_DOTCLOCK_CLKRUN_SHIFT);
730 crmfb_write_reg(sc, CRMFB_DOTCLOCK, d);
731 delay(1000);
732
733 /* reset FIFO */
734 d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_TILESIZE);
735 d |= (1 << CRMFB_FRM_TILESIZE_FIFOR_SHIFT);
736 crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
737 d &= ~(1 << CRMFB_FRM_TILESIZE_FIFOR_SHIFT);
738 crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
739
740 /* setup colour mode */
741 switch (depth) {
742 case 8:
743 h = CRMFB_MODE_TYP_I8;
744 break;
745 case 16:
746 h = CRMFB_MODE_TYP_ARGB5;
747 break;
748 case 32:
749 h = CRMFB_MODE_TYP_RGB8;
750 break;
751 default:
752 panic("Unsupported depth");
753 }
754 d = h << CRMFB_MODE_TYP_SHIFT;
755 d |= CRMFB_MODE_BUF_BOTH << CRMFB_MODE_BUF_SHIFT;
756 for (i = 0; i < (32 * 4); i += 4)
757 bus_space_write_4(sc->sc_iot, sc->sc_ioh, CRMFB_MODE + i, d);
758 wbflush();
759
760 /* setup tile pointer, but don't turn on DMA yet! */
761 h = DMAADDR(sc->sc_dmai);
762 d = (h >> 9) << CRMFB_FRM_CONTROL_TILEPTR_SHIFT;
763 crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
764
765 /* init framebuffer width and pixel size */
766 d = (1 << CRMFB_FRM_TILESIZE_WIDTH_SHIFT);
767 switch (depth) {
768 case 8:
769 h = CRMFB_FRM_TILESIZE_DEPTH_8;
770 break;
771 case 16:
772 h = CRMFB_FRM_TILESIZE_DEPTH_16;
773 break;
774 case 32:
775 h = CRMFB_FRM_TILESIZE_DEPTH_32;
776 break;
777 default:
778 panic("Unsupported depth");
779 }
780 d |= (h << CRMFB_FRM_TILESIZE_DEPTH_SHIFT);
781 crmfb_write_reg(sc, CRMFB_FRM_TILESIZE, d);
782
783 /* init framebuffer height, we use the trick that the Linux
784 * driver uses to fool the CRM out of tiled mode and into
785 * linear mode
786 */
787 h = sc->sc_width * sc->sc_height / (512 / (depth >> 3));
788 d = h << CRMFB_FRM_PIXSIZE_HEIGHT_SHIFT;
789 crmfb_write_reg(sc, CRMFB_FRM_PIXSIZE, d);
790
791 /* turn off firmware overlay and hardware cursor */
792 crmfb_write_reg(sc, CRMFB_OVR_WIDTH_TILE, 0);
793 crmfb_write_reg(sc, CRMFB_CURSOR_CONTROL, 0);
794
795 /* enable drawing again */
796 d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_DOTCLOCK);
797 d |= (1 << CRMFB_DOTCLOCK_CLKRUN_SHIFT);
798 crmfb_write_reg(sc, CRMFB_DOTCLOCK, d);
799 crmfb_write_reg(sc, CRMFB_VT_XY, 0);
800
801 /* turn on DMA for the framebuffer */
802 d = bus_space_read_4(sc->sc_iot, sc->sc_ioh, CRMFB_FRM_CONTROL);
803 d |= (1 << CRMFB_FRM_CONTROL_DMAEN_SHIFT);
804 crmfb_write_reg(sc, CRMFB_FRM_CONTROL, d);
805
806 sc->sc_depth = depth;
807 return 0;
808 }
809
810 static void
811 crmfb_setup_palette(struct crmfb_softc *sc)
812 {
813 int i;
814
815 for (i = 0; i < 256; i++) {
816 crmfb_set_palette(sc, i, rasops_cmap[(i * 3) + 2],
817 rasops_cmap[(i * 3) + 1], rasops_cmap[(i * 3) + 0]);
818 sc->sc_cmap_red[i] = rasops_cmap[(i * 3) + 2];
819 sc->sc_cmap_green[i] = rasops_cmap[(i * 3) + 1];
820 sc->sc_cmap_blue[i] = rasops_cmap[(i * 3) + 0];
821 }
822 }
823