genfb.c revision 1.69
1/*	$NetBSD: genfb.c,v 1.69 2019/08/07 13:23:12 rin Exp $ */
2
3/*-
4 * Copyright (c) 2007 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: genfb.c,v 1.69 2019/08/07 13:23:12 rin 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/kmem.h>
42
43#include <dev/wscons/wsconsio.h>
44#include <dev/wscons/wsdisplayvar.h>
45#include <dev/rasops/rasops.h>
46#include <dev/wsfont/wsfont.h>
47
48#include <dev/wscons/wsdisplay_vconsvar.h>
49
50#include <dev/wsfb/genfbvar.h>
51
52#include <dev/videomode/videomode.h>
53#include <dev/videomode/edidvar.h>
54
55#ifdef GENFB_DISABLE_TEXT
56#include <sys/reboot.h>
57#define DISABLESPLASH (boothowto & (RB_SINGLE | RB_USERCONF | RB_ASKNAME | \
58		AB_VERBOSE | AB_DEBUG) )
59#endif
60
61#ifdef _KERNEL_OPT
62#include "opt_genfb.h"
63#include "opt_wsfb.h"
64#include "opt_rasops.h"
65#endif
66
67#ifdef GENFB_DEBUG
68#define GPRINTF panic
69#else
70#define GPRINTF aprint_debug
71#endif
72
73#define GENFB_BRIGHTNESS_STEP 15
74#define	GENFB_CHAR_WIDTH_MM 3
75
76static int	genfb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
77static paddr_t	genfb_mmap(void *, void *, off_t, int);
78static void	genfb_pollc(void *, int);
79
80static void	genfb_init_screen(void *, struct vcons_screen *, int, long *);
81static int	genfb_calc_hsize(struct genfb_softc *);
82static int	genfb_calc_cols(struct genfb_softc *);
83
84static int	genfb_putcmap(struct genfb_softc *, struct wsdisplay_cmap *);
85static int 	genfb_getcmap(struct genfb_softc *, struct wsdisplay_cmap *);
86static int 	genfb_putpalreg(struct genfb_softc *, uint8_t, uint8_t,
87			    uint8_t, uint8_t);
88static void	genfb_init_palette(struct genfb_softc *);
89
90static void	genfb_brightness_up(device_t);
91static void	genfb_brightness_down(device_t);
92
93extern const u_char rasops_cmap[768];
94
95static int genfb_cnattach_called = 0;
96static int genfb_enabled = 1;
97
98static struct genfb_softc *genfb_softc = NULL;
99
100void
101genfb_init(struct genfb_softc *sc)
102{
103	prop_dictionary_t dict;
104	uint64_t cmap_cb, pmf_cb, mode_cb, bl_cb, br_cb, fbaddr;
105	uint32_t fboffset;
106	bool console;
107
108	dict = device_properties(sc->sc_dev);
109#ifdef GENFB_DEBUG
110	printf("%s", prop_dictionary_externalize(dict));
111#endif
112	prop_dictionary_get_bool(dict, "is_console", &console);
113
114	if (!prop_dictionary_get_uint32(dict, "width", &sc->sc_width)) {
115		GPRINTF("no width property\n");
116		return;
117	}
118	if (!prop_dictionary_get_uint32(dict, "height", &sc->sc_height)) {
119		GPRINTF("no height property\n");
120		return;
121	}
122	if (!prop_dictionary_get_uint32(dict, "depth", &sc->sc_depth)) {
123		GPRINTF("no depth property\n");
124		return;
125	}
126
127	/* XXX should be a 64bit value */
128	if (!prop_dictionary_get_uint32(dict, "address", &fboffset)) {
129		GPRINTF("no address property\n");
130		return;
131	}
132
133	sc->sc_fboffset = fboffset;
134
135	sc->sc_fbaddr = NULL;
136	if (prop_dictionary_get_uint64(dict, "virtual_address", &fbaddr)) {
137		sc->sc_fbaddr = (void *)(uintptr_t)fbaddr;
138	}
139
140	sc->sc_shadowfb = NULL;
141	if (!prop_dictionary_get_bool(dict, "enable_shadowfb",
142	    &sc->sc_enable_shadowfb))
143#ifdef GENFB_SHADOWFB
144		sc->sc_enable_shadowfb = true;
145#else
146		sc->sc_enable_shadowfb = false;
147#endif
148
149	if (!prop_dictionary_get_uint32(dict, "linebytes", &sc->sc_stride))
150		sc->sc_stride = (sc->sc_width * sc->sc_depth) >> 3;
151
152	/*
153	 * deal with a bug in the Raptor firmware which always sets
154	 * stride = width even when depth != 8
155	 */
156	if (sc->sc_stride < sc->sc_width * (sc->sc_depth >> 3))
157		sc->sc_stride = sc->sc_width * (sc->sc_depth >> 3);
158
159	sc->sc_fbsize = sc->sc_height * sc->sc_stride;
160
161	/* optional colour map callback */
162	sc->sc_cmcb = NULL;
163	if (prop_dictionary_get_uint64(dict, "cmap_callback", &cmap_cb)) {
164		if (cmap_cb != 0)
165			sc->sc_cmcb = (void *)(vaddr_t)cmap_cb;
166	}
167
168	/* optional pmf callback */
169	sc->sc_pmfcb = NULL;
170	if (prop_dictionary_get_uint64(dict, "pmf_callback", &pmf_cb)) {
171		if (pmf_cb != 0)
172			sc->sc_pmfcb = (void *)(vaddr_t)pmf_cb;
173	}
174
175	/* optional mode callback */
176	sc->sc_modecb = NULL;
177	if (prop_dictionary_get_uint64(dict, "mode_callback", &mode_cb)) {
178		if (mode_cb != 0)
179			sc->sc_modecb = (void *)(vaddr_t)mode_cb;
180	}
181
182	/* optional backlight control callback */
183	sc->sc_backlight = NULL;
184	if (prop_dictionary_get_uint64(dict, "backlight_callback", &bl_cb)) {
185		if (bl_cb != 0) {
186			sc->sc_backlight = (void *)(vaddr_t)bl_cb;
187			aprint_naive_dev(sc->sc_dev,
188			    "enabling backlight control\n");
189		}
190	}
191
192	/* optional brightness control callback */
193	sc->sc_brightness = NULL;
194	if (prop_dictionary_get_uint64(dict, "brightness_callback", &br_cb)) {
195		if (br_cb != 0) {
196			sc->sc_brightness = (void *)(vaddr_t)br_cb;
197			aprint_naive_dev(sc->sc_dev,
198			    "enabling brightness control\n");
199			if (console &&
200			    sc->sc_brightness->gpc_upd_parameter != NULL) {
201				pmf_event_register(sc->sc_dev,
202				    PMFE_DISPLAY_BRIGHTNESS_UP,
203				    genfb_brightness_up, TRUE);
204				pmf_event_register(sc->sc_dev,
205				    PMFE_DISPLAY_BRIGHTNESS_DOWN,
206				    genfb_brightness_down, TRUE);
207			}
208		}
209	}
210}
211
212int
213genfb_attach(struct genfb_softc *sc, struct genfb_ops *ops)
214{
215	struct wsemuldisplaydev_attach_args aa;
216	prop_dictionary_t dict;
217	struct rasops_info *ri;
218	uint16_t crow;
219	long defattr;
220	bool console;
221#ifdef SPLASHSCREEN
222	int i, j;
223	int error = ENXIO;
224#endif
225
226	dict = device_properties(sc->sc_dev);
227	prop_dictionary_get_bool(dict, "is_console", &console);
228
229	if (prop_dictionary_get_uint16(dict, "cursor-row", &crow) == false)
230		crow = 0;
231	if (prop_dictionary_get_bool(dict, "clear-screen", &sc->sc_want_clear)
232	    == false)
233		sc->sc_want_clear = true;
234
235	aprint_verbose_dev(sc->sc_dev, "framebuffer at %p, size %dx%d, depth %d, "
236	    "stride %d\n",
237	    sc->sc_fboffset ? (void *)(intptr_t)sc->sc_fboffset : sc->sc_fbaddr,
238	    sc->sc_width, sc->sc_height, sc->sc_depth, sc->sc_stride);
239
240	sc->sc_defaultscreen_descr = (struct wsscreen_descr){
241		"default",
242		0, 0,
243		NULL,
244		8, 16,
245		WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE |
246		  WSSCREEN_RESIZE,
247		NULL
248	};
249	sc->sc_screens[0] = &sc->sc_defaultscreen_descr;
250	sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens};
251	memcpy(&sc->sc_ops, ops, sizeof(struct genfb_ops));
252	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
253	if (sc->sc_modecb != NULL)
254		sc->sc_modecb->gmc_setmode(sc, sc->sc_mode);
255
256	sc->sc_accessops.ioctl = genfb_ioctl;
257	sc->sc_accessops.mmap = genfb_mmap;
258	sc->sc_accessops.pollc = genfb_pollc;
259
260	if (sc->sc_enable_shadowfb) {
261		sc->sc_shadowfb = kmem_alloc(sc->sc_fbsize, KM_SLEEP);
262		if (sc->sc_want_clear == false)
263			memcpy(sc->sc_shadowfb, sc->sc_fbaddr, sc->sc_fbsize);
264		aprint_verbose_dev(sc->sc_dev,
265		    "shadow framebuffer enabled, size %zu KB\n",
266		    sc->sc_fbsize >> 10);
267	}
268
269	vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr,
270	    &sc->sc_accessops);
271	sc->vd.init_screen = genfb_init_screen;
272
273	/* Do not print anything between this point and the screen
274	 * clear operation below.  Otherwise it will be lost. */
275
276	ri = &sc->sc_console_screen.scr_ri;
277
278	vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1,
279	    &defattr);
280	sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC;
281
282#ifdef SPLASHSCREEN
283/*
284 * If system isn't going to go multiuser, or user has requested to see
285 * boot text, don't render splash screen immediately
286 */
287	if (DISABLESPLASH)
288#endif
289		vcons_redraw_screen(&sc->sc_console_screen);
290
291	sc->sc_defaultscreen_descr.textops = &ri->ri_ops;
292	sc->sc_defaultscreen_descr.capabilities = ri->ri_caps;
293	sc->sc_defaultscreen_descr.nrows = ri->ri_rows;
294	sc->sc_defaultscreen_descr.ncols = ri->ri_cols;
295
296	if (crow >= ri->ri_rows) {
297		crow = 0;
298		sc->sc_want_clear = 1;
299	}
300
301	if (console)
302		wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, crow,
303		    defattr);
304
305	/* Clear the whole screen to bring it to a known state. */
306	if (sc->sc_want_clear)
307		(*ri->ri_ops.eraserows)(ri, 0, ri->ri_rows, defattr);
308
309#ifdef SPLASHSCREEN
310	j = 0;
311	for (i = 0; i < uimin(1 << sc->sc_depth, 256); i++) {
312		if (i >= SPLASH_CMAP_OFFSET &&
313		    i < SPLASH_CMAP_OFFSET + SPLASH_CMAP_SIZE) {
314			splash_get_cmap(i,
315			    &sc->sc_cmap_red[i],
316			    &sc->sc_cmap_green[i],
317			    &sc->sc_cmap_blue[i]);
318		} else {
319			sc->sc_cmap_red[i] = rasops_cmap[j];
320			sc->sc_cmap_green[i] = rasops_cmap[j + 1];
321			sc->sc_cmap_blue[i] = rasops_cmap[j + 2];
322		}
323		j += 3;
324	}
325	genfb_restore_palette(sc);
326
327	sc->sc_splash.si_depth = sc->sc_depth;
328	sc->sc_splash.si_bits = sc->sc_console_screen.scr_ri.ri_origbits;
329	sc->sc_splash.si_hwbits = sc->sc_fbaddr;
330	sc->sc_splash.si_width = sc->sc_width;
331	sc->sc_splash.si_height = sc->sc_height;
332	sc->sc_splash.si_stride = sc->sc_stride;
333	sc->sc_splash.si_fillrect = NULL;
334	if (!DISABLESPLASH) {
335		error = splash_render(&sc->sc_splash,
336		    SPLASH_F_CENTER|SPLASH_F_FILL);
337		if (error) {
338			SCREEN_ENABLE_DRAWING(&sc->sc_console_screen);
339			genfb_init_palette(sc);
340			vcons_replay_msgbuf(&sc->sc_console_screen);
341		}
342	}
343#else
344	genfb_init_palette(sc);
345	if (console)
346		vcons_replay_msgbuf(&sc->sc_console_screen);
347#endif
348
349	if (genfb_softc == NULL)
350		genfb_softc = sc;
351
352	aa.console = console;
353	aa.scrdata = &sc->sc_screenlist;
354	aa.accessops = &sc->sc_accessops;
355	aa.accesscookie = &sc->vd;
356
357#ifdef GENFB_DISABLE_TEXT
358	if (!DISABLESPLASH && error == 0)
359		SCREEN_DISABLE_DRAWING(&sc->sc_console_screen);
360#endif
361
362	config_found_ia(sc->sc_dev, "wsemuldisplaydev", &aa,
363	    wsemuldisplaydevprint);
364
365	return 0;
366}
367
368static int
369genfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
370	struct lwp *l)
371{
372	struct vcons_data *vd = v;
373	struct genfb_softc *sc = vd->cookie;
374	struct wsdisplay_fbinfo *wdf;
375	struct vcons_screen *ms = vd->active;
376	struct wsdisplay_param *param;
377	int new_mode, error, val, ret;
378
379	switch (cmd) {
380		case WSDISPLAYIO_GINFO:
381			if (ms == NULL)
382				return ENODEV;
383			wdf = (void *)data;
384			wdf->height = ms->scr_ri.ri_height;
385			wdf->width = ms->scr_ri.ri_width;
386			wdf->depth = ms->scr_ri.ri_depth;
387			wdf->cmsize = 256;
388			return 0;
389
390		case WSDISPLAYIO_GETCMAP:
391			return genfb_getcmap(sc,
392			    (struct wsdisplay_cmap *)data);
393
394		case WSDISPLAYIO_PUTCMAP:
395			return genfb_putcmap(sc,
396			    (struct wsdisplay_cmap *)data);
397
398		case WSDISPLAYIO_LINEBYTES:
399			*(u_int *)data = sc->sc_stride;
400			return 0;
401
402		case WSDISPLAYIO_SMODE:
403			new_mode = *(int *)data;
404
405			/* notify the bus backend */
406			error = 0;
407			if (sc->sc_ops.genfb_ioctl)
408				error = sc->sc_ops.genfb_ioctl(sc, vs,
409					    cmd, data, flag, l);
410			if (error && error != EPASSTHROUGH)
411				return error;
412
413			if (new_mode != sc->sc_mode) {
414				sc->sc_mode = new_mode;
415				if (sc->sc_modecb != NULL)
416					sc->sc_modecb->gmc_setmode(sc,
417					    sc->sc_mode);
418				if (new_mode == WSDISPLAYIO_MODE_EMUL) {
419					genfb_restore_palette(sc);
420					vcons_redraw_screen(ms);
421				}
422			}
423			return 0;
424
425		case WSDISPLAYIO_SSPLASH:
426#if defined(SPLASHSCREEN)
427			if(*(int *)data == 1) {
428				SCREEN_DISABLE_DRAWING(&sc->sc_console_screen);
429				splash_render(&sc->sc_splash,
430						SPLASH_F_CENTER|SPLASH_F_FILL);
431			} else {
432				SCREEN_ENABLE_DRAWING(&sc->sc_console_screen);
433				genfb_init_palette(sc);
434			}
435			vcons_redraw_screen(ms);
436			return 0;
437#else
438			return ENODEV;
439#endif
440		case WSDISPLAYIO_GETPARAM:
441			param = (struct wsdisplay_param *)data;
442			switch (param->param) {
443			case WSDISPLAYIO_PARAM_BRIGHTNESS:
444				if (sc->sc_brightness == NULL)
445					return EPASSTHROUGH;
446				param->min = 0;
447				param->max = 255;
448				return sc->sc_brightness->gpc_get_parameter(
449				    sc->sc_brightness->gpc_cookie,
450				    &param->curval);
451			case WSDISPLAYIO_PARAM_BACKLIGHT:
452				if (sc->sc_backlight == NULL)
453					return EPASSTHROUGH;
454				param->min = 0;
455				param->max = 1;
456				return sc->sc_backlight->gpc_get_parameter(
457				    sc->sc_backlight->gpc_cookie,
458				    &param->curval);
459			}
460			return EPASSTHROUGH;
461
462		case WSDISPLAYIO_SETPARAM:
463			param = (struct wsdisplay_param *)data;
464			switch (param->param) {
465			case WSDISPLAYIO_PARAM_BRIGHTNESS:
466				if (sc->sc_brightness == NULL)
467					return EPASSTHROUGH;
468				val = param->curval;
469				if (val < 0) val = 0;
470				if (val > 255) val = 255;
471				return sc->sc_brightness->gpc_set_parameter(
472				    sc->sc_brightness->gpc_cookie, val);
473			case WSDISPLAYIO_PARAM_BACKLIGHT:
474				if (sc->sc_backlight == NULL)
475					return EPASSTHROUGH;
476				val = param->curval;
477				if (val < 0) val = 0;
478				if (val > 1) val = 1;
479				return sc->sc_backlight->gpc_set_parameter(
480				    sc->sc_backlight->gpc_cookie, val);
481			}
482			return EPASSTHROUGH;
483	}
484	ret = EPASSTHROUGH;
485	if (sc->sc_ops.genfb_ioctl)
486		ret = sc->sc_ops.genfb_ioctl(sc, vs, cmd, data, flag, l);
487	if (ret != EPASSTHROUGH)
488		return ret;
489	/*
490	 * XXX
491	 * handle these only if there either is no ioctl() handler or it didn't
492	 * know how to deal with them. This allows bus frontends to override
493	 * them but still provides fallback implementations
494	 */
495	switch (cmd) {
496		case WSDISPLAYIO_GET_EDID: {
497
498			struct wsdisplayio_edid_info *d = data;
499			return wsdisplayio_get_edid(sc->sc_dev, d);
500		}
501
502		case WSDISPLAYIO_GET_FBINFO: {
503			struct wsdisplayio_fbinfo *fbi = data;
504			return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi);
505		}
506	}
507	return EPASSTHROUGH;
508}
509
510static paddr_t
511genfb_mmap(void *v, void *vs, off_t offset, int prot)
512{
513	struct vcons_data *vd = v;
514	struct genfb_softc *sc = vd->cookie;
515
516	if (sc->sc_ops.genfb_mmap)
517		return sc->sc_ops.genfb_mmap(sc, vs, offset, prot);
518
519	return -1;
520}
521
522static void
523genfb_pollc(void *v, int on)
524{
525	struct vcons_data *vd = v;
526	struct genfb_softc *sc = vd->cookie;
527
528	if (sc == NULL)
529		return;
530
531	if (on)
532		genfb_enable_polling(sc->sc_dev);
533	else
534		genfb_disable_polling(sc->sc_dev);
535}
536
537static void
538genfb_init_screen(void *cookie, struct vcons_screen *scr,
539    int existing, long *defattr)
540{
541	struct genfb_softc *sc = cookie;
542	struct rasops_info *ri = &scr->scr_ri;
543	int wantcols;
544	bool is_bgr;
545
546	ri->ri_depth = sc->sc_depth;
547	ri->ri_width = sc->sc_width;
548	ri->ri_height = sc->sc_height;
549	ri->ri_stride = sc->sc_stride;
550	ri->ri_flg = RI_CENTER;
551	if (sc->sc_want_clear)
552		ri->ri_flg |= RI_FULLCLEAR;
553
554	scr->scr_flags |= VCONS_LOADFONT;
555
556	if (sc->sc_shadowfb != NULL) {
557		ri->ri_hwbits = (char *)sc->sc_fbaddr;
558		ri->ri_bits = (char *)sc->sc_shadowfb;
559	} else {
560		ri->ri_bits = (char *)sc->sc_fbaddr;
561		scr->scr_flags |= VCONS_DONT_READ;
562	}
563
564	if (existing && sc->sc_want_clear)
565		ri->ri_flg |= RI_CLEAR;
566
567	switch (ri->ri_depth) {
568	case 32:
569	case 24:
570		ri->ri_flg |= RI_ENABLE_ALPHA;
571
572		prop_dictionary_get_bool(device_properties(sc->sc_dev),
573		    "is_bgr", &is_bgr);
574		if (is_bgr) {
575			/* someone requested BGR */
576			ri->ri_rnum = 8;
577			ri->ri_gnum = 8;
578			ri->ri_bnum = 8;
579			ri->ri_rpos = 0;
580			ri->ri_gpos = 8;
581			ri->ri_bpos = 16;
582		} else {
583			/* assume RGB */
584			ri->ri_rnum = 8;
585			ri->ri_gnum = 8;
586			ri->ri_bnum = 8;
587			ri->ri_rpos = 16;
588			ri->ri_gpos = 8;
589			ri->ri_bpos = 0;
590		}
591		break;
592
593	case 16:
594	case 15:
595		ri->ri_flg |= RI_ENABLE_ALPHA;
596		break;
597
598	case 8:
599		if (sc->sc_cmcb != NULL)
600			ri->ri_flg |= RI_ENABLE_ALPHA | RI_8BIT_IS_RGB;
601		break;
602
603	case 2:
604		ri->ri_flg |= RI_ENABLE_ALPHA;
605		break;
606
607	default:
608		break;
609	}
610
611	wantcols = genfb_calc_cols(sc);
612
613	rasops_init(ri, 0, wantcols);
614	ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_HILIT | WSSCREEN_UNDERLINE |
615		  WSSCREEN_RESIZE;
616	rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight,
617		    sc->sc_width / ri->ri_font->fontwidth);
618
619	/* TODO: actually center output */
620	ri->ri_hw = scr;
621
622#ifdef GENFB_DISABLE_TEXT
623	if (scr == &sc->sc_console_screen && !DISABLESPLASH)
624		SCREEN_DISABLE_DRAWING(&sc->sc_console_screen);
625#endif
626}
627
628/* Returns the width of the display in millimeters, or 0 if not known. */
629static int
630genfb_calc_hsize(struct genfb_softc *sc)
631{
632	device_t dev = sc->sc_dev;
633	prop_dictionary_t dict = device_properties(dev);
634	prop_data_t edid_data;
635	struct edid_info edid;
636	const char *edid_ptr;
637
638	edid_data = prop_dictionary_get(dict, "EDID");
639	if (edid_data == NULL || prop_data_size(edid_data) < 128)
640		return 0;
641
642	edid_ptr = prop_data_data_nocopy(edid_data);
643	if (edid_parse(__UNCONST(edid_ptr), &edid) != 0)
644		return 0;
645
646	return (int)edid.edid_max_hsize * 10;
647}
648
649/* Return the minimum number of character columns based on DPI */
650static int
651genfb_calc_cols(struct genfb_softc *sc)
652{
653	const int hsize = genfb_calc_hsize(sc);
654
655	return MAX(RASOPS_DEFAULT_WIDTH, hsize / GENFB_CHAR_WIDTH_MM);
656}
657
658static int
659genfb_putcmap(struct genfb_softc *sc, struct wsdisplay_cmap *cm)
660{
661	u_char *r, *g, *b;
662	u_int index = cm->index;
663	u_int count = cm->count;
664	int i, error;
665	u_char rbuf[256], gbuf[256], bbuf[256];
666
667#ifdef GENFB_DEBUG
668	aprint_debug("putcmap: %d %d\n",index, count);
669#endif
670	if (index >= 256 || count > 256 || index + count > 256)
671		return EINVAL;
672
673	error = copyin(cm->red, &rbuf[index], count);
674	if (error)
675		return error;
676	error = copyin(cm->green, &gbuf[index], count);
677	if (error)
678		return error;
679	error = copyin(cm->blue, &bbuf[index], count);
680	if (error)
681		return error;
682
683	memcpy(&sc->sc_cmap_red[index], &rbuf[index], count);
684	memcpy(&sc->sc_cmap_green[index], &gbuf[index], count);
685	memcpy(&sc->sc_cmap_blue[index], &bbuf[index], count);
686
687	r = &sc->sc_cmap_red[index];
688	g = &sc->sc_cmap_green[index];
689	b = &sc->sc_cmap_blue[index];
690
691	for (i = 0; i < count; i++) {
692		genfb_putpalreg(sc, index, *r, *g, *b);
693		index++;
694		r++, g++, b++;
695	}
696	return 0;
697}
698
699static int
700genfb_getcmap(struct genfb_softc *sc, struct wsdisplay_cmap *cm)
701{
702	u_int index = cm->index;
703	u_int count = cm->count;
704	int error;
705
706	if (index >= 256 || count > 256 || index + count > 256)
707		return EINVAL;
708
709	error = copyout(&sc->sc_cmap_red[index],   cm->red,   count);
710	if (error)
711		return error;
712	error = copyout(&sc->sc_cmap_green[index], cm->green, count);
713	if (error)
714		return error;
715	error = copyout(&sc->sc_cmap_blue[index],  cm->blue,  count);
716	if (error)
717		return error;
718
719	return 0;
720}
721
722void
723genfb_restore_palette(struct genfb_softc *sc)
724{
725	int i;
726
727	if (sc->sc_depth <= 8) {
728		for (i = 0; i < (1 << sc->sc_depth); i++) {
729			genfb_putpalreg(sc, i, sc->sc_cmap_red[i],
730			    sc->sc_cmap_green[i], sc->sc_cmap_blue[i]);
731		}
732	}
733}
734
735static void
736genfb_init_palette(struct genfb_softc *sc)
737{
738	int i, j, tmp;
739
740	if (sc->sc_depth == 8) {
741		/* generate an r3g3b2 colour map */
742		for (i = 0; i < 256; i++) {
743			tmp = i & 0xe0;
744			/*
745			 * replicate bits so 0xe0 maps to a red value of 0xff
746			 * in order to make white look actually white
747			 */
748			tmp |= (tmp >> 3) | (tmp >> 6);
749			sc->sc_cmap_red[i] = tmp;
750
751			tmp = (i & 0x1c) << 3;
752			tmp |= (tmp >> 3) | (tmp >> 6);
753			sc->sc_cmap_green[i] = tmp;
754
755			tmp = (i & 0x03) << 6;
756			tmp |= tmp >> 2;
757			tmp |= tmp >> 4;
758			sc->sc_cmap_blue[i] = tmp;
759
760			genfb_putpalreg(sc, i, sc->sc_cmap_red[i],
761				       sc->sc_cmap_green[i],
762				       sc->sc_cmap_blue[i]);
763		}
764	} else {
765		/* steal rasops' ANSI cmap */
766		j = 0;
767		for (i = 0; i < 256; i++) {
768			sc->sc_cmap_red[i] = rasops_cmap[j];
769			sc->sc_cmap_green[i] = rasops_cmap[j + 1];
770			sc->sc_cmap_blue[i] = rasops_cmap[j + 2];
771			j += 3;
772		}
773	}
774}
775
776static int
777genfb_putpalreg(struct genfb_softc *sc, uint8_t idx, uint8_t r, uint8_t g,
778    uint8_t b)
779{
780
781	if (sc->sc_cmcb) {
782
783		sc->sc_cmcb->gcc_set_mapreg(sc->sc_cmcb->gcc_cookie,
784		    idx, r, g, b);
785	}
786	return 0;
787}
788
789void
790genfb_cnattach(void)
791{
792	genfb_cnattach_called = 1;
793}
794
795void
796genfb_disable(void)
797{
798	genfb_enabled = 0;
799}
800
801int
802genfb_is_console(void)
803{
804	return genfb_cnattach_called;
805}
806
807int
808genfb_is_enabled(void)
809{
810	return genfb_enabled;
811}
812
813int
814genfb_borrow(bus_addr_t addr, bus_space_handle_t *hdlp)
815{
816	struct genfb_softc *sc = genfb_softc;
817
818	if (sc && sc->sc_ops.genfb_borrow)
819		return sc->sc_ops.genfb_borrow(sc, addr, hdlp);
820	return 0;
821}
822
823static void
824genfb_brightness_up(device_t dev)
825{
826	struct genfb_softc *sc = device_private(dev);
827
828	KASSERT(sc->sc_brightness != NULL &&
829		sc->sc_brightness->gpc_upd_parameter != NULL);
830
831	(void)sc->sc_brightness->gpc_upd_parameter(
832	    sc->sc_brightness->gpc_cookie, GENFB_BRIGHTNESS_STEP);
833}
834
835static void
836genfb_brightness_down(device_t dev)
837{
838	struct genfb_softc *sc = device_private(dev);
839
840	KASSERT(sc->sc_brightness != NULL &&
841		sc->sc_brightness->gpc_upd_parameter != NULL);
842
843	(void)sc->sc_brightness->gpc_upd_parameter(
844	    sc->sc_brightness->gpc_cookie, - GENFB_BRIGHTNESS_STEP);
845}
846
847void
848genfb_enable_polling(device_t dev)
849{
850	struct genfb_softc *sc = device_private(dev);
851
852	if (sc->sc_console_screen.scr_vd) {
853		SCREEN_ENABLE_DRAWING(&sc->sc_console_screen);
854		vcons_hard_switch(&sc->sc_console_screen);
855		vcons_enable_polling(&sc->vd);
856		if (sc->sc_ops.genfb_enable_polling)
857			(*sc->sc_ops.genfb_enable_polling)(sc);
858	}
859}
860
861void
862genfb_disable_polling(device_t dev)
863{
864	struct genfb_softc *sc = device_private(dev);
865
866	if (sc->sc_console_screen.scr_vd) {
867		if (sc->sc_ops.genfb_disable_polling)
868			(*sc->sc_ops.genfb_disable_polling)(sc);
869		vcons_disable_polling(&sc->vd);
870	}
871}
872