1/*	$NetBSD: hd64461video.c,v 1.58 2023/12/20 14:50:02 thorpej Exp $	*/
2
3/*-
4 * Copyright (c) 2001, 2002, 2004 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: hd64461video.c,v 1.58 2023/12/20 14:50:02 thorpej Exp $");
34
35#include "opt_hd64461video.h"
36// #define HD64461VIDEO_HWACCEL
37
38#include <sys/param.h>
39#include <sys/kernel.h>
40#include <sys/systm.h>
41#include <sys/device.h>
42#include <sys/bus.h>
43
44#include <sys/conf.h> /* cdev_decl */
45#include <dev/cons.h> /* consdev */
46
47/* ioctl */
48#include <sys/ioctl.h>
49#include <sys/buf.h>
50#include <uvm/uvm_extern.h>
51
52#include <machine/intr.h>
53
54#include <hpcsh/dev/hd64461/hd64461var.h>
55#include <hpcsh/dev/hd64461/hd64461reg.h>
56#include <hpcsh/dev/hd64461/hd64461videoreg.h>
57
58#include <dev/wscons/wsdisplayvar.h>
59#include <dev/rasops/rasops.h>
60
61#include <dev/wscons/wsconsio.h>
62#include <dev/hpc/hpcfbvar.h>
63#include <dev/hpc/hpcfbio.h>
64#include <dev/hpc/video_subr.h>
65
66#include <machine/config_hook.h>
67#include <machine/bootinfo.h>
68
69#include <machine/platid.h>
70#include <machine/platid_mask.h>
71
72#ifdef	HD64461VIDEO_DEBUG
73#define DPRINTF_ENABLE
74#define DPRINTF_DEBUG	hd64461video_debug
75#endif
76#include <machine/debug.h>
77
78struct hd64461video_chip;
79struct hd64461video_font {
80	struct wsdisplay_font wsfont;
81	int c, cw, cstep;
82	int loaded;
83};
84
85struct hd64461video_softc {
86	device_t sc_dev;
87
88	enum hd64461_module_id sc_module_id;
89	struct hd64461video_chip *sc_vc;
90
91	struct hd64461video_font sc_font;
92};
93
94enum hd64461video_display_mode {
95	LCD256_C,
96	LCD64K_C,
97	LCD64_MONO,
98	LCD16_MONO,
99	LCD4_MONO,
100	LCD2_MONO,
101	CRT256_C,
102	LCDCRT
103};
104
105STATIC struct hd64461video_chip {
106	struct video_chip vc;
107	enum hd64461video_display_mode mode;
108	struct hpcfb_dspconf hd;
109	struct hpcfb_fbconf hf;
110	uint8_t *off_screen_addr;
111	size_t off_screen_size;
112
113	struct callout unblank_ch;
114	int blanked;
115
116	int console;
117} hd64461video_chip;
118
119void	hd64461video_cnprobe(struct consdev *);
120void	hd64461video_cninit(struct consdev *);
121
122STATIC int hd64461video_match(device_t, cfdata_t, void *);
123STATIC void hd64461video_attach(device_t, device_t, void *);
124
125STATIC void hd64461video_setup_hpcfbif(struct hd64461video_chip *);
126STATIC void hd64461video_update_videochip_status(struct hd64461video_chip *);
127STATIC size_t hd64461video_frame_buffer_size(struct hd64461video_chip *);
128STATIC void hd64461video_hwaccel_init(struct hd64461video_chip *);
129
130STATIC void hd64461video_set_clut(struct hd64461video_chip *, int, int,
131    uint8_t *, uint8_t *, uint8_t *);
132STATIC void hd64461video_get_clut(struct hd64461video_chip *, int, int,
133    uint8_t *, uint8_t *, uint8_t *);
134STATIC int hd64461video_power(void *, int, long, void *);
135STATIC void hd64461video_off(struct hd64461video_chip *);
136STATIC void hd64461video_on(struct hd64461video_chip *);
137STATIC void hd64461video_display_onoff(void *, bool);
138STATIC void hd64461video_display_on(void *);
139
140#if notyet
141STATIC void hd64461video_set_display_mode(struct hd64461video_chip *);
142STATIC void hd64461video_set_display_mode_lcdc(struct hd64461video_chip *);
143STATIC void hd64461video_set_display_mode_crtc(struct hd64461video_chip *);
144#endif
145
146#ifdef HD64461VIDEO_DEBUG
147STATIC void hd64461video_info(struct hd64461video_softc *);
148STATIC void hd64461video_dump(void) __attribute__((__unused__));
149#endif
150
151CFATTACH_DECL_NEW(hd64461video, sizeof(struct hd64461video_softc),
152    hd64461video_match, hd64461video_attach, NULL, NULL);
153
154STATIC int hd64461video_ioctl(void *, u_long, void *, int, struct lwp *);
155STATIC paddr_t hd64461video_mmap(void *, off_t, int);
156
157#ifdef HD64461VIDEO_HWACCEL
158STATIC void hd64461video_cursor(void *, int, int, int, int, int);
159STATIC void hd64461video_bitblit(void *, int, int, int, int, int, int);
160STATIC void hd64461video_erase(void *, int, int, int, int, int);
161STATIC void hd64461video_putchar(void *, int, int, struct wsdisplay_font *,
162    int, int, uint, int);
163STATIC void hd64461video_setclut(void *, struct rasops_info *);
164STATIC void hd64461video_font(void *, struct wsdisplay_font *);
165STATIC void hd64461video_iodone(void *);
166
167/* font */
168STATIC void hd64461video_font_load_16bpp(uint16_t *, uint8_t *, int, int, int);
169STATIC void hd64461video_font_load_8bpp(uint8_t *, uint8_t *, int, int, int);
170STATIC void hd64461video_font_set_attr(struct hd64461video_softc *,
171    struct wsdisplay_font *);
172STATIC void hd64461video_font_load(struct hd64461video_softc *);
173STATIC vaddr_t hd64461video_font_start_addr(struct hd64461video_softc *, int);
174#endif /* HD64461VIDEO_HWACCEL */
175
176STATIC struct hpcfb_accessops hd64461video_ha = {
177	.ioctl	= hd64461video_ioctl,
178	.mmap	= hd64461video_mmap,
179#ifdef HD64461VIDEO_HWACCEL
180	.cursor	= hd64461video_cursor,
181	.bitblit= hd64461video_bitblit,
182	.erase	= hd64461video_erase,
183	.putchar= hd64461video_putchar,
184	.setclut= hd64461video_setclut,
185	.font	= hd64461video_font,
186	.iodone	= hd64461video_iodone
187#endif /* HD64461VIDEO_HWACCEL */
188};
189
190
191STATIC int
192hd64461video_match(device_t parent, cfdata_t cf, void *aux)
193{
194	struct hd64461_attach_args *ha = aux;
195
196	return (ha->ha_module_id == HD64461_MODULE_VIDEO);
197}
198
199STATIC void
200hd64461video_attach(device_t parent, device_t self, void *aux)
201{
202	struct hd64461_attach_args *ha = aux;
203	struct hd64461video_softc *sc;
204	struct hpcfb_attach_args hfa;
205	struct video_chip *vc = &hd64461video_chip.vc;
206	char pbuf[9];
207	size_t fbsize, on_screen_size;
208
209	aprint_naive("\n");
210	aprint_normal(": ");
211
212	sc = device_private(self);
213	sc->sc_dev = self;
214
215	sc->sc_module_id = ha->ha_module_id;
216	sc->sc_vc = &hd64461video_chip;
217
218	/* detect frame buffer size */
219	fbsize = hd64461video_frame_buffer_size(&hd64461video_chip);
220	format_bytes(pbuf, sizeof(pbuf), fbsize);
221	aprint_normal("frame buffer = %s ", pbuf);
222
223	/* update chip status */
224	hd64461video_update_videochip_status(&hd64461video_chip);
225
226	hd64461video_display_onoff(&hd64461video_chip, true);
227//	hd64461video_set_display_mode(&hd64461video_chip);
228
229	if (hd64461video_chip.console)
230		aprint_normal(", console");
231
232	aprint_normal("\n");
233#ifdef HD64461VIDEO_DEBUG
234	hd64461video_info(sc);
235	hd64461video_dump();
236#endif
237	/* Add a hard power hook to power saving */
238	config_hook(CONFIG_HOOK_PMEVENT, CONFIG_HOOK_PMEVENT_HARDPOWER,
239	    CONFIG_HOOK_SHARE, hd64461video_power, sc);
240
241	/* setup hpcfb interface */
242	hd64461video_setup_hpcfbif(&hd64461video_chip);
243
244	/* setup off-screen buffer */
245	on_screen_size = (vc->vc_fbwidth * vc->vc_fbheight * vc->vc_fbdepth) /
246	    NBBY;
247	hd64461video_chip.off_screen_addr = (uint8_t *)vc->vc_fbvaddr +
248	    on_screen_size;
249	hd64461video_chip.off_screen_size = fbsize - on_screen_size;
250	/* clean up off-screen area */
251	{
252		uint8_t *p = hd64461video_chip.off_screen_addr;
253		uint8_t *end = p + hd64461video_chip.off_screen_size;
254		while (p < end)
255			*p++ = 0xff;
256	}
257
258	/* initialize hardware acceralation */
259	hd64461video_hwaccel_init(&hd64461video_chip);
260
261	/* register interface to hpcfb */
262	hfa.ha_console	   = hd64461video_chip.console;
263	hfa.ha_accessops   = &hd64461video_ha;
264	hfa.ha_accessctx   = sc;
265	hfa.ha_curfbconf   = 0;
266	hfa.ha_nfbconf	   = 1;
267	hfa.ha_fbconflist  = &hd64461video_chip.hf;
268	hfa.ha_curdspconf  = 0;
269	hfa.ha_ndspconf	   = 1;
270	hfa.ha_dspconflist = &hd64461video_chip.hd;
271
272	config_found(self, &hfa, hpcfbprint, CFARGS_NONE);
273
274	/*
275	 * XXX: TODO: for now this device manages power using
276	 * config_hook(9) registered with hpcapm(4).
277	 *
278	 * We cannot yet switch it to pmf(9) hooks because only apm(4)
279	 * uses them, apmdev(4) doesn't, but hpcapm(4) is the parent
280	 * device for both, so its hooks are always run.
281	 *
282	 * We probably want to register shutdown hook with pmf(9) to
283	 * make sure display is powered on before we reboot in case we
284	 * end up in ddb early on.
285	 */
286	if (!pmf_device_register(self, NULL, NULL))
287		aprint_error_dev(self, "unable to establish power handler\n");
288}
289
290/* console support */
291void
292hd64461video_cninit(struct consdev *cndev)
293{
294	hd64461video_chip.console = 1;
295	hd64461video_chip.vc.vc_reverse = video_reverse_color();
296
297	hd64461video_update_videochip_status(&hd64461video_chip);
298	hd64461video_setup_hpcfbif(&hd64461video_chip);
299	hpcfb_cnattach(&hd64461video_chip.hf);
300
301	cn_tab->cn_pri = CN_INTERNAL;
302}
303
304void
305hd64461video_cnprobe(struct consdev *cndev)
306{
307#if NWSDISPLAY > 0
308	extern const struct cdevsw wsdisplay_cdevsw;
309	int maj, unit;
310#endif
311	cndev->cn_dev = NODEV;
312	cndev->cn_pri = CN_NORMAL;
313
314#if NWSDISPLAY > 0
315	unit = 0;
316	maj = cdevsw_lookup_major(&wsdisplay_cdevsw);
317
318	if (maj != -1) {
319		cndev->cn_pri = CN_INTERNAL;
320		cndev->cn_dev = makedev(maj, unit);
321	}
322#endif /* NWSDISPLAY > 0 */
323}
324
325/* hpcfb support */
326STATIC void
327hd64461video_setup_hpcfbif(struct hd64461video_chip *hvc)
328{
329	struct video_chip *vc = &hvc->vc;
330	struct hpcfb_fbconf *fb = &hvc->hf;
331	vaddr_t fbvaddr = vc->vc_fbvaddr;
332	int height = vc->vc_fbheight;
333	int width = vc->vc_fbwidth;
334	int depth = vc->vc_fbdepth;
335
336	memset(fb, 0, sizeof(struct hpcfb_fbconf));
337
338	fb->hf_conf_index	= 0;	/* configuration index		*/
339	fb->hf_nconfs		= 1;   	/* how many configurations	*/
340	strncpy(fb->hf_name, "HD64461 video module", HPCFB_MAXNAMELEN);
341
342	/* frame buffer name */
343	strncpy(fb->hf_conf_name, "LCD", HPCFB_MAXNAMELEN);
344
345	/* configuration name */
346	fb->hf_height		= height;
347	fb->hf_width		= width;
348	fb->hf_baseaddr		= (u_long)fbvaddr;
349	fb->hf_offset		= (u_long)fbvaddr -
350	    sh3_ptob(sh3_btop(fbvaddr));
351
352	/* frame buffer start offset */
353	fb->hf_bytes_per_line	= (width * depth) / NBBY;
354	fb->hf_nplanes		= 1;
355	fb->hf_bytes_per_plane	= height * fb->hf_bytes_per_line;
356
357	fb->hf_access_flags |= HPCFB_ACCESS_BYTE;
358	fb->hf_access_flags |= HPCFB_ACCESS_WORD;
359	fb->hf_access_flags |= HPCFB_ACCESS_DWORD;
360	if (vc->vc_reverse)
361		fb->hf_access_flags |= HPCFB_ACCESS_REVERSE;
362
363	switch (depth) {
364	default:
365		panic("%s: not supported color depth", __func__);
366		/* NOTREACHED */
367	case 16:
368		fb->hf_class = HPCFB_CLASS_RGBCOLOR;
369		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
370		fb->hf_pack_width = 16;
371		fb->hf_pixels_per_pack = 1;
372		fb->hf_pixel_width = 16;
373		/*
374		 * XXX: uwe: if I RTFS correctly, this really means
375		 * that uint16_t pixel is fetched as little endian.
376		 */
377		fb->hf_order_flags = HPCFB_REVORDER_BYTE;
378
379		fb->hf_class_data_length = sizeof(struct hf_rgb_tag);
380		/* reserved for future use */
381		fb->hf_u.hf_rgb.hf_flags = 0;
382
383		fb->hf_u.hf_rgb.hf_red_width = 5;
384		fb->hf_u.hf_rgb.hf_red_shift = 11;
385		fb->hf_u.hf_rgb.hf_green_width = 6;
386		fb->hf_u.hf_rgb.hf_green_shift = 5;
387		fb->hf_u.hf_rgb.hf_blue_width = 5;
388		fb->hf_u.hf_rgb.hf_blue_shift = 0;
389		fb->hf_u.hf_rgb.hf_alpha_width = 0;
390		fb->hf_u.hf_rgb.hf_alpha_shift = 0;
391		break;
392
393	case 8:
394		fb->hf_class = HPCFB_CLASS_INDEXCOLOR;
395		fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
396		fb->hf_pack_width = 8;
397		fb->hf_pixels_per_pack = 1;
398		fb->hf_pixel_width = 8;
399		fb->hf_class_data_length = sizeof(struct hf_indexed_tag);
400		/* reserved for future use */
401		fb->hf_u.hf_indexed.hf_flags = 0;
402		break;
403	}
404}
405
406STATIC void
407hd64461video_hwaccel_init(struct hd64461video_chip *hvc)
408{
409	uint16_t r;
410
411	r = HD64461_LCDGRCFGR_ACCRESET;
412	switch (hvc->vc.vc_fbdepth) {
413	default:
414		panic("no bitblit acceralation.");
415	case 16:
416		break;
417	case 8:
418		r |= HD64461_LCDGRCFGR_COLORDEPTH_8BPP;
419		break;
420	}
421	hd64461_reg_write_2(HD64461_LCDGRCFGR_REG16, r);
422
423	while ((hd64461_reg_read_2(HD64461_LCDGRCFGR_REG16) &
424	    HD64461_LCDGRCFGR_ACCSTATUS) != 0)
425		continue;
426
427	r &= ~HD64461_LCDGRCFGR_ACCRESET;
428	hd64461_reg_write_2(HD64461_LCDGRCFGR_REG16, r);
429
430	while ((hd64461_reg_read_2(HD64461_LCDGRCFGR_REG16) &
431	    HD64461_LCDGRCFGR_ACCSTATUS) != 0)
432		continue;
433
434	hd64461_reg_write_2(HD64461_LCDGRDOR_REG16,
435	    (hvc->vc.vc_fbwidth - 1) & HD64461_LCDGRDOR_MASK);
436}
437
438/* hpcfb ops */
439STATIC int
440hd64461video_ioctl(void *v, u_long cmd, void *data, int flag, struct lwp *l)
441{
442	struct hd64461video_softc *sc = (struct hd64461video_softc *)v;
443	struct hpcfb_fbconf *hf = &sc->sc_vc->hf;
444	struct hpcfb_fbconf *fbconf;
445	struct hpcfb_dspconf *dspconf;
446	struct wsdisplay_cmap *cmap;
447	struct wsdisplay_param *dispparam;
448	long id, idmax;
449	int turnoff;
450	uint8_t *r, *g, *b;
451	int error;
452	size_t idx, cnt;
453
454	switch (cmd) {
455	case WSDISPLAYIO_GVIDEO:
456		*(u_int *)data = sc->sc_vc->blanked ?
457		    WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON;
458		return (0);
459
460	case WSDISPLAYIO_SVIDEO:
461		turnoff = (*(u_int *)data == WSDISPLAYIO_VIDEO_OFF);
462		if (sc->sc_vc->blanked != turnoff) {
463			sc->sc_vc->blanked = turnoff;
464			if (turnoff)
465				hd64461video_off(sc->sc_vc);
466			else
467				hd64461video_on(sc->sc_vc);
468		}
469
470		return (0);
471
472	case WSDISPLAYIO_GETPARAM:
473		dispparam = (struct wsdisplay_param *)data;
474		dispparam->min = 0;
475		switch (dispparam->param) {
476		case WSDISPLAYIO_PARAM_BACKLIGHT:
477			id = CONFIG_HOOK_POWER_LCDLIGHT;
478			idmax = -1;
479			dispparam->max = ~0;
480			break;
481		case WSDISPLAYIO_PARAM_BRIGHTNESS:
482			id = CONFIG_HOOK_BRIGHTNESS;
483			idmax = CONFIG_HOOK_BRIGHTNESS_MAX;
484			break;
485		case WSDISPLAYIO_PARAM_CONTRAST:
486			id = CONFIG_HOOK_CONTRAST;
487			idmax = CONFIG_HOOK_CONTRAST_MAX;
488			break;
489		default:
490			return (EINVAL);
491		}
492
493		if (idmax >= 0) {
494			error = config_hook_call(CONFIG_HOOK_GET, idmax,
495						 &dispparam->max);
496			if (error)
497				return (error);
498		}
499		return config_hook_call(CONFIG_HOOK_GET, id,
500					&dispparam->curval);
501
502	case WSDISPLAYIO_SETPARAM:
503		dispparam = (struct wsdisplay_param *)data;
504		switch (dispparam->param) {
505		case WSDISPLAYIO_PARAM_BACKLIGHT:
506			id = CONFIG_HOOK_POWER_LCDLIGHT;
507			break;
508		case WSDISPLAYIO_PARAM_BRIGHTNESS:
509			id = CONFIG_HOOK_BRIGHTNESS;
510			break;
511		case WSDISPLAYIO_PARAM_CONTRAST:
512			id = CONFIG_HOOK_CONTRAST;
513			break;
514		default:
515			return (EINVAL);
516		}
517		return config_hook_call(CONFIG_HOOK_SET, id,
518					&dispparam->curval);
519
520	case WSDISPLAYIO_GETCMAP:
521		cmap = (struct wsdisplay_cmap *)data;
522		cnt = cmap->count;
523		idx = cmap->index;
524
525		if (hf->hf_class != HPCFB_CLASS_INDEXCOLOR ||
526		    hf->hf_pack_width != 8 ||
527		    !LEGAL_CLUT_INDEX(idx) ||
528		    !LEGAL_CLUT_INDEX(idx + cnt -1)) {
529			return (EINVAL);
530		}
531
532		error = cmap_work_alloc(&r, &g, &b, 0, cnt);
533		if (error)
534			goto out;
535		hd64461video_get_clut(sc->sc_vc, idx, cnt, r, g, b);
536		error = copyout(r, cmap->red, cnt);
537		if (error)
538			goto out;
539		error = copyout(g, cmap->green,cnt);
540		if (error)
541			goto out;
542		error = copyout(b, cmap->blue, cnt);
543
544out:
545		cmap_work_free(r, g, b, 0);
546		return error;
547
548	case WSDISPLAYIO_PUTCMAP:
549		cmap = (struct wsdisplay_cmap *)data;
550		cnt = cmap->count;
551		idx = cmap->index;
552
553		if (hf->hf_class != HPCFB_CLASS_INDEXCOLOR ||
554		    hf->hf_pack_width != 8 ||
555		    !LEGAL_CLUT_INDEX(idx) ||
556		    !LEGAL_CLUT_INDEX(idx + cnt -1)) {
557			return (EINVAL);
558		}
559
560		error = cmap_work_alloc(&r, &g, &b, 0, cnt);
561		if (error)
562			goto out;
563
564		error = copyin(cmap->red, r, cnt);
565		if (error)
566			goto out;
567		error = copyin(cmap->green,g, cnt);
568		if (error)
569			goto out;
570		error = copyin(cmap->blue, b, cnt);
571		if (error)
572			goto out;
573		hd64461video_set_clut(sc->sc_vc, idx, cnt, r, g, b);
574		goto out;
575
576	case HPCFBIO_GCONF:
577		fbconf = (struct hpcfb_fbconf *)data;
578		if (fbconf->hf_conf_index != 0 &&
579		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
580			return (EINVAL);
581		}
582		*fbconf = *hf;	/* structure assignment */
583		return (0);
584
585	case HPCFBIO_SCONF:
586		fbconf = (struct hpcfb_fbconf *)data;
587		if (fbconf->hf_conf_index != 0 &&
588		    fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
589			return (EINVAL);
590		}
591		/*
592		 * nothing to do because we have only one configuration
593		 */
594		return (0);
595
596	case HPCFBIO_GDSPCONF:
597		dspconf = (struct hpcfb_dspconf *)data;
598		if ((dspconf->hd_unit_index != 0 &&
599		    dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
600		    (dspconf->hd_conf_index != 0 &&
601			dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
602			return (EINVAL);
603		}
604		*dspconf = sc->sc_vc->hd;	/* structure assignment */
605		return (0);
606
607	case HPCFBIO_SDSPCONF:
608		dspconf = (struct hpcfb_dspconf *)data;
609		if ((dspconf->hd_unit_index != 0 &&
610		    dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
611		    (dspconf->hd_conf_index != 0 &&
612			dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
613			return (EINVAL);
614		}
615		/*
616		 * nothing to do
617		 * because we have only one unit and one configuration
618		 */
619		return (0);
620
621	case HPCFBIO_GOP:
622	case HPCFBIO_SOP:
623		/* XXX not implemented yet */
624		return (EINVAL);
625	}
626
627	return (EPASSTHROUGH);
628}
629
630STATIC paddr_t
631hd64461video_mmap(void *ctx, off_t offset, int prot)
632{
633	struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
634	struct hpcfb_fbconf *hf = &sc->sc_vc->hf;
635
636	if (offset < 0 || (hf->hf_bytes_per_plane + hf->hf_offset) < offset)
637		return (-1);
638
639	return (sh3_btop(HD64461_FBBASE + offset));
640}
641
642
643#ifdef HD64461VIDEO_HWACCEL
644
645STATIC void
646hd64461video_cursor(void *ctx, int on, int xd, int yd, int w, int h)
647{
648	struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
649	int xw, yh, width, bpp, adr;
650	uint16_t r;
651
652	width = sc->sc_vc->vc.vc_fbwidth;
653	bpp = sc->sc_vc->vc.vc_fbdepth;
654	xw = w - 1;
655	yh = h - 1;
656
657	/* Wait until previous command done. */
658	hd64461video_iodone(ctx);
659
660	/* Destination addr */
661	adr = width * yd + xd;
662	if (bpp == 16)
663		adr *= 2;
664	hd64461_reg_write_2(HD64461_LCDBBTDSARH_REG16,
665	    HD64461_LCDBBTDSARH(adr));
666	hd64461_reg_write_2(HD64461_LCDBBTDSARL_REG16,
667	    HD64461_LCDBBTDSARL(adr));
668
669	// Width
670	hd64461_reg_write_2(HD64461_LCDBBTDWR_REG16,
671	    xw & HD64461_LCDBBTDWR_MASK);
672
673	// Height
674	hd64461_reg_write_2(HD64461_LCDBBTDHR_REG16,
675	    yh & HD64461_LCDBBTDHR_MASK);
676
677	// Operation (Destination Invert)
678	hd64461_reg_write_2(HD64461_LCDBBTROPR_REG16,
679	    HD64461_LCDC_BITBLT_DSTINVERT);
680
681	// BitBLT mode (Destination Invert)
682	hd64461_reg_write_2(HD64461_LCDBBTMDR_REG16, 0);
683
684	// Kick.
685	r = hd64461_reg_read_2(HD64461_LCDGRCFGR_REG16);
686	r &= ~HD64461_LCDGRCFGR_ACCSTART_MASK;
687	r |= HD64461_LCDGRCFGR_ACCSTART_BITBLT;
688	hd64461_reg_write_2(HD64461_LCDGRCFGR_REG16, r);
689}
690
691STATIC void
692hd64461video_bitblit(void *ctx, int xs, int ys, int xd, int yd, int h, int w)
693{
694	struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
695	int xw, yh, width, bpp, condition_a, adr;
696	uint16_t r;
697
698	xw = w - 1;
699	yh = h - 1;
700	width = sc->sc_vc->vc.vc_fbwidth;
701	bpp = sc->sc_vc->vc.vc_fbdepth;
702	condition_a = ((ys == yd) && (xs <= xd)) || (ys < yd);
703
704	hd64461video_iodone(ctx);
705
706	// Source addr
707	if (condition_a)
708		adr = (width * (ys + yh)) + (xs + xw);
709	else
710		adr = width * ys + xs;
711	if (bpp == 16)
712		adr *= 2;
713
714	hd64461_reg_write_2(HD64461_LCDBBTSSARH_REG16,
715	    HD64461_LCDBBTSSARH(adr));
716	hd64461_reg_write_2(HD64461_LCDBBTSSARL_REG16,
717	    HD64461_LCDBBTSSARL(adr));
718
719	// Destination addr
720	if (condition_a)
721		adr = (width * (yd + yh)) + (xd + xw);
722	else
723		adr = width * yd + xd;
724	if (bpp == 16)
725		adr *= 2;
726
727	hd64461_reg_write_2(HD64461_LCDBBTDSARH_REG16,
728	    HD64461_LCDBBTDSARH(adr));
729	hd64461_reg_write_2(HD64461_LCDBBTDSARL_REG16,
730	    HD64461_LCDBBTDSARL(adr));
731
732	// Width
733	hd64461_reg_write_2(HD64461_LCDBBTDWR_REG16,
734	    xw & HD64461_LCDBBTDWR_MASK);
735
736	// Height
737	hd64461_reg_write_2(HD64461_LCDBBTDHR_REG16,
738	    yh & HD64461_LCDBBTDHR_MASK);
739
740	// Operation (source copy)
741	hd64461_reg_write_2(HD64461_LCDBBTROPR_REG16,
742	    HD64461_LCDC_BITBLT_SRCCOPY);
743
744	// BitBLT mode (on screen to on screen)
745	r = HD64461_LCDBBTMDR_SET(0,
746	    HD64461_LCDBBTMDR_ON_SCREEN_TO_ON_SCREEN);
747	if (condition_a)	/* reverse direction */
748	    r |= HD64461_LCDBBTMDR_SCANDRCT_RL_BT;
749	hd64461_reg_write_2(HD64461_LCDBBTMDR_REG16, r);
750
751	// Kick.
752	r = hd64461_reg_read_2(HD64461_LCDGRCFGR_REG16);
753	r &= ~HD64461_LCDGRCFGR_ACCSTART_MASK;
754	r |= HD64461_LCDGRCFGR_ACCSTART_BITBLT;
755	hd64461_reg_write_2(HD64461_LCDGRCFGR_REG16, r);
756}
757
758STATIC void
759hd64461video_erase(void *ctx, int xd, int yd, int h, int w, int attr)
760{
761	struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
762	int xw, yh, width, bpp, adr;
763	uint16_t r;
764
765	width = sc->sc_vc->vc.vc_fbwidth;
766	bpp = sc->sc_vc->vc.vc_fbdepth;
767	xw = w - 1;
768	yh = h - 1;
769
770	/* Wait until previous command done. */
771	hd64461video_iodone(ctx);
772
773	/* Destination addr */
774	adr = width * yd + xd;
775	if (bpp == 16)
776		adr *= 2;
777	hd64461_reg_write_2(HD64461_LCDBBTDSARH_REG16,
778	    HD64461_LCDBBTDSARH(adr));
779	hd64461_reg_write_2(HD64461_LCDBBTDSARL_REG16,
780	    HD64461_LCDBBTDSARL(adr));
781
782	// Width
783	hd64461_reg_write_2(HD64461_LCDBBTDWR_REG16,
784	    xw & HD64461_LCDBBTDWR_MASK);
785
786	// Height
787	hd64461_reg_write_2(HD64461_LCDBBTDHR_REG16,
788	    yh & HD64461_LCDBBTDHR_MASK);
789
790	// Color
791	hd64461_reg_write_2(HD64461_LCDGRSCR_REG16, 0); //XXX black only
792
793	// Operation (Solid Color Fill)
794	hd64461_reg_write_2(HD64461_LCDBBTROPR_REG16,
795	    HD64461_LCDC_BITBLT_PATCOPY);
796
797	// BitBLT mode (Solid Color)
798	hd64461_reg_write_2(HD64461_LCDBBTMDR_REG16,
799	    HD64461_LCDBBTMDR_PATSELECT_SOLIDCOLOR);
800
801	// Kick.
802	r = hd64461_reg_read_2(HD64461_LCDGRCFGR_REG16);
803	r &= ~HD64461_LCDGRCFGR_ACCSTART_MASK;
804	r |= HD64461_LCDGRCFGR_ACCSTART_BITBLT;
805	hd64461_reg_write_2(HD64461_LCDGRCFGR_REG16, r);
806}
807
808STATIC void
809hd64461video_putchar(void *ctx, int row, int col, struct wsdisplay_font *font,
810    int fclr, int uclr, u_int uc, int attr)
811{
812	struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
813	int w, h, cw;
814
815	w = font->fontwidth;
816	h = font->fontheight;
817	cw = sc->sc_font.cw;
818	hd64461video_bitblit(ctx, (uc % cw) * w,
819	    sc->sc_vc->vc.vc_fbheight + (uc / cw) * h, row, col, h, w);
820}
821
822STATIC void
823hd64461video_setclut(void *ctx, struct rasops_info *info)
824{
825	struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
826
827	if (sc->sc_vc->vc.vc_fbdepth != 8)
828		return;
829}
830
831STATIC void
832hd64461video_font(void *ctx, struct wsdisplay_font *font)
833{
834	struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
835
836	hd64461video_font_set_attr(sc, font);
837	hd64461video_font_load(sc);
838}
839
840STATIC void
841hd64461video_iodone(void *ctx)
842{
843
844	while ((hd64461_reg_read_2(HD64461_LCDGRCFGR_REG16) &
845	    HD64461_LCDGRCFGR_ACCSTATUS) != 0)
846		continue;
847}
848
849/* internal */
850STATIC void
851hd64461video_font_load_16bpp(uint16_t *d, uint8_t *s, int w, int h, int step)
852{
853	int i, j, n;
854	n = step / sizeof(uint16_t);
855
856	for (i = 0; i < h; i++, d += n) {
857		for (j = 0; j < w; j++) {
858			d[j] = *s & (1 << (w - j - 1)) ? 0xffff : 0x0000;
859		}
860		s++;
861	}
862}
863
864STATIC void
865hd64461video_font_load_8bpp(uint8_t *d, uint8_t *s, int w, int h, int step)
866{
867	int i, j, n;
868	n = step / sizeof(uint8_t);
869
870	for (i = 0; i < h; i++, d += n) {
871		for (j = 0; j < w; j++) {
872			d[j] = *s & (1 << (w - j - 1)) ? 0xff : 0x00;
873		}
874		s++;
875	}
876}
877
878STATIC void
879hd64461video_font_set_attr(struct hd64461video_softc *sc,
880    struct wsdisplay_font *f)
881{
882	struct hd64461video_chip *hvc = sc->sc_vc;
883	struct wsdisplay_font *font = &sc->sc_font.wsfont;
884	int w, h, bpp;
885
886	w	= f->fontwidth;
887	h	= f->fontheight;
888	bpp	= hvc->vc.vc_fbdepth;
889
890	*font = *f;
891	sc->sc_font.c = (w * bpp) / NBBY;
892	sc->sc_font.cw = hvc->hf.hf_width / w;
893	sc->sc_font.cstep = ((w * h * bpp) / NBBY) * sc->sc_font.cw;
894
895	DPRINTF("c = %d cw = %d cstep = %d\n", sc->sc_font.c,
896	    sc->sc_font.cw, sc->sc_font.cstep);
897
898}
899
900/* return frame buffer virtual address of character #n */
901STATIC vaddr_t
902hd64461video_font_start_addr(struct hd64461video_softc *sc, int n)
903{
904	struct hd64461video_chip *hvc = sc->sc_vc;
905	struct hd64461video_font *font = &sc->sc_font;
906	vaddr_t base;
907
908	base = (vaddr_t)hvc->off_screen_addr;
909	base += (n / font->cw) * font->cstep + font->c * (n % font->cw);
910
911	return base;
912}
913
914STATIC void
915hd64461video_font_load(struct hd64461video_softc *sc)
916{
917	struct hd64461video_chip *hvc = sc->sc_vc;
918	struct wsdisplay_font *font = &sc->sc_font.wsfont;
919	uint8_t *q;
920	int w, h, step, i, n;
921
922	if (sc->sc_font.loaded) {
923		printf("reload font\n");
924	}
925
926	w	= font->fontwidth;
927	h	= font->fontheight;
928	step	= sc->sc_font.cw * sc->sc_font.c;
929	n	= (w * h) / NBBY;
930	q	= font->data;
931
932	DPRINTF("%s (%dx%d) %d+%d\n", font->name, w, h, font->firstchar,
933	    font->numchars);
934	DPRINTF("bitorder %d byteorder %d stride %d\n", font->bitorder,
935	    font->byteorder, font->stride);
936
937	switch (hvc->vc.vc_fbdepth) {
938	case 8:
939		for (i = font->firstchar; i < font->numchars; i++) {
940			hd64461video_font_load_8bpp
941			    ((uint8_t *)hd64461video_font_start_addr(sc, i),
942				q, w, h, step);
943			q += n;
944		}
945		break;
946	case 16:
947		for (i = font->firstchar; i < font->numchars; i++) {
948			hd64461video_font_load_16bpp
949			    ((uint16_t *)hd64461video_font_start_addr(sc, i),
950				q, w, h, step);
951			q += n;
952		}
953		break;
954	}
955
956	sc->sc_font.loaded = true;
957}
958#endif /* HD64461VIDEO_HWACCEL */
959
960STATIC void
961hd64461video_update_videochip_status(struct hd64461video_chip *hvc)
962{
963	struct video_chip *vc = &hvc->vc;
964	uint16_t r;
965	int i;
966	int depth, width, height;
967
968	depth = 0;		/* XXX: -Wuninitialized */
969
970	/* display mode */
971	r = hd64461_reg_read_2(HD64461_LCDLDR3_REG16);
972	i = HD64461_LCDLDR3_CG(r);
973	switch (i) {
974	case HD64461_LCDLDR3_CG_COLOR16:
975		depth = 16;
976		hvc->mode = LCD64K_C;
977		break;
978	case HD64461_LCDLDR3_CG_COLOR8:
979		depth = 8;
980		hvc->mode = LCD256_C;
981		break;
982	case HD64461_LCDLDR3_CG_GRAY6:
983		depth = 6;
984		hvc->mode = LCD64_MONO;
985		break;
986	case HD64461_LCDLDR3_CG_GRAY4:
987		depth = 4;
988		hvc->mode = LCD16_MONO;
989		break;
990	case HD64461_LCDLDR3_CG_GRAY2:
991		depth = 2;
992		hvc->mode = LCD4_MONO;
993		break;
994	case HD64461_LCDLDR3_CG_GRAY1:
995		depth = 1;
996		hvc->mode = LCD2_MONO;
997		break;
998	}
999
1000	r = hd64461_reg_read_2(HD64461_LCDCCR_REG16);
1001	i = HD64461_LCDCCR_DSPSEL(i);
1002	switch (i) {
1003	case HD64461_LCDCCR_DSPSEL_LCD_CRT:
1004		depth = 8;
1005		hvc->mode = LCDCRT;
1006		break;
1007	case HD64461_LCDCCR_DSPSEL_CRT:
1008		depth = 8;
1009		hvc->mode = CRT256_C;
1010		break;
1011	case HD64461_LCDCCR_DSPSEL_LCD:
1012		/* nothing to do */
1013		break;
1014	}
1015
1016	callout_init(&hvc->unblank_ch, 0);
1017	hvc->blanked = 0;
1018
1019	width = bootinfo->fb_width;
1020	height = bootinfo->fb_height;
1021
1022	vc->vc_fbvaddr	= HD64461_FBBASE;
1023	vc->vc_fbpaddr	= HD64461_FBBASE;
1024	vc->vc_fbdepth	= depth;
1025	vc->vc_fbsize	= (width * height * depth) / NBBY;
1026	vc->vc_fbwidth	= width;
1027	vc->vc_fbheight	= height;
1028}
1029
1030#if notyet
1031STATIC void
1032hd64461video_set_display_mode(struct hd64461video_chip *hvc)
1033{
1034
1035	if (hvc->mode == LCDCRT || hvc->mode == CRT256_C)
1036		hd64461video_set_display_mode_crtc(hvc);
1037
1038	hd64461video_set_display_mode_lcdc(hvc);
1039}
1040
1041STATIC void
1042hd64461video_set_display_mode_lcdc(struct hd64461video_chip *hvc)
1043{
1044	struct {
1045		uint16_t clor;	/* display size 640 x 240 */
1046		uint16_t ldr3;
1047		const char *name;
1048	} *conf, disp_conf[] = {
1049		[LCD256_C]	= { 640, HD64461_LCDLDR3_CG_COLOR8,
1050				    "8bit color" },
1051		[LCD64K_C]	= { 640 * 2, HD64461_LCDLDR3_CG_COLOR16,
1052				    "16bit color" },
1053		[LCD64_MONO]	= { 640, HD64461_LCDLDR3_CG_GRAY6 ,
1054				    "6bit grayscale" },
1055		[LCD16_MONO]	= { 640 / 2, HD64461_LCDLDR3_CG_GRAY4,
1056				    "4bit grayscale" },
1057		[LCD4_MONO]	= { 640 / 4, HD64461_LCDLDR3_CG_GRAY2,
1058				    "2bit grayscale" },
1059		[LCD2_MONO]	= { 640 / 8, HD64461_LCDLDR3_CG_GRAY1,
1060				    "monochrome" },
1061	};
1062	uint16_t r;
1063	int omode;
1064
1065	conf = &disp_conf[hvc->mode];
1066
1067	hd64461_reg_write_2(HD64461_LCDCLOR_REG16, conf->clor);
1068	r = hd64461_reg_read_2(HD64461_LCDLDR3_REG16);
1069	omode = HD64461_LCDLDR3_CG(r);
1070	r = HD64461_LCDLDR3_CG_CLR(r);
1071	r = HD64461_LCDLDR3_CG_SET(r, conf->ldr3);
1072	hd64461_reg_write_2(HD64461_LCDLDR3_REG16, r);
1073
1074	printf("%s ", conf->name);
1075}
1076
1077STATIC void
1078hd64461video_set_display_mode_crtc(struct hd64461video_chip *hvc)
1079{
1080	/* not yet */
1081}
1082
1083#endif /* notyet */
1084
1085STATIC size_t
1086hd64461video_frame_buffer_size(struct hd64461video_chip *hvc)
1087{
1088	vaddr_t page, startaddr, endaddr;
1089	int x;
1090
1091	startaddr = HD64461_FBBASE;
1092	endaddr = startaddr + HD64461_FBSIZE - 1;
1093
1094	page = startaddr;
1095
1096	x = random();
1097	*(volatile int *)(page + 0) = x;
1098	*(volatile int *)(page + 4) = ~x;
1099
1100	if (*(volatile int *)(page + 0) != x ||
1101	    *(volatile int *)(page + 4) != ~x)
1102		return (0);
1103
1104	for (page += HD64461_FBPAGESIZE; page < endaddr;
1105	    page += HD64461_FBPAGESIZE) {
1106		if (*(volatile int *)(page + 0) == x &&
1107		    *(volatile int *)(page + 4) == ~x)
1108			goto fbend_found;
1109	}
1110
1111	page -= HD64461_FBPAGESIZE;
1112	*(volatile int *)(page + 0) = x;
1113	*(volatile int *)(page + 4) = ~x;
1114
1115	if (*(volatile int *)(page + 0) != x ||
1116	    *(volatile int *)(page + 4) != ~x)
1117		return (0);
1118
1119 fbend_found:
1120	return (page - startaddr);
1121}
1122
1123STATIC void
1124hd64461video_set_clut(struct hd64461video_chip *vc, int idx, int cnt,
1125    uint8_t *r, uint8_t *g, uint8_t *b)
1126{
1127	KASSERT(r && g && b);
1128
1129	/* index palette */
1130	hd64461_reg_write_2(HD64461_LCDCPTWAR_REG16,
1131	    HD64461_LCDCPTWAR_SET(0, idx));
1132	/* set data */
1133	while (cnt && LEGAL_CLUT_INDEX(idx)) {
1134		uint16_t v;
1135#define	HD64461VIDEO_SET_CLUT(x)					\
1136		v = (x >> 2) & 0x3f;					\
1137		hd64461_reg_write_2(HD64461_LCDCPTWDR_REG16, v)
1138		HD64461VIDEO_SET_CLUT(*r);
1139		HD64461VIDEO_SET_CLUT(*g);
1140		HD64461VIDEO_SET_CLUT(*b);
1141#undef	HD64461VIDEO_SET_CLUT
1142		r++, g++, b++;
1143		idx++, cnt--;
1144	}
1145}
1146
1147STATIC void
1148hd64461video_get_clut(struct hd64461video_chip *vc, int idx, int cnt,
1149    uint8_t *r, uint8_t *g, uint8_t *b)
1150{
1151	KASSERT(r && g && b);
1152
1153	/* index palette */
1154	hd64461_reg_write_2(HD64461_LCDCPTRAR_REG16,
1155	    HD64461_LCDCPTRAR_SET(0, idx));
1156
1157	/* get data */
1158	while (cnt && LEGAL_CLUT_INDEX(idx)) {
1159		uint16_t v;
1160#define	HD64461VIDEO_GET_CLUT(x)					\
1161	v = hd64461_reg_read_2(HD64461_LCDCPTRDR_REG16);		\
1162	x = HD64461_LCDCPTRDR(v);					\
1163	x <<= 2
1164		HD64461VIDEO_GET_CLUT(*r);
1165		HD64461VIDEO_GET_CLUT(*g);
1166		HD64461VIDEO_GET_CLUT(*b);
1167#undef	HD64461VIDEO_GET_CLUT
1168		r++, g++, b++;
1169		idx++, cnt--;
1170	}
1171}
1172
1173STATIC int
1174hd64461video_power(void *ctx, int type, long id, void *msg)
1175{
1176	struct hd64461video_softc *sc = ctx;
1177	struct hd64461video_chip *hvc = sc->sc_vc;
1178
1179	switch ((int)msg) {
1180	case PWR_RESUME:
1181		DPRINTF("%s: ON%s\n", device_xname(sc->sc_dev),
1182			sc->sc_vc->blanked ? " (blanked)" : "");
1183		if (!sc->sc_vc->blanked)
1184			hd64461video_on(hvc);
1185		break;
1186	case PWR_SUSPEND:
1187		/* FALLTHROUGH */
1188	case PWR_STANDBY:
1189		DPRINTF("%s: OFF\n", device_xname(sc->sc_dev));
1190		hd64461video_off(hvc);
1191		break;
1192	}
1193
1194	return 0;
1195}
1196
1197STATIC void
1198hd64461video_off(struct hd64461video_chip *vc)
1199{
1200
1201	callout_stop(&vc->unblank_ch);
1202
1203	/* turn off display in LCDC */
1204	hd64461video_display_onoff(vc, false);
1205
1206	/* turn off the LCD */
1207	config_hook_call(CONFIG_HOOK_POWERCONTROL,
1208			 CONFIG_HOOK_POWERCONTROL_LCD,
1209			 (void *)0);
1210}
1211
1212STATIC void
1213hd64461video_on(struct hd64461video_chip *vc)
1214{
1215	int err;
1216
1217	/* turn on the LCD */
1218	err = config_hook_call(CONFIG_HOOK_POWERCONTROL,
1219			       CONFIG_HOOK_POWERCONTROL_LCD,
1220			       (void *)1);
1221
1222	if (err == 0)
1223		/* let the LCD warm up before turning on the display */
1224		callout_reset(&vc->unblank_ch, hz/2,
1225		    hd64461video_display_on, vc);
1226	else
1227		hd64461video_display_onoff(vc, true);
1228}
1229
1230STATIC void
1231hd64461video_display_on(void *arg)
1232{
1233
1234	hd64461video_display_onoff(arg, true);
1235}
1236
1237STATIC void
1238hd64461video_display_onoff(void *arg, bool on)
1239{
1240	/* struct hd64461video_chip *vc = arg; */
1241	uint16_t r;
1242
1243	if (platid_match(&platid, &platid_mask_MACH_HITACHI_PERSONA))
1244		return;
1245
1246	/* turn on/off display in LCDC */
1247	r = hd64461_reg_read_2(HD64461_LCDLDR1_REG16);
1248	if (on)
1249		r |= HD64461_LCDLDR1_DON;
1250	else
1251		r &= ~HD64461_LCDLDR1_DON;
1252	hd64461_reg_write_2(HD64461_LCDLDR1_REG16, r);
1253}
1254
1255#ifdef HD64461VIDEO_DEBUG
1256STATIC void
1257hd64461video_info(struct hd64461video_softc *sc)
1258{
1259	uint16_t r;
1260	int color;
1261	int i;
1262
1263	dbg_banner_function();
1264	printf("---[LCD]---\n");
1265	/* Base Address Register */
1266	r = hd64461_reg_read_2(HD64461_LCDCBAR_REG16);
1267	printf("LCDCBAR Frame buffer base address (4KB align): 0x%08x\n",
1268	    HD64461_LCDCBAR_BASEADDR(r));
1269
1270	/* Line Address Offset Register */
1271	r = hd64461_reg_read_2(HD64461_LCDCLOR_REG16);
1272	printf("LCDCLOR Line address offset: %d\n", HD64461_LCDCLOR(r));
1273
1274	/* LCDC Control Register */
1275	r = hd64461_reg_read_2(HD64461_LCDCCR_REG16);
1276	i = HD64461_LCDCCR_DSPSEL(r);
1277#define	DBG_BITMASK_PRINT(r, m)	dbg_bitmask_print(r, HD64461_LCDCCR_##m, #m)
1278	printf("LCDCCR (LCD Control Register)\n");
1279	DBG_BITMASK_PRINT(r, STBAK);
1280	DBG_BITMASK_PRINT(r, STREQ);
1281	DBG_BITMASK_PRINT(r, MOFF);
1282	DBG_BITMASK_PRINT(r, REFSEL);
1283	DBG_BITMASK_PRINT(r, EPON);
1284	DBG_BITMASK_PRINT(r, SPON);
1285	printf("\n");
1286#undef	DBG_BITMASK_PRINT
1287	printf("LCDCCR Display select LCD[%c] CRT[%c]\n",
1288	    i == HD64461_LCDCCR_DSPSEL_LCD_CRT ||
1289	    i == HD64461_LCDCCR_DSPSEL_LCD ? 'x' : '_',
1290	    i == HD64461_LCDCCR_DSPSEL_LCD_CRT ||
1291	    i == HD64461_LCDCCR_DSPSEL_CRT ? 'x' : '_');
1292
1293	/* LCD Display Register */
1294	/* 1 */
1295	r = hd64461_reg_read_2(HD64461_LCDLDR1_REG16);
1296	printf("(LCD Display Register)\n");
1297#define	DBG_BITMASK_PRINT(r, m)	dbg_bitmask_print(r, HD64461_LCDLDR1_##m, #m)
1298	printf("LCDLDR1: ");
1299	DBG_BITMASK_PRINT(r, DINV);
1300	DBG_BITMASK_PRINT(r, DON);
1301	printf("\n");
1302#undef	DBG_BITMASK_PRINT
1303	/* 2 */
1304	r = hd64461_reg_read_2(HD64461_LCDLDR2_REG16);
1305	i = HD64461_LCDLDR2_LM(r);
1306#define	DBG_BITMASK_PRINT(r, m)	dbg_bitmask_print(r, HD64461_LCDLDR2_##m, #m)
1307	printf("LCDLDR2: ");
1308	DBG_BITMASK_PRINT(r, CC1);
1309	DBG_BITMASK_PRINT(r, CC2);
1310#undef	DBG_BITMASK_PRINT
1311	color = 0;
1312	switch (i) {
1313	default:
1314		panic("unknown unknown LCD interface.");
1315		break;
1316	case HD64461_LCDLDR2_LM_COLOR:
1317		color = 1;
1318		printf("Color");
1319		break;
1320	case HD64461_LCDLDR2_LM_GRAY8:
1321		printf("8-bit grayscale");
1322		break;
1323	case HD64461_LCDLDR2_LM_GRAY4:
1324		printf("4-bit grayscale");
1325		break;
1326	}
1327	printf(" LCD interface\n");
1328	/* 3 */
1329	printf("LCDLDR3: ");
1330	r = hd64461_reg_read_2(HD64461_LCDLDR3_REG16);
1331	i = HD64461_LCDLDR3_CS(r);
1332	printf("CS ");
1333	switch (i) {
1334	case 0:
1335		printf("15");
1336		break;
1337	case 1:
1338		printf("2.5");
1339		break;
1340	case 2:
1341		printf("3.75");
1342		break;
1343	case 4:
1344		printf("5");
1345		break;
1346	case 8:
1347		printf("7.5");
1348		break;
1349	case 16:
1350		printf("10");
1351		break;
1352	}
1353	printf("%s MHz ", color ? "" : "/2");
1354	i = HD64461_LCDLDR3_CG(r);
1355	switch (i) {
1356	case HD64461_LCDLDR3_CG_COLOR16:
1357		printf("Color 64K colors\n");
1358		break;
1359	case HD64461_LCDLDR3_CG_COLOR8:
1360		printf("Color 256 colors\n");
1361		break;
1362	case HD64461_LCDLDR3_CG_GRAY6:
1363		printf("6-bit Grayscale\n");
1364		break;
1365	case HD64461_LCDLDR3_CG_GRAY4:
1366		printf("4-bit Grayscale\n");
1367		break;
1368	case HD64461_LCDLDR3_CG_GRAY2:
1369		printf("2-bit Grayscale\n");
1370		break;
1371	case HD64461_LCDLDR3_CG_GRAY1:
1372		printf("1-bit Grayscale\n");
1373		break;
1374	}
1375
1376	/* LCD Number of Characters in Horizontal Register */
1377	r = hd64461_reg_read_2(HD64461_LCDLDHNCR_REG16);
1378	printf("LDHNCR: NHD %d NHT %d (# of horizontal characters)\n",
1379	    HD64461_LCDLDHNCR_NHD(r), HD64461_LCDLDHNCR_NHT(r));
1380
1381	/* Start Position of Horizontal Register */
1382	r = hd64461_reg_read_2(HD64461_LCDLDHNSR_REG16);
1383	printf("LDHNSR: HSW %d HSP %d (start position of horizontal)\n",
1384	    HD64461_LCDLDHNSR_HSW(r), HD64461_LCDLDHNSR_HSP(r));
1385
1386	/* Total Vertical Lines Register */
1387	r = hd64461_reg_read_2(HD64461_LCDLDVNTR_REG16);
1388	printf("LDVNTR: %d (total vertical lines)\n",
1389	    HD64461_LCDLDVNTR_VTL(r));
1390
1391	/* Display Vertical Lines Register */
1392	r = hd64461_reg_read_2(HD64461_LCDLDVNDR_REG16);
1393	printf("LDVNDR: %d (display vertical lines)\n",
1394	    HD64461_LCDLDVSPR_VSP(r));
1395
1396	/* Vertical Synchronization Position Register */
1397	r = hd64461_reg_read_2(HD64461_LCDLDVSPR_REG16);
1398	printf("LDVSPR: %d (vertical synchronization position)\n",
1399	    HD64461_LCDLDVSPR_VSP(r));
1400
1401	/*
1402	 * CRT Control Register
1403	 */
1404	printf("---[CRT]---\n");
1405	r = hd64461_reg_read_2(HD64461_LCDCRTVTR_REG16);
1406	printf("CRTVTR: %d (CRTC total vertical lines)\n",
1407	    HD64461_LCDCRTVTR(r));
1408	r = hd64461_reg_read_2(HD64461_LCDCRTVRSR_REG16);
1409	printf("CRTVRSR: %d (CRTC vertical retrace start line)\n",
1410	    HD64461_LCDCRTVRSR(r));
1411	r = hd64461_reg_read_2(HD64461_LCDCRTVRER_REG16);
1412	printf("CRTVRER: %d (CRTC vertical retrace end line)\n",
1413	    HD64461_LCDCRTVRER(r));
1414
1415}
1416
1417STATIC void
1418hd64461video_dump(void)
1419{
1420	uint16_t r;
1421	printf("---[Display Mode Setting]---\n");
1422#define	DUMPREG(x)							\
1423	r = hd64461_reg_read_2(HD64461_LCD ## x ## _REG16);		\
1424	__dbg_bit_print(r, sizeof(uint16_t), 0, 0, #x, DBG_BIT_PRINT_COUNT)
1425	DUMPREG(CBAR);
1426	DUMPREG(CLOR);
1427	DUMPREG(CCR);
1428	DUMPREG(LDR1);
1429	DUMPREG(LDR2);
1430	DUMPREG(LDHNCR);
1431	DUMPREG(LDHNSR);
1432	DUMPREG(LDVNTR);
1433	DUMPREG(LDVNDR);
1434	DUMPREG(LDVSPR);
1435	DUMPREG(LDR3);
1436	DUMPREG(CRTVTR);
1437	DUMPREG(CRTVRSR);
1438	DUMPREG(CRTVRER);
1439#undef	DUMPREG
1440	dbg_banner_line();
1441}
1442
1443#endif /* HD64461VIDEO_DEBUG */
1444