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