xref: /openbsd-src/sys/arch/armv7/omap/amdisplay.c (revision 0c9d3924ff519b4d30a14c8930549f5259118233)
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