xref: /netbsd-src/sys/arch/hpcmips/dev/mq200.c (revision 17dd36da8292193180754d5047c0926dbb56818c)
1 /*	$NetBSD: mq200.c,v 1.14 2001/03/26 09:40:45 sato 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 infomation.\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 #if BYTE_ORDER == LITTLE_ENDIAN
417 		fb->hf_swap_flags = HPCFB_SWAP_BYTE;
418 #endif
419 		fb->hf_pack_width = 16;
420 		fb->hf_pixels_per_pack = 1;
421 		fb->hf_pixel_width = 16;
422 
423 		fb->hf_class_data_length = sizeof(struct hf_rgb_tag);
424 		fb->hf_u.hf_rgb.hf_flags = 0;	/* reserved for future use */
425 
426 		fb->hf_u.hf_rgb.hf_red_width = 5;
427 		fb->hf_u.hf_rgb.hf_red_shift = 11;
428 		fb->hf_u.hf_rgb.hf_green_width = 6;
429 		fb->hf_u.hf_rgb.hf_green_shift = 5;
430 		fb->hf_u.hf_rgb.hf_blue_width = 5;
431 		fb->hf_u.hf_rgb.hf_blue_shift = 0;
432 		fb->hf_u.hf_rgb.hf_alpha_width = 0;
433 		fb->hf_u.hf_rgb.hf_alpha_shift = 0;
434 		break;
435 
436 	default:
437 		printf("unknown type (=%d).\n", bootinfo->fb_type);
438 		return (-1);
439 		break;
440 	}
441 
442 	return (0); /* no error */
443 }
444 
445 int
446 mq200_ioctl(v, cmd, data, flag, p)
447 	void *v;
448 	u_long cmd;
449 	caddr_t data;
450 	int flag;
451 	struct proc *p;
452 {
453 	struct mq200_softc *sc = (struct mq200_softc *)v;
454 	struct hpcfb_fbconf *fbconf;
455 	struct hpcfb_dspconf *dspconf;
456 	struct wsdisplay_cmap *cmap;
457 	struct wsdisplay_param *dispparam;
458 
459 	switch (cmd) {
460 	case WSDISPLAYIO_GETCMAP:
461 		cmap = (struct wsdisplay_cmap*)data;
462 
463 		if (sc->sc_fbconf.hf_class != HPCFB_CLASS_INDEXCOLOR ||
464 		    sc->sc_fbconf.hf_pack_width != 8 ||
465 		    256 <= cmap->index ||
466 		    256 < (cmap->index + cmap->count))
467 			return (EINVAL);
468 
469 #if 0
470 		if (!uvm_useracc(cmap->red, cmap->count, B_WRITE) ||
471 		    !uvm_useracc(cmap->green, cmap->count, B_WRITE) ||
472 		    !uvm_useracc(cmap->blue, cmap->count, B_WRITE))
473 			return (EFAULT);
474 
475 		copyout(&bivideo_cmap_r[cmap->index], cmap->red, cmap->count);
476 		copyout(&bivideo_cmap_g[cmap->index], cmap->green,cmap->count);
477 		copyout(&bivideo_cmap_b[cmap->index], cmap->blue, cmap->count);
478 #endif
479 
480 		return (0);
481 
482 	case WSDISPLAYIO_PUTCMAP:
483 		/*
484 		 * This driver can't set color map.
485 		 */
486 		return (EINVAL);
487 
488 	case WSDISPLAYIO_SVIDEO:
489 		if (*(int *)data == WSDISPLAYIO_VIDEO_OFF)
490 			sc->sc_powerstate |= PWRSTAT_VIDEOOFF;
491 		else
492 			sc->sc_powerstate &= ~PWRSTAT_VIDEOOFF;
493 		mq200_update_powerstate(sc, PWRSTAT_ALL);
494 		return 0;
495 
496 	case WSDISPLAYIO_GVIDEO:
497 		*(int *)data = (sc->sc_powerstate&PWRSTAT_VIDEOOFF) ?
498 				WSDISPLAYIO_VIDEO_OFF:WSDISPLAYIO_VIDEO_ON;
499 		return 0;
500 
501 	case WSDISPLAYIO_GETPARAM:
502 		dispparam = (struct wsdisplay_param*)data;
503 		switch (dispparam->param) {
504 		case WSDISPLAYIO_PARAM_BACKLIGHT:
505 			VPRINTF("ioctl: GET:BACKLIGHT\n");
506 			mq200_init_brightness(sc, 0);
507 			mq200_init_backlight(sc, 0);
508 			VPRINTF("ioctl: GET:(real)BACKLIGHT %d\n",
509 				 (sc->sc_powerstate&PWRSTAT_BACKLIGHT)? 1: 0);
510 			dispparam->min = 0;
511 			dispparam->max = 1;
512 			if (sc->sc_max_brightness > 0)
513 				dispparam->curval = sc->sc_brightness > 0? 1: 0;
514 			else
515 				dispparam->curval =
516 				    (sc->sc_powerstate&PWRSTAT_BACKLIGHT) ? 1: 0;
517 			VPRINTF("ioctl: GET:BACKLIGHT:%d(%s)\n",
518 				dispparam->curval,
519 				sc->sc_max_brightness > 0? "brightness": "light");
520 			return 0;
521 			break;
522 		case WSDISPLAYIO_PARAM_CONTRAST:
523 			VPRINTF("ioctl: GET:CONTRAST\n");
524 			mq200_init_contrast(sc, 0);
525 			if (sc->sc_max_contrast > 0) {
526 				dispparam->min = 0;
527 				dispparam->max = sc->sc_max_contrast;
528 				dispparam->curval = sc->sc_contrast;
529 				VPRINTF("ioctl: GET:CONTRAST max=%d, current=%d\n", sc->sc_max_contrast, sc->sc_contrast);
530 				return 0;
531 			} else {
532 				VPRINTF("ioctl: GET:CONTRAST EINVAL\n");
533 				return (EINVAL);
534 			}
535 			break;
536 		case WSDISPLAYIO_PARAM_BRIGHTNESS:
537 			VPRINTF("ioctl: GET:BRIGHTNESS\n");
538 			mq200_init_brightness(sc, 0);
539 			if (sc->sc_max_brightness > 0) {
540 				dispparam->min = 0;
541 				dispparam->max = sc->sc_max_brightness;
542 				dispparam->curval = sc->sc_brightness;
543 				VPRINTF("ioctl: GET:BRIGHTNESS max=%d, current=%d\n", sc->sc_max_brightness, sc->sc_brightness);
544 				return 0;
545 			} else {
546 				VPRINTF("ioctl: GET:BRIGHTNESS EINVAL\n");
547 				return (EINVAL);
548 			}
549 			return (EINVAL);
550 		default:
551 			return (EINVAL);
552 		}
553 		return (0);
554 
555 	case WSDISPLAYIO_SETPARAM:
556 		dispparam = (struct wsdisplay_param*)data;
557 		switch (dispparam->param) {
558 		case WSDISPLAYIO_PARAM_BACKLIGHT:
559 			VPRINTF("ioctl: SET:BACKLIGHT\n");
560 			if (dispparam->curval < 0 ||
561 			    1 < dispparam->curval)
562 				return (EINVAL);
563 			mq200_init_brightness(sc, 0);
564 			VPRINTF("ioctl: SET:max brightness=%d\n", sc->sc_max_brightness);
565 			if (sc->sc_max_brightness > 0) { /* dimmer */
566 				if (dispparam->curval == 0){
567 					sc->sc_brightness_save = sc->sc_brightness;
568 					mq200_set_brightness(sc, 0);	/* min */
569 				} else {
570 					if (sc->sc_brightness_save == 0)
571 						sc->sc_brightness_save = sc->sc_max_brightness;
572 					mq200_set_brightness(sc, sc->sc_brightness_save);
573 				}
574 				VPRINTF("ioctl: SET:BACKLIGHT: brightness=%d\n", sc->sc_brightness);
575 			} else { /* off */
576 				if (dispparam->curval == 0)
577 					sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT;
578 				else
579 					sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
580 				VPRINTF("ioctl: SET:BACKLIGHT: powerstate %d\n",
581 						(sc->sc_powerstate & PWRSTAT_BACKLIGHT)?1:0);
582 				mq200_update_powerstate(sc, PWRSTAT_BACKLIGHT);
583 				VPRINTF("ioctl: SET:BACKLIGHT:%d\n",
584 					(sc->sc_powerstate & PWRSTAT_BACKLIGHT)?1:0);
585 			}
586 			return 0;
587 			break;
588 		case WSDISPLAYIO_PARAM_CONTRAST:
589 			VPRINTF("ioctl: SET:CONTRAST\n");
590 			mq200_init_contrast(sc, 0);
591 			if (dispparam->curval < 0 ||
592 			    sc->sc_max_contrast < dispparam->curval)
593 				return (EINVAL);
594 			if (sc->sc_max_contrast > 0) {
595 				int org = sc->sc_contrast;
596 				mq200_set_contrast(sc, dispparam->curval);
597 				VPRINTF("ioctl: SET:CONTRAST org=%d, current=%d\n", org, sc->sc_contrast);
598 				VPRINTF("ioctl: SETPARAM:CONTRAST org=%d, current=%d\n", org, sc->sc_contrast);
599 				return 0;
600 			} else {
601 				VPRINTF("ioctl: SET:CONTRAST EINVAL\n");
602 				return (EINVAL);
603 			}
604 			break;
605 		case WSDISPLAYIO_PARAM_BRIGHTNESS:
606 			VPRINTF("ioctl: SET:BRIGHTNESS\n");
607 			mq200_init_brightness(sc, 0);
608 			if (dispparam->curval < 0 ||
609 			    sc->sc_max_brightness < dispparam->curval)
610 				return (EINVAL);
611 			if (sc->sc_max_brightness > 0) {
612 				int org = sc->sc_brightness;
613 				mq200_set_brightness(sc, dispparam->curval);
614 				VPRINTF("ioctl: SET:BRIGHTNESS org=%d, current=%d\n", org, sc->sc_brightness);
615 				return 0;
616 			} else {
617 				VPRINTF("ioctl: SET:BRIGHTNESS EINVAL\n");
618 				return (EINVAL);
619 			}
620 			break;
621 		default:
622 			return (EINVAL);
623 		}
624 		return (0);
625 
626 	case HPCFBIO_GCONF:
627 		fbconf = (struct hpcfb_fbconf *)data;
628 		if (fbconf->hf_conf_index != 0 &&
629 		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
630 			return (EINVAL);
631 		}
632 		*fbconf = sc->sc_fbconf;	/* structure assignment */
633 		return (0);
634 	case HPCFBIO_SCONF:
635 		fbconf = (struct hpcfb_fbconf *)data;
636 		if (fbconf->hf_conf_index != 0 &&
637 		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
638 			return (EINVAL);
639 		}
640 		/*
641 		 * nothing to do because we have only one configration
642 		 */
643 		return (0);
644 	case HPCFBIO_GDSPCONF:
645 		dspconf = (struct hpcfb_dspconf *)data;
646 		if ((dspconf->hd_unit_index != 0 &&
647 		     dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
648 		    (dspconf->hd_conf_index != 0 &&
649 		     dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
650 			return (EINVAL);
651 		}
652 		*dspconf = sc->sc_dspconf;	/* structure assignment */
653 		return (0);
654 	case HPCFBIO_SDSPCONF:
655 		dspconf = (struct hpcfb_dspconf *)data;
656 		if ((dspconf->hd_unit_index != 0 &&
657 		     dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
658 		    (dspconf->hd_conf_index != 0 &&
659 		     dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
660 			return (EINVAL);
661 		}
662 		/*
663 		 * nothing to do
664 		 * because we have only one unit and one configration
665 		 */
666 		return (0);
667 	case HPCFBIO_GOP:
668 	case HPCFBIO_SOP:
669 		/*
670 		 * curently not implemented...
671 		 */
672 		return (EINVAL);
673 	}
674 
675 	return (ENOTTY);
676 }
677 
678 paddr_t
679 mq200_mmap(ctx, offset, prot)
680 	void *ctx;
681 	off_t offset;
682 	int prot;
683 {
684 	struct mq200_softc *sc = (struct mq200_softc *)ctx;
685 
686 	if (offset < 0 || MQ200_MAPSIZE <= offset)
687 		return -1;
688 
689 	return mips_btop(sc->sc_baseaddr + offset);
690 }
691 
692 
693 void
694 mq200_init_backlight(sc, inattach)
695 	struct mq200_softc *sc;
696 	int inattach;
697 {
698 	int val = -1;
699 
700 	if (sc->sc_lcd_inited&BACKLIGHT_INITED)
701 		return;
702 
703 	if (config_hook_call(CONFIG_HOOK_GET,
704 	     CONFIG_HOOK_POWER_LCDLIGHT, &val) != -1) {
705 		/* we can get real light state */
706 		VPRINTF("init_backlight: real backlight=%d\n", val);
707 		if (val == 0)
708 			sc->sc_powerstate &= ~PWRSTAT_BACKLIGHT;
709 		else
710 			sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
711 		sc->sc_lcd_inited |= BACKLIGHT_INITED;
712 	} else if (inattach) {
713 		/*
714 		   we cannot get real light state in attach time
715 		   because light device not yet attached.
716 		   we will retry in !inattach.
717 		   temporary assume light is on.
718 		 */
719 		sc->sc_powerstate |= PWRSTAT_BACKLIGHT;
720 	} else {
721 		/* we cannot get real light state, so work by myself state */
722 		sc->sc_lcd_inited |= BACKLIGHT_INITED;
723 	}
724 }
725 
726 void
727 mq200_init_brightness(sc, inattach)
728 	struct mq200_softc *sc;
729 	int inattach;
730 {
731 	int val = -1;
732 
733 	if (sc->sc_lcd_inited&BRIGHTNESS_INITED)
734 		return;
735 
736 	VPRINTF("init_brightness\n");
737 	if (config_hook_call(CONFIG_HOOK_GET,
738 	     CONFIG_HOOK_BRIGHTNESS_MAX, &val) != -1) {
739 		/* we can get real brightness max */
740 		VPRINTF("init_brightness: real brightness max=%d\n", val);
741 		sc->sc_max_brightness = val;
742 		val = -1;
743 		if (config_hook_call(CONFIG_HOOK_GET,
744 		     CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
745 			/* we can get real brightness */
746 			VPRINTF("init_brightness: real brightness=%d\n", val);
747 			sc->sc_brightness_save = sc->sc_brightness = val;
748 		} else {
749 			sc->sc_brightness_save =
750 			sc->sc_brightness = sc->sc_max_brightness;
751 		}
752 		sc->sc_lcd_inited |= BRIGHTNESS_INITED;
753 	} else if (inattach) {
754 		/*
755 		   we cannot get real brightness in attach time
756 		   because brightness device not yet attached.
757 		   we will retry in !inattach.
758 		 */
759 		sc->sc_max_brightness = -1;
760 		sc->sc_brightness = -1;
761 		sc->sc_brightness_save = -1;
762 	} else {
763 		/* we cannot get real brightness */
764 		sc->sc_lcd_inited |= BRIGHTNESS_INITED;
765 	}
766 
767 	return;
768 }
769 
770 
771 void
772 mq200_init_contrast(sc, inattach)
773 	struct mq200_softc *sc;
774 	int inattach;
775 {
776 	int val = -1;
777 
778 	if (sc->sc_lcd_inited&CONTRAST_INITED)
779 		return;
780 
781 	VPRINTF("init_contrast\n");
782 	if (config_hook_call(CONFIG_HOOK_GET,
783 	     CONFIG_HOOK_CONTRAST_MAX, &val) != -1) {
784 		/* we can get real contrast max */
785 		VPRINTF("init_contrast: real contrast max=%d\n", val);
786 		sc->sc_max_contrast = val;
787 		val = -1;
788 		if (config_hook_call(CONFIG_HOOK_GET,
789 		     CONFIG_HOOK_CONTRAST, &val) != -1) {
790 			/* we can get real contrast */
791 			VPRINTF("init_contrast: real contrast=%d\n", val);
792 			sc->sc_contrast = val;
793 		} else {
794 			sc->sc_contrast = sc->sc_max_contrast;
795 		}
796 		sc->sc_lcd_inited |= CONTRAST_INITED;
797 	} else if (inattach) {
798 		/*
799 		   we cannot get real contrast in attach time
800 		   because contrast device not yet attached.
801 		   we will retry in !inattach.
802 		 */
803 		sc->sc_max_contrast = -1;
804 		sc->sc_contrast = -1;
805 	} else {
806 		/* we cannot get real contrast */
807 		sc->sc_lcd_inited |= CONTRAST_INITED;
808 	}
809 
810 	return;
811 }
812 
813 
814 void
815 mq200_set_brightness(sc, val)
816 	struct mq200_softc *sc;
817 	int val;
818 {
819 	sc->sc_brightness = val;
820 
821 	config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_BRIGHTNESS, &val);
822 	if (config_hook_call(CONFIG_HOOK_GET,
823 	     CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
824 		sc->sc_brightness = val;
825 	}
826 }
827 
828 void
829 mq200_set_contrast(sc, val)
830 	struct mq200_softc *sc;
831 	int val;
832 {
833 	sc->sc_contrast = val;
834 
835 	config_hook_call(CONFIG_HOOK_SET, CONFIG_HOOK_CONTRAST, &val);
836 	if (config_hook_call(CONFIG_HOOK_GET,
837 	     CONFIG_HOOK_CONTRAST, &val) != -1) {
838 		sc->sc_contrast = val;
839 	}
840 }
841