1/*	$NetBSD: plumvideo.c,v 1.39 2008/04/28 20:23:21 martin Exp $ */
2
3/*-
4 * Copyright (c) 1999-2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33__KERNEL_RCSID(0, "$NetBSD: plumvideo.c,v 1.39 2008/04/28 20:23:21 martin Exp $");
34
35#undef PLUMVIDEODEBUG
36
37#include "plumohci.h" /* Plum2 OHCI shared memory allocated on V-RAM */
38#include "bivideo.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/device.h>
43
44#include <sys/ioctl.h>
45#include <sys/buf.h>
46#include <uvm/uvm_extern.h>
47
48#include <dev/cons.h> /* consdev */
49
50#include <mips/cache.h>
51
52#include <machine/bus.h>
53#include <machine/intr.h>
54#include <machine/config_hook.h>
55
56#include <hpcmips/tx/tx39var.h>
57#include <hpcmips/dev/plumvar.h>
58#include <hpcmips/dev/plumicuvar.h>
59#include <hpcmips/dev/plumpowervar.h>
60#include <hpcmips/dev/plumvideoreg.h>
61
62#include <machine/bootinfo.h>
63
64#include <dev/wscons/wsdisplayvar.h>
65#include <dev/rasops/rasops.h>
66#include <dev/hpc/video_subr.h>
67
68#include <dev/wscons/wsconsio.h>
69#include <dev/hpc/hpcfbvar.h>
70#include <dev/hpc/hpcfbio.h>
71#if NBIVIDEO > 0
72#include <dev/hpc/bivideovar.h>
73#endif
74
75#ifdef PLUMVIDEODEBUG
76int	plumvideo_debug = 1;
77#define	DPRINTF(arg) if (plumvideo_debug) printf arg;
78#define	DPRINTFN(n, arg) if (plumvideo_debug > (n)) printf arg;
79#else
80#define	DPRINTF(arg)
81#define DPRINTFN(n, arg)
82#endif
83
84struct plumvideo_softc {
85	struct device sc_dev;
86	tx_chipset_tag_t sc_tc;
87	plum_chipset_tag_t sc_pc;
88
89	void *sc_powerhook;	/* power management hook */
90	int sc_console;
91
92	int sc_backlight;
93	int sc_brightness;
94	int sc_max_brightness;
95
96	/* control register */
97	bus_space_tag_t sc_regt;
98	bus_space_handle_t sc_regh;
99	/* frame buffer */
100	bus_space_tag_t sc_fbiot;
101	bus_space_handle_t sc_fbioh;
102	/* clut buffer (8bpp only) */
103	bus_space_tag_t sc_clutiot;
104	bus_space_handle_t sc_clutioh;
105	/* bitblt */
106	bus_space_tag_t sc_bitbltt;
107	bus_space_handle_t sc_bitblth;
108
109	struct video_chip sc_chip;
110	struct hpcfb_fbconf sc_fbconf;
111	struct hpcfb_dspconf sc_dspconf;
112};
113
114int	plumvideo_match(struct device*, struct cfdata*, void*);
115void	plumvideo_attach(struct device*, struct device*, void*);
116
117int	plumvideo_ioctl(void *, u_long, void *, int, struct lwp *);
118paddr_t	plumvideo_mmap(void *, off_t, int);
119
120CFATTACH_DECL(plumvideo, sizeof(struct plumvideo_softc),
121    plumvideo_match, plumvideo_attach, NULL, NULL);
122
123struct hpcfb_accessops plumvideo_ha = {
124	plumvideo_ioctl, plumvideo_mmap
125};
126
127int	plumvideo_power(void *, int, long, void *);
128
129int	plumvideo_init(struct plumvideo_softc *, int *);
130void	plumvideo_hpcfbinit(struct plumvideo_softc *, int);
131
132void	plumvideo_clut_default(struct plumvideo_softc *);
133void	plumvideo_clut_set(struct plumvideo_softc *, u_int32_t *, int, int);
134void	plumvideo_clut_get(struct plumvideo_softc *, u_int32_t *, int, int);
135void	__plumvideo_clut_access(struct plumvideo_softc *, u_int32_t *, int, int,
136	    void (*)(bus_space_tag_t, bus_space_handle_t, u_int32_t *, int, int));
137static void _flush_cache(void) __attribute__((__unused__)); /* !!! */
138static void plumvideo_init_backlight(struct plumvideo_softc *);
139static void plumvideo_backlight(struct plumvideo_softc *, int);
140static void plumvideo_brightness(struct plumvideo_softc *, int);
141
142#ifdef PLUMVIDEODEBUG
143void	plumvideo_dump(struct plumvideo_softc*);
144#endif
145
146#define ON	1
147#define OFF	0
148
149int
150plumvideo_match(struct device *parent, struct cfdata *cf, void *aux)
151{
152	/*
153	 * VRAM area also uses as UHOSTC shared RAM.
154	 */
155	return (2); /* 1st attach group */
156}
157
158void
159plumvideo_attach(struct device *parent, struct device *self, void *aux)
160{
161	struct plum_attach_args *pa = aux;
162	struct plumvideo_softc *sc = (void*)self;
163	struct hpcfb_attach_args ha;
164	int console, reverse_flag;
165
166	sc->sc_console = console = cn_tab ? 0 : 1;
167	sc->sc_pc	= pa->pa_pc;
168	sc->sc_regt	= pa->pa_regt;
169	sc->sc_fbiot = sc->sc_clutiot = sc->sc_bitbltt  = pa->pa_iot;
170
171	printf(": ");
172
173	/* map register area */
174	if (bus_space_map(sc->sc_regt, PLUM_VIDEO_REGBASE,
175	    PLUM_VIDEO_REGSIZE, 0, &sc->sc_regh)) {
176		printf("register map failed\n");
177		return;
178	}
179
180	/* initialize backlight and brightness values */
181	plumvideo_init_backlight(sc);
182
183	/* power control */
184	plumvideo_power(sc, 0, 0,
185	    (void *)(console ? PWR_RESUME : PWR_SUSPEND));
186	/* Add a hard power hook to power saving */
187	sc->sc_powerhook = config_hook(CONFIG_HOOK_PMEVENT,
188	    CONFIG_HOOK_PMEVENT_HARDPOWER,
189	    CONFIG_HOOK_SHARE,
190	    plumvideo_power, sc);
191	if (sc->sc_powerhook == 0)
192		printf("WARNING unable to establish hard power hook");
193
194	/*
195	 *  Initialize LCD controller
196	 *	map V-RAM area.
197	 *	reinstall bootinfo structure.
198	 *	some OHCI shared-buffer hack. XXX
199	 */
200	if (plumvideo_init(sc, &reverse_flag) != 0)
201		return;
202
203	printf("\n");
204
205	/* Attach frame buffer device */
206	plumvideo_hpcfbinit(sc, reverse_flag);
207
208#ifdef PLUMVIDEODEBUG
209	if (plumvideo_debug > 0)
210		plumvideo_dump(sc);
211	/* attach debug draw routine (debugging use) */
212	video_attach_drawfunc(&sc->sc_chip);
213	tx_conf_register_video(sc->sc_pc->pc_tc, &sc->sc_chip);
214#endif /* PLUMVIDEODEBUG */
215
216	if(console && hpcfb_cnattach(&sc->sc_fbconf) != 0) {
217		panic("plumvideo_attach: can't init fb console");
218	}
219
220	ha.ha_console = console;
221	ha.ha_accessops = &plumvideo_ha;
222	ha.ha_accessctx = sc;
223	ha.ha_curfbconf = 0;
224	ha.ha_nfbconf = 1;
225	ha.ha_fbconflist = &sc->sc_fbconf;
226	ha.ha_curdspconf = 0;
227	ha.ha_ndspconf = 1;
228	ha.ha_dspconflist = &sc->sc_dspconf;
229
230	config_found(self, &ha, hpcfbprint);
231#if NBIVIDEO > 0
232	/* bivideo is no longer need */
233	bivideo_dont_attach = 1;
234#endif /* NBIVIDEO > 0 */
235}
236
237void
238plumvideo_hpcfbinit(struct plumvideo_softc *sc, int reverse_flag)
239{
240	struct hpcfb_fbconf *fb = &sc->sc_fbconf;
241	struct video_chip *chip = &sc->sc_chip;
242	vaddr_t fbvaddr = (vaddr_t)sc->sc_fbioh;
243	int height = chip->vc_fbheight;
244	int width = chip->vc_fbwidth;
245	int depth = chip->vc_fbdepth;
246
247	memset(fb, 0, sizeof(struct hpcfb_fbconf));
248
249	fb->hf_conf_index	= 0;	/* configuration index		*/
250	fb->hf_nconfs		= 1;   	/* how many configurations	*/
251	strncpy(fb->hf_name, "PLUM built-in video", HPCFB_MAXNAMELEN);
252	/* frame buffer name		*/
253	strncpy(fb->hf_conf_name, "LCD", HPCFB_MAXNAMELEN);
254	/* configuration name		*/
255	fb->hf_height		= height;
256	fb->hf_width		= width;
257	fb->hf_baseaddr		= (u_long)fbvaddr;
258	fb->hf_offset		= (u_long)fbvaddr - mips_ptob(mips_btop(fbvaddr));
259	/* frame buffer start offset   	*/
260	fb->hf_bytes_per_line	= (width * depth) / NBBY;
261	fb->hf_nplanes		= 1;
262	fb->hf_bytes_per_plane	= height * fb->hf_bytes_per_line;
263
264	fb->hf_access_flags |= HPCFB_ACCESS_BYTE;
265	fb->hf_access_flags |= HPCFB_ACCESS_WORD;
266	fb->hf_access_flags |= HPCFB_ACCESS_DWORD;
267	if (reverse_flag)
268		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
269
270	switch (depth) {
271	default:
272		panic("plumvideo_hpcfbinit: not supported color depth");
273		/* NOTREACHED */
274	case 16:
275		fb->hf_class = HPCFB_CLASS_RGBCOLOR;
276		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
277		fb->hf_pack_width = 16;
278		fb->hf_pixels_per_pack = 1;
279		fb->hf_pixel_width = 16;
280
281		fb->hf_class_data_length = sizeof(struct hf_rgb_tag);
282		/* reserved for future use */
283		fb->hf_u.hf_rgb.hf_flags = 0;
284
285		fb->hf_u.hf_rgb.hf_red_width = 5;
286		fb->hf_u.hf_rgb.hf_red_shift = 11;
287		fb->hf_u.hf_rgb.hf_green_width = 6;
288		fb->hf_u.hf_rgb.hf_green_shift = 5;
289		fb->hf_u.hf_rgb.hf_blue_width = 5;
290		fb->hf_u.hf_rgb.hf_blue_shift = 0;
291		fb->hf_u.hf_rgb.hf_alpha_width = 0;
292		fb->hf_u.hf_rgb.hf_alpha_shift = 0;
293		break;
294
295	case 8:
296		fb->hf_class = HPCFB_CLASS_INDEXCOLOR;
297		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
298		fb->hf_pack_width = 8;
299		fb->hf_pixels_per_pack = 1;
300		fb->hf_pixel_width = 8;
301		fb->hf_class_data_length = sizeof(struct hf_indexed_tag);
302		/* reserved for future use */
303		fb->hf_u.hf_indexed.hf_flags = 0;
304		break;
305	}
306}
307
308int
309plumvideo_init(struct plumvideo_softc *sc, int *reverse)
310{
311	struct video_chip *chip = &sc->sc_chip;
312	bus_space_tag_t regt = sc->sc_regt;
313	bus_space_handle_t regh = sc->sc_regh;
314	plumreg_t reg;
315	size_t vram_size;
316	int bpp, width, height, vram_pitch;
317
318	*reverse = video_reverse_color();
319	chip->vc_v = sc->sc_pc->pc_tc;
320#if notyet
321	/* map BitBlt area */
322	if (bus_space_map(sc->sc_bitbltt,
323	    PLUM_VIDEO_BITBLT_IOBASE,
324	    PLUM_VIDEO_BITBLT_IOSIZE, 0,
325	    &sc->sc_bitblth)) {
326		printf(": BitBlt map failed\n");
327		return (1);
328	}
329#endif
330	reg = plum_conf_read(regt, regh, PLUM_VIDEO_PLGMD_REG);
331
332	switch (reg & PLUM_VIDEO_PLGMD_GMODE_MASK) {
333	case PLUM_VIDEO_PLGMD_16BPP:
334#if NPLUMOHCI > 0 /* reserve V-RAM area for USB OHCI */
335		/* FALLTHROUGH */
336#else
337		bpp = 16;
338		break;
339#endif
340	default:
341		bootinfo->fb_type = *reverse ? BIFB_D8_FF : BIFB_D8_00;
342		reg &= ~PLUM_VIDEO_PLGMD_GMODE_MASK;
343		plum_conf_write(regt, regh, PLUM_VIDEO_PLGMD_REG, reg);
344		reg |= PLUM_VIDEO_PLGMD_8BPP;
345		plum_conf_write(regt, regh, PLUM_VIDEO_PLGMD_REG, reg);
346#if notyet
347		/* change BitBlt color depth */
348		plum_conf_write(sc->sc_bitbltt, sc->sc_bitblth, 0x8, 0);
349#endif
350		/* FALLTHROUGH */
351	case PLUM_VIDEO_PLGMD_8BPP:
352		bpp = 8;
353		break;
354	}
355	chip->vc_fbdepth = bpp;
356
357	/*
358	 * Get display size from WindowsCE setted.
359	 */
360	chip->vc_fbwidth = width = bootinfo->fb_width =
361	    plum_conf_read(regt, regh, PLUM_VIDEO_PLHPX_REG) + 1;
362	chip->vc_fbheight = height = bootinfo->fb_height =
363	    plum_conf_read(regt, regh, PLUM_VIDEO_PLVT_REG) -
364	    plum_conf_read(regt, regh, PLUM_VIDEO_PLVDS_REG);
365
366	/*
367	 * set line byte length to bootinfo and LCD controller.
368	 */
369	vram_pitch = bootinfo->fb_line_bytes = (width * bpp) / NBBY;
370	plum_conf_write(regt, regh, PLUM_VIDEO_PLPIT1_REG, vram_pitch);
371	plum_conf_write(regt, regh, PLUM_VIDEO_PLPIT2_REG,
372	    vram_pitch & PLUM_VIDEO_PLPIT2_MASK);
373	plum_conf_write(regt, regh, PLUM_VIDEO_PLOFS_REG, vram_pitch);
374
375	/*
376	 * boot messages and map CLUT(if any).
377	 */
378	printf("display mode: ");
379	switch (bpp) {
380	default:
381		printf("disabled ");
382		break;
383	case 8:
384		printf("8bpp ");
385		/* map CLUT area */
386		if (bus_space_map(sc->sc_clutiot,
387		    PLUM_VIDEO_CLUT_LCD_IOBASE,
388		    PLUM_VIDEO_CLUT_LCD_IOSIZE, 0,
389		    &sc->sc_clutioh)) {
390			printf(": CLUT map failed\n");
391			return (1);
392		}
393		/* install default CLUT */
394		plumvideo_clut_default(sc);
395		break;
396	case 16:
397		printf("16bpp ");
398		break;
399	}
400
401	/*
402	 * calcurate frame buffer size.
403	 */
404	reg = plum_conf_read(regt, regh, PLUM_VIDEO_PLGMD_REG);
405	vram_size = (width * height * bpp) / NBBY;
406	vram_size = mips_round_page(vram_size);
407	chip->vc_fbsize = vram_size;
408
409	/*
410	 * map V-RAM area.
411	 */
412	if (bus_space_map(sc->sc_fbiot, PLUM_VIDEO_VRAM_IOBASE,
413	    vram_size, 0, &sc->sc_fbioh)) {
414		printf(": V-RAM map failed\n");
415		return (1);
416	}
417
418	bootinfo->fb_addr = (unsigned char *)sc->sc_fbioh;
419	chip->vc_fbvaddr = (vaddr_t)sc->sc_fbioh;
420	chip->vc_fbpaddr = PLUM_VIDEO_VRAM_IOBASE_PHYSICAL;
421
422	return (0);
423}
424
425int
426plumvideo_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
427{
428	struct plumvideo_softc *sc = (struct plumvideo_softc *)v;
429	struct hpcfb_fbconf *fbconf;
430	struct hpcfb_dspconf *dspconf;
431	struct wsdisplay_cmap *cmap;
432	struct wsdisplay_param *dispparam;
433	u_int8_t *r, *g, *b;
434	u_int32_t *rgb;
435	int idx, error;
436	size_t cnt;
437
438	switch (cmd) {
439	case WSDISPLAYIO_GETCMAP:
440		cmap = (struct wsdisplay_cmap *)data;
441		cnt = cmap->count;
442		idx = cmap->index;
443
444		if (sc->sc_fbconf.hf_class != HPCFB_CLASS_INDEXCOLOR ||
445		    sc->sc_fbconf.hf_pack_width != 8 ||
446		    !LEGAL_CLUT_INDEX(idx) ||
447		    !LEGAL_CLUT_INDEX(idx + cnt - 1)) {
448			return (EINVAL);
449		}
450
451		error = cmap_work_alloc(&r, &g, &b, &rgb, cnt);
452		if (error)
453			goto out;
454		plumvideo_clut_get(sc, rgb, idx, cnt);
455		rgb24_decompose(rgb, r, g, b, cnt);
456
457		error = copyout(r, cmap->red, cnt);
458		if (error)
459			goto out;
460		error = copyout(g, cmap->green, cnt);
461		if (error)
462			goto out;
463		error = copyout(b, cmap->blue, cnt);
464
465out:
466		cmap_work_free(r, g, b, rgb);
467		return error;
468
469	case WSDISPLAYIO_PUTCMAP:
470		cmap = (struct wsdisplay_cmap *)data;
471		cnt = cmap->count;
472		idx = cmap->index;
473
474		if (sc->sc_fbconf.hf_class != HPCFB_CLASS_INDEXCOLOR ||
475		    sc->sc_fbconf.hf_pack_width != 8 ||
476		    !LEGAL_CLUT_INDEX(idx) ||
477		    !LEGAL_CLUT_INDEX(idx + cnt - 1)) {
478			return (EINVAL);
479		}
480
481		error = cmap_work_alloc(&r, &g, &b, &rgb, cnt);
482		if (error)
483			goto out;
484		error = copyin(cmap->red, r, cnt);
485		if (error)
486			goto out;
487		error = copyin(cmap->green, g, cnt);
488		if (error)
489			goto out;
490		error = copyin(cmap->blue, b, cnt);
491		if (error)
492			goto out;
493		rgb24_compose(rgb, r, g, b, cnt);
494		plumvideo_clut_set(sc, rgb, idx, cnt);
495		goto out;
496
497	case WSDISPLAYIO_SVIDEO:
498		if (*(int *)data == WSDISPLAYIO_VIDEO_OFF)
499			plumvideo_backlight(sc, 0);
500		else
501			plumvideo_backlight(sc, 1);
502		return 0;
503
504	case WSDISPLAYIO_GVIDEO:
505		*(int *)data = sc->sc_backlight ?
506			WSDISPLAYIO_VIDEO_ON : WSDISPLAYIO_VIDEO_OFF;
507		return 0;
508
509	case WSDISPLAYIO_GETPARAM:
510		dispparam = (struct wsdisplay_param *)data;
511		switch (dispparam->param) {
512		case WSDISPLAYIO_PARAM_BACKLIGHT:
513			dispparam->min = 0;
514			dispparam->max = 1;
515			dispparam->curval = sc->sc_backlight;
516			break;
517		case WSDISPLAYIO_PARAM_BRIGHTNESS:
518			if (sc->sc_max_brightness <= 0)
519				return EINVAL;
520			dispparam->min = 0;
521			dispparam->max = sc->sc_max_brightness;
522			dispparam->curval = sc->sc_brightness;
523			break;
524		default:
525			return EINVAL;
526		}
527		return 0;
528
529	case WSDISPLAYIO_SETPARAM:
530		dispparam = (struct wsdisplay_param * )data;
531		switch (dispparam->param) {
532		case WSDISPLAYIO_PARAM_BACKLIGHT:
533			if (dispparam->curval < 0 || 1 < dispparam->curval)
534				return EINVAL;
535			plumvideo_backlight(sc, dispparam->curval);
536			break;
537		case WSDISPLAYIO_PARAM_BRIGHTNESS:
538			if (sc->sc_max_brightness <= 0)
539				return EINVAL;
540			if (dispparam->curval < 0 ||
541			    sc->sc_max_brightness < dispparam->curval)
542				return EINVAL;
543			plumvideo_brightness(sc, dispparam->curval);
544			break;
545		default:
546			return EINVAL;
547		}
548		return 0;
549
550	case HPCFBIO_GCONF:
551		fbconf = (struct hpcfb_fbconf *)data;
552		if (fbconf->hf_conf_index != 0 &&
553		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
554			return (EINVAL);
555		}
556		*fbconf = sc->sc_fbconf;	/* structure assignment */
557		return (0);
558
559	case HPCFBIO_SCONF:
560		fbconf = (struct hpcfb_fbconf *)data;
561		if (fbconf->hf_conf_index != 0 &&
562		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
563			return (EINVAL);
564		}
565		/*
566		 * nothing to do because we have only one configuration
567		 */
568		return (0);
569
570	case HPCFBIO_GDSPCONF:
571		dspconf = (struct hpcfb_dspconf *)data;
572		if ((dspconf->hd_unit_index != 0 &&
573		    dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
574		    (dspconf->hd_conf_index != 0 &&
575			dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
576			return (EINVAL);
577		}
578		*dspconf = sc->sc_dspconf;	/* structure assignment */
579		return (0);
580
581	case HPCFBIO_SDSPCONF:
582		dspconf = (struct hpcfb_dspconf *)data;
583		if ((dspconf->hd_unit_index != 0 &&
584		    dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
585		    (dspconf->hd_conf_index != 0 &&
586			dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
587			return (EINVAL);
588		}
589		/*
590		 * nothing to do
591		 * because we have only one unit and one configuration
592		 */
593		return (0);
594
595	case HPCFBIO_GOP:
596	case HPCFBIO_SOP:
597		/* XXX not implemented yet */
598		return (EINVAL);
599	}
600
601	return (EPASSTHROUGH);
602}
603
604paddr_t
605plumvideo_mmap(void *ctx, off_t offset, int prot)
606{
607	struct plumvideo_softc *sc = (struct plumvideo_softc *)ctx;
608
609	if (offset < 0 || (sc->sc_fbconf.hf_bytes_per_plane +
610	    sc->sc_fbconf.hf_offset) <  offset) {
611		return (-1);
612	}
613
614	return (mips_btop(PLUM_VIDEO_VRAM_IOBASE_PHYSICAL + offset));
615}
616
617static void __plumvideo_clut_get(bus_space_tag_t, bus_space_handle_t,
618    u_int32_t *, int, int);
619static void __plumvideo_clut_get(bus_space_tag_t iot, bus_space_handle_t ioh,
620    u_int32_t *rgb, int beg, int cnt)
621{
622	int i;
623
624	for (i = 0, beg *= 4; i < cnt; i++, beg += 4) {
625		*rgb++ = bus_space_read_4(iot, ioh, beg) &
626		    0x00ffffff;
627	}
628}
629
630void
631plumvideo_clut_get(struct plumvideo_softc *sc, u_int32_t *rgb, int beg,
632    int cnt)
633{
634	KASSERT(rgb);
635	KASSERT(LEGAL_CLUT_INDEX(beg));
636	KASSERT(LEGAL_CLUT_INDEX(beg + cnt - 1));
637	__plumvideo_clut_access(sc, rgb, beg, cnt, __plumvideo_clut_get);
638}
639
640static void __plumvideo_clut_set(bus_space_tag_t, bus_space_handle_t,
641    u_int32_t *, int, int);
642static void __plumvideo_clut_set(bus_space_tag_t iot, bus_space_handle_t ioh,
643    u_int32_t *rgb, int beg, int cnt)
644{
645	int i;
646
647	for (i = 0, beg *= 4; i < cnt; i++, beg +=4) {
648		bus_space_write_4(iot, ioh, beg,
649		    *rgb++ & 0x00ffffff);
650	}
651}
652
653void
654plumvideo_clut_set(struct plumvideo_softc *sc, u_int32_t *rgb, int beg,
655    int cnt)
656{
657	KASSERT(rgb);
658	KASSERT(LEGAL_CLUT_INDEX(beg));
659	KASSERT(LEGAL_CLUT_INDEX(beg + cnt - 1));
660	__plumvideo_clut_access(sc, rgb, beg, cnt, __plumvideo_clut_set);
661}
662
663static void __plumvideo_clut_default(bus_space_tag_t, bus_space_handle_t,
664    u_int32_t *, int, int);
665static void __plumvideo_clut_default(bus_space_tag_t iot, bus_space_handle_t ioh,
666    u_int32_t *rgb, int beg, int cnt)
667{
668	static const u_int8_t compo6[6] = { 0, 51, 102, 153, 204, 255 };
669	static const u_int32_t ansi_color[16] = {
670		0x000000, 0xff0000, 0x00ff00, 0xffff00,
671		0x0000ff, 0xff00ff, 0x00ffff, 0xffffff,
672		0x000000, 0x800000, 0x008000, 0x808000,
673		0x000080, 0x800080, 0x008080, 0x808080,
674	};
675	int i, r, g, b;
676
677	/* ANSI escape sequence */
678	for (i = 0; i < 16; i++) {
679		bus_space_write_4(iot, ioh, i << 2, ansi_color[i]);
680	}
681	/* 16 - 31, gray scale */
682	for ( ; i < 32; i++) {
683		int j = (i - 16) * 17;
684		bus_space_write_4(iot, ioh, i << 2, RGB24(j, j, j));
685	}
686	/* 32 - 247, RGB color */
687	for (r = 0; r < 6; r++) {
688		for (g = 0; g < 6; g++) {
689			for (b = 0; b < 6; b++) {
690				bus_space_write_4(iot, ioh, i << 2,
691				    RGB24(compo6[r],
692					compo6[g],
693					compo6[b]));
694				i++;
695			}
696		}
697	}
698	/* 248 - 245, just white */
699	for ( ; i < 256; i++) {
700		bus_space_write_4(iot, ioh, i << 2, 0xffffff);
701	}
702}
703
704void
705plumvideo_clut_default(struct plumvideo_softc *sc)
706{
707	__plumvideo_clut_access(sc, NULL, 0, 256, __plumvideo_clut_default);
708}
709
710void
711__plumvideo_clut_access(struct plumvideo_softc *sc, u_int32_t *rgb, int beg,
712    int cnt, void (*palette_func)(bus_space_tag_t, bus_space_handle_t,
713    u_int32_t *, int, int))
714{
715	bus_space_tag_t regt = sc->sc_regt;
716	bus_space_handle_t regh = sc->sc_regh;
717	plumreg_t val, gmode;
718
719	/* display off */
720	val = bus_space_read_4(regt, regh, PLUM_VIDEO_PLGMD_REG);
721	gmode = val & PLUM_VIDEO_PLGMD_GMODE_MASK;
722	val &= ~PLUM_VIDEO_PLGMD_GMODE_MASK;
723	bus_space_write_4(regt, regh, PLUM_VIDEO_PLGMD_REG, val);
724
725	/* palette access disable */
726	val &= ~PLUM_VIDEO_PLGMD_PALETTE_ENABLE;
727	bus_space_write_4(regt, regh, PLUM_VIDEO_PLGMD_REG, val);
728
729	/* change palette mode to CPU */
730	val &= ~PLUM_VIDEO_PLGMD_MODE_DISPLAY;
731	bus_space_write_4(regt, regh, PLUM_VIDEO_PLGMD_REG, val);
732
733	/* palette access */
734	(*palette_func) (sc->sc_clutiot, sc->sc_clutioh, rgb, beg, cnt);
735
736	/* change palette mode to Display */
737	val |= PLUM_VIDEO_PLGMD_MODE_DISPLAY;
738	bus_space_write_4(regt, regh, PLUM_VIDEO_PLGMD_REG, val);
739
740	/* palette access enable */
741	val |= PLUM_VIDEO_PLGMD_PALETTE_ENABLE;
742	bus_space_write_4(regt, regh, PLUM_VIDEO_PLGMD_REG, val);
743
744	/* display on */
745	val |= gmode;
746	bus_space_write_4(regt, regh, PLUM_VIDEO_PLGMD_REG, val);
747}
748
749/* !!! */
750static void
751_flush_cache(void)
752{
753	mips_dcache_wbinv_all();
754	mips_icache_sync_all();
755}
756
757int
758plumvideo_power(void *ctx, int type, long id, void *msg)
759{
760	struct plumvideo_softc *sc = ctx;
761	int why = (int)msg;
762
763	switch (why) {
764	case PWR_RESUME:
765		if (!sc->sc_console)
766			return (0); /* serial console */
767
768		DPRINTF(("%s: ON\n", sc->sc_dev.dv_xname));
769		/* power on */
770		plumvideo_backlight(sc, 1);
771		break;
772	case PWR_SUSPEND:
773		/* FALLTHROUGH */
774	case PWR_STANDBY:
775		DPRINTF(("%s: OFF\n", sc->sc_dev.dv_xname));
776		/* power off */
777		plumvideo_backlight(sc, 0);
778		break;
779	}
780
781	return (0);
782}
783
784static void
785plumvideo_init_backlight(struct plumvideo_softc *sc)
786{
787	int val;
788
789	val = -1;
790	if (config_hook_call(CONFIG_HOOK_GET,
791	    CONFIG_HOOK_POWER_LCDLIGHT, &val) != -1) {
792		/* we can get real backlight state */
793		sc->sc_backlight = val;
794	}
795
796	val = -1;
797	if (config_hook_call(CONFIG_HOOK_GET,
798	    CONFIG_HOOK_BRIGHTNESS_MAX, &val) != -1) {
799		/* we can get real brightness max */
800		sc->sc_max_brightness = val;
801
802		val = -1;
803		if (config_hook_call(CONFIG_HOOK_GET,
804		    CONFIG_HOOK_BRIGHTNESS, &val) != -1) {
805			/* we can get real brightness */
806			sc->sc_brightness = val;
807		} else {
808			sc->sc_brightness = sc->sc_max_brightness;
809		}
810	}
811}
812
813static void
814plumvideo_backlight(struct plumvideo_softc *sc, int on)
815{
816	plum_chipset_tag_t pc = sc->sc_pc;
817	bus_space_tag_t regt = sc->sc_regt;
818	bus_space_handle_t regh = sc->sc_regh;
819
820	sc->sc_backlight = on;
821	if (on) {
822		/* LCD on */
823		plum_power_establish(pc, PLUM_PWR_LCD);
824		/* backlight on */
825		plum_power_establish(pc, PLUM_PWR_BKL);
826		plum_conf_write(regt, regh, PLUM_VIDEO_PLLUM_REG,
827				PLUM_VIDEO_PLLUM_MAX);
828	} else {
829		/* backlight off */
830		plum_conf_write(regt, regh, PLUM_VIDEO_PLLUM_REG,
831				PLUM_VIDEO_PLLUM_MIN);
832		plum_power_disestablish(pc, PLUM_PWR_BKL);
833		/* LCD off */
834		plum_power_disestablish(pc, PLUM_PWR_LCD);
835	}
836	/* call machine dependent backlight control */
837	config_hook_call(CONFIG_HOOK_SET,
838			 CONFIG_HOOK_POWER_LCDLIGHT, (void *)on);
839}
840
841static void
842plumvideo_brightness(struct plumvideo_softc *sc, int val)
843{
844
845	sc->sc_brightness = val;
846	/* call machine dependent brightness control */
847	if (sc->sc_backlight)
848		config_hook_call(CONFIG_HOOK_SET,
849				 CONFIG_HOOK_BRIGHTNESS, &val);
850}
851
852#ifdef PLUMVIDEODEBUG
853void
854plumvideo_dump(struct plumvideo_softc *sc)
855{
856	bus_space_tag_t regt = sc->sc_regt;
857	bus_space_handle_t regh = sc->sc_regh;
858
859	plumreg_t reg;
860	int i;
861
862	for (i = 0; i < 0x160; i += 4) {
863		reg = plum_conf_read(regt, regh, i);
864		printf("0x%03x %08x", i, reg);
865		dbg_bit_print(reg);
866	}
867}
868#endif /* PLUMVIDEODEBUG */
869