1*0c9d3924Smiod /* $OpenBSD: amdisplay.c,v 1.20 2024/11/06 07:09:45 miod Exp $ */ 22b401c1dSians /* 351bdad2cSians * Copyright (c) 2016 Ian Sutton <ians@openbsd.org> 42b401c1dSians * 52b401c1dSians * Permission to use, copy, modify, and distribute this software for any 62b401c1dSians * purpose with or without fee is hereby granted, provided that the above 72b401c1dSians * copyright notice and this permission notice appear in all copies. 82b401c1dSians * 92b401c1dSians * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 102b401c1dSians * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 112b401c1dSians * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 122b401c1dSians * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 132b401c1dSians * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 142b401c1dSians * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 152b401c1dSians * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 162b401c1dSians */ 172b401c1dSians 182b401c1dSians #include <sys/param.h> 192b401c1dSians #include <sys/systm.h> 202b401c1dSians #include <sys/device.h> 212b401c1dSians #include <sys/malloc.h> 222b401c1dSians 232b401c1dSians #include <dev/wscons/wsconsio.h> 242b401c1dSians #include <dev/wscons/wsdisplayvar.h> 252b401c1dSians #include <dev/rasops/rasops.h> 262b401c1dSians #include <dev/videomode/videomode.h> 272b401c1dSians #include <dev/videomode/edidvar.h> 282b401c1dSians 292b401c1dSians #include <dev/ofw/openfirm.h> 302b401c1dSians #include <dev/ofw/ofw_pinctrl.h> 312b401c1dSians #include <dev/ofw/fdt.h> 322b401c1dSians #include <machine/fdt.h> 332b401c1dSians 342b401c1dSians #include <armv7/omap/prcmvar.h> 352b401c1dSians #include <armv7/omap/amdisplayreg.h> 362b401c1dSians #include <armv7/omap/nxphdmivar.h> 372b401c1dSians 382b401c1dSians #ifdef LCD_DEBUG 392b401c1dSians #define str(X) #X 402b401c1dSians int lcd_dbg_thresh = 20; 412b401c1dSians #define DPRINTF(n,s) do { if ((n) <= lcd_dbg_thresh) printf s; } while (0) 422b401c1dSians #else 432b401c1dSians #define DPRINTF(n,s) do {} while (0) 442b401c1dSians #endif 452b401c1dSians 462b401c1dSians #define DEVNAME(_s) ((_s)->sc_dev.dv_xname) 472b401c1dSians 482252d02cSkettenis #define LCD_MAX_PELCLK 170000 /* kHz */ 492b401c1dSians #define LCD_MASTER_OSC 24000000 /* Hz */ 502b401c1dSians #define LCD_M1_MAX 2048 512b401c1dSians #define LCD_M2_MAX 31 522b401c1dSians #define LCD_N_MAX 128 532b401c1dSians 542b401c1dSians #define HREAD4(sc, reg) \ 552b401c1dSians (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 562b401c1dSians #define HWRITE4(sc, reg, val) \ 572b401c1dSians bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 582b401c1dSians #define HSET4(sc, reg, bits) \ 592b401c1dSians HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 602b401c1dSians #define HCLR4(sc, reg, bits) \ 612b401c1dSians HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 622b401c1dSians 632b401c1dSians struct amdisplay_softc { 642b401c1dSians struct device sc_dev; 652b401c1dSians bus_space_tag_t sc_iot; 662b401c1dSians bus_space_handle_t sc_ioh; 672b401c1dSians bus_dma_tag_t sc_dmat; 682b401c1dSians void *sc_ih; 692b401c1dSians 702b401c1dSians int sc_flags; 712b401c1dSians #define LCD_RESET_PENDING (1 << 0) 722b401c1dSians #define LCD_MODE_COMPAT (1 << 1) 732b401c1dSians #define LCD_MODE_ALLOC (1 << 2) 742b401c1dSians 75a834c853Skettenis struct edid_info sc_edid; 762b401c1dSians struct videomode *sc_active_mode; 772b401c1dSians int sc_active_depth; 782b401c1dSians 792b401c1dSians bus_dmamap_t sc_fb0_dma; 802b401c1dSians bus_dma_segment_t sc_fb0_dma_segs[1]; 812b401c1dSians void *sc_fb0; 822b401c1dSians int sc_fb_dma_nsegs; 832b401c1dSians bus_size_t sc_fb_size; 842b401c1dSians 852b401c1dSians struct rasops_info sc_ro; 862b401c1dSians struct wsscreen_descr sc_wsd; 872b401c1dSians struct wsscreen_list sc_wsl; 882b401c1dSians struct wsscreen_descr *sc_scrlist[1]; 892b401c1dSians }; 902b401c1dSians 912b401c1dSians int amdisplay_match(struct device *, void *, void *); 922b401c1dSians void amdisplay_attach(struct device *, struct device *, void *); 932b401c1dSians int amdisplay_detach(struct device *, int); 942b401c1dSians int amdisplay_intr(void *); 952b401c1dSians 962b401c1dSians int amdisplay_ioctl(void *, u_long, caddr_t, int, struct proc *); 972b401c1dSians paddr_t amdisplay_mmap(void *, off_t, int); 982b401c1dSians int amdisplay_alloc_screen(void *, const struct wsscreen_descr *, 99e0c3e559Sjsg void **, int *, int *, uint32_t *); 1002b401c1dSians 1012b401c1dSians int amdisplay_setup_dma(struct amdisplay_softc *); 1022b401c1dSians void amdisplay_conf_crt_timings(struct amdisplay_softc *); 1032b401c1dSians void amdisplay_calc_freq(uint64_t); 1042b401c1dSians 1052b401c1dSians struct wsdisplay_accessops amdisplay_accessops = { 1062b401c1dSians .ioctl = amdisplay_ioctl, 1072b401c1dSians .mmap = amdisplay_mmap, 1082b401c1dSians .alloc_screen = amdisplay_alloc_screen, 1092b401c1dSians .free_screen = rasops_free_screen, 1102b401c1dSians .show_screen = rasops_show_screen, 1112b401c1dSians .getchar = rasops_getchar, 1122b401c1dSians .load_font = rasops_load_font, 1132b401c1dSians .list_font = rasops_list_font, 1142b401c1dSians }; 1152b401c1dSians 1169fdf0c62Smpi const struct cfattach amdisplay_ca = { 1172b401c1dSians sizeof(struct amdisplay_softc), amdisplay_match, amdisplay_attach, 1182b401c1dSians amdisplay_detach 1192b401c1dSians }; 1202b401c1dSians 1212b401c1dSians struct cfdriver amdisplay_cd = { 1222b401c1dSians NULL, "amdisplay", DV_DULL 1232b401c1dSians }; 1242b401c1dSians 1252b401c1dSians #ifdef LCD_DEBUG 1262b401c1dSians void 1272b401c1dSians preg(uint32_t reg, char *rn, struct amdisplay_softc *sc) 1282b401c1dSians { 1292b401c1dSians uint32_t read; 1302b401c1dSians 1312b401c1dSians read = HREAD4(sc, reg); 1322b401c1dSians DPRINTF(16, ("%s: %s: 0x%08x\n", DEVNAME(sc), rn, read)); 1332b401c1dSians } 1342b401c1dSians 1352b401c1dSians void 1362b401c1dSians dumpregs(struct amdisplay_softc *sc) 1372b401c1dSians { 1382b401c1dSians preg(LCD_PID, str(AMDISPLAY_PID), sc); 1392b401c1dSians preg(LCD_CTRL, str(AMDISPLAY_CTRL), sc); 1402b401c1dSians preg(LCD_RASTER_CTRL, str(AMDISPLAY_RASTER_CTRL), sc); 1412b401c1dSians preg(LCD_RASTER_TIMING_0, str(AMDISPLAY_RASTER_TIMING_0), sc); 1422b401c1dSians preg(LCD_RASTER_TIMING_1, str(AMDISPLAY_RASTER_TIMING_1), sc); 1432b401c1dSians preg(LCD_RASTER_TIMING_2, str(AMDISPLAY_RASTER_TIMING_2), sc); 1442b401c1dSians preg(LCD_RASTER_SUBPANEL, str(AMDISPLAY_RASTER_SUBPANEL), sc); 1452b401c1dSians preg(LCD_RASTER_SUBPANEL_2, str(AMDISPLAY_RASTER_SUBPANEL_2), sc); 1462b401c1dSians preg(LCD_LCDDMA_CTRL, str(AMDISPLAY_LCDDMA_CTRL), sc); 1472b401c1dSians 1482b401c1dSians /* accessing these regs is liable to occur during CPU lockout period */ 1492b401c1dSians #if 0 1502b401c1dSians preg(LCD_LCDDMA_FB0_BASE, str(AMDISPLAY_LCDDMA_FB0_BASE), sc); 1512b401c1dSians preg(LCD_LCDDMA_FB0_CEILING, str(AMDISPLAY_LCDDMA_FB0_CEILING), sc); 1522b401c1dSians preg(LCD_LCDDMA_FB1_BASE, str(AMDISPLAY_LCDDMA_FB1_BASE), sc); 1532b401c1dSians preg(LCD_LCDDMA_FB1_CEILING, str(AMDISPLAY_LCDDMA_FB1_CEILING), sc); 1542b401c1dSians #endif 1552b401c1dSians 1562b401c1dSians preg(LCD_SYSCONFIG, str(AMDISPLAY_SYSCONFIG), sc); 1572b401c1dSians preg(LCD_IRQSTATUS_RAW, str(AMDISPLAY_IRQSTATUS_RAW), sc); 1582b401c1dSians preg(LCD_IRQSTATUS, str(AMDISPLAY_IRQSTATUS), sc); 1592b401c1dSians preg(LCD_IRQENABLE_SET, str(AMDISPLAY_IRQENABLE_SET), sc); 1602b401c1dSians preg(LCD_IRQENABLE_CLEAR, str(AMDISPLAY_IRQENABLE_CLEAR), sc); 1612b401c1dSians preg(LCD_CLKC_ENABLE, str(AMDISPLAY_CLKC_ENABLE), sc); 1622b401c1dSians preg(LCD_CLKC_RESET, str(AMDISPLAY_CLKC_RESET), sc); 1632b401c1dSians } 1642b401c1dSians #endif 1652b401c1dSians 1662b401c1dSians int 1672b401c1dSians amdisplay_match(struct device *parent, void *v, void *aux) 1682b401c1dSians { 1692b401c1dSians struct fdt_attach_args *faa = aux; 1702b401c1dSians return OF_is_compatible(faa->fa_node, "ti,am33xx-tilcdc"); 1712b401c1dSians } 1722b401c1dSians 1732b401c1dSians void 1742b401c1dSians amdisplay_attach(struct device *parent, struct device *self, void *args) 1752b401c1dSians { 1762b401c1dSians struct amdisplay_softc *sc = (struct amdisplay_softc *) self; 1772b401c1dSians struct fdt_attach_args *faa = args; 1782b401c1dSians struct wsemuldisplaydev_attach_args wsaa; 179fd864923Sjsg uint64_t pel_clk = 0; 180f320bd4dSpatrick uint32_t reg; 1812b401c1dSians uint8_t *edid_buf; 1822b401c1dSians int stride, i = 0; 1832b401c1dSians 1842b401c1dSians sc->sc_iot = faa->fa_iot; 1852b401c1dSians sc->sc_dmat = faa->fa_dmat; 1862b401c1dSians if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 1872b401c1dSians faa->fa_reg[0].size, 0, &sc->sc_ioh)) 1882b401c1dSians panic("%s: bus_space_map failed!", __func__); 1892b401c1dSians 1902b401c1dSians /* enable clock module */ 1912b401c1dSians prcm_enablemodule(PRCM_LCDC); 1922b401c1dSians 1932b401c1dSians /* force ourselves out of standby/idle states */ 1942b401c1dSians reg = HREAD4(sc, LCD_SYSCONFIG); 1952b401c1dSians reg &= ~(LCD_SYSCONFIG_STANDBYMODE | LCD_SYSCONFIG_IDLEMODE); 1962b401c1dSians reg |= (0x2 << LCD_SYSCONFIG_STANDBYMODE_SHAMT) 1972b401c1dSians | (0x2 << LCD_SYSCONFIG_IDLEMODE_SHAMT); 1982b401c1dSians HWRITE4(sc, LCD_SYSCONFIG, reg); 1992b401c1dSians 200f320bd4dSpatrick sc->sc_ih = arm_intr_establish_fdt(faa->fa_node, IPL_BIO, 201f320bd4dSpatrick amdisplay_intr, sc, DEVNAME(sc)); 2022b401c1dSians 2032b401c1dSians printf("\n"); 2042b401c1dSians 2052b401c1dSians /* read/parse EDID bits from TDA19988 HDMI PHY */ 2062b401c1dSians edid_buf = malloc(EDID_LENGTH, M_DEVBUF, M_WAITOK | M_ZERO); 2072b401c1dSians sc->sc_active_mode = malloc(sizeof(struct videomode), M_DEVBUF, 2082b401c1dSians M_WAITOK | M_ZERO); 2092b401c1dSians sc->sc_flags |= LCD_MODE_ALLOC; 2102b401c1dSians 2112b401c1dSians if (nxphdmi_get_edid(edid_buf, EDID_LENGTH) || 212*0c9d3924Smiod edid_parse(DEVNAME(sc), edid_buf, &sc->sc_edid)) { 2132b401c1dSians printf("%s: no display attached.\n", DEVNAME(sc)); 2142b401c1dSians free(edid_buf, M_DEVBUF, EDID_LENGTH); 2152b401c1dSians amdisplay_detach(self, 0); 2162b401c1dSians return; 2172b401c1dSians } 2182b401c1dSians 219d37b69caSclaudio free(edid_buf, M_DEVBUF, EDID_LENGTH); 220d37b69caSclaudio 221c07d3a0fSmiod #if defined(LCD_DEBUG) && defined(EDID_DEBUG) 222a834c853Skettenis edid_print(&sc->sc_edid); 2232b401c1dSians #endif 2242b401c1dSians 2252b401c1dSians /* determine max supported resolution our clock signal can handle */ 226a834c853Skettenis for (; i < sc->sc_edid.edid_nmodes - 1; i++) { 227a834c853Skettenis if (sc->sc_edid.edid_modes[i].dot_clock < LCD_MAX_PELCLK && 228a834c853Skettenis sc->sc_edid.edid_modes[i].dot_clock > pel_clk) { 229a834c853Skettenis pel_clk = sc->sc_edid.edid_modes[i].dot_clock; 230a834c853Skettenis memcpy(sc->sc_active_mode, &sc->sc_edid.edid_modes[i], 2312b401c1dSians sizeof(struct videomode)); 2322b401c1dSians } 2332b401c1dSians } 2342b401c1dSians 2352b401c1dSians i = 0; 2362252d02cSkettenis printf("%s: %s :: %d kHz pclk\n", DEVNAME(sc), 2372b401c1dSians sc->sc_active_mode->name, sc->sc_active_mode->dot_clock); 2382b401c1dSians 2392b401c1dSians pel_clk *= 2000; 2402b401c1dSians amdisplay_calc_freq(pel_clk); 2412b401c1dSians 2422b401c1dSians sc->sc_active_mode->flags |= VID_HSKEW; 2432b401c1dSians sc->sc_active_depth = 16; 2442b401c1dSians 2452b401c1dSians /* configure DMA framebuffer */ 2462b401c1dSians if (amdisplay_setup_dma(sc)) { 2472b401c1dSians printf("%s: couldn't allocate DMA framebuffer\n", DEVNAME(sc)); 2482b401c1dSians amdisplay_detach(self, 0); 2492b401c1dSians return; 2502b401c1dSians } 2512b401c1dSians 2522b401c1dSians /* setup rasops */ 2532b401c1dSians stride = sc->sc_active_mode->hdisplay * sc->sc_active_depth / 8; 2542b401c1dSians 2552b401c1dSians sc->sc_ro.ri_depth = sc->sc_active_depth; 2562b401c1dSians sc->sc_ro.ri_width = sc->sc_active_mode->hdisplay; 2572b401c1dSians sc->sc_ro.ri_height = sc->sc_active_mode->vdisplay; 2582b401c1dSians sc->sc_ro.ri_stride = stride; 2592b401c1dSians sc->sc_ro.ri_bits = sc->sc_fb0; 2602b401c1dSians 2612b401c1dSians sc->sc_ro.ri_rpos = 0; 2622b401c1dSians sc->sc_ro.ri_rnum = 5; 2632b401c1dSians sc->sc_ro.ri_gpos = 5; 2642b401c1dSians sc->sc_ro.ri_gnum = 6; 2652b401c1dSians sc->sc_ro.ri_bpos = 11; 2662b401c1dSians sc->sc_ro.ri_bnum = 5; 2672b401c1dSians sc->sc_ro.ri_hw = sc; 2682b401c1dSians sc->sc_ro.ri_flg = RI_CENTER | RI_VCONS | RI_WRONLY | 2692b401c1dSians RI_CLEAR | RI_FULLCLEAR; 2702b401c1dSians 2712b401c1dSians if (rasops_init(&sc->sc_ro, 200, 200)) { 2722b401c1dSians printf("%s: no rasops\n", DEVNAME(sc)); 2732b401c1dSians amdisplay_detach(self, 0); 2742b401c1dSians return; 2752b401c1dSians } 2762b401c1dSians 2772b401c1dSians /* ensure controller is off */ 2782b401c1dSians HCLR4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN); 2792b401c1dSians delay(100); 2802b401c1dSians 2812b401c1dSians /* set clock divisor needed for 640x480 VGA timings */ 2822b401c1dSians reg = HREAD4(sc, LCD_CTRL); 2832b401c1dSians reg &= ~LCD_CTRL_CLKDIV; 2842b401c1dSians reg |= (0x2 << LCD_CTRL_CLKDIV_SHAMT); 2852b401c1dSians 2862b401c1dSians /* select raster mode & reset-on-underflow, write */ 2872b401c1dSians reg |= LCD_CTRL_MODESEL; 2882b401c1dSians HWRITE4(sc, LCD_CTRL, reg); 2892b401c1dSians 29036fd90dcSjsg /* set stn565 + active matrix + palette loading only mode, delay */ 2912b401c1dSians reg = HREAD4(sc, LCD_RASTER_CTRL); 2922b401c1dSians reg &= 0xF8000C7C; 2932b401c1dSians reg |= (LCD_RASTER_CTRL_LCDTFT) 2942b401c1dSians | (0x02 << LCD_RASTER_CTRL_PALMODE_SHAMT) 2952b401c1dSians | (0xFF << LCD_RASTER_CTRL_REQDLY_SHAMT); 2962b401c1dSians HWRITE4(sc, LCD_RASTER_CTRL, reg); 2972b401c1dSians 2982b401c1dSians /* set timing values */ 2992b401c1dSians amdisplay_conf_crt_timings(sc); 3002b401c1dSians 3012b401c1dSians /* configure HDMI transmitter (TDA19988) with our mode details */ 3022b401c1dSians nxphdmi_set_videomode(sc->sc_active_mode); 3032b401c1dSians 3042b401c1dSians /* latch pins/pads according to fdt node */ 3052b401c1dSians pinctrl_byphandle(LCD_FDT_PHANDLE); 3062b401c1dSians 3072b401c1dSians /* configure DMA transfer settings */ 3082b401c1dSians reg = HREAD4(sc, LCD_LCDDMA_CTRL); 3092b401c1dSians reg &= ~(LCD_LCDDMA_CTRL_DMA_MASTER_PRIO 3102b401c1dSians | LCD_LCDDMA_CTRL_TH_FIFO_READY 3112b401c1dSians | LCD_LCDDMA_CTRL_BURST_SIZE 3122b401c1dSians | LCD_LCDDMA_CTRL_BYTE_SWAP 3132b401c1dSians | LCD_LCDDMA_CTRL_BIGENDIAN 3142b401c1dSians | LCD_LCDDMA_CTRL_FRAME_MODE); 3152b401c1dSians reg |= (0x4 << LCD_LCDDMA_CTRL_BURST_SIZE_SHAMT) 3162b401c1dSians | LCD_LCDDMA_CTRL_FRAME_MODE; 3172b401c1dSians HWRITE4(sc, LCD_LCDDMA_CTRL, reg); 3182b401c1dSians 3192b401c1dSians /* set framebuffer location + bounds */ 3202b401c1dSians HWRITE4(sc, LCD_LCDDMA_FB0, sc->sc_fb0_dma_segs[0].ds_addr); 3212b401c1dSians HWRITE4(sc, LCD_LCDDMA_FB0_CEIL, (sc->sc_fb0_dma_segs[0].ds_addr 3222b401c1dSians + sc->sc_fb_size)); 3232b401c1dSians HWRITE4(sc, LCD_LCDDMA_FB1, sc->sc_fb0_dma_segs[0].ds_addr); 3242b401c1dSians HWRITE4(sc, LCD_LCDDMA_FB1_CEIL, (sc->sc_fb0_dma_segs[0].ds_addr 3252b401c1dSians + sc->sc_fb_size)); 3262b401c1dSians 3272b401c1dSians /* enable all intrs. */ 3282b401c1dSians reg = 0; 3292b401c1dSians reg |= (LCD_IRQ_EOF1 | LCD_IRQ_EOF0 | LCD_IRQ_PL | LCD_IRQ_FUF | 3302b401c1dSians LCD_IRQ_ACB | LCD_IRQ_SYNC | LCD_IRQ_RR_DONE | LCD_IRQ_DONE); 3312b401c1dSians 3322b401c1dSians HWRITE4(sc, LCD_IRQENABLE_SET, reg); 3332b401c1dSians 3342b401c1dSians /* enable dma & core clocks */ 3352b401c1dSians HSET4(sc, LCD_CLKC_ENABLE, LCD_CLKC_ENABLE_DMA_CLK_EN 3362b401c1dSians | LCD_CLKC_ENABLE_CORE_CLK_EN 3372b401c1dSians | LCD_CLKC_ENABLE_LIDD_CLK_EN); 3382b401c1dSians 3392b401c1dSians /* perform controller clock reset */ 3402b401c1dSians HSET4(sc, LCD_CLKC_RESET, LCD_CLKC_RESET_MAIN_RST); 3412b401c1dSians delay(100); 3422b401c1dSians HCLR4(sc, LCD_CLKC_RESET, LCD_CLKC_RESET_MAIN_RST); 3432b401c1dSians 3442b401c1dSians /* configure wsdisplay descr. */ 3452b401c1dSians strlcpy(sc->sc_wsd.name, "std", sizeof(sc->sc_wsd.name)); 3462b401c1dSians sc->sc_wsd.capabilities = sc->sc_ro.ri_caps; 3472b401c1dSians sc->sc_wsd.nrows = sc->sc_ro.ri_rows; 3482b401c1dSians sc->sc_wsd.ncols = sc->sc_ro.ri_cols; 3492b401c1dSians sc->sc_wsd.textops = &sc->sc_ro.ri_ops; 3502b401c1dSians sc->sc_wsd.fontwidth = sc->sc_ro.ri_font->fontwidth; 3512b401c1dSians sc->sc_wsd.fontheight = sc->sc_ro.ri_font->fontheight; 3522b401c1dSians 3532b401c1dSians sc->sc_scrlist[0] = &sc->sc_wsd; 3542b401c1dSians sc->sc_wsl.nscreens = 1; 3552b401c1dSians sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist; 3562b401c1dSians 3572b401c1dSians /* attach console */ 3582b401c1dSians memset(&wsaa, 0, sizeof(wsaa)); 3592b401c1dSians wsaa.scrdata = &sc->sc_wsl; 3602b401c1dSians wsaa.accessops = &amdisplay_accessops; 3612b401c1dSians wsaa.accesscookie = &sc->sc_ro; 3622b401c1dSians 3632b401c1dSians config_found_sm(self, &wsaa, wsemuldisplaydevprint, 3642b401c1dSians wsemuldisplaydevsubmatch); 3652b401c1dSians 3662b401c1dSians /* enable controller */ 3672b401c1dSians HSET4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN); 3682b401c1dSians } 3692b401c1dSians 3702b401c1dSians int 3712b401c1dSians amdisplay_detach(struct device *self, int flags) 3722b401c1dSians { 3732b401c1dSians struct amdisplay_softc *sc = (struct amdisplay_softc *)self; 3742b401c1dSians 3752b401c1dSians if (ISSET(sc->sc_flags, LCD_MODE_ALLOC)) 3762b401c1dSians free(sc->sc_active_mode, M_DEVBUF, sizeof(struct videomode)); 3772b401c1dSians 3782b401c1dSians if (!sc->sc_fb0) 3792b401c1dSians return 0; 3802b401c1dSians 3812b401c1dSians bus_dmamap_sync(sc->sc_dmat, sc->sc_fb0_dma, 0, sc->sc_fb_size, 3822b401c1dSians BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 3832b401c1dSians 3842b401c1dSians bus_dmamap_unload(sc->sc_dmat, sc->sc_fb0_dma); 3852b401c1dSians bus_dmamem_unmap(sc->sc_dmat, (caddr_t)(sc->sc_fb0), sc->sc_fb_size); 3862b401c1dSians bus_dmamem_free(sc->sc_dmat, sc->sc_fb0_dma_segs, sc->sc_fb_dma_nsegs); 3872b401c1dSians bus_dmamap_destroy(sc->sc_dmat, sc->sc_fb0_dma); 3882b401c1dSians 3892b401c1dSians return 0; 3902b401c1dSians } 3912b401c1dSians 3922b401c1dSians int 3932b401c1dSians amdisplay_intr(void *arg) 3942b401c1dSians { 3952b401c1dSians struct amdisplay_softc *sc = arg; 3962b401c1dSians uint32_t reg; 3972b401c1dSians 3982b401c1dSians reg = HREAD4(sc, LCD_IRQSTATUS); 3992b401c1dSians HWRITE4(sc, LCD_IRQSTATUS, reg); 4002b401c1dSians 4012b401c1dSians DPRINTF(25, ("%s: intr 0x%08x\n", DEVNAME(sc), reg)); 4022b401c1dSians 4032b401c1dSians if (ISSET(reg, LCD_IRQ_PL)) { 4042b401c1dSians DPRINTF(10, ("%s: palette loaded, irq: 0x%08x\n", 4052b401c1dSians DEVNAME(sc), reg)); 4062b401c1dSians HCLR4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN); 4072b401c1dSians delay(100); 4082b401c1dSians HCLR4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_PALMODE); 4092b401c1dSians HSET4(sc, LCD_RASTER_CTRL, 0x02 << LCD_RASTER_CTRL_PALMODE_SHAMT); 4102b401c1dSians HSET4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN); 41192ab049dSians } 41292ab049dSians 41392ab049dSians if (ISSET(reg, LCD_IRQ_FUF)) { 4142b401c1dSians DPRINTF(15, ("%s: FIFO underflow\n", DEVNAME(sc))); 41592ab049dSians } 41692ab049dSians 41792ab049dSians if (ISSET(reg, LCD_IRQ_SYNC)) { 4182b401c1dSians sc->sc_flags |= LCD_RESET_PENDING; 4192b401c1dSians DPRINTF(18, ("%s: sync lost\n", DEVNAME(sc))); 42092ab049dSians } 42192ab049dSians 42292ab049dSians if (ISSET(reg, LCD_IRQ_RR_DONE)) { 4232b401c1dSians DPRINTF(21, ("%s: frame done\n", DEVNAME(sc))); 4242b401c1dSians HWRITE4(sc, LCD_LCDDMA_FB0, sc->sc_fb0_dma_segs[0].ds_addr); 4252b401c1dSians HWRITE4(sc, LCD_LCDDMA_FB0_CEIL, (sc->sc_fb0_dma_segs[0].ds_addr 4262b401c1dSians + sc->sc_fb_size) - 1); 42792ab049dSians } 42892ab049dSians 42992ab049dSians if (ISSET(reg, LCD_IRQ_EOF0)) { 4302b401c1dSians DPRINTF(21, ("%s: framebuffer 0 done\n", DEVNAME(sc))); 43192ab049dSians } 43292ab049dSians 43392ab049dSians if (ISSET(reg, LCD_IRQ_EOF1)) { 4342b401c1dSians DPRINTF(21, ("%s: framebuffer 1 done\n", DEVNAME(sc))); 43592ab049dSians } 43692ab049dSians 43792ab049dSians if (ISSET(reg, LCD_IRQ_DONE)) { 4382b401c1dSians if (ISSET(sc->sc_flags, LCD_RESET_PENDING)) { 4392b401c1dSians HWRITE4(sc, LCD_IRQSTATUS, 0xFFFFFFFF); 4402b401c1dSians HSET4(sc, LCD_CLKC_RESET, LCD_CLKC_RESET_MAIN_RST); 4412b401c1dSians delay(10); 4422b401c1dSians HCLR4(sc, LCD_CLKC_RESET, LCD_CLKC_RESET_MAIN_RST); 4432b401c1dSians HSET4(sc, LCD_RASTER_CTRL, LCD_RASTER_CTRL_LCDEN); 4442b401c1dSians sc->sc_flags &= ~LCD_RESET_PENDING; 4452b401c1dSians } 4462b401c1dSians DPRINTF(15, ("%s: last frame done\n", DEVNAME(sc))); 44792ab049dSians } 44892ab049dSians 44992ab049dSians if (ISSET(reg, LCD_IRQ_ACB)) { 4502b401c1dSians DPRINTF(15, ("%s: AC bias event\n", DEVNAME(sc))); 4512b401c1dSians } 4522b401c1dSians 4532b401c1dSians HWRITE4(sc, LCD_IRQ_END, 0); 4542b401c1dSians 4552b401c1dSians return 0; 4562b401c1dSians } 4572b401c1dSians 4582b401c1dSians int 4592b401c1dSians amdisplay_setup_dma(struct amdisplay_softc *sc) 4602b401c1dSians { 4612b401c1dSians bus_size_t bsize; 4622b401c1dSians 4632b401c1dSians bsize = (sc->sc_active_mode->hdisplay * sc->sc_active_mode->vdisplay 4642b401c1dSians * sc->sc_active_depth) >> 3; 4652b401c1dSians 4662b401c1dSians sc->sc_fb_size = bsize; 4672b401c1dSians sc->sc_fb_dma_nsegs = 1; 4682b401c1dSians 4692b401c1dSians if (bus_dmamap_create(sc->sc_dmat, sc->sc_fb_size, sc->sc_fb_dma_nsegs, 4702b401c1dSians sc->sc_fb_size, 0, BUS_DMA_NOWAIT, &(sc->sc_fb0_dma))) 4712b401c1dSians return -1; 4722b401c1dSians 4732b401c1dSians if (bus_dmamem_alloc(sc->sc_dmat, sc->sc_fb_size, 4, 0, 4742b401c1dSians sc->sc_fb0_dma_segs, 1, &(sc->sc_fb_dma_nsegs), 4752b401c1dSians BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) 4762b401c1dSians return -2; 4772b401c1dSians 4782b401c1dSians if (bus_dmamem_map(sc->sc_dmat, sc->sc_fb0_dma_segs, 4792b401c1dSians sc->sc_fb_dma_nsegs, sc->sc_fb_size, (caddr_t *)&(sc->sc_fb0), 4802b401c1dSians BUS_DMA_NOWAIT | BUS_DMA_COHERENT | BUS_DMA_NOCACHE)) 4812b401c1dSians return -3; 4822b401c1dSians 4832b401c1dSians if (bus_dmamap_load(sc->sc_dmat, sc->sc_fb0_dma, sc->sc_fb0, bsize, 4842b401c1dSians NULL, BUS_DMA_NOWAIT)) 4852b401c1dSians return -4; 4862b401c1dSians 4872b401c1dSians memset(sc->sc_fb0, 0, bsize); 4882b401c1dSians 4892b401c1dSians bus_dmamap_sync(sc->sc_dmat, sc->sc_fb0_dma, 0, bsize, 4902b401c1dSians BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD); 4912b401c1dSians 4922b401c1dSians return 0; 4932b401c1dSians } 4942b401c1dSians 4952b401c1dSians void 4962b401c1dSians amdisplay_conf_crt_timings(struct amdisplay_softc *sc) 4972b401c1dSians { 4982b401c1dSians uint32_t timing0, timing1, timing2; 4992b401c1dSians uint32_t hbp, hfp, hsw, vbp, vfp, vsw, width, height; 5002b401c1dSians struct videomode *m = sc->sc_active_mode; 5012b401c1dSians 5022b401c1dSians timing0 = 0; 5032b401c1dSians timing1 = 0; 5042b401c1dSians timing2 = 0; 5052b401c1dSians 5062b401c1dSians hbp = (m->htotal - m->hsync_end) - 1; 5072b401c1dSians hfp = (m->hsync_start - m->hdisplay) - 1; 5082b401c1dSians hsw = (m->hsync_end - m->hsync_start) - 1; 5092b401c1dSians 5102b401c1dSians vbp = (m->vtotal - m->vsync_end); 5112b401c1dSians vfp = (m->vsync_start - m->vdisplay); 5122b401c1dSians vsw = (m->vsync_end - m->vsync_start) - 1; 5132b401c1dSians 5142b401c1dSians height = m->vdisplay - 1; 5152b401c1dSians width = m->hdisplay - 1; 5162b401c1dSians 5172b401c1dSians /* Horizontal back porch */ 5182b401c1dSians timing0 |= (hbp & 0xff) << LCD_RASTER_TIMING_0_HBP_SHAMT; 5192b401c1dSians timing2 |= ((hbp >> 8) & 3) << LCD_RASTER_TIMING_2_HPB_HIGHBITS_SHAMT; 5202b401c1dSians /* Horizontal front porch */ 5212b401c1dSians timing0 |= (hfp & 0xff) << LCD_RASTER_TIMING_0_HFP_SHAMT; 5222b401c1dSians timing2 |= ((hfp >> 8) & 3) << 0; 5232b401c1dSians /* Horizontal sync width */ 5242b401c1dSians timing0 |= (hsw & 0x3f) << LCD_RASTER_TIMING_0_HSW_SHAMT; 5252b401c1dSians timing2 |= ((hsw >> 6) & 0xf) << LCD_RASTER_TIMING_2_HSW_HIGHBITS_SHAMT; 5262b401c1dSians 5272b401c1dSians /* Vertical back porch, front porch, sync width */ 5282b401c1dSians timing1 |= (vbp & 0xff) << LCD_RASTER_TIMING_1_VBP_SHAMT; 5292b401c1dSians timing1 |= (vfp & 0xff) << LCD_RASTER_TIMING_1_VFP_SHAMT; 5302b401c1dSians timing1 |= (vsw & 0x3f) << LCD_RASTER_TIMING_1_VSW_SHAMT; 5312b401c1dSians 5322b401c1dSians /* Pixels per line */ 5332b401c1dSians timing0 |= ((width >> 10) & 1) 5342b401c1dSians << LCD_RASTER_TIMING_0_PPLMSB_SHAMT; 5352b401c1dSians timing0 |= ((width >> 4) & 0x3f) 5362b401c1dSians << LCD_RASTER_TIMING_0_PPLLSB_SHAMT; 5372b401c1dSians 5382b401c1dSians /* Lines per panel */ 5392b401c1dSians timing1 |= (height & 0x3ff); 5402b401c1dSians timing2 |= ((height >> 10 ) & 1) 5412b401c1dSians << LCD_RASTER_TIMING_2_LPP_B10_SHAMT; 5422b401c1dSians 5432b401c1dSians /* waveform settings */ 5442b401c1dSians timing2 |= LCD_RASTER_TIMING_2_PHSVS_ON_OFF; 5452b401c1dSians timing2 |= (0xff << LCD_RASTER_TIMING_2_ACBI_SHAMT); 5462b401c1dSians 5472b401c1dSians if (!ISSET(m->flags, VID_NHSYNC)) 5482b401c1dSians timing2 |= LCD_RASTER_TIMING_2_IHS; 5492b401c1dSians if (!ISSET(m->flags, VID_NVSYNC)) 5502b401c1dSians timing2 |= LCD_RASTER_TIMING_2_IVS; 5512b401c1dSians 5522b401c1dSians HWRITE4(sc, LCD_RASTER_TIMING_0, timing0); 5532b401c1dSians HWRITE4(sc, LCD_RASTER_TIMING_1, timing1); 5542b401c1dSians HWRITE4(sc, LCD_RASTER_TIMING_2, timing2); 5552b401c1dSians } 5562b401c1dSians 5572b401c1dSians void 5582b401c1dSians amdisplay_calc_freq(uint64_t freq) 5592b401c1dSians { 5602b401c1dSians uint64_t mul, div, i, j, delta, min_delta; 5612b401c1dSians 5622b401c1dSians min_delta = freq; 5632b401c1dSians for (i = 1; i < LCD_M1_MAX; i++) { 5642b401c1dSians for (j = 1; j < LCD_N_MAX; j++) { 5652b401c1dSians delta = abs(freq - i * (LCD_MASTER_OSC / j)); 5662b401c1dSians if (delta < min_delta) { 5672b401c1dSians mul = i; 5682b401c1dSians div = j; 5692b401c1dSians min_delta = delta; 5702b401c1dSians } 5712b401c1dSians if (min_delta == 0) 5722b401c1dSians break; 5732b401c1dSians } 5742b401c1dSians } 5752b401c1dSians div--; 5762b401c1dSians 5772b401c1dSians prcm_setclock(4, div); 5782b401c1dSians prcm_setclock(3, mul); 5792b401c1dSians prcm_setclock(5, 1); 5802b401c1dSians } 5812b401c1dSians 5822b401c1dSians int 5832b401c1dSians amdisplay_ioctl(void *sconf, u_long cmd, caddr_t data, int flat, struct proc *p) 5842b401c1dSians { 5852b401c1dSians struct rasops_info *ri = sconf; 5862b401c1dSians struct wsdisplay_fbinfo *wdf; 5872b401c1dSians 5882b401c1dSians switch (cmd) { 5892b401c1dSians case WSDISPLAYIO_GTYPE: 5907321c2f6Sjsg *(u_int *)data = WSDISPLAY_TYPE_UNKNOWN; 5912b401c1dSians return 0; 5922b401c1dSians case WSDISPLAYIO_GINFO: 5932b401c1dSians wdf = (struct wsdisplay_fbinfo *)data; 5942b401c1dSians wdf->width = ri->ri_width; 5952b401c1dSians wdf->height = ri->ri_height; 5962b401c1dSians wdf->depth = ri->ri_depth; 59763294167Skettenis wdf->stride = ri->ri_stride; 59863294167Skettenis wdf->offset = 0; 5992b401c1dSians wdf->cmsize = 0; 6002b401c1dSians break; 6012b401c1dSians case WSDISPLAYIO_LINEBYTES: 6022b401c1dSians *(u_int *)data = ri->ri_stride; 6032b401c1dSians break; 6042b401c1dSians case WSDISPLAYIO_SMODE: 6052b401c1dSians break; 6062b401c1dSians case WSDISPLAYIO_GETSUPPORTEDDEPTH: 6072b401c1dSians switch (ri->ri_depth) { 6082b401c1dSians case 32: 6092b401c1dSians *(u_int *)data = WSDISPLAYIO_DEPTH_24_32; 6102b401c1dSians break; 6112b401c1dSians case 24: 6122b401c1dSians *(u_int *)data = WSDISPLAYIO_DEPTH_24_24; 6132b401c1dSians break; 6142b401c1dSians case 16: 6152b401c1dSians *(u_int *)data = WSDISPLAYIO_DEPTH_16; 6162b401c1dSians break; 6172b401c1dSians case 15: 6182b401c1dSians *(u_int *)data = WSDISPLAYIO_DEPTH_15; 6192b401c1dSians break; 6202b401c1dSians default: 6212b401c1dSians return -1; 6222b401c1dSians } 6232b401c1dSians break; 6242b401c1dSians default: 6252b401c1dSians return -1; 6262b401c1dSians } 6272b401c1dSians 6282b401c1dSians return 0; 6292b401c1dSians } 6302b401c1dSians 6312b401c1dSians paddr_t 6322b401c1dSians amdisplay_mmap(void *sconf, off_t off, int prot) 6332b401c1dSians { 6342b401c1dSians struct rasops_info *ri = sconf; 6352b401c1dSians struct amdisplay_softc *sc = ri->ri_hw; 6362b401c1dSians 6372b401c1dSians if (off < 0 || off >= sc->sc_fb_size) 6382b401c1dSians return -1; 6392b401c1dSians 6402b401c1dSians return bus_dmamem_mmap(sc->sc_dmat, &sc->sc_fb0_dma_segs[0], 6412b401c1dSians sc->sc_fb_dma_nsegs, off, prot, BUS_DMA_COHERENT); 6422b401c1dSians } 6432b401c1dSians 6442b401c1dSians int 6452b401c1dSians amdisplay_alloc_screen(void *sconf, const struct wsscreen_descr *type, 646e0c3e559Sjsg void **cookiep, int *curxp, int *curyp, uint32_t *attrp) 6472b401c1dSians { 6482b401c1dSians return rasops_alloc_screen(sconf, cookiep, curxp, curyp, attrp); 6492b401c1dSians } 650