/*-
* Copyright (c) 2001, 2002, 2004 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by UCHIYAMA Yasushi.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
if (hd64461video_chip.console)
aprint_normal(", console");
aprint_normal("\n");
#ifdef HD64461VIDEO_DEBUG
hd64461video_info(sc);
hd64461video_dump();
#endif
/* Add a hard power hook to power saving */
config_hook(CONFIG_HOOK_PMEVENT, CONFIG_HOOK_PMEVENT_HARDPOWER,
CONFIG_HOOK_SHARE, hd64461video_power, sc);
/*
* XXX: TODO: for now this device manages power using
* config_hook(9) registered with hpcapm(4).
*
* We cannot yet switch it to pmf(9) hooks because only apm(4)
* uses them, apmdev(4) doesn't, but hpcapm(4) is the parent
* device for both, so its hooks are always run.
*
* We probably want to register shutdown hook with pmf(9) to
* make sure display is powered on before we reboot in case we
* end up in ddb early on.
*/
if (!pmf_device_register(self, NULL, NULL))
aprint_error_dev(self, "unable to establish power handler\n");
}
/* hpcfb support */
STATIC void
hd64461video_setup_hpcfbif(struct hd64461video_chip *hvc)
{
struct video_chip *vc = &hvc->vc;
struct hpcfb_fbconf *fb = &hvc->hf;
vaddr_t fbvaddr = vc->vc_fbvaddr;
int height = vc->vc_fbheight;
int width = vc->vc_fbwidth;
int depth = vc->vc_fbdepth;
memset(fb, 0, sizeof(struct hpcfb_fbconf));
fb->hf_conf_index = 0; /* configuration index */
fb->hf_nconfs = 1; /* how many configurations */
strncpy(fb->hf_name, "HD64461 video module", HPCFB_MAXNAMELEN);
/* frame buffer name */
strncpy(fb->hf_conf_name, "LCD", HPCFB_MAXNAMELEN);
switch (depth) {
default:
panic("%s: not supported color depth", __func__);
/* NOTREACHED */
case 16:
fb->hf_class = HPCFB_CLASS_RGBCOLOR;
fb->hf_access_flags |= HPCFB_ACCESS_STATIC;
fb->hf_pack_width = 16;
fb->hf_pixels_per_pack = 1;
fb->hf_pixel_width = 16;
/*
* XXX: uwe: if I RTFS correctly, this really means
* that uint16_t pixel is fetched as little endian.
*/
fb->hf_order_flags = HPCFB_REVORDER_BYTE;
fb->hf_class_data_length = sizeof(struct hf_rgb_tag);
/* reserved for future use */
fb->hf_u.hf_rgb.hf_flags = 0;
case HPCFBIO_SCONF:
fbconf = (struct hpcfb_fbconf *)data;
if (fbconf->hf_conf_index != 0 &&
fbconf->hf_conf_index != HPCFB_CURRENT_CONFIG) {
return (EINVAL);
}
/*
* nothing to do because we have only one configuration
*/
return (0);
case HPCFBIO_SDSPCONF:
dspconf = (struct hpcfb_dspconf *)data;
if ((dspconf->hd_unit_index != 0 &&
dspconf->hd_unit_index != HPCFB_CURRENT_UNIT) ||
(dspconf->hd_conf_index != 0 &&
dspconf->hd_conf_index != HPCFB_CURRENT_CONFIG)) {
return (EINVAL);
}
/*
* nothing to do
* because we have only one unit and one configuration
*/
return (0);
case HPCFBIO_GOP:
case HPCFBIO_SOP:
/* XXX not implemented yet */
return (EINVAL);
}
STATIC void
hd64461video_cursor(void *ctx, int on, int xd, int yd, int w, int h)
{
struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
int xw, yh, width, bpp, adr;
uint16_t r;
width = sc->sc_vc->vc.vc_fbwidth;
bpp = sc->sc_vc->vc.vc_fbdepth;
xw = w - 1;
yh = h - 1;
/* Wait until previous command done. */
hd64461video_iodone(ctx);
// Kick.
r = hd64461_reg_read_2(HD64461_LCDGRCFGR_REG16);
r &= ~HD64461_LCDGRCFGR_ACCSTART_MASK;
r |= HD64461_LCDGRCFGR_ACCSTART_BITBLT;
hd64461_reg_write_2(HD64461_LCDGRCFGR_REG16, r);
}
STATIC void
hd64461video_bitblit(void *ctx, int xs, int ys, int xd, int yd, int h, int w)
{
struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
int xw, yh, width, bpp, condition_a, adr;
uint16_t r;
// BitBLT mode (on screen to on screen)
r = HD64461_LCDBBTMDR_SET(0,
HD64461_LCDBBTMDR_ON_SCREEN_TO_ON_SCREEN);
if (condition_a) /* reverse direction */
r |= HD64461_LCDBBTMDR_SCANDRCT_RL_BT;
hd64461_reg_write_2(HD64461_LCDBBTMDR_REG16, r);
// Kick.
r = hd64461_reg_read_2(HD64461_LCDGRCFGR_REG16);
r &= ~HD64461_LCDGRCFGR_ACCSTART_MASK;
r |= HD64461_LCDGRCFGR_ACCSTART_BITBLT;
hd64461_reg_write_2(HD64461_LCDGRCFGR_REG16, r);
}
STATIC void
hd64461video_erase(void *ctx, int xd, int yd, int h, int w, int attr)
{
struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
int xw, yh, width, bpp, adr;
uint16_t r;
width = sc->sc_vc->vc.vc_fbwidth;
bpp = sc->sc_vc->vc.vc_fbdepth;
xw = w - 1;
yh = h - 1;
/* Wait until previous command done. */
hd64461video_iodone(ctx);
// Kick.
r = hd64461_reg_read_2(HD64461_LCDGRCFGR_REG16);
r &= ~HD64461_LCDGRCFGR_ACCSTART_MASK;
r |= HD64461_LCDGRCFGR_ACCSTART_BITBLT;
hd64461_reg_write_2(HD64461_LCDGRCFGR_REG16, r);
}
STATIC void
hd64461video_putchar(void *ctx, int row, int col, struct wsdisplay_font *font,
int fclr, int uclr, u_int uc, int attr)
{
struct hd64461video_softc *sc = (struct hd64461video_softc *)ctx;
int w, h, cw;
/* turn off display in LCDC */
hd64461video_display_onoff(vc, false);
/* turn off the LCD */
config_hook_call(CONFIG_HOOK_POWERCONTROL,
CONFIG_HOOK_POWERCONTROL_LCD,
(void *)0);
}
STATIC void
hd64461video_on(struct hd64461video_chip *vc)
{
int err;
/* turn on the LCD */
err = config_hook_call(CONFIG_HOOK_POWERCONTROL,
CONFIG_HOOK_POWERCONTROL_LCD,
(void *)1);
if (err == 0)
/* let the LCD warm up before turning on the display */
callout_reset(&vc->unblank_ch, hz/2,
hd64461video_display_on, vc);
else
hd64461video_display_onoff(vc, true);
}
if (platid_match(&platid, &platid_mask_MACH_HITACHI_PERSONA))
return;
/* turn on/off display in LCDC */
r = hd64461_reg_read_2(HD64461_LCDLDR1_REG16);
if (on)
r |= HD64461_LCDLDR1_DON;
else
r &= ~HD64461_LCDLDR1_DON;
hd64461_reg_write_2(HD64461_LCDLDR1_REG16, r);
}
#ifdef HD64461VIDEO_DEBUG
STATIC void
hd64461video_info(struct hd64461video_softc *sc)
{
uint16_t r;
int color;
int i;
dbg_banner_function();
printf("---[LCD]---\n");
/* Base Address Register */
r = hd64461_reg_read_2(HD64461_LCDCBAR_REG16);
printf("LCDCBAR Frame buffer base address (4KB align): 0x%08x\n",
HD64461_LCDCBAR_BASEADDR(r));
/* Line Address Offset Register */
r = hd64461_reg_read_2(HD64461_LCDCLOR_REG16);
printf("LCDCLOR Line address offset: %d\n", HD64461_LCDCLOR(r));
/* LCDC Control Register */
r = hd64461_reg_read_2(HD64461_LCDCCR_REG16);
i = HD64461_LCDCCR_DSPSEL(r);
#define DBG_BITMASK_PRINT(r, m) dbg_bitmask_print(r, HD64461_LCDCCR_##m, #m)
printf("LCDCCR (LCD Control Register)\n");
DBG_BITMASK_PRINT(r, STBAK);
DBG_BITMASK_PRINT(r, STREQ);
DBG_BITMASK_PRINT(r, MOFF);
DBG_BITMASK_PRINT(r, REFSEL);
DBG_BITMASK_PRINT(r, EPON);
DBG_BITMASK_PRINT(r, SPON);
printf("\n");
#undef DBG_BITMASK_PRINT
printf("LCDCCR Display select LCD[%c] CRT[%c]\n",
i == HD64461_LCDCCR_DSPSEL_LCD_CRT ||
i == HD64461_LCDCCR_DSPSEL_LCD ? 'x' : '_',
i == HD64461_LCDCCR_DSPSEL_LCD_CRT ||
i == HD64461_LCDCCR_DSPSEL_CRT ? 'x' : '_');
/* LCD Display Register */
/* 1 */
r = hd64461_reg_read_2(HD64461_LCDLDR1_REG16);
printf("(LCD Display Register)\n");
#define DBG_BITMASK_PRINT(r, m) dbg_bitmask_print(r, HD64461_LCDLDR1_##m, #m)
printf("LCDLDR1: ");
DBG_BITMASK_PRINT(r, DINV);
DBG_BITMASK_PRINT(r, DON);
printf("\n");
#undef DBG_BITMASK_PRINT
/* 2 */
r = hd64461_reg_read_2(HD64461_LCDLDR2_REG16);
i = HD64461_LCDLDR2_LM(r);
#define DBG_BITMASK_PRINT(r, m) dbg_bitmask_print(r, HD64461_LCDLDR2_##m, #m)
printf("LCDLDR2: ");
DBG_BITMASK_PRINT(r, CC1);
DBG_BITMASK_PRINT(r, CC2);
#undef DBG_BITMASK_PRINT
color = 0;
switch (i) {
default:
panic("unknown unknown LCD interface.");
break;
case HD64461_LCDLDR2_LM_COLOR:
color = 1;
printf("Color");
break;
case HD64461_LCDLDR2_LM_GRAY8:
printf("8-bit grayscale");
break;
case HD64461_LCDLDR2_LM_GRAY4:
printf("4-bit grayscale");
break;
}
printf(" LCD interface\n");
/* 3 */
printf("LCDLDR3: ");
r = hd64461_reg_read_2(HD64461_LCDLDR3_REG16);
i = HD64461_LCDLDR3_CS(r);
printf("CS ");
switch (i) {
case 0:
printf("15");
break;
case 1:
printf("2.5");
break;
case 2:
printf("3.75");
break;
case 4:
printf("5");
break;
case 8:
printf("7.5");
break;
case 16:
printf("10");
break;
}
printf("%s MHz ", color ? "" : "/2");
i = HD64461_LCDLDR3_CG(r);
switch (i) {
case HD64461_LCDLDR3_CG_COLOR16:
printf("Color 64K colors\n");
break;
case HD64461_LCDLDR3_CG_COLOR8:
printf("Color 256 colors\n");
break;
case HD64461_LCDLDR3_CG_GRAY6:
printf("6-bit Grayscale\n");
break;
case HD64461_LCDLDR3_CG_GRAY4:
printf("4-bit Grayscale\n");
break;
case HD64461_LCDLDR3_CG_GRAY2:
printf("2-bit Grayscale\n");
break;
case HD64461_LCDLDR3_CG_GRAY1:
printf("1-bit Grayscale\n");
break;
}
/* LCD Number of Characters in Horizontal Register */
r = hd64461_reg_read_2(HD64461_LCDLDHNCR_REG16);
printf("LDHNCR: NHD %d NHT %d (# of horizontal characters)\n",
HD64461_LCDLDHNCR_NHD(r), HD64461_LCDLDHNCR_NHT(r));
/* Start Position of Horizontal Register */
r = hd64461_reg_read_2(HD64461_LCDLDHNSR_REG16);
printf("LDHNSR: HSW %d HSP %d (start position of horizontal)\n",
HD64461_LCDLDHNSR_HSW(r), HD64461_LCDLDHNSR_HSP(r));
/* Total Vertical Lines Register */
r = hd64461_reg_read_2(HD64461_LCDLDVNTR_REG16);
printf("LDVNTR: %d (total vertical lines)\n",
HD64461_LCDLDVNTR_VTL(r));