static void awin_tcon0_enable(struct awin_tcon_softc *sc, bool enable) { uint32_t val; /* turn on/off backlight */ if (sc->sc_lcdblk_pin_name != NULL) { awin_gpio_pindata_write(&sc->sc_lcdblk_pin, enable ? 1 : 0); } /* turn on/off LCD */ if (sc->sc_lcdpwr_pin_name != NULL) { awin_gpio_pindata_write(&sc->sc_lcdpwr_pin, enable ? 1 : 0); } /* and finally disable of enable the tcon */ KASSERT(sc->sc_output_type != OUTPUT_HDMI); awin_debe_enable(device_unit(sc->sc_dev), enable); delay(20000); if (enable) { val = TCON_READ(sc, AWIN_TCON_GCTL_REG); val |= AWIN_TCON_GCTL_EN; TCON_WRITE(sc, AWIN_TCON_GCTL_REG, val); val = TCON_READ(sc, AWIN_TCON0_CTL_REG); val |= AWIN_TCONx_CTL_EN; TCON_WRITE(sc, AWIN_TCON0_CTL_REG, val); val = TCON_READ(sc, AWIN_TCON0_LVDS_IF_REG); val |= AWIN_TCON0_LVDS_IF_EN; TCON_WRITE(sc, AWIN_TCON0_LVDS_IF_REG, val); TCON_WRITE(sc, AWIN_TCON0_IO_TRI_REG, 0); } else { TCON_WRITE(sc, AWIN_TCON0_IO_TRI_REG, 0xffffffff); val = TCON_READ(sc, AWIN_TCON0_LVDS_IF_REG); val &= ~AWIN_TCON0_LVDS_IF_EN; TCON_WRITE(sc, AWIN_TCON0_LVDS_IF_REG, val); val = TCON_READ(sc, AWIN_TCON0_CTL_REG); val &= ~AWIN_TCONx_CTL_EN; TCON_WRITE(sc, AWIN_TCON0_CTL_REG, val); val = TCON_READ(sc, AWIN_TCON_GCTL_REG); val &= ~AWIN_TCON_GCTL_EN; TCON_WRITE(sc, AWIN_TCON_GCTL_REG, val); } }
static void awin_hdmi_read_edid(struct awin_hdmi_softc *sc) { const struct videomode *mode; char edid[128]; struct edid_info ei; int retry = 4; u_int display_mode; memset(edid, 0, sizeof(edid)); memset(&ei, 0, sizeof(ei)); while (--retry > 0) { if (!awin_hdmi_read_edid_block(sc, edid, 0)) break; } if (retry == 0) { device_printf(sc->sc_dev, "failed to read EDID\n"); } else { if (edid_parse(edid, &ei) != 0) { device_printf(sc->sc_dev, "failed to parse EDID\n"); } #ifdef AWIN_HDMI_DEBUG else { edid_print(&ei); } #endif } if (sc->sc_display_mode == DISPLAY_MODE_AUTO) display_mode = awin_hdmi_get_display_mode(sc, &ei); else display_mode = sc->sc_display_mode; const char *forced = sc->sc_display_mode == DISPLAY_MODE_AUTO ? "auto-detected" : "forced"; device_printf(sc->sc_dev, "%s mode (%s)\n", display_mode == DISPLAY_MODE_HDMI ? "HDMI" : "DVI", forced); strlcpy(sc->sc_display_vendor, ei.edid_vendorname, sizeof(sc->sc_display_vendor)); strlcpy(sc->sc_display_product, ei.edid_productname, sizeof(sc->sc_display_product)); sc->sc_current_display_mode = display_mode; mode = ei.edid_preferred_mode; if (mode == NULL) mode = pick_mode_by_ref(640, 480, 60); if (mode != NULL) { awin_hdmi_video_enable(sc, false); awin_tcon_enable(false); delay(20000); awin_debe_set_videomode(mode); awin_tcon_set_videomode(mode); awin_hdmi_set_videomode(sc, mode, display_mode); awin_hdmi_set_audiomode(sc, mode, display_mode); awin_debe_enable(true); delay(20000); awin_tcon_enable(true); delay(20000); awin_hdmi_video_enable(sc, true); } }
void awin_tcon1_enable(int unit, bool enable) { struct awin_tcon_softc *sc; device_t dev; uint32_t val; dev = device_find_by_driver_unit("awintcon", unit); if (dev == NULL) { printf("TCON%d: no driver found\n", unit); return; } sc = device_private(dev); KASSERT((sc->sc_output_type == OUTPUT_HDMI) || (sc->sc_output_type == OUTPUT_VGA)); awin_debe_enable(device_unit(sc->sc_dev), enable); delay(20000); if (enable) { val = TCON_READ(sc, AWIN_TCON_GCTL_REG); val |= AWIN_TCON_GCTL_EN; TCON_WRITE(sc, AWIN_TCON_GCTL_REG, val); val = TCON_READ(sc, AWIN_TCON1_CTL_REG); val |= AWIN_TCONx_CTL_EN; TCON_WRITE(sc, AWIN_TCON1_CTL_REG, val); if (sc->sc_output_type == OUTPUT_VGA) { TCON_WRITE(sc, AWIN_TCON1_IO_TRI_REG, 0x0cffffff); } else TCON_WRITE(sc, AWIN_TCON1_IO_TRI_REG, 0); } else { TCON_WRITE(sc, AWIN_TCON1_IO_TRI_REG, 0xffffffff); val = TCON_READ(sc, AWIN_TCON1_CTL_REG); val &= ~AWIN_TCONx_CTL_EN; TCON_WRITE(sc, AWIN_TCON1_CTL_REG, val); val = TCON_READ(sc, AWIN_TCON_GCTL_REG); val &= ~AWIN_TCON_GCTL_EN; TCON_WRITE(sc, AWIN_TCON_GCTL_REG, val); } KASSERT(tcon_mux_inited); val = bus_space_read_4(sc->sc_bst, tcon_mux_bsh, 0); #ifdef AWIN_TCON_DEBUG printf("awin_tcon1_enable(%d) %d val 0x%x", unit, enable, val); #endif val &= ~ AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC; if (unit == 0) { val |= __SHIFTIN(AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC_LCDC0_TCON1, AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC); } else if (unit == 1) { val |= __SHIFTIN(AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC_LCDC1_TCON1, AWIN_TCON_MUX_CTL_HDMI_OUTPUT_SRC); } #ifdef AWIN_TCON_DEBUG printf(" -> 0x%x", val); #endif bus_space_write_4(sc->sc_bst, tcon_mux_bsh, 0, val); #ifdef AWIN_TCON_DEBUG printf(": 0x%" PRIxBSH " 0x%" PRIxBSH " 0x%x 0x%x\n", sc->sc_bsh, tcon_mux_bsh, bus_space_read_4(sc->sc_bst, tcon_mux_bsh, 0), TCON_READ(sc, AWIN_TCON_MUX_CTL_REG)); #endif }
static void awin_tcon0_set_video(struct awin_tcon_softc *sc) { int32_t lcd_x, lcd_y, lcd_dclk_freq; int32_t lcd_hbp, lcd_ht, lcd_vbp, lcd_vt; int32_t lcd_hspw, lcd_vspw, lcd_io_cfg0; uint32_t vblk, start_delay; prop_dictionary_t cfg = device_properties(sc->sc_dev); uint32_t val; bool propb; bool dualchan = false; static struct videomode mode; if (!prop_dictionary_get_int32(cfg, "lcd_x", &lcd_x)) { aprint_error_dev(sc->sc_dev, ": can't read lcd_x\n"); return; } if (!prop_dictionary_get_int32(cfg, "lcd_y", &lcd_y)) { aprint_error_dev(sc->sc_dev, ": can't read lcd_y\n"); return; } if (!prop_dictionary_get_int32(cfg, "lcd_dclk_freq", &lcd_dclk_freq)) { aprint_error_dev(sc->sc_dev, ": can't read lcd_dclk_freq\n"); return; } if (!prop_dictionary_get_int32(cfg, "lcd_hbp", &lcd_hbp)) { aprint_error_dev(sc->sc_dev, ": can't read lcd_hbp\n"); return; } if (!prop_dictionary_get_int32(cfg, "lcd_ht", &lcd_ht)) { aprint_error_dev(sc->sc_dev, ": can't read lcd_ht\n"); return; } if (!prop_dictionary_get_int32(cfg, "lcd_vbp", &lcd_vbp)) { aprint_error_dev(sc->sc_dev, ": can't read lcd_vbp\n"); return; } if (!prop_dictionary_get_int32(cfg, "lcd_vt", &lcd_vt)) { aprint_error_dev(sc->sc_dev, ": can't read lcd_vt\n"); return; } if (!prop_dictionary_get_int32(cfg, "lcd_hspw", &lcd_hspw)) { aprint_error_dev(sc->sc_dev, ": can't read lcd_hspw\n"); return; } if (!prop_dictionary_get_int32(cfg, "lcd_vspw", &lcd_vspw)) { aprint_error_dev(sc->sc_dev, ": can't read lcd_vspw\n"); return; } if (!prop_dictionary_get_int32(cfg, "lcd_io_cfg0", &lcd_io_cfg0)) { aprint_error_dev(sc->sc_dev, ": can't read lcd_io_cfg0\n"); return; } if (prop_dictionary_get_bool(cfg, "lvds_dual", &propb) && propb) dualchan = true; if (!awin_gpio_pinset_available(&awin_lvds0_pinset)) { aprint_error_dev(sc->sc_dev, "lvds0 pins not available\n"); return; } if (dualchan && !awin_gpio_pinset_available(&awin_lvds1_pinset)) { aprint_error_dev(sc->sc_dev, "lvds1 pins not available\n"); return; } awin_gpio_pinset_acquire(&awin_lvds0_pinset); if (dualchan) { awin_gpio_pinset_acquire(&awin_lvds1_pinset); } prop_dictionary_get_cstring_nocopy(cfg, "lcd_power_en", &sc->sc_lcdpwr_pin_name); if (sc->sc_lcdpwr_pin_name != NULL) { if (!awin_gpio_pin_reserve( sc->sc_lcdpwr_pin_name, &sc->sc_lcdpwr_pin)) { aprint_error_dev(sc->sc_dev, "failed to reserve GPIO \"%s\" for LCD power\n", sc->sc_lcdpwr_pin_name); sc->sc_lcdpwr_pin_name = NULL; } else { aprint_verbose_dev(sc->sc_dev, ": using GPIO \"%s\" for LCD power\n", sc->sc_lcdpwr_pin_name); } } prop_dictionary_get_cstring_nocopy(cfg, "lcd_bl_en", &sc->sc_lcdblk_pin_name); if (sc->sc_lcdblk_pin_name != NULL) { if (!awin_gpio_pin_reserve( sc->sc_lcdblk_pin_name, &sc->sc_lcdblk_pin)) { aprint_error_dev(sc->sc_dev, "failed to reserve GPIO \"%s\" for backlight\n", sc->sc_lcdblk_pin_name); sc->sc_lcdblk_pin_name = NULL; } else { if (sc->sc_lcdpwr_pin_name == NULL) { aprint_verbose_dev(sc->sc_dev, ": using GPIO \"%s\" for backlight\n", sc->sc_lcdblk_pin_name); } else { aprint_verbose( ", GPIO \"%s\" for backlight\n", sc->sc_lcdblk_pin_name); } } } if (sc->sc_lcdpwr_pin_name != NULL) { awin_gpio_pindata_write(&sc->sc_lcdpwr_pin, 1); } vblk = (lcd_vt / 2) - lcd_y; start_delay = (vblk >= 32) ? 30 : (vblk - 2); if (lcd_dclk_freq > 150) /* hardware limit ? */ lcd_dclk_freq = 150; awin_tcon_set_pll(sc, lcd_dclk_freq * 1000, 7); val = AWIN_TCONx_CTL_EN; val |= __SHIFTIN(start_delay, AWIN_TCONx_CTL_START_DELAY); /* * the DE selector selects the primary DEBE for this tcon: * 0 selects debe0 for tcon0 and debe1 for tcon1 */ val |= __SHIFTIN(AWIN_TCONx_CTL_SRC_SEL_DE0, AWIN_TCONx_CTL_SRC_SEL); TCON_WRITE(sc, AWIN_TCON0_CTL_REG, val); val = (lcd_x - 1) << 16 | (lcd_y - 1); TCON_WRITE(sc, AWIN_TCON0_BASIC0_REG, val); val = (lcd_ht - 1) << 16 | (lcd_hbp - 1); TCON_WRITE(sc, AWIN_TCON0_BASIC1_REG, val); val = (lcd_vt) << 16 | (lcd_vbp - 1); TCON_WRITE(sc, AWIN_TCON0_BASIC2_REG, val); val = ((lcd_hspw > 0) ? (lcd_hspw - 1) : 0) << 16; val |= ((lcd_vspw > 0) ? (lcd_vspw - 1) : 0); TCON_WRITE(sc, AWIN_TCON0_BASIC3_REG, val); val = 0; if (dualchan) val |= AWIN_TCON0_LVDS_IF_DUALCHAN; if (prop_dictionary_get_bool(cfg, "lvds_mode_jeida", &propb) && propb) val |= AWIN_TCON0_LVDS_IF_MODE_JEIDA; if (prop_dictionary_get_bool(cfg, "lvds_18bits", &propb) && propb) val |= AWIN_TCON0_LVDS_IF_18BITS; TCON_WRITE(sc, AWIN_TCON0_LVDS_IF_REG, val); TCON_WRITE(sc, AWIN_TCON0_IO_POL_REG, lcd_io_cfg0); TCON_WRITE(sc, AWIN_TCON0_IO_TRI_REG, 0); TCON_WRITE(sc, AWIN_TCON_GINT1_REG, __SHIFTIN(start_delay + 2, AWIN_TCON_GINT1_TCON0_LINENO)); val = 0xf0000000; val &= ~AWIN_TCON0_DCLK_DIV; val |= __SHIFTIN(sc->sc_clk_div, AWIN_TCON0_DCLK_DIV); TCON_WRITE(sc, AWIN_TCON0_DCLK_REG, val); mode.dot_clock = lcd_dclk_freq; mode.hdisplay = lcd_x; mode.hsync_start = lcd_ht - lcd_hbp; mode.hsync_end = lcd_hspw + mode.hsync_start; mode.htotal = lcd_ht; mode.vdisplay = lcd_y; mode.vsync_start = lcd_vt - lcd_vbp; mode.vsync_end = lcd_vspw + mode.vsync_start; mode.vtotal = lcd_vt; mode.flags = 0; mode.name = NULL; awin_debe_set_videomode(sc->sc_debe_unit, &mode); /* and finally, enable it */ awin_debe_enable(sc->sc_debe_unit, true); delay(20000); val = TCON_READ(sc, AWIN_TCON_GCTL_REG); val |= AWIN_TCON_GCTL_EN; TCON_WRITE(sc, AWIN_TCON_GCTL_REG, val); delay(20000); val = TCON_READ(sc, AWIN_TCON0_LVDS_IF_REG); val |= AWIN_TCON0_LVDS_IF_EN; TCON_WRITE(sc, AWIN_TCON0_LVDS_IF_REG, val); /* XXX * magic values here from linux. these are not documented * in the A20 user manual, and other Allwiner LVDS-capable SoC * documentation don't make sense with these values */ val = TCON_READ(sc, AWIN_TCON_LVDS_ANA0); val |= 0x3F310000; TCON_WRITE(sc, AWIN_TCON_LVDS_ANA0, val); val = TCON_READ(sc, AWIN_TCON_LVDS_ANA0); val |= 1 << 22; TCON_WRITE(sc, AWIN_TCON_LVDS_ANA0, val); delay(2); val = TCON_READ(sc, AWIN_TCON_LVDS_ANA1); val |= (0x1f << 26 | 0x1f << 10); TCON_WRITE(sc, AWIN_TCON_LVDS_ANA1, val); delay(2); val = TCON_READ(sc, AWIN_TCON_LVDS_ANA1); val |= (0x1f << 16 | 0x1f << 0); TCON_WRITE(sc, AWIN_TCON_LVDS_ANA1, val); val = TCON_READ(sc, AWIN_TCON_LVDS_ANA0); val |= 1 << 22; TCON_WRITE(sc, AWIN_TCON_LVDS_ANA0, val); if (sc->sc_lcdblk_pin_name != NULL) { awin_gpio_pindata_write(&sc->sc_lcdblk_pin, 1); } }