1/*	$NetBSD: bivideo.c,v 1.37 2022/05/28 10:36:23 andvar Exp $	*/
2
3/*-
4 * Copyright (c) 1999-2001
5 *         Shin Takemura and PocketBSD Project. 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 the PocketBSD project
18 *	and its contributors.
19 * 4. Neither the name of the project nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 */
36
37#include <sys/cdefs.h>
38__KERNEL_RCSID(0, "$NetBSD: bivideo.c,v 1.37 2022/05/28 10:36:23 andvar Exp $");
39
40#ifdef _KERNEL_OPT
41#include "opt_hpcfb.h"
42#endif
43
44#include <sys/param.h>
45#include <sys/systm.h>
46#include <sys/device.h>
47#include <sys/buf.h>
48#include <sys/ioctl.h>
49#include <sys/reboot.h>
50
51#include <sys/bus.h>
52#include <machine/autoconf.h>
53#include <machine/bootinfo.h>
54#include <machine/config_hook.h>
55
56#include <dev/wscons/wsconsio.h>
57#include <dev/wscons/wsdisplayvar.h>
58
59#include <dev/rasops/rasops.h>
60
61#include <dev/hpc/hpcfbvar.h>
62#include <dev/hpc/hpcfbio.h>
63#include <dev/hpc/bivideovar.h>
64#include <dev/hpc/hpccmapvar.h>
65
66#ifdef FBDEBUG
67#define VPRINTF(arg)	do { if (bootverbose) printf arg; } while (0)
68#else
69#define VPRINTF(arg)	/* nothing */
70#endif
71
72/*
73 *  global variables
74 */
75int bivideo_dont_attach = 0;
76
77/*
78 *  function prototypes
79 */
80int	bivideomatch(device_t, cfdata_t, void *);
81void	bivideoattach(device_t, device_t, void *);
82int	bivideo_ioctl(void *, u_long, void *, int, struct lwp *);
83paddr_t	bivideo_mmap(void *, off_t, int);
84
85struct bivideo_softc {
86	struct hpcfb_fbconf	sc_fbconf;
87	struct hpcfb_dspconf	sc_dspconf;
88	int			sc_powerstate;
89#define PWRSTAT_SUSPEND		(1<<0)
90#define PWRSTAT_VIDEOOFF	(1<<1)
91#define PWRSTAT_LCD		(1<<2)
92#define PWRSTAT_BACKLIGHT	(1<<3)
93#define PWRSTAT_ALL		(0xffffffff)
94	int			sc_lcd_inited;
95#define BACKLIGHT_INITED	(1<<0)
96#define BRIGHTNESS_INITED	(1<<1)
97#define CONTRAST_INITED		(1<<2)
98	int			sc_brightness;
99	int			sc_brightness_save;
100	int			sc_max_brightness;
101	int			sc_contrast;
102	int			sc_max_contrast;
103
104};
105
106static int bivideo_init(struct hpcfb_fbconf *);
107static void bivideo_power(int, void *);
108static void bivideo_update_powerstate(struct bivideo_softc *, int);
109static bool bivideo_suspend(device_t, const pmf_qual_t *);
110static bool bivideo_resume(device_t, const pmf_qual_t *);
111void	bivideo_init_backlight(struct bivideo_softc *, int);
112void	bivideo_init_brightness(struct bivideo_softc *, int);
113void	bivideo_init_contrast(struct bivideo_softc *, int);
114void	bivideo_set_brightness(struct bivideo_softc *, int);
115void	bivideo_set_contrast(struct bivideo_softc *, int);
116
117#if defined __mips__ || defined __sh__ || defined __arm__
118#define __BTOP(x)		((paddr_t)(x) >> PGSHIFT)
119#define __PTOB(x)		((paddr_t)(x) << PGSHIFT)
120#else
121#error "define btop, ptob."
122#endif
123
124/*
125 *  static variables
126 */
127CFATTACH_DECL_NEW(bivideo, sizeof(struct bivideo_softc),
128    bivideomatch, bivideoattach, NULL, NULL);
129
130struct hpcfb_accessops bivideo_ha = {
131	bivideo_ioctl, bivideo_mmap
132};
133
134static int console_flag = 0;
135static int attach_flag = 0;
136
137/*
138 *  function bodies
139 */
140int
141bivideomatch(device_t parent, cfdata_t match, void *aux)
142{
143	struct mainbus_attach_args *ma = aux;
144
145	if (bivideo_dont_attach ||
146	    strcmp(ma->ma_name, match->cf_name))
147		return 0;
148
149	return (1);
150}
151
152void
153bivideoattach(device_t parent, device_t self, void *aux)
154{
155	struct bivideo_softc *sc = device_private(self);
156	struct hpcfb_attach_args ha;
157
158	if (attach_flag) {
159		panic("%s(%d): bivideo attached twice", __FILE__, __LINE__);
160	}
161	attach_flag = 1;
162
163	printf(": ");
164	if (bivideo_init(&sc->sc_fbconf) != 0) {
165		/* just return so that hpcfb will not be attached */
166		return;
167	}
168
169	printf("pseudo video controller");
170	if (console_flag) {
171		printf(", console");
172	}
173	printf("\n");
174	printf("%s: framebuffer address: 0x%08lx\n",
175		device_xname(self), (u_long)bootinfo->fb_addr);
176
177	/* Add a suspend hook to power saving */
178	sc->sc_powerstate = 0;
179	if (!pmf_device_register(self, bivideo_suspend, bivideo_resume))
180		aprint_error_dev(self, "unable to establish power handler\n");
181
182	/* initialize backlight brightness and lcd contrast */
183	sc->sc_lcd_inited = 0;
184	bivideo_init_brightness(sc, 1);
185	bivideo_init_contrast(sc, 1);
186	bivideo_init_backlight(sc, 1);
187
188	ha.ha_console = console_flag;
189	ha.ha_accessops = &bivideo_ha;
190	ha.ha_accessctx = sc;
191	ha.ha_curfbconf = 0;
192	ha.ha_nfbconf = 1;
193	ha.ha_fbconflist = &sc->sc_fbconf;
194	ha.ha_curdspconf = 0;
195	ha.ha_ndspconf = 1;
196	ha.ha_dspconflist = &sc->sc_dspconf;
197
198	config_found(self, &ha, hpcfbprint, CFARGS_NONE);
199}
200
201int
202bivideo_getcnfb(struct hpcfb_fbconf *fb)
203{
204	console_flag = 1;
205
206	return bivideo_init(fb);
207}
208
209static int
210bivideo_init(struct hpcfb_fbconf *fb)
211{
212	/*
213	 * get fb settings from bootinfo
214	 */
215	if (bootinfo == NULL ||
216	    bootinfo->fb_addr == 0 ||
217	    bootinfo->fb_line_bytes == 0 ||
218	    bootinfo->fb_width == 0 ||
219	    bootinfo->fb_height == 0) {
220		printf("no frame buffer information.\n");
221		return (-1);
222	}
223
224	/* zero fill */
225	memset(fb, 0, sizeof(*fb));
226
227	fb->hf_conf_index	= 0;	/* configuration index		*/
228	fb->hf_nconfs		= 1;   	/* how many configurations	*/
229	strcpy(fb->hf_name, "built-in video");
230					/* frame buffer name		*/
231	strcpy(fb->hf_conf_name, "default");
232					/* configuration name		*/
233	fb->hf_height		= bootinfo->fb_height;
234	fb->hf_width		= bootinfo->fb_width;
235	fb->hf_baseaddr		= (u_long)bootinfo->fb_addr;
236	fb->hf_offset		= (u_long)bootinfo->fb_addr -
237				      __PTOB(__BTOP(bootinfo->fb_addr));
238					/* frame buffer start offset   	*/
239	fb->hf_bytes_per_line	= bootinfo->fb_line_bytes;
240	fb->hf_nplanes		= 1;
241	fb->hf_bytes_per_plane	= bootinfo->fb_height *
242					bootinfo->fb_line_bytes;
243
244	fb->hf_access_flags |= HPCFB_ACCESS_BYTE;
245	fb->hf_access_flags |= HPCFB_ACCESS_WORD;
246	fb->hf_access_flags |= HPCFB_ACCESS_DWORD;
247
248	switch (bootinfo->fb_type) {
249		/*
250		 * gray scale
251		 */
252	case BIFB_D2_M2L_3:
253	case BIFB_D2_M2L_3x2:
254		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
255		/* fall through */
256	case BIFB_D2_M2L_0:
257	case BIFB_D2_M2L_0x2:
258		fb->hf_class = HPCFB_CLASS_GRAYSCALE;
259		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
260		fb->hf_pack_width = 8;
261		fb->hf_pixels_per_pack = 4;
262		fb->hf_pixel_width = 2;
263		fb->hf_class_data_length = sizeof(struct hf_gray_tag);
264		fb->hf_u.hf_gray.hf_flags = 0;	/* reserved for future use */
265		break;
266
267	case BIFB_D4_M2L_F:
268	case BIFB_D4_M2L_Fx2:
269		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
270		/* fall through */
271	case BIFB_D4_M2L_0:
272	case BIFB_D4_M2L_0x2:
273		fb->hf_class = HPCFB_CLASS_GRAYSCALE;
274		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
275		fb->hf_pack_width = 8;
276		fb->hf_pixels_per_pack = 2;
277		fb->hf_pixel_width = 4;
278		fb->hf_class_data_length = sizeof(struct hf_gray_tag);
279		fb->hf_u.hf_gray.hf_flags = 0;	/* reserved for future use */
280		break;
281
282		/*
283		 * indexed color
284		 */
285	case BIFB_D8_FF:
286		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
287		/* fall through */
288	case BIFB_D8_00:
289		fb->hf_class = HPCFB_CLASS_INDEXCOLOR;
290		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
291		fb->hf_pack_width = 8;
292		fb->hf_pixels_per_pack = 1;
293		fb->hf_pixel_width = 8;
294		fb->hf_class_data_length = sizeof(struct hf_indexed_tag);
295		fb->hf_u.hf_indexed.hf_flags = 0; /* reserved for future use */
296		break;
297
298		/*
299		 * RGB color
300		 */
301	case BIFB_D16_FFFF:
302		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
303		/* fall through */
304	case BIFB_D16_0000:
305		fb->hf_class = HPCFB_CLASS_RGBCOLOR;
306		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
307#if BYTE_ORDER == LITTLE_ENDIAN
308		fb->hf_order_flags = HPCFB_REVORDER_BYTE;
309#endif
310		fb->hf_pack_width = 16;
311		fb->hf_pixels_per_pack = 1;
312		fb->hf_pixel_width = 16;
313
314		fb->hf_class_data_length = sizeof(struct hf_rgb_tag);
315		fb->hf_u.hf_rgb.hf_flags = 0;	/* reserved for future use */
316
317		fb->hf_u.hf_rgb.hf_red_width = 5;
318		fb->hf_u.hf_rgb.hf_red_shift = 11;
319		fb->hf_u.hf_rgb.hf_green_width = 6;
320		fb->hf_u.hf_rgb.hf_green_shift = 5;
321		fb->hf_u.hf_rgb.hf_blue_width = 5;
322		fb->hf_u.hf_rgb.hf_blue_shift = 0;
323		fb->hf_u.hf_rgb.hf_alpha_width = 0;
324		fb->hf_u.hf_rgb.hf_alpha_shift = 0;
325		break;
326
327	default:
328		printf("unsupported type %d.\n", bootinfo->fb_type);
329		return (-1);
330		break;
331	}
332
333	return (0); /* no error */
334}
335
336static void
337bivideo_power(int why, void *arg)
338{
339	struct bivideo_softc *sc = arg;
340
341	switch (why) {
342	case PWR_SUSPEND:
343	case PWR_STANDBY:
344		sc->sc_powerstate |= PWRSTAT_SUSPEND;
345		bivideo_update_powerstate(sc, PWRSTAT_ALL);
346		break;
347	case PWR_RESUME:
348		sc->sc_powerstate &= ~PWRSTAT_SUSPEND;
349		bivideo_update_powerstate(sc, PWRSTAT_ALL);
350		break;
351	}
352}
353
354static void
355bivideo_update_powerstate(struct bivideo_softc *sc, int updates)
356{
357	if (updates & PWRSTAT_LCD)
358		config_hook_call(CONFIG_HOOK_POWERCONTROL,
359		    CONFIG_HOOK_POWERCONTROL_LCD,
360		    (void*)!(sc->sc_powerstate &
361				(PWRSTAT_VIDEOOFF|PWRSTAT_SUSPEND)));
362
363	if (updates & PWRSTAT_BACKLIGHT)
364		config_hook_call(CONFIG_HOOK_POWERCONTROL,
365		    CONFIG_HOOK_POWERCONTROL_LCDLIGHT,
366		    (void*)(!(sc->sc_powerstate &
367				(PWRSTAT_VIDEOOFF|PWRSTAT_SUSPEND)) &&
368			     (sc->sc_powerstate & PWRSTAT_BACKLIGHT)));
369}
370
371static bool
372bivideo_suspend(device_t self, const pmf_qual_t *qual)
373{
374	struct bivideo_softc *sc = device_private(self);
375
376	bivideo_power(PWR_SUSPEND, sc);
377	return true;
378}
379
380static bool
381bivideo_resume(device_t self, const pmf_qual_t *qual)
382{
383	struct bivideo_softc *sc = device_private(self);
384
385	bivideo_power(PWR_RESUME, sc);
386	return true;
387}
388
389int
390bivideo_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
391{
392	struct bivideo_softc *sc = (struct bivideo_softc *)v;
393	struct hpcfb_fbconf *fbconf;
394	struct hpcfb_dspconf *dspconf;
395	struct wsdisplay_cmap *cmap;
396	struct wsdisplay_param *dispparam;
397	int error;
398
399	switch (cmd) {
400	case WSDISPLAYIO_GETCMAP:
401		cmap = (struct wsdisplay_cmap *)data;
402
403		if (sc->sc_fbconf.hf_class != HPCFB_CLASS_INDEXCOLOR ||
404		    sc->sc_fbconf.hf_pack_width != 8 ||
405		    cmap->index >= 256 ||
406		    cmap->count > 256 - cmap->index)
407			return (EINVAL);
408
409		error = copyout(&bivideo_cmap_r[cmap->index], cmap->red,
410				cmap->count);
411		if (error)
412			return error;
413		error = copyout(&bivideo_cmap_g[cmap->index], cmap->green,
414				cmap->count);
415		if (error)
416			return error;
417		error = copyout(&bivideo_cmap_b[cmap->index], cmap->blue,
418				cmap->count);
419		return error;
420
421	case WSDISPLAYIO_PUTCMAP:
422		/*
423		 * This driver can't set color map.
424		 */
425		return (EINVAL);
426
427	case WSDISPLAYIO_SVIDEO:
428		if (*(int *)data == WSDISPLAYIO_VIDEO_OFF)
429			sc->sc_powerstate |= PWRSTAT_VIDEOOFF;
430		else
431			sc->sc_powerstate &= ~PWRSTAT_VIDEOOFF;
432		bivideo_update_powerstate(sc, PWRSTAT_ALL);
433		return 0;
434
435	case WSDISPLAYIO_GVIDEO:
436		*(int *)data = (sc->sc_powerstate&PWRSTAT_VIDEOOFF) ?
437				WSDISPLAYIO_VIDEO_OFF:WSDISPLAYIO_VIDEO_ON;
438		return 0;
439
440
441	case WSDISPLAYIO_GETPARAM:
442		dispparam = (struct wsdisplay_param*)data;
443		switch (dispparam->param) {
444		case WSDISPLAYIO_PARAM_BACKLIGHT:
445			VPRINTF(("bivideo_ioctl: GET:BACKLIGHT\n"));
446			bivideo_init_brightness(sc, 0);
447			bivideo_init_backlight(sc, 0);
448			VPRINTF(("bivideo_ioctl: GET:(real)BACKLIGHT %d\n",
449				 (sc->sc_powerstate&PWRSTAT_BACKLIGHT)? 1: 0));
450			dispparam->min = 0;
451			dispparam->max = 1;
452			if (sc->sc_max_brightness > 0)
453				dispparam->curval = sc->sc_brightness > 0? 1: 0;
454			else
455				dispparam->curval =
456				    (sc->sc_powerstate&PWRSTAT_BACKLIGHT) ? 1: 0;
457			VPRINTF(("bivideo_ioctl: GET:BACKLIGHT:%d(%s)\n",
458				dispparam->curval,
459				sc->sc_max_brightness > 0? "brightness": "light"));
460			return 0;
461			break;
462		case WSDISPLAYIO_PARAM_CONTRAST:
463			VPRINTF(("bivideo_ioctl: GET:CONTRAST\n"));
464			bivideo_init_contrast(sc, 0);
465			if (sc->sc_max_contrast > 0) {
466				dispparam->min = 0;
467				dispparam->max = sc->sc_max_contrast;
468				dispparam->curval = sc->sc_contrast;
469				VPRINTF(("bivideo_ioctl: GET:CONTRAST max=%d, current=%d\n", sc->sc_max_contrast, sc->sc_contrast));
470				return 0;
471			} else {
472				VPRINTF(("bivideo_ioctl: GET:CONTRAST EINVAL\n"));
473				return (EINVAL);
474			}
475			break;
476		case WSDISPLAYIO_PARAM_BRIGHTNESS:
477			VPRINTF(("bivideo_ioctl: GET:BRIGHTNESS\n"));
478			bivideo_init_brightness(sc, 0);
479			if (sc->sc_max_brightness > 0) {
480				dispparam->min = 0;
481				dispparam->max = sc->sc_max_brightness;
482				dispparam->curval = sc->sc_brightness;
483				VPRINTF(("bivideo_ioctl: GET:BRIGHTNESS max=%d, current=%d\n", sc->sc_max_brightness, sc->sc_brightness));
484				return 0;
485			} else {
486				VPRINTF(("bivideo_ioctl: GET:BRIGHTNESS EINVAL\n"));
487				return (EINVAL);
488			}
489			return (EINVAL);
490		default:
491			return (EINVAL);
492		}
493		return (0);
494
495	case WSDISPLAYIO_SETPARAM:
496		dispparam = (struct wsdisplay_param*)data;
497		switch (dispparam->param) {
498		case WSDISPLAYIO_PARAM_BACKLIGHT:
499			VPRINTF(("bivideo_ioctl: SET:BACKLIGHT\n"));
500			if (dispparam->curval < 0 ||
501			    1 < dispparam->curval)
502				return (EINVAL);
503			bivideo_init_brightness(sc, 0);
504			VPRINTF(("bivideo_ioctl: SET:max brightness=%d\n", sc->sc_max_brightness));
505			if (sc->sc_max_brightness > 0) { /* dimmer */
506				if (dispparam->curval == 0){
507					sc->sc_brightness_save = sc->sc_brightness;
508					bivideo_set_brightness(sc, 0);	/* min */
509				} else {
510					if (sc->sc_brightness_save == 0)
511						sc->sc_brightness_save = sc->sc_max_brightness;
512					bivideo_set_brightness(sc, sc->sc_brightness_save);
513				}
514				VPRINTF(("bivideo_ioctl: SET:BACKLIGHT:brightness=%d\n", sc->sc_brightness));
515			} else { /* off */
516				if (dispparam->curval == 0)
517					sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT;
518				else
519					sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
520				VPRINTF(("bivideo_ioctl: SET:BACKLIGHT:powerstate %d\n",
521						(sc->sc_powerstate & PWRSTAT_BACKLIGHT)?1:0));
522				bivideo_update_powerstate(sc, PWRSTAT_BACKLIGHT);
523				VPRINTF(("bivideo_ioctl: SET:BACKLIGHT:%d\n",
524					(sc->sc_powerstate & PWRSTAT_BACKLIGHT)?1:0));
525			}
526			return 0;
527			break;
528		case WSDISPLAYIO_PARAM_CONTRAST:
529			VPRINTF(("bivideo_ioctl: SET:CONTRAST\n"));
530			bivideo_init_contrast(sc, 0);
531			if (dispparam->curval < 0 ||
532			    sc->sc_max_contrast < dispparam->curval)
533				return (EINVAL);
534			if (sc->sc_max_contrast > 0) {
535#ifdef FBDEBUG
536				int org = sc->sc_contrast;
537#endif
538				bivideo_set_contrast(sc, dispparam->curval);
539				VPRINTF(("bivideo_ioctl: SET:CONTRAST org=%d, current=%d\n", org, sc->sc_contrast));
540				return 0;
541			} else {
542				VPRINTF(("bivideo_ioctl: SET:CONTRAST EINVAL\n"));
543				return (EINVAL);
544			}
545			break;
546		case WSDISPLAYIO_PARAM_BRIGHTNESS:
547			VPRINTF(("bivideo_ioctl: SET:BRIGHTNESS\n"));
548			bivideo_init_brightness(sc, 0);
549			if (dispparam->curval < 0 ||
550			    sc->sc_max_brightness < dispparam->curval)
551				return (EINVAL);
552			if (sc->sc_max_brightness > 0) {
553#ifdef FBDEBUG
554				int org = sc->sc_brightness;
555#endif
556				bivideo_set_brightness(sc, dispparam->curval);
557				VPRINTF(("bivideo_ioctl: SET:BRIGHTNESS org=%d, current=%d\n", org, sc->sc_brightness));
558				return 0;
559			} else {
560				VPRINTF(("bivideo_ioctl: SET:BRIGHTNESS EINVAL\n"));
561				return (EINVAL);
562			}
563			break;
564		default:
565			return (EINVAL);
566		}
567		return (0);
568
569	case HPCFBIO_GCONF:
570		fbconf = (struct hpcfb_fbconf *)data;
571		if (fbconf->hf_conf_index != 0 &&
572		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
573			return (EINVAL);
574		}
575		*fbconf = sc->sc_fbconf;	/* structure assignment */
576		return (0);
577	case HPCFBIO_SCONF:
578		fbconf = (struct hpcfb_fbconf *)data;
579		if (fbconf->hf_conf_index != 0 &&
580		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
581			return (EINVAL);
582		}
583		/*
584		 * nothing to do because we have only one configuration
585		 */
586		return (0);
587	case HPCFBIO_GDSPCONF:
588		dspconf = (struct hpcfb_dspconf *)data;
589		if ((dspconf->hd_unit_index != 0 &&
590		     dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
591		    (dspconf->hd_conf_index != 0 &&
592		     dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
593			return (EINVAL);
594		}
595		*dspconf = sc->sc_dspconf;	/* structure assignment */
596		return (0);
597	case HPCFBIO_SDSPCONF:
598		dspconf = (struct hpcfb_dspconf *)data;
599		if ((dspconf->hd_unit_index != 0 &&
600		     dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
601		    (dspconf->hd_conf_index != 0 &&
602		     dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
603			return (EINVAL);
604		}
605		/*
606		 * nothing to do
607		 * because we have only one unit and one configuration
608		 */
609		return (0);
610	case HPCFBIO_GOP:
611	case HPCFBIO_SOP:
612		/*
613		 * currently not implemented...
614		 */
615		return (EINVAL);
616	}
617
618	return (EPASSTHROUGH);
619}
620
621paddr_t
622bivideo_mmap(void *ctx, off_t offset, int prot)
623{
624	struct bivideo_softc *sc = (struct bivideo_softc *)ctx;
625
626	if (offset < 0 ||
627	    (sc->sc_fbconf.hf_bytes_per_plane +
628		sc->sc_fbconf.hf_offset) <  offset)
629		return -1;
630
631	return __BTOP((u_long)bootinfo->fb_addr + offset);
632}
633
634
635void
636bivideo_init_backlight(struct bivideo_softc *sc, int inattach)
637{
638	int val = -1;
639
640	if (sc->sc_lcd_inited&BACKLIGHT_INITED)
641		return;
642
643	if (config_hook_call(CONFIG_HOOK_GET,
644	     CONFIG_HOOK_POWER_LCDLIGHT, &val) != -1) {
645		/* we can get real light state */
646		VPRINTF(("bivideo_init_backlight: real backlight=%d\n", val));
647		if (val == 0)
648			sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT;
649		else
650			sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
651		sc->sc_lcd_inited |= BACKLIGHT_INITED;
652	} else if (inattach) {
653		/*
654		   we cannot get real light state in attach time
655		   because light device not yet attached.
656		   we will retry in !inattach.
657		   temporary assume light is on.
658		 */
659		sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
660	} else {
661		/* we cannot get real light state, so work by myself state */
662		sc->sc_lcd_inited |= BACKLIGHT_INITED;
663	}
664}
665
666void
667bivideo_init_brightness(struct bivideo_softc *sc, int inattach)
668{
669	int val = -1;
670
671	if (sc->sc_lcd_inited&BRIGHTNESS_INITED)
672		return;
673
674	VPRINTF(("bivideo_init_brightness\n"));
675	if (config_hook_call(CONFIG_HOOK_GET,
676	     CONFIG_HOOK_BRIGHTNESS_MAX, &val) != -1) {
677		/* we can get real brightness max */
678		VPRINTF(("bivideo_init_brightness: real brightness max=%d\n", val));
679		sc->sc_max_brightness = val;
680		val = -1;
681		if (config_hook_call(CONFIG_HOOK_GET,
682		     CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
683			/* we can get real brightness */
684			VPRINTF(("bivideo_init_brightness: real brightness=%d\n", val));
685			sc->sc_brightness_save = sc->sc_brightness = val;
686		} else {
687			sc->sc_brightness_save =
688			sc->sc_brightness = sc->sc_max_brightness;
689		}
690		sc->sc_lcd_inited |= BRIGHTNESS_INITED;
691	} else if (inattach) {
692		/*
693		   we cannot get real brightness in attach time
694		   because brightness device not yet attached.
695		   we will retry in !inattach.
696		 */
697		sc->sc_max_brightness = -1;
698		sc->sc_brightness = -1;
699		sc->sc_brightness_save = -1;
700	} else {
701		/* we cannot get real brightness */
702		sc->sc_lcd_inited |= BRIGHTNESS_INITED;
703	}
704
705	return;
706}
707
708void
709bivideo_init_contrast(struct bivideo_softc *sc, int inattach)
710{
711	int val = -1;
712
713	if (sc->sc_lcd_inited&CONTRAST_INITED)
714		return;
715
716	VPRINTF(("bivideo_init_contrast\n"));
717	if (config_hook_call(CONFIG_HOOK_GET,
718	     CONFIG_HOOK_CONTRAST_MAX, &val) != -1) {
719		/* we can get real contrast max */
720		VPRINTF(("bivideo_init_contrast: real contrast max=%d\n", val));
721		sc->sc_max_contrast = val;
722		val = -1;
723		if (config_hook_call(CONFIG_HOOK_GET,
724		     CONFIG_HOOK_CONTRAST, &val) != -1) {
725			/* we can get real contrast */
726			VPRINTF(("bivideo_init_contrast: real contrast=%d\n", val));
727			sc->sc_contrast = val;
728		} else {
729			sc->sc_contrast = sc->sc_max_contrast;
730		}
731		sc->sc_lcd_inited |= CONTRAST_INITED;
732	} else if (inattach) {
733		/*
734		   we cannot get real contrast in attach time
735		   because contrast device not yet attached.
736		   we will retry in !inattach.
737		 */
738		sc->sc_max_contrast = -1;
739		sc->sc_contrast = -1;
740	} else {
741		/* we cannot get real contrast */
742		sc->sc_lcd_inited |= CONTRAST_INITED;
743	}
744
745	return;
746}
747
748void
749bivideo_set_brightness(struct bivideo_softc *sc, int val)
750{
751	sc->sc_brightness = val;
752
753	config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS, &val);
754	if (config_hook_call(CONFIG_HOOK_GET,
755	     CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
756		sc->sc_brightness = val;
757	}
758}
759
760void
761bivideo_set_contrast(struct bivideo_softc *sc, int val)
762{
763	sc->sc_contrast = val;
764
765	config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST, &val);
766	if (config_hook_call(CONFIG_HOOK_GET,
767	     CONFIG_HOOK_CONTRAST, &val) != -1) {
768		sc->sc_contrast = val;
769	}
770}
771