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