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