1/* $NetBSD: vidcvideo.c,v 1.50 2022/09/27 06:36:42 skrll Exp $ */
2
3/*
4 * Copyright (c) 2001 Reinoud Zandijk
5 * Copyright (c) 1998, 1999 Tohru Nishimura.  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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * Created vidcvideo.c from /dev/tc/cfb.c to fit the Acorn/ARM VIDC1 and VIDC20 chips
28 *
29 */
30
31#include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
32
33__KERNEL_RCSID(0, "$NetBSD: vidcvideo.c,v 1.50 2022/09/27 06:36:42 skrll Exp $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/device.h>
39#include <sys/buf.h>
40#include <sys/ioctl.h>
41
42#include <arm/mainbus/mainbus.h>
43#include <sys/bus.h>
44#include <machine/intr.h>
45
46#include <dev/wscons/wsconsio.h>
47#include <dev/wscons/wsdisplayvar.h>
48
49#include <dev/rasops/rasops.h>
50#include <dev/wsfont/wsfont.h>
51
52#include <dev/wscons/wsdisplay_vconsvar.h>
53
54#include <uvm/uvm_extern.h>
55#include <arm/arm32/pmap.h>
56#include <arm/cpufunc.h>
57
58/* for vidc_mode ... needs to be MI independent one day */
59#include <arm/iomd/vidc.h>
60#include <arm/iomd/vidc20config.h>
61#include <arm/iomd/vidcvideo.h>
62#include <machine/bootconfig.h>
63
64/* FOR DEBUG */
65extern videomemory_t videomemory;
66
67struct hwcmap256 {
68#define	CMAP_SIZE	256	/* 256 R/G/B entries */
69	uint8_t r[CMAP_SIZE];
70	uint8_t g[CMAP_SIZE];
71	uint8_t b[CMAP_SIZE];
72};
73
74
75/* XXX for CURSOR_MAX_WIDTH = 32 */
76struct hwcursor32 {
77	struct wsdisplay_curpos cc_pos;
78	struct wsdisplay_curpos cc_hot;
79	struct wsdisplay_curpos cc_size;
80	uint8_t cc_color[6];		/* how many? */
81	uint32_t cc_image[(CURSOR_MAX_WIDTH/4) * CURSOR_MAX_HEIGHT];
82	uint32_t cc_mask[(CURSOR_MAX_WIDTH/4) * CURSOR_MAX_HEIGHT];
83};
84
85
86struct fb_devconfig {
87	vaddr_t dc_vaddr;		/* memory space virtual base address */
88	paddr_t dc_paddr;		/* memory space physical base address */
89	vsize_t dc_size;		/* size of slot memory */
90	int	dc_width;		/* width of frame buffer */
91	int	dc_height;		/* height of frame buffer */
92	int	dc_log2_depth;		/* log2 of bits per pixel */
93	int	dc_depth;		/* depth, bits per pixel */
94	int	dc_rowbytes;		/* bytes in a FB scan line */
95	vaddr_t	dc_videobase;		/* base of flat frame buffer */
96	int	dc_blanked;		/* currently has video disabled */
97	void   *dc_hwscroll_cookie;	/* cookie for hardware scroll */
98	void   *dc_ih;			/* interrupt handler for dc */
99
100	int	dc_curenb;		/* is cursor sprite enabled ?	*/
101	int	_internal_dc_changed;	/* need update of hardware	*/
102	int	dc_writeback_delay;	/* Screenarea write back vsync counter */
103#define	WSDISPLAY_CMAP_DOLUT	0x20
104#define WSDISPLAY_VIDEO_ONOFF	0x40
105#define WSDISPLAY_WB_COUNTER	0x80
106
107	struct hwcmap256	dc_cmap;   /* software copy of colormap	*/
108	struct hwcursor32	dc_cursor; /* software copy of cursor	*/
109
110	struct vidc_mode	mode_info;
111
112	struct wsdisplay_emulops orig_ri_ops;	/* Rasops functions for deligation */
113
114	/* virtual console support */
115	struct vcons_data dc_vd;
116	struct vcons_screen dc_console;
117};
118
119
120struct vidcvideo_softc {
121	device_t sc_dev;
122	struct fb_devconfig *sc_dc;	/* device configuration		*/
123};
124
125
126/* Function prototypes for glue */
127static int  vidcvideo_match(device_t , cfdata_t , void *);
128static void vidcvideo_attach(device_t , device_t , void *);
129
130
131/* config glue */
132CFATTACH_DECL_NEW(vidcvideo, sizeof(struct vidcvideo_softc),
133    vidcvideo_match, vidcvideo_attach, NULL, NULL);
134
135static struct fb_devconfig vidcvideo_console_dc;
136static bool vidcvideo_is_console = false;
137
138
139static struct wsscreen_descr vidcvideo_stdscreen = {
140	"std", 0, 0,
141	NULL, /* textops */
142	8, 16,
143	WSSCREEN_WSCOLORS | WSSCREEN_HILIT,
144	NULL
145};
146
147static const struct wsscreen_descr *_vidcvideo_scrlist[] = {
148	&vidcvideo_stdscreen,
149};
150
151static const struct wsscreen_list vidcvideo_screenlist = {
152	sizeof(_vidcvideo_scrlist) / sizeof(struct wsscreen_descr *),
153	_vidcvideo_scrlist
154};
155
156static int	vidcvideoioctl(void *, void *, u_long, void *, int,
157    struct lwp *);
158static paddr_t	vidcvideommap(void *, void *, off_t, int);
159static void	vidcvideoinit_screen(void *, struct vcons_screen *, int, long *);
160
161static void	vidcvideo_queue_dc_change(struct fb_devconfig*, int);
162static int	flush_dc_changes_to_screen(struct fb_devconfig*);
163
164static struct wsdisplay_accessops vidcvideo_accessops = {
165	vidcvideoioctl,
166	vidcvideommap,
167	NULL,	/* alloc_screen */
168	NULL,	/* free_screen */
169	NULL,	/* show_screen */
170	NULL,	/* load_font */
171	NULL,	/* pollc */
172	NULL	/* scroll */
173};
174
175
176/* Function prototypes */
177int         vidcvideo_cnattach(vaddr_t);
178static void vidcvideo_colourmap_and_cursor_init(struct fb_devconfig *);
179
180static int  get_cmap(struct vidcvideo_softc *, struct wsdisplay_cmap *);
181static int  set_cmap(struct vidcvideo_softc *, struct wsdisplay_cmap *);
182static int  set_cursor(struct vidcvideo_softc *, struct wsdisplay_cursor *);
183static int  get_cursor(struct vidcvideo_softc *, struct wsdisplay_cursor *);
184static void set_curpos(struct vidcvideo_softc *, struct wsdisplay_curpos *);
185static void vidcvideo_getdevconfig(vaddr_t, u_int, struct fb_devconfig *);
186
187static int  vidcvideointr(void *);
188
189/* Acceleration function prototypes */
190static void vv_copyrows(void *, int, int, int);
191static void vv_eraserows(void *, int, int, long);
192static void vv_putchar(void *c, int row, int col, u_int uc, long attr);
193
194
195static int
196vidcvideo_match(device_t parent, cfdata_t match, void *aux)
197{
198
199	/* Can't probe AFAIK ; how ? */
200	return 1;
201}
202
203
204static void
205vidcvideo_getdevconfig(vaddr_t dense_addr, u_int mem_size,
206		struct fb_devconfig *dc)
207{
208
209	dc->dc_vaddr = dense_addr;
210	(void) pmap_extract(pmap_kernel(), dc->dc_vaddr, &(dc->dc_paddr));
211
212	vidcvideo_getmode(&dc->mode_info);
213
214	dc->dc_width = dc->mode_info.timings.hdisplay;
215	dc->dc_height = dc->mode_info.timings.vdisplay;
216	dc->dc_log2_depth = dc->mode_info.log2_bpp;
217	dc->dc_depth = 1 << dc->dc_log2_depth;
218	dc->dc_videobase = dc->dc_vaddr;
219	dc->dc_blanked = 0;
220
221	/* this should/could be done somewhat more elegant! */
222	switch (dc->dc_depth) {
223		case 1:
224			dc->dc_rowbytes = dc->dc_width / 8;
225			break;
226		case 2:
227			dc->dc_rowbytes = dc->dc_width / 4;
228			break;
229		case 4:
230			dc->dc_rowbytes = dc->dc_width / 2;
231			break;
232		case 8:
233			dc->dc_rowbytes = dc->dc_width;
234			break;
235		case 16:
236			dc->dc_rowbytes = dc->dc_width * 2;
237			break;
238		case 32:
239			dc->dc_rowbytes = dc->dc_width * 4;
240			break;
241		default:
242			printf("Unknown colour depth %d ... what to do ?", dc->dc_depth);
243			break;
244	}
245
246	/* setup the correct size */
247	dc->dc_size = mem_size;
248
249	/* initialize colormap and cursor resource */
250	vidcvideo_colourmap_and_cursor_init(dc);
251
252	/* blank the memory */
253	memset((void*)dc->dc_vaddr, 0, dc->dc_size);
254
255	/* intitialise miscelanious */
256	dc->dc_writeback_delay = 0;
257}
258
259static void
260vidcvideoinit_screen(void *cookie, struct vcons_screen *scr,
261		    int existing, long *defattr)
262{
263	struct rasops_info *ri = &scr->scr_ri;
264	struct fb_devconfig *dc = cookie;
265
266	if ((scr == &dc->dc_console) && (dc->dc_vd.active != NULL))
267		return;
268
269	ri->ri_flg    = RI_NO_AUTO;	/* RI_CENTER | RI_FULLCLEAR; */
270	ri->ri_depth  = dc->dc_depth;
271	ri->ri_bits   = (void *) dc->dc_videobase;
272	ri->ri_width  = dc->dc_width;
273	ri->ri_height = dc->dc_height;
274	ri->ri_stride = dc->dc_rowbytes;
275	ri->ri_hw     = &dc->dc_console;	/* link back */
276
277	rasops_init(ri,
278	    ri->ri_height / 8,
279	    ri->ri_width / 8);
280
281	ri->ri_caps = WSSCREEN_WSCOLORS;
282
283	rasops_reconfig(ri,
284	    ri->ri_height / ri->ri_font->fontheight,
285	    ri->ri_width / ri->ri_font->fontwidth);
286
287	/*
288	 * Provide a hook for the acceleration functions and make a copy of the
289	 * original rasops functions for passing on calls
290	 */
291	memcpy(&(dc->orig_ri_ops), &(ri->ri_ops),
292	    sizeof(struct wsdisplay_emulops));
293
294	/* add our accelerated functions */
295	ri->ri_ops.eraserows = vv_eraserows;
296	ri->ri_ops.copyrows  = vv_copyrows;
297
298	/* add the extra activity measuring functions; they just delegate on */
299	ri->ri_ops.putchar   = vv_putchar;
300
301	vidcvideo_stdscreen.nrows = ri->ri_rows;
302	vidcvideo_stdscreen.ncols = ri->ri_cols;
303	vidcvideo_stdscreen.textops = &ri->ri_ops;
304	vidcvideo_stdscreen.capabilities = ri->ri_caps;
305}
306
307static void
308vidcvideo_attach(device_t parent, device_t self, void *aux)
309{
310	struct vidcvideo_softc *sc = device_private(self);
311	struct fb_devconfig *dc;
312	struct wsemuldisplaydev_attach_args waa;
313	long defattr;
314
315	sc->sc_dev = self;
316
317	dc = sc->sc_dc = &vidcvideo_console_dc;
318
319	/*
320	 * for reasons which are crazy we init vidcvideo twice,
321	 * the second time sets up the cursor
322	 */
323	vidcvideo_init();
324	if (!vidcvideo_is_console) {
325		vidcvideo_getdevconfig(videomemory.vidm_vbase,
326				videomemory.vidm_size,
327			       	sc->sc_dc);
328	}
329
330	vcons_init(&dc->dc_vd, dc, &vidcvideo_stdscreen, &vidcvideo_accessops);
331	dc->dc_vd.init_screen = vidcvideoinit_screen;
332
333	vcons_init_screen(&dc->dc_vd, &dc->dc_console, 1, &defattr);
334
335	dc->dc_console.scr_flags |= VCONS_SCREEN_IS_STATIC;
336
337	vidcvideo_printdetails();
338	aprint_normal(": mode %s, %dbpp\n", dc->mode_info.timings.name,
339	    dc->dc_depth);
340
341	/* set up interrupt flags */
342	vidcvideo_queue_dc_change(dc, WSDISPLAY_CMAP_DOLUT);
343
344	/* Establish an interrupt handler, and clear any pending interrupts */
345	dc->dc_ih = intr_claim(IRQ_FLYBACK, IPL_TTY, "vblank", vidcvideointr, dc);
346
347	waa.console = (vidcvideo_is_console ? 1 : 0);
348	waa.scrdata = &vidcvideo_screenlist;
349	waa.accessops = &vidcvideo_accessops;
350	waa.accesscookie = &dc->dc_vd;
351
352	config_found(self, &waa, wsemuldisplaydevprint, CFARGS_NONE);
353}
354
355
356static int
357vidcvideoioctl(void *v, void *vs, u_long cmd, void *data, int flag,
358    struct lwp *l)
359{
360	struct vcons_data *vd = v;
361	struct vidcvideo_softc *sc = vd->cookie;
362	struct fb_devconfig *dc = sc->sc_dc;
363	struct vcons_screen *ms = vd->active;
364	int state;
365
366	switch (cmd) {
367	case WSDISPLAYIO_GTYPE:
368		*(u_int *)data = WSDISPLAY_TYPE_VIDC;
369		return 0;
370
371	case WSDISPLAYIO_GINFO:
372		if (ms == NULL)
373			return ENODEV;
374#define	wsd_fbip ((struct wsdisplay_fbinfo *)data)
375		wsd_fbip->height = dc->dc_height;
376		wsd_fbip->width  = dc->dc_width;
377		wsd_fbip->depth  = dc->dc_depth;
378		wsd_fbip->cmsize = CMAP_SIZE;
379#undef fbt
380		return 0;
381
382	case WSDISPLAYIO_GETCMAP:
383		return get_cmap(sc, (struct wsdisplay_cmap *)data);
384
385	case WSDISPLAYIO_PUTCMAP:
386		return set_cmap(sc, (struct wsdisplay_cmap *)data);
387
388	case WSDISPLAYIO_LINEBYTES:
389		*(u_int *)data = dc->dc_rowbytes;
390		return 0;
391
392	case WSDISPLAYIO_SVIDEO:
393		state = *(int *)data;
394		dc->dc_blanked = (state == WSDISPLAYIO_VIDEO_OFF);
395		vidcvideo_queue_dc_change(dc, WSDISPLAY_VIDEO_ONOFF);
396		/* done on video blank */
397		return 0;
398
399	case WSDISPLAYIO_GVIDEO:
400		*(u_int *)data = dc->dc_blanked ?
401		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
402		return 0;
403
404	case WSDISPLAYIO_GCURPOS:
405		*(struct wsdisplay_curpos *)data = dc->dc_cursor.cc_pos;
406		return 0;
407
408	case WSDISPLAYIO_SCURPOS:
409		set_curpos(sc, (struct wsdisplay_curpos *)data);
410		vidcvideo_queue_dc_change(dc, WSDISPLAY_CURSOR_DOPOS);
411		return 0;
412
413	case WSDISPLAYIO_GCURMAX:
414		((struct wsdisplay_curpos *)data)->x = CURSOR_MAX_WIDTH;
415		((struct wsdisplay_curpos *)data)->y = CURSOR_MAX_HEIGHT;
416		return 0;
417
418	case WSDISPLAYIO_GCURSOR:
419		return get_cursor(sc, (struct wsdisplay_cursor *)data);
420
421	case WSDISPLAYIO_SCURSOR:
422		return set_cursor(sc, (struct wsdisplay_cursor *)data);
423
424	case WSDISPLAYIO_SMODE:
425		state = *(int *)data;
426		if (state == WSDISPLAYIO_MODE_MAPPED)
427			dc->dc_hwscroll_cookie = vidcvideo_hwscroll_reset();
428		if (state == WSDISPLAYIO_MODE_EMUL)
429			vidcvideo_hwscroll_back(dc->dc_hwscroll_cookie);
430		vidcvideo_progr_scroll();
431
432		return 0;
433	}
434	return EPASSTHROUGH;
435}
436
437
438paddr_t
439vidcvideommap(void *v, void *vs, off_t offset, int prot)
440{
441	struct vcons_data *vd = v;
442	struct vidcvideo_softc *sc = vd->cookie;
443
444	if (offset >= sc->sc_dc->dc_size || offset < 0)
445		return -1;
446
447	return arm_btop(sc->sc_dc->dc_paddr + offset);
448}
449
450
451/* EXPORT */ int
452vidcvideo_cnattach(vaddr_t addr)
453{
454	struct fb_devconfig *dc = &vidcvideo_console_dc;
455	struct rasops_info *ri;
456	long defattr;
457
458	vidcvideo_init();
459
460	/* fetch current framebuffer config */
461	vidcvideo_getdevconfig(videomemory.vidm_vbase,
462			videomemory.vidm_size,
463		       	dc);
464
465	dc->dc_vd.active = NULL;
466	vidcvideoinit_screen(dc, &dc->dc_console, 1, &defattr);
467
468	ri = &(dc->dc_console.scr_ri);
469	ri->ri_hw = &dc->dc_console;
470	dc->dc_console.scr_cookie = dc;
471
472	(*ri->ri_ops.allocattr)(ri,
473				WS_DEFAULT_FG, /* fg */
474				WS_DEFAULT_BG, /* bg */
475				0,           /* wsattrs */
476				&defattr);
477
478	wsdisplay_cnattach(&vidcvideo_stdscreen,
479			   ri,   /* emulcookie */
480		       	   0, 0, /* cursor position */
481			   defattr);
482
483	vidcvideo_is_console = true;
484
485	return 0;
486}
487
488
489static int
490vidcvideointr(void *arg)
491{
492	struct fb_devconfig *dc = arg;
493
494	return flush_dc_changes_to_screen(dc);
495}
496
497static int
498flush_dc_changes_to_screen(struct fb_devconfig *dc)
499{
500	int v, cleared = 0;
501
502	v = dc->_internal_dc_changed;
503
504	if (v == 0) {
505		disable_irq(IRQ_FLYBACK);
506		return 1;
507	}
508
509	if (v & WSDISPLAY_WB_COUNTER) {
510	    	dc->dc_writeback_delay--;
511		if (dc->dc_writeback_delay == 0) {
512		    	cpu_dcache_wb_range(dc->dc_vaddr, dc->dc_size);
513			cleared |= WSDISPLAY_WB_COUNTER;
514		}
515	}
516
517	if (v & WSDISPLAY_CMAP_DOLUT) {
518		struct hwcmap256 *cm = &dc->dc_cmap;
519		int index;
520
521		if (dc->dc_depth == 4) {
522			/* palette for 4 bpp is different from 8bpp */
523			vidcvideo_write(VIDC_PALREG, 0x00000000);
524			for (index=0; index < (1 << dc->dc_depth); index++)
525				vidcvideo_write(VIDC_PALETTE,
526					VIDC_COL(cm->r[index],
527						 cm->g[index],
528						 cm->b[index]));
529		}
530
531		if (dc->dc_depth == 8) {
532			/*
533			 * dunno what to do in more than 8bpp
534			 * palettes only make sense in 8bpp and less modes
535			 * on VIDC
536			 */
537			vidcvideo_write(VIDC_PALREG, 0x00000000);
538			for (index = 0; index < CMAP_SIZE; index++) {
539				vidcvideo_write(VIDC_PALETTE,
540					VIDC_COL(cm->r[index], cm->g[index],
541					    cm->b[index]));
542			}
543		}
544		cleared |= WSDISPLAY_CMAP_DOLUT;
545	}
546
547	if (v & WSDISPLAY_VIDEO_ONOFF) {
548		vidcvideo_blank(dc->dc_blanked);
549		cleared |= WSDISPLAY_VIDEO_ONOFF;
550	}
551
552	if (v & (WSDISPLAY_CURSOR_DOPOS | WSDISPLAY_CURSOR_DOHOT)) {
553		int x, y;
554		x = dc->dc_cursor.cc_pos.x - dc->dc_cursor.cc_hot.x;
555		y = dc->dc_cursor.cc_pos.y - dc->dc_cursor.cc_hot.y;
556
557		vidcvideo_updatecursor(x, y);
558		cleared |= WSDISPLAY_CURSOR_DOPOS | WSDISPLAY_CURSOR_DOHOT;
559	}
560
561	if (v & WSDISPLAY_CURSOR_DOCUR) {
562		vidcvideo_enablecursor(dc->dc_curenb);
563		cleared |= WSDISPLAY_CURSOR_DOCUR;
564	}
565
566	dc->_internal_dc_changed ^= cleared;
567
568	if (dc->_internal_dc_changed == 0) {
569		disable_irq(IRQ_FLYBACK);
570	}
571
572	return 1;
573}
574
575
576static void vidcvideo_queue_dc_change(struct fb_devconfig *dc, int dc_change)
577{
578	dc->_internal_dc_changed |= dc_change;
579
580	if (curcpl() == IPL_HIGH) {
581		/* running in ddb or without interrupts */
582	    	dc->dc_writeback_delay = 1;
583		flush_dc_changes_to_screen(dc);
584	} else {
585		/*
586		 * running with interrupts so handle this in the next
587		 * vsync
588		 */
589		if (dc->dc_ih) {
590			enable_irq(IRQ_FLYBACK);
591		}
592	}
593}
594
595
596static const u_char ri_col_data[6][6] = {
597	{ 0,  0,  0,   0,  0,  0},	/*  1 bpp */
598	{ 0,  0,  0,   0,  0,  0},	/*  2 bpp */
599	{ 0,  0,  0,   0,  0,  0},	/*  4 bpp */
600	{ 0,  0,  0,   0,  0,  0},	/*  8 bpp */
601	{ 6,  5,  5,   0,  6, 11},	/* 16 bpp */
602	{ 8,  8,  8,   0,  8, 16},	/* 32 bpp */
603};
604
605static void
606vidcvideo_colourmap_and_cursor_init(struct fb_devconfig *dc)
607{
608	struct rasops_info *ri = &dc->dc_console.scr_ri;
609	const u_char *rgbdat;
610	struct hwcmap256 *cm;
611	const uint8_t *p;
612	int index;
613
614	/* Whatever we do later... just make sure we have a
615	 * sane palette to start with
616	 */
617	vidcvideo_stdpalette();
618
619	/* set up rgb bit pattern values for rasops_init */
620	rgbdat = ri_col_data[dc->dc_log2_depth];
621	ri->ri_rnum = rgbdat[0];
622	ri->ri_gnum = rgbdat[1];
623	ri->ri_bnum = rgbdat[2];
624	ri->ri_rpos = rgbdat[3];
625	ri->ri_gpos = rgbdat[4];
626	ri->ri_bpos = rgbdat[5];
627
628	/* initialise color map */
629	cm = &dc->dc_cmap;
630	p = rasops_cmap;
631	for (index = 0; index < CMAP_SIZE; index++, p += 3) {
632		cm->r[index] = p[0];
633		cm->g[index] = p[1];
634		cm->b[index] = p[2];
635	}
636	/* flush to hardware */
637	vidcvideo_queue_dc_change(dc, WSDISPLAY_CMAP_DOLUT);
638}
639
640
641static int
642get_cmap(struct vidcvideo_softc *sc, struct wsdisplay_cmap *p)
643{
644	u_int index = p->index, count = p->count;
645	int error;
646
647	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
648		return EINVAL;
649
650	error = copyout(&sc->sc_dc->dc_cmap.r[index], p->red, count);
651	if (error)
652		return error;
653	error = copyout(&sc->sc_dc->dc_cmap.g[index], p->green, count);
654	if (error)
655		return error;
656	error = copyout(&sc->sc_dc->dc_cmap.b[index], p->blue, count);
657	return error;
658}
659
660
661static int
662set_cmap(struct vidcvideo_softc *sc, struct wsdisplay_cmap *p)
663{
664    	struct fb_devconfig *dc = sc->sc_dc;
665	struct hwcmap256 cmap;
666	u_int index = p->index, count = p->count;
667	int error;
668
669	if (index >= CMAP_SIZE || count > CMAP_SIZE - index)
670		return EINVAL;
671
672	error = copyin(p->red, &cmap.r[index], count);
673	if (error)
674		return error;
675	error = copyin(p->green, &cmap.g[index], count);
676	if (error)
677		return error;
678	error = copyin(p->blue, &cmap.b[index], count);
679	if (error)
680		return error;
681	memcpy(&dc->dc_cmap.r[index], &cmap.r[index], count);
682	memcpy(&dc->dc_cmap.g[index], &cmap.g[index], count);
683	memcpy(&dc->dc_cmap.b[index], &cmap.b[index], count);
684	vidcvideo_queue_dc_change(dc, WSDISPLAY_CMAP_DOLUT);
685	return 0;
686}
687
688
689static int
690set_cursor(struct vidcvideo_softc *sc, struct wsdisplay_cursor *p)
691{
692#define	cc (&dc->dc_cursor)
693    	struct fb_devconfig *dc = sc->sc_dc;
694	u_int v, index = 0, count = 0, icount = 0;
695	uint8_t r[2], g[2], b[2], image[512], mask[512];
696	int error;
697
698	/* XXX gcc does not detect identical conditions */
699	index = count = icount = 0;
700
701	v = p->which;
702	if (v & WSDISPLAY_CURSOR_DOCMAP) {
703		index = p->cmap.index;
704		count = p->cmap.count;
705		if (index >= CURSOR_MAX_COLOURS ||
706		    (index + count) > CURSOR_MAX_COLOURS)
707			return EINVAL;
708		error = copyin(p->cmap.red, &r[index], count);
709		if (error)
710			return error;
711		error = copyin(p->cmap.green, &g[index], count);
712		if (error)
713			return error;
714		error = copyin(p->cmap.blue, &b[index], count);
715		if (error)
716			return error;
717	}
718	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
719		if (p->size.x > CURSOR_MAX_WIDTH ||
720		    p->size.y > CURSOR_MAX_HEIGHT)
721			return EINVAL;
722		icount = sizeof(uint32_t) * p->size.y;
723		error = copyin(p->image, &image, icount);
724		if (error)
725			return error;
726		error = copyin(p->mask, &mask, icount);
727		if (error)
728			return error;
729	}
730
731	if (v & WSDISPLAY_CURSOR_DOCUR)
732		dc->dc_curenb = p->enable;
733	if (v & WSDISPLAY_CURSOR_DOPOS)
734		set_curpos(sc, &p->pos);
735	if (v & WSDISPLAY_CURSOR_DOHOT)
736		cc->cc_hot = p->hot;
737	if (v & WSDISPLAY_CURSOR_DOCMAP) {
738		memcpy(&cc->cc_color[index], &r[index], count);
739		memcpy(&cc->cc_color[index + 2], &g[index], count);
740		memcpy(&cc->cc_color[index + 4], &b[index], count);
741	}
742	if (v & WSDISPLAY_CURSOR_DOSHAPE) {
743		cc->cc_size = p->size;
744		memset(cc->cc_image, 0, sizeof cc->cc_image);
745		memcpy(cc->cc_image, image, icount);
746		memset(cc->cc_mask, 0, sizeof cc->cc_mask);
747		memcpy(cc->cc_mask, mask, icount);
748	}
749	vidcvideo_queue_dc_change(dc, v);
750
751	return 0;
752#undef cc
753}
754
755
756static int
757get_cursor(struct vidcvideo_softc *sc, struct wsdisplay_cursor *p)
758{
759
760	return EPASSTHROUGH; /* XXX */
761}
762
763
764static void
765set_curpos(struct vidcvideo_softc *sc, struct wsdisplay_curpos *curpos)
766{
767	struct fb_devconfig *dc = sc->sc_dc;
768	int x = curpos->x, y = curpos->y;
769
770	if (y < 0)
771		y = 0;
772	else if (y > dc->dc_height)
773		y = dc->dc_height;
774	if (x < 0)
775		x = 0;
776	else if (x > dc->dc_width)
777		x = dc->dc_width;
778	dc->dc_cursor.cc_pos.x = x;
779	dc->dc_cursor.cc_pos.y = y;
780}
781
782
783static void vv_copyrows(void *id, int srcrow, int dstrow, int nrows)
784{
785	struct rasops_info *ri = id;
786	int height, size;
787	unsigned char *src, *dst;
788	struct vcons_screen *scr = ri->ri_hw;
789	struct fb_devconfig *dc = (struct fb_devconfig *) (scr->scr_cookie);
790
791	/* All movements are done in multiples of character heigths */
792	height = ri->ri_font->fontheight * nrows;
793	size   = height * ri->ri_stride;
794
795	/* check if we are full screen scrolling */
796
797#if 0
798	int scrollup   = (srcrow + nrows >= ri->ri_rows);
799	int scrolldown = (dstrow + nrows >= ri->ri_rows);
800	if ((scrollup || scrolldown) &&
801	    (videomemory.vidm_type == VIDEOMEM_TYPE_VRAM)) {
802		int offset = (srcrow - dstrow) * ri->ri_yscale;
803		ri->ri_bits = vidcvideo_hwscroll(offset);
804		vidcvideo_progr_scroll();	/* sadistic ; shouldnt this be on vsync? */
805
806		/* wipe out remains of the screen if necessary */
807		if (ri->ri_emuheight != ri->ri_height)
808			vv_eraserows(id, ri->ri_rows, 1, 0);
809		return;
810	}
811#endif
812
813	/*
814	 * Else we just copy the area : we're braindead for now
815	 * Note: we can't use hardware scrolling when the softc isnt
816	 * known yet...  if its not known we dont have interrupts and
817	 * we can't change the display address reliable other than in
818	 * a Vsync
819	 */
820
821	src = ri->ri_bits + srcrow * ri->ri_font->fontheight * ri->ri_stride;
822	dst = ri->ri_bits + dstrow * ri->ri_font->fontheight * ri->ri_stride;
823
824	memmove(dst, src, size);
825
826	/* delay the write back operation of the screen area */
827	dc->dc_writeback_delay = SCREEN_WRITE_BACK_DELAY;
828	vidcvideo_queue_dc_change(dc, WSDISPLAY_WB_COUNTER);
829}
830
831
832static void vv_eraserows(void *id, int startrow, int nrows, long attr)
833{
834	struct rasops_info *ri = id;
835	int height;
836	unsigned char *src;
837	struct vcons_screen *scr = ri->ri_hw;
838	struct fb_devconfig *dc = (struct fb_devconfig *) (scr->scr_cookie);
839
840	/* we're braindead for now */
841	height = ri->ri_font->fontheight * nrows * ri->ri_stride;
842
843	src = ri->ri_bits + startrow * ri->ri_font->fontheight * ri->ri_stride;
844
845	memset(src, 0, height);
846
847	/* delay the write back operation of the screen area */
848	dc->dc_writeback_delay = SCREEN_WRITE_BACK_DELAY;
849	vidcvideo_queue_dc_change(dc, WSDISPLAY_WB_COUNTER);
850}
851
852
853static void vv_putchar(void *id, int row, int col, u_int uc, long attr)
854{
855    	struct rasops_info *ri = id;
856	struct vcons_screen *scr = ri->ri_hw;
857	struct fb_devconfig *dc = (struct fb_devconfig *) (scr->scr_cookie);
858
859	/* just delegate */
860	dc->orig_ri_ops.putchar(id, row, col, uc, attr);
861
862	/* delay the write back operation of the screen area */
863	dc->dc_writeback_delay = SCREEN_WRITE_BACK_DELAY;
864	vidcvideo_queue_dc_change(dc, WSDISPLAY_WB_COUNTER);
865}
866