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