1/*	$NetBSD: mq200.c,v 1.36 2022/05/28 15:57:18 andvar Exp $	*/
2
3/*-
4 * Copyright (c) 2000, 2001 TAKEMURA Shin
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. The name of the author may not be used to endorse or promote products
16 *    derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 *
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: mq200.c,v 1.36 2022/05/28 15:57:18 andvar Exp $");
34
35#include <sys/param.h>
36#include <sys/bus.h>
37#include <sys/device.h>
38#include <sys/kernel.h>
39#include <sys/systm.h>
40#include <sys/reboot.h>
41
42#include <uvm/uvm_extern.h>
43
44#include <dev/wscons/wsconsio.h>
45
46#include <mips/locore.h>
47
48#include <machine/bootinfo.h>
49#include <machine/autoconf.h>
50#include <machine/config_hook.h>
51#include <machine/platid.h>
52#include <machine/platid_mask.h>
53
54#include "opt_mq200.h"
55#include <hpcmips/dev/mq200reg.h>
56#include <hpcmips/dev/mq200var.h>
57#include <hpcmips/dev/mq200priv.h>
58
59#include "bivideo.h"
60#if NBIVIDEO > 0
61#include <dev/hpc/bivideovar.h>
62#endif
63
64/*
65 * function prototypes
66 */
67static void	mq200_power(int, void *);
68static int	mq200_hardpower(void *, int, long, void *);
69static int	mq200_fbinit(struct hpcfb_fbconf *);
70static int	mq200_ioctl(void *, u_long, void *, int, struct lwp *);
71static paddr_t	mq200_mmap(void *, off_t offset, int);
72static void	mq200_update_powerstate(struct mq200_softc *, int);
73void	mq200_init_backlight(struct mq200_softc *, int);
74void	mq200_init_brightness(struct mq200_softc *, int);
75void	mq200_init_contrast(struct mq200_softc *, int);
76void	mq200_set_brightness(struct mq200_softc *, int);
77void	mq200_set_contrast(struct mq200_softc *, int);
78
79/*
80 * static variables
81 */
82struct hpcfb_accessops mq200_ha = {
83	mq200_ioctl, mq200_mmap
84};
85
86#ifdef MQ200_DEBUG
87int mq200_debug = MQ200DEBUG_CONF;
88#endif
89
90int
91mq200_probe(bus_space_tag_t iot, bus_space_handle_t ioh)
92{
93	unsigned long regval;
94
95#if NBIVIDEO > 0
96	if (bivideo_dont_attach) /* some video driver already attached */
97		return (0);
98#endif /* NBIVIDEO > 0 */
99
100	regval = bus_space_read_4(iot, ioh, MQ200_PC00R);
101	VPRINTF("probe: vendor id=%04lx product id=%04lx\n",
102	    regval & 0xffff, (regval >> 16) & 0xffff);
103	if (regval != ((MQ200_PRODUCT_ID << 16) | MQ200_VENDOR_ID))
104		return (0);
105
106	return (1);
107}
108
109void
110mq200_attach(struct mq200_softc *sc)
111{
112	unsigned long regval;
113	struct hpcfb_attach_args ha;
114	int console = (bootinfo->bi_cnuse & BI_CNUSE_SERIAL) ? 0 : 1;
115
116	printf(": ");
117	if (mq200_fbinit(&sc->sc_fbconf) != 0) {
118		/* just return so that hpcfb will not be attached */
119		return;
120	}
121
122	sc->sc_fbconf.hf_baseaddr = (u_long)bootinfo->fb_addr;
123	sc->sc_fbconf.hf_offset	= (u_long)sc->sc_fbconf.hf_baseaddr -
124	    MIPS_PHYS_TO_KSEG1(mips_ptob(mips_btop(sc->sc_baseaddr)));
125	DPRINTF("hf_baseaddr=%lx\n", sc->sc_fbconf.hf_baseaddr);
126	DPRINTF("hf_offset=%lx\n", sc->sc_fbconf.hf_offset);
127
128	regval = mq200_read(sc, MQ200_PC08R);
129	printf("MQ200 Rev.%02lx video controller", regval & 0xff);
130	if (console) {
131		printf(", console");
132	}
133	printf("\n");
134        printf("%s: framebuffer address: 0x%08lx\n",
135	    device_xname(sc->sc_dev), (u_long)bootinfo->fb_addr);
136
137	/*
138	 * setup registers
139	 */
140	sc->sc_flags = 0;
141	sc->sc_baseclock = 12288;	/* 12.288 MHz */
142#ifdef MQ200_DEBUG
143	if (bootverbose) {
144		/* dump current setting	*/
145		mq200_dump_all(sc);
146		mq200_dump_pll(sc);
147	}
148#endif
149	mq200_setup_regctx(sc);
150	mq200_mdsetup(sc);
151	if (sc->sc_md) {
152		int mode;
153
154		switch (sc->sc_fbconf.hf_pixel_width) {
155		case  1:	mode = MQ200_GCC_1BPP;		break;
156		case  2:	mode = MQ200_GCC_2BPP;		break;
157		case  4:	mode = MQ200_GCC_4BPP;		break;
158		case  8:	mode = MQ200_GCC_8BPP;		break;
159		case 16:	mode = MQ200_GCC_16BPP_DIRECT;	break;
160		default:
161			printf("%s: %dbpp isn't supported\n",
162			    device_xname(sc->sc_dev), sc->sc_fbconf.hf_pixel_width);
163			return;
164		}
165
166		if (sc->sc_md->md_flags & MQ200_MD_HAVEFP) {
167			sc->sc_flags |= MQ200_SC_GC2_ENABLE;	/* FP	*/
168		}
169#if MQ200_USECRT
170		if (sc->sc_md->md_flags & MQ200_MD_HAVECRT) {
171			int i;
172			sc->sc_flags |= MQ200_SC_GC1_ENABLE;	/* CRT	*/
173			for (i = 0; i < mq200_crt_nparams; i++) {
174				sc->sc_crt = &mq200_crt_params[i];
175				if (sc->sc_md->md_fp_width <=
176				    mq200_crt_params[i].width &&
177				    sc->sc_md->md_fp_height <=
178				    mq200_crt_params[i].height)
179					break;
180			}
181		}
182#endif
183		mq200_setup(sc);
184
185		if (sc->sc_flags & MQ200_SC_GC2_ENABLE)	/* FP	*/
186			mq200_win_enable(sc, MQ200_GC2, mode,
187			    sc->sc_fbconf.hf_baseaddr,
188			    sc->sc_fbconf.hf_width, sc->sc_fbconf.hf_height,
189			    sc->sc_fbconf.hf_bytes_per_plane);
190		if (sc->sc_flags & MQ200_SC_GC1_ENABLE)	/* CRT	*/
191			mq200_win_enable(sc, MQ200_GC1, mode,
192			    sc->sc_fbconf.hf_baseaddr,
193			    sc->sc_fbconf.hf_width, sc->sc_fbconf.hf_height,
194			    sc->sc_fbconf.hf_bytes_per_plane);
195	}
196#ifdef MQ200_DEBUG
197	if (sc->sc_md == NULL || bootverbose) {
198		mq200_dump_pll(sc);
199	}
200#endif
201
202	/* Add a power hook to power saving */
203	sc->sc_mq200pwstate = MQ200_POWERSTATE_D0;
204	sc->sc_powerhook = powerhook_establish(device_xname(sc->sc_dev),
205	    mq200_power, sc);
206	if (sc->sc_powerhook == NULL)
207		printf("%s: WARNING: unable to establish power hook\n",
208		    device_xname(sc->sc_dev));
209
210	/* Add a hard power hook to power saving */
211	sc->sc_hardpowerhook = config_hook(CONFIG_HOOK_PMEVENT,
212	    CONFIG_HOOK_PMEVENT_HARDPOWER,
213	    CONFIG_HOOK_SHARE,
214	    mq200_hardpower, sc);
215	if (sc->sc_hardpowerhook == NULL)
216		printf("%s: WARNING: unable to establish hard power hook\n",
217		    device_xname(sc->sc_dev));
218
219	/* initialize backlight brightness and lcd contrast */
220	sc->sc_lcd_inited = 0;
221	mq200_init_brightness(sc, 1);
222	mq200_init_contrast(sc, 1);
223	mq200_init_backlight(sc, 1);
224
225	if (console && hpcfb_cnattach(&sc->sc_fbconf) != 0) {
226		panic("mq200_attach: can't init fb console");
227	}
228
229	ha.ha_console = console;
230	ha.ha_accessops = &mq200_ha;
231	ha.ha_accessctx = sc;
232	ha.ha_curfbconf = 0;
233	ha.ha_nfbconf = 1;
234	ha.ha_fbconflist = &sc->sc_fbconf;
235	ha.ha_curdspconf = 0;
236	ha.ha_ndspconf = 1;
237	ha.ha_dspconflist = &sc->sc_dspconf;
238
239	config_found(sc->sc_dev, &ha, hpcfbprint, CFARGS_NONE);
240
241#if NBIVIDEO > 0
242	/*
243	 * bivideo is no longer need
244	 */
245	bivideo_dont_attach = 1;
246#endif /* NBIVIDEO > 0 */
247}
248
249static void
250mq200_update_powerstate(struct mq200_softc *sc, int updates)
251{
252
253	if (updates & PWRSTAT_LCD)
254		config_hook_call(CONFIG_HOOK_POWERCONTROL,
255		    CONFIG_HOOK_POWERCONTROL_LCD,
256		    (void*)!(sc->sc_powerstate &
257			(PWRSTAT_VIDEOOFF|PWRSTAT_SUSPEND)));
258
259	if (updates & PWRSTAT_BACKLIGHT)
260		config_hook_call(CONFIG_HOOK_POWERCONTROL,
261		    CONFIG_HOOK_POWERCONTROL_LCDLIGHT,
262		    (void*)(!(sc->sc_powerstate &
263			(PWRSTAT_VIDEOOFF|PWRSTAT_SUSPEND)) &&
264			(sc->sc_powerstate & PWRSTAT_BACKLIGHT)));
265}
266
267static void
268mq200_power(int why, void *arg)
269{
270	struct mq200_softc *sc = arg;
271
272	switch (why) {
273	case PWR_SUSPEND:
274		sc->sc_powerstate |= PWRSTAT_SUSPEND;
275		mq200_update_powerstate(sc, PWRSTAT_ALL);
276		break;
277	case PWR_STANDBY:
278		sc->sc_powerstate |= PWRSTAT_SUSPEND;
279		mq200_update_powerstate(sc, PWRSTAT_ALL);
280		break;
281	case PWR_RESUME:
282		sc->sc_powerstate &= ~PWRSTAT_SUSPEND;
283		mq200_update_powerstate(sc, PWRSTAT_ALL);
284		break;
285	}
286}
287
288static int
289mq200_hardpower(void *ctx, int type, long id, void *msg)
290{
291	struct mq200_softc *sc = ctx;
292	int why = (int)msg;
293
294	switch (why) {
295	case PWR_SUSPEND:
296		sc->sc_mq200pwstate = MQ200_POWERSTATE_D2;
297		break;
298	case PWR_STANDBY:
299		sc->sc_mq200pwstate = MQ200_POWERSTATE_D3;
300		break;
301	case PWR_RESUME:
302		sc->sc_mq200pwstate = MQ200_POWERSTATE_D0;
303		break;
304	}
305
306	bus_space_write_4(sc->sc_iot, sc->sc_ioh,
307	    MQ200_PMCSR, sc->sc_mq200pwstate);
308
309	/*
310	 * you should wait until the
311	 * power state transit sequence will end.
312	 */
313	{
314		unsigned long tmp;
315		do {
316			tmp = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
317			    MQ200_PMCSR);
318		} while ((tmp & 0x3) != (sc->sc_mq200pwstate & 0x3));
319		delay(100000); /* XXX */
320	}
321
322	return (0);
323}
324
325
326static int
327mq200_fbinit(struct hpcfb_fbconf *fb)
328{
329
330	/*
331	 * get fb settings from bootinfo
332	 */
333	if (bootinfo == NULL ||
334	    bootinfo->fb_addr == 0 ||
335	    bootinfo->fb_line_bytes == 0 ||
336	    bootinfo->fb_width == 0 ||
337	    bootinfo->fb_height == 0) {
338		printf("no frame buffer information.\n");
339		return (-1);
340	}
341
342	/* zero fill */
343	memset(fb, 0, sizeof(*fb));
344
345	fb->hf_conf_index	= 0;	/* configuration index		*/
346	fb->hf_nconfs		= 1;   	/* how many configurations	*/
347	strcpy(fb->hf_name, "built-in video");
348					/* frame buffer name		*/
349	strcpy(fb->hf_conf_name, "default");
350					/* configuration name		*/
351	fb->hf_height		= bootinfo->fb_height;
352	fb->hf_width		= bootinfo->fb_width;
353	fb->hf_baseaddr		= mips_ptob(mips_btop(bootinfo->fb_addr));
354	fb->hf_offset		= (u_long)bootinfo->fb_addr - fb->hf_baseaddr;
355					/* frame buffer start offset   	*/
356	fb->hf_bytes_per_line	= bootinfo->fb_line_bytes;
357	fb->hf_nplanes		= 1;
358	fb->hf_bytes_per_plane	= bootinfo->fb_height *
359	    bootinfo->fb_line_bytes;
360
361	fb->hf_access_flags |= HPCFB_ACCESS_BYTE;
362	fb->hf_access_flags |= HPCFB_ACCESS_WORD;
363	fb->hf_access_flags |= HPCFB_ACCESS_DWORD;
364
365	switch (bootinfo->fb_type) {
366		/*
367		 * monochrome
368		 */
369	case BIFB_D1_M2L_1:
370		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
371		/* fall through */
372	case BIFB_D1_M2L_0:
373		fb->hf_class = HPCFB_CLASS_GRAYSCALE;
374		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
375		fb->hf_pack_width = 8;
376		fb->hf_pixels_per_pack = 8;
377		fb->hf_pixel_width = 1;
378		fb->hf_class_data_length = sizeof(struct hf_gray_tag);
379		fb->hf_u.hf_gray.hf_flags = 0;	/* reserved for future use */
380		break;
381
382		/*
383		 * gray scale
384		 */
385	case BIFB_D2_M2L_3:
386	case BIFB_D2_M2L_3x2:
387		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
388		/* fall through */
389	case BIFB_D2_M2L_0:
390	case BIFB_D2_M2L_0x2:
391		fb->hf_class = HPCFB_CLASS_GRAYSCALE;
392		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
393		fb->hf_pack_width = 8;
394		fb->hf_pixels_per_pack = 4;
395		fb->hf_pixel_width = 2;
396		fb->hf_class_data_length = sizeof(struct hf_gray_tag);
397		fb->hf_u.hf_gray.hf_flags = 0;	/* reserved for future use */
398		break;
399
400	case BIFB_D4_M2L_F:
401	case BIFB_D4_M2L_Fx2:
402		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
403		/* fall through */
404	case BIFB_D4_M2L_0:
405	case BIFB_D4_M2L_0x2:
406		fb->hf_class = HPCFB_CLASS_GRAYSCALE;
407		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
408		fb->hf_pack_width = 8;
409		fb->hf_pixels_per_pack = 2;
410		fb->hf_pixel_width = 4;
411		fb->hf_class_data_length = sizeof(struct hf_gray_tag);
412		fb->hf_u.hf_gray.hf_flags = 0;	/* reserved for future use */
413		break;
414
415		/*
416		 * indexed color
417		 */
418	case BIFB_D8_FF:
419		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
420		/* fall through */
421	case BIFB_D8_00:
422		fb->hf_class = HPCFB_CLASS_INDEXCOLOR;
423		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
424		fb->hf_pack_width = 8;
425		fb->hf_pixels_per_pack = 1;
426		fb->hf_pixel_width = 8;
427		fb->hf_class_data_length = sizeof(struct hf_indexed_tag);
428		fb->hf_u.hf_indexed.hf_flags = 0; /* reserved for future use */
429		break;
430
431		/*
432		 * RGB color
433		 */
434	case BIFB_D16_FFFF:
435		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
436		/* fall through */
437	case BIFB_D16_0000:
438		fb->hf_class = HPCFB_CLASS_RGBCOLOR;
439		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
440		fb->hf_order_flags = HPCFB_REVORDER_BYTE;
441		fb->hf_pack_width = 16;
442		fb->hf_pixels_per_pack = 1;
443		fb->hf_pixel_width = 16;
444
445		fb->hf_class_data_length = sizeof(struct hf_rgb_tag);
446		fb->hf_u.hf_rgb.hf_flags = 0;	/* reserved for future use */
447
448		fb->hf_u.hf_rgb.hf_red_width = 5;
449		fb->hf_u.hf_rgb.hf_red_shift = 11;
450		fb->hf_u.hf_rgb.hf_green_width = 6;
451		fb->hf_u.hf_rgb.hf_green_shift = 5;
452		fb->hf_u.hf_rgb.hf_blue_width = 5;
453		fb->hf_u.hf_rgb.hf_blue_shift = 0;
454		fb->hf_u.hf_rgb.hf_alpha_width = 0;
455		fb->hf_u.hf_rgb.hf_alpha_shift = 0;
456		break;
457
458	default:
459		printf("unknown type (=%d).\n", bootinfo->fb_type);
460		return (-1);
461		break;
462	}
463
464	return (0); /* no error */
465}
466
467int
468mq200_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
469{
470	struct mq200_softc *sc = (struct mq200_softc *)v;
471	struct hpcfb_fbconf *fbconf;
472	struct hpcfb_dspconf *dspconf;
473	struct wsdisplay_cmap *cmap;
474	struct wsdisplay_param *dispparam;
475
476	switch (cmd) {
477	case WSDISPLAYIO_GETCMAP:
478		cmap = (struct wsdisplay_cmap *)data;
479
480		if (sc->sc_fbconf.hf_class != HPCFB_CLASS_INDEXCOLOR ||
481		    sc->sc_fbconf.hf_pack_width != 8 ||
482		    256 <= cmap->index ||
483		    256 - cmap->index < cmap->count)
484			return (EINVAL);
485
486		/*
487		 * This driver can't get color map.
488		 */
489		return (EINVAL);
490
491	case WSDISPLAYIO_PUTCMAP:
492		/*
493		 * This driver can't set color map.
494		 */
495		return (EINVAL);
496
497	case WSDISPLAYIO_SVIDEO:
498		if (*(int *)data == WSDISPLAYIO_VIDEO_OFF)
499			sc->sc_powerstate |= PWRSTAT_VIDEOOFF;
500		else
501			sc->sc_powerstate &= ~PWRSTAT_VIDEOOFF;
502		mq200_update_powerstate(sc, PWRSTAT_ALL);
503		return 0;
504
505	case WSDISPLAYIO_GVIDEO:
506		*(int *)data = (sc->sc_powerstate&PWRSTAT_VIDEOOFF) ?
507		    WSDISPLAYIO_VIDEO_OFF:WSDISPLAYIO_VIDEO_ON;
508		return 0;
509
510	case WSDISPLAYIO_GETPARAM:
511		dispparam = (struct wsdisplay_param*)data;
512		switch (dispparam->param) {
513		case WSDISPLAYIO_PARAM_BACKLIGHT:
514			VPRINTF("ioctl: GET:BACKLIGHT\n");
515			mq200_init_brightness(sc, 0);
516			mq200_init_backlight(sc, 0);
517			VPRINTF("ioctl: GET:(real)BACKLIGHT %d\n",
518			    (sc->sc_powerstate&PWRSTAT_BACKLIGHT)? 1: 0);
519			dispparam->min = 0;
520			dispparam->max = 1;
521			if (sc->sc_max_brightness > 0)
522				dispparam->curval = sc->sc_brightness > 0
523				    ? 1: 0;
524			else
525				dispparam->curval =
526				    (sc->sc_powerstate&PWRSTAT_BACKLIGHT)
527				    ? 1: 0;
528			VPRINTF("ioctl: GET:BACKLIGHT:%d(%s)\n",
529			    dispparam->curval,
530			    sc->sc_max_brightness > 0? "brightness": "light");
531			return 0;
532			break;
533		case WSDISPLAYIO_PARAM_CONTRAST:
534			VPRINTF("ioctl: GET:CONTRAST\n");
535			mq200_init_contrast(sc, 0);
536			if (sc->sc_max_contrast > 0) {
537				dispparam->min = 0;
538				dispparam->max = sc->sc_max_contrast;
539				dispparam->curval = sc->sc_contrast;
540				VPRINTF("ioctl: GET:CONTRAST"
541				    " max=%d, current=%d\n",
542				    sc->sc_max_contrast, sc->sc_contrast);
543				return 0;
544			} else {
545				VPRINTF("ioctl: GET:CONTRAST EINVAL\n");
546				return (EINVAL);
547			}
548			break;
549		case WSDISPLAYIO_PARAM_BRIGHTNESS:
550			VPRINTF("ioctl: GET:BRIGHTNESS\n");
551			mq200_init_brightness(sc, 0);
552			if (sc->sc_max_brightness > 0) {
553				dispparam->min = 0;
554				dispparam->max = sc->sc_max_brightness;
555				dispparam->curval = sc->sc_brightness;
556				VPRINTF("ioctl: GET:BRIGHTNESS"
557				    " max=%d, current=%d\n",
558				    sc->sc_max_brightness, sc->sc_brightness);
559				return 0;
560			} else {
561				VPRINTF("ioctl: GET:BRIGHTNESS EINVAL\n");
562				return (EINVAL);
563			}
564			return (EINVAL);
565		default:
566			return (EINVAL);
567		}
568		return (0);
569
570	case WSDISPLAYIO_SETPARAM:
571		dispparam = (struct wsdisplay_param*)data;
572		switch (dispparam->param) {
573		case WSDISPLAYIO_PARAM_BACKLIGHT:
574			VPRINTF("ioctl: SET:BACKLIGHT\n");
575			if (dispparam->curval < 0 ||
576			    1 < dispparam->curval)
577				return (EINVAL);
578			mq200_init_brightness(sc, 0);
579			VPRINTF("ioctl: SET:max brightness=%d\n",
580			    sc->sc_max_brightness);
581			if (sc->sc_max_brightness > 0) { /* dimmer */
582				if (dispparam->curval == 0){
583					sc->sc_brightness_save =
584					    sc->sc_brightness;
585					mq200_set_brightness(sc, 0); /* min */
586				} else {
587					if (sc->sc_brightness_save == 0)
588						sc->sc_brightness_save =
589						    sc->sc_max_brightness;
590					mq200_set_brightness(sc,
591					    sc->sc_brightness_save);
592				}
593				VPRINTF("ioctl: SET:BACKLIGHT:"
594				    " brightness=%d\n", sc->sc_brightness);
595			} else { /* off */
596				if (dispparam->curval == 0)
597					sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT;
598				else
599					sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
600				VPRINTF("ioctl: SET:BACKLIGHT:"
601				    " powerstate %d\n",
602				    (sc->sc_powerstate & PWRSTAT_BACKLIGHT)
603				    ? 1 : 0);
604				mq200_update_powerstate(sc, PWRSTAT_BACKLIGHT);
605				VPRINTF("ioctl: SET:BACKLIGHT:%d\n",
606				    (sc->sc_powerstate & PWRSTAT_BACKLIGHT)
607				    ? 1 : 0);
608			}
609			return 0;
610			break;
611		case WSDISPLAYIO_PARAM_CONTRAST:
612			VPRINTF("ioctl: SET:CONTRAST\n");
613			mq200_init_contrast(sc, 0);
614			if (dispparam->curval < 0 ||
615			    sc->sc_max_contrast < dispparam->curval)
616				return (EINVAL);
617			if (sc->sc_max_contrast > 0) {
618				int org = sc->sc_contrast;
619				mq200_set_contrast(sc, dispparam->curval);
620				VPRINTF("ioctl: SET:CONTRAST"
621				    " org=%d, current=%d\n", org,
622				    sc->sc_contrast);
623				VPRINTF("ioctl: SETPARAM:"
624				    " CONTRAST org=%d, current=%d\n", org,
625				    sc->sc_contrast);
626				return 0;
627			} else {
628				VPRINTF("ioctl: SET:CONTRAST EINVAL\n");
629				return (EINVAL);
630			}
631			break;
632		case WSDISPLAYIO_PARAM_BRIGHTNESS:
633			VPRINTF("ioctl: SET:BRIGHTNESS\n");
634			mq200_init_brightness(sc, 0);
635			if (dispparam->curval < 0 ||
636			    sc->sc_max_brightness < dispparam->curval)
637				return (EINVAL);
638			if (sc->sc_max_brightness > 0) {
639				int org = sc->sc_brightness;
640				mq200_set_brightness(sc, dispparam->curval);
641				VPRINTF("ioctl: SET:BRIGHTNESS"
642				    " org=%d, current=%d\n", org,
643				    sc->sc_brightness);
644				return 0;
645			} else {
646				VPRINTF("ioctl: SET:BRIGHTNESS EINVAL\n");
647				return (EINVAL);
648			}
649			break;
650		default:
651			return (EINVAL);
652		}
653		return (0);
654
655	case HPCFBIO_GCONF:
656		fbconf = (struct hpcfb_fbconf *)data;
657		if (fbconf->hf_conf_index != 0 &&
658		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
659			return (EINVAL);
660		}
661		*fbconf = sc->sc_fbconf;	/* structure assignment */
662		return (0);
663	case HPCFBIO_SCONF:
664		fbconf = (struct hpcfb_fbconf *)data;
665		if (fbconf->hf_conf_index != 0 &&
666		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
667			return (EINVAL);
668		}
669		/*
670		 * nothing to do because we have only one configuration
671		 */
672		return (0);
673	case HPCFBIO_GDSPCONF:
674		dspconf = (struct hpcfb_dspconf *)data;
675		if ((dspconf->hd_unit_index != 0 &&
676		    dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
677		    (dspconf->hd_conf_index != 0 &&
678			dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
679			return (EINVAL);
680		}
681		*dspconf = sc->sc_dspconf;	/* structure assignment */
682		return (0);
683	case HPCFBIO_SDSPCONF:
684		dspconf = (struct hpcfb_dspconf *)data;
685		if ((dspconf->hd_unit_index != 0 &&
686		    dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
687		    (dspconf->hd_conf_index != 0 &&
688			dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
689			return (EINVAL);
690		}
691		/*
692		 * nothing to do
693		 * because we have only one unit and one configuration
694		 */
695		return (0);
696	case HPCFBIO_GOP:
697	case HPCFBIO_SOP:
698		/*
699		 * currently not implemented...
700		 */
701		return (EINVAL);
702	}
703
704	return (EPASSTHROUGH);
705}
706
707paddr_t
708mq200_mmap(void *ctx, off_t offset, int prot)
709{
710	struct mq200_softc *sc = (struct mq200_softc *)ctx;
711
712	if (offset < 0 || MQ200_MAPSIZE <= offset)
713		return -1;
714
715	return mips_btop(sc->sc_baseaddr + offset);
716}
717
718
719void
720mq200_init_backlight(struct mq200_softc *sc, int inattach)
721{
722	int val = -1;
723
724	if (sc->sc_lcd_inited&BACKLIGHT_INITED)
725		return;
726
727	if (config_hook_call(CONFIG_HOOK_GET,
728	    CONFIG_HOOK_POWER_LCDLIGHT, &val) != -1) {
729		/* we can get real light state */
730		VPRINTF("init_backlight: real backlight=%d\n", val);
731		if (val == 0)
732			sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT;
733		else
734			sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
735		sc->sc_lcd_inited |= BACKLIGHT_INITED;
736	} else if (inattach) {
737		/*
738		   we cannot get real light state in attach time
739		   because light device not yet attached.
740		   we will retry in !inattach.
741		   temporary assume light is on.
742		*/
743		sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
744	} else {
745		/* we cannot get real light state, so work by myself state */
746		sc->sc_lcd_inited |= BACKLIGHT_INITED;
747	}
748}
749
750void
751mq200_init_brightness(struct mq200_softc *sc, int inattach)
752{
753	int val = -1;
754
755	if (sc->sc_lcd_inited&BRIGHTNESS_INITED)
756		return;
757
758	VPRINTF("init_brightness\n");
759	if (config_hook_call(CONFIG_HOOK_GET,
760	    CONFIG_HOOK_BRIGHTNESS_MAX, &val) != -1) {
761		/* we can get real brightness max */
762		VPRINTF("init_brightness: real brightness max=%d\n", val);
763		sc->sc_max_brightness = val;
764		val = -1;
765		if (config_hook_call(CONFIG_HOOK_GET,
766		    CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
767			/* we can get real brightness */
768			VPRINTF("init_brightness: real brightness=%d\n", val);
769			sc->sc_brightness_save = sc->sc_brightness = val;
770		} else {
771			sc->sc_brightness_save =
772			    sc->sc_brightness = sc->sc_max_brightness;
773		}
774		sc->sc_lcd_inited |= BRIGHTNESS_INITED;
775	} else if (inattach) {
776		/*
777		   we cannot get real brightness in attach time
778		   because brightness device not yet attached.
779		   we will retry in !inattach.
780		*/
781		sc->sc_max_brightness = -1;
782		sc->sc_brightness = -1;
783		sc->sc_brightness_save = -1;
784	} else {
785		/* we cannot get real brightness */
786		sc->sc_lcd_inited |= BRIGHTNESS_INITED;
787	}
788
789	return;
790}
791
792
793void
794mq200_init_contrast(struct mq200_softc *sc, int inattach)
795{
796	int val = -1;
797
798	if (sc->sc_lcd_inited&CONTRAST_INITED)
799		return;
800
801	VPRINTF("init_contrast\n");
802	if (config_hook_call(CONFIG_HOOK_GET,
803	    CONFIG_HOOK_CONTRAST_MAX, &val) != -1) {
804		/* we can get real contrast max */
805		VPRINTF("init_contrast: real contrast max=%d\n", val);
806		sc->sc_max_contrast = val;
807		val = -1;
808		if (config_hook_call(CONFIG_HOOK_GET,
809		    CONFIG_HOOK_CONTRAST, &val) != -1) {
810			/* we can get real contrast */
811			VPRINTF("init_contrast: real contrast=%d\n", val);
812			sc->sc_contrast = val;
813		} else {
814			sc->sc_contrast = sc->sc_max_contrast;
815		}
816		sc->sc_lcd_inited |= CONTRAST_INITED;
817	} else if (inattach) {
818		/*
819		   we cannot get real contrast in attach time
820		   because contrast device not yet attached.
821		   we will retry in !inattach.
822		*/
823		sc->sc_max_contrast = -1;
824		sc->sc_contrast = -1;
825	} else {
826		/* we cannot get real contrast */
827		sc->sc_lcd_inited |= CONTRAST_INITED;
828	}
829
830	return;
831}
832
833
834void
835mq200_set_brightness(struct mq200_softc *sc, int val)
836{
837	sc->sc_brightness = val;
838
839	config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS, &val);
840	if (config_hook_call(CONFIG_HOOK_GET,
841	    CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
842		sc->sc_brightness = val;
843	}
844}
845
846void
847mq200_set_contrast(struct mq200_softc *sc, int val)
848{
849	sc->sc_contrast = val;
850
851	config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST, &val);
852	if (config_hook_call(CONFIG_HOOK_GET,
853	    CONFIG_HOOK_CONTRAST, &val) != -1) {
854		sc->sc_contrast = val;
855	}
856}
857