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