svn commit: r284534 - in head/sys: arm/conf arm/ti/am335x boot/fdt/dts/arm

Oleksandr Tymoshenko gonzo at FreeBSD.org
Thu Jun 18 00:57:54 UTC 2015


Author: gonzo
Date: Thu Jun 18 00:57:52 2015
New Revision: 284534
URL: https://svnweb.freebsd.org/changeset/base/284534

Log:
  Add HDMI support to Beaglebone Black:
  
  - Add driver for TDA19988 HDMI framer
  - Add simple interface to communicate with HDMI sink: read EDID and set videomode
  - Add event-based API to notify LCD controller when HDMI sink is available
  - Add HDMI framer node and add refernce to it to lcdc node. This part of
      DTS tree is custom and does not match Linux DTS because Linux uses
      combination of pseudo-node in DTS and hardcoded driver information
      that does not map to our model.

Added:
  head/sys/arm/ti/am335x/hdmi.h   (contents, props changed)
  head/sys/arm/ti/am335x/hdmi_if.m   (contents, props changed)
Modified:
  head/sys/arm/conf/BEAGLEBONE
  head/sys/arm/ti/am335x/am335x_lcd.c
  head/sys/arm/ti/am335x/am335x_lcd.h
  head/sys/arm/ti/am335x/files.am335x
  head/sys/boot/fdt/dts/arm/beaglebone-black.dts

Modified: head/sys/arm/conf/BEAGLEBONE
==============================================================================
--- head/sys/arm/conf/BEAGLEBONE	Thu Jun 18 00:22:14 2015	(r284533)
+++ head/sys/arm/conf/BEAGLEBONE	Thu Jun 18 00:57:52 2015	(r284534)
@@ -138,3 +138,11 @@ device		fdt_pinctrl
 
 # Flattened Device Tree
 options 	FDT			# Configure using FDT/DTB data
+
+# Comment following lines for boot console on serial port
+device		vt
+device		videomode
+device		hdmi
+device		ums
+device		ukbd
+device		kbdmux

Modified: head/sys/arm/ti/am335x/am335x_lcd.c
==============================================================================
--- head/sys/arm/ti/am335x/am335x_lcd.c	Thu Jun 18 00:22:14 2015	(r284533)
+++ head/sys/arm/ti/am335x/am335x_lcd.c	Thu Jun 18 00:57:52 2015	(r284534)
@@ -52,6 +52,10 @@ __FBSDID("$FreeBSD$");
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
+#include <arm/ti/am335x/hdmi.h>
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
 #include <dev/fb/fbreg.h>
 #ifdef DEV_SC
 #include <dev/syscons/syscons.h>
@@ -66,6 +70,7 @@ __FBSDID("$FreeBSD$");
 #include "am335x_pwm.h"
 
 #include "fb_if.h"
+#include "hdmi_if.h"
 
 #define	LCD_PID			0x00
 #define	LCD_CTRL		0x04
@@ -176,11 +181,20 @@ __FBSDID("$FreeBSD$");
 #define	LCD_WRITE4(_sc, reg, value)	\
     bus_write_4((_sc)->sc_mem_res, reg, value);
 
-
 /* Backlight is controlled by eCAS interface on PWM unit 0 */
 #define	PWM_UNIT	0
 #define	PWM_PERIOD	100
 
+#define	MODE_HBP(mode)	((mode)->htotal - (mode)->hsync_end)
+#define	MODE_HFP(mode)	((mode)->hsync_start - (mode)->hdisplay)
+#define	MODE_HSW(mode)	((mode)->hsync_end - (mode)->hsync_start)
+#define	MODE_VBP(mode)	((mode)->vtotal - (mode)->vsync_end)
+#define	MODE_VFP(mode)	((mode)->vsync_start - (mode)->vdisplay)
+#define	MODE_VSW(mode)	((mode)->vsync_end - (mode)->vsync_start)
+
+#define	MAX_PIXEL_CLOCK	126000
+#define	MAX_BANDWIDTH	(1280*1024*60)
+
 struct am335x_lcd_softc {
 	device_t		sc_dev;
 	struct fb_info		sc_fb_info;
@@ -191,12 +205,18 @@ struct am335x_lcd_softc {
 	int			sc_backlight;
 	struct sysctl_oid	*sc_oid;
 
+	struct panel_info	sc_panel;
+
 	/* Framebuffer */
 	bus_dma_tag_t		sc_dma_tag;
 	bus_dmamap_t		sc_dma_map;
 	size_t			sc_fb_size;
 	bus_addr_t		sc_fb_phys;
 	uint8_t			*sc_fb_base;
+
+	/* HDMI framer */
+	phandle_t		sc_hdmi_framer;
+	eventhandler_tag	sc_hdmi_evh;
 };
 
 static void
@@ -214,13 +234,22 @@ am335x_fb_dmamap_cb(void *arg, bus_dma_s
 static uint32_t
 am335x_lcd_calc_divisor(uint32_t reference, uint32_t freq)
 {
-	uint32_t div;
+	uint32_t div, i;
+	uint32_t delta, min_delta;
+
+	min_delta = freq;
+	div = 255;
+
 	/* Raster mode case: divisors are in range from 2 to 255 */
-	for (div = 2; div < 255; div++)
-		if (reference/div <= freq)
-			return (div);
+	for (i = 2; i < 255; i++) {
+		delta = abs(reference/i - freq);
+		if (delta < min_delta) {
+			div = i;
+			min_delta = delta;
+		}
+	}
 
-	return (255);
+	return (div);
 }
 
 static int
@@ -229,7 +258,7 @@ am335x_lcd_sysctl_backlight(SYSCTL_HANDL
 	struct am335x_lcd_softc *sc = (struct am335x_lcd_softc*)arg1;
 	int error;
 	int backlight;
-       
+
 	backlight = sc->sc_backlight;
 	error = sysctl_handle_int(oidp, &backlight, 0, req);
 
@@ -251,6 +280,81 @@ am335x_lcd_sysctl_backlight(SYSCTL_HANDL
 	return (error);
 }
 
+static uint32_t
+am335x_mode_vrefresh(const struct videomode *mode)
+{
+	uint32_t refresh;
+
+	/* Calculate vertical refresh rate */
+        refresh = (mode->dot_clock * 1000 / mode->htotal);
+	refresh = (refresh + mode->vtotal / 2) / mode->vtotal;
+
+	if (mode->flags & VID_INTERLACE)
+		refresh *= 2;
+	if (mode->flags & VID_DBLSCAN)
+		refresh /= 2;
+
+	return refresh;
+}
+
+static int
+am335x_mode_is_valid(const struct videomode *mode)
+{
+	uint32_t hbp, hfp, hsw;
+	uint32_t vbp, vfp, vsw;
+
+	if (mode->dot_clock > MAX_PIXEL_CLOCK)
+		return (0);
+
+	if (mode->hdisplay & 0xf)
+		return (0);
+
+	if (mode->vdisplay > 2048)
+		return (0);
+
+	/* Check ranges for timing parameters */
+	hbp = MODE_HBP(mode) - 1;
+	hfp = MODE_HFP(mode) - 1;
+	hsw = MODE_HSW(mode) - 1;
+	vbp = MODE_VBP(mode);
+	vfp = MODE_VFP(mode);
+	vsw = MODE_VSW(mode) - 1;
+
+	if (hbp > 0x3ff)
+		return (0);
+	if (hfp > 0x3ff)
+		return (0);
+	if (hsw > 0x3ff)
+		return (0);
+
+	if (vbp > 0xff)
+		return (0);
+	if (vfp > 0xff)
+		return (0);
+	if (vsw > 0x3f)
+		return (0);
+	if (mode->vdisplay*mode->hdisplay*am335x_mode_vrefresh(mode) 
+	    > MAX_BANDWIDTH)
+		return (0);
+
+	return (1);
+}
+
+static void
+am335x_read_hdmi_property(device_t dev)
+{
+	phandle_t node;
+	phandle_t hdmi_xref;
+	struct am335x_lcd_softc *sc;
+
+	sc = device_get_softc(dev);
+	node = ofw_bus_get_node(dev);
+	if (OF_getencprop(node, "hdmi", &hdmi_xref, sizeof(hdmi_xref)) == -1)
+		sc->sc_hdmi_framer = 0;
+	else
+		sc->sc_hdmi_framer = hdmi_xref; 
+}
+
 static int
 am335x_read_property(device_t dev, phandle_t node, const char *name, uint32_t *val)
 {
@@ -343,44 +447,34 @@ out:
 static int
 am335x_read_panel_info(device_t dev, phandle_t node, struct panel_info *panel)
 {
-	int error;
 	phandle_t panel_info_node;
 
 	panel_info_node = ofw_bus_find_child(node, "panel-info");
 	if (panel_info_node == 0)
 		return (-1);
 
-	error = 0;
+	am335x_read_property(dev, panel_info_node,
+	    "ac-bias", &panel->ac_bias);
 
-	if ((error = am335x_read_property(dev, panel_info_node,
-	    "ac-bias", &panel->ac_bias)))
-		goto out;
+	am335x_read_property(dev, panel_info_node,
+	    "ac-bias-intrpt", &panel->ac_bias_intrpt);
 
-	if ((error = am335x_read_property(dev, panel_info_node,
-	    "ac-bias-intrpt", &panel->ac_bias_intrpt)))
-		goto out;
+	am335x_read_property(dev, panel_info_node,
+	    "dma-burst-sz", &panel->dma_burst_sz);
 
-	if ((error = am335x_read_property(dev, panel_info_node,
-	    "dma-burst-sz", &panel->dma_burst_sz)))
-		goto out;
+	am335x_read_property(dev, panel_info_node,
+	    "bpp", &panel->bpp);
 
-	if ((error = am335x_read_property(dev, panel_info_node,
-	    "bpp", &panel->bpp)))
-		goto out;
+	am335x_read_property(dev, panel_info_node,
+	    "fdd", &panel->fdd);
 
-	if ((error = am335x_read_property(dev, panel_info_node,
-	    "fdd", &panel->fdd)))
-		goto out;
+	am335x_read_property(dev, panel_info_node,
+	    "sync-edge", &panel->sync_edge);
 
-	if ((error = am335x_read_property(dev, panel_info_node,
-	    "sync-edge", &panel->sync_edge)))
-		goto out;
-
-	error = am335x_read_property(dev, panel_info_node,
+	am335x_read_property(dev, panel_info_node,
 	    "sync-ctrl", &panel->sync_ctrl);
 
-out:
-	return (error);
+	return (0);
 }
 
 static void
@@ -442,119 +536,75 @@ done:
 	reg = LCD_READ4(sc, LCD_END_OF_INT_IND);
 }
 
-static int
-am335x_lcd_probe(device_t dev)
+static const struct videomode *
+am335x_lcd_pick_mode(struct edid_info *ei)
 {
-#ifdef DEV_SC
-	int err;
-#endif
+	const struct videomode *videomode;
+	const struct videomode *m;
+	int n;
 
-	if (!ofw_bus_status_okay(dev))
-		return (ENXIO);
+	/* Get standard VGA as default */
+	videomode = NULL;
 
-	if (!ofw_bus_is_compatible(dev, "ti,am33xx-tilcdc"))
-		return (ENXIO);
+	/*
+	 * Pick a mode.
+	 */
+	if (ei->edid_preferred_mode != NULL) {
+		if (am335x_mode_is_valid(ei->edid_preferred_mode))
+			videomode = ei->edid_preferred_mode;
+	}
 
-	device_set_desc(dev, "AM335x LCD controller");
+	if (videomode == NULL) {
+		m = ei->edid_modes;
 
-#ifdef DEV_SC
-	err = sc_probe_unit(device_get_unit(dev), 
-	    device_get_flags(dev) | SC_AUTODETECT_KBD);
-	if (err != 0)
-		return (err);
-#endif
+		sort_modes(ei->edid_modes,
+		    &ei->edid_preferred_mode,
+		    ei->edid_nmodes);
+		for (n = 0; n < ei->edid_nmodes; n++)
+			if (am335x_mode_is_valid(&m[n])) {
+				videomode = &m[n];
+				break;
+			}
+	}
 
-	return (BUS_PROBE_DEFAULT);
+	return videomode;
 }
 
 static int
-am335x_lcd_attach(device_t dev)
+am335x_lcd_configure(struct am335x_lcd_softc *sc)
 {
-	struct am335x_lcd_softc *sc;
-	int rid;
 	int div;
-	struct panel_info panel;
 	uint32_t reg, timing0, timing1, timing2;
-	struct sysctl_ctx_list *ctx;
-	struct sysctl_oid *tree;
 	uint32_t burst_log;
-	int err;
 	size_t dma_size;
 	uint32_t hbp, hfp, hsw;
 	uint32_t vbp, vfp, vsw;
 	uint32_t width, height;
-	phandle_t root, panel_node;
-
-	sc = device_get_softc(dev);
-	sc->sc_dev = dev;
-
-	root = OF_finddevice("/");
-	if (root == 0) {
-		device_printf(dev, "failed to get FDT root node\n");
-		return (ENXIO);
-	}
-
-	panel_node = fdt_find_compatible(root, "ti,tilcdc,panel", 1);
-	if (panel_node == 0) {
-		device_printf(dev, "failed to find compatible panel in FDT blob\n");
-		return (ENXIO);
-	}
-
-	if (am335x_read_panel_info(dev, panel_node, &panel)) {
-		device_printf(dev, "failed to read panel info\n");
-		return (ENXIO);
-	}
+	unsigned int ref_freq;
+	int err;
 
-	if (am335x_read_timing(dev, panel_node, &panel)) {
-		device_printf(dev, "failed to read timings\n");
+	/*
+	 * try to adjust clock to get double of requested frequency
+	 * HDMI/DVI displays are very sensitive to error in frequncy value
+	 */
+	if (ti_prcm_clk_set_source_freq(LCDC_CLK, sc->sc_panel.panel_pxl_clk*2)) {
+		device_printf(sc->sc_dev, "can't set source frequency\n");
 		return (ENXIO);
 	}
 
-	int ref_freq = 0;
-	ti_prcm_clk_enable(LCDC_CLK);
 	if (ti_prcm_clk_get_source_freq(LCDC_CLK, &ref_freq)) {
-		device_printf(dev, "Can't get reference frequency\n");
-		return (ENXIO);
-	}
-
-	rid = 0;
-	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
-	    RF_ACTIVE);
-	if (!sc->sc_mem_res) {
-		device_printf(dev, "cannot allocate memory window\n");
-		return (ENXIO);
-	}
-
-	rid = 0;
-	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
-	    RF_ACTIVE);
-	if (!sc->sc_irq_res) {
-		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
-		device_printf(dev, "cannot allocate interrupt\n");
-		return (ENXIO);
-	}
-
-	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
-			NULL, am335x_lcd_intr, sc,
-			&sc->sc_intr_hl) != 0) {
-		bus_release_resource(dev, SYS_RES_IRQ, rid,
-		    sc->sc_irq_res);
-		bus_release_resource(dev, SYS_RES_MEMORY, rid,
-		    sc->sc_mem_res);
-		device_printf(dev, "Unable to setup the irq handler.\n");
+		device_printf(sc->sc_dev, "can't get reference frequency\n");
 		return (ENXIO);
 	}
 
-	LCD_LOCK_INIT(sc);
-
 	/* Panle initialization */
-	dma_size = round_page(panel.panel_width*panel.panel_height*panel.bpp/8);
+	dma_size = round_page(sc->sc_panel.panel_width*sc->sc_panel.panel_height*sc->sc_panel.bpp/8);
 
 	/*
 	 * Now allocate framebuffer memory
 	 */
 	err = bus_dma_tag_create(
-	    bus_get_dma_tag(dev),
+	    bus_get_dma_tag(sc->sc_dev),
 	    4, 0,		/* alignment, boundary */
 	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
 	    BUS_SPACE_MAXADDR,		/* highaddr */
@@ -564,49 +614,49 @@ am335x_lcd_attach(device_t dev)
 	    NULL, NULL,			/* lockfunc, lockarg */
 	    &sc->sc_dma_tag);
 	if (err)
-		goto fail;
+		goto done;
 
 	err = bus_dmamem_alloc(sc->sc_dma_tag, (void **)&sc->sc_fb_base,
 	    BUS_DMA_COHERENT, &sc->sc_dma_map);
 
 	if (err) {
-		device_printf(dev, "cannot allocate framebuffer\n");
-		goto fail;
+		device_printf(sc->sc_dev, "cannot allocate framebuffer\n");
+		goto done;
 	}
 
 	err = bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, sc->sc_fb_base,
 	    dma_size, am335x_fb_dmamap_cb, &sc->sc_fb_phys, BUS_DMA_NOWAIT);
 
 	if (err) {
-		device_printf(dev, "cannot load DMA map\n");
-		goto fail;
+		device_printf(sc->sc_dev, "cannot load DMA map\n");
+		goto done;
 	}
 
 	/* Make sure it's blank */
-	memset(sc->sc_fb_base, 0x00, dma_size);
+	memset(sc->sc_fb_base, 0x0, dma_size);
 
 	/* Calculate actual FB Size */
-	sc->sc_fb_size = panel.panel_width*panel.panel_height*panel.bpp/8;
+	sc->sc_fb_size = sc->sc_panel.panel_width*sc->sc_panel.panel_height*sc->sc_panel.bpp/8;
 
 	/* Only raster mode is supported */
 	reg = CTRL_RASTER_MODE;
-	div = am335x_lcd_calc_divisor(ref_freq, panel.panel_pxl_clk);
+	div = am335x_lcd_calc_divisor(ref_freq, sc->sc_panel.panel_pxl_clk);
 	reg |= (div << CTRL_DIV_SHIFT);
 	LCD_WRITE4(sc, LCD_CTRL, reg); 
 
 	/* Set timing */
 	timing0 = timing1 = timing2 = 0;
 
-	hbp = panel.panel_hbp - 1;
-	hfp = panel.panel_hfp - 1;
-	hsw = panel.panel_hsw - 1;
-
-	vbp = panel.panel_vbp;
-	vfp = panel.panel_vfp;
-	vsw = panel.panel_vsw - 1;
+	hbp = sc->sc_panel.panel_hbp - 1;
+	hfp = sc->sc_panel.panel_hfp - 1;
+	hsw = sc->sc_panel.panel_hsw - 1;
+
+	vbp = sc->sc_panel.panel_vbp;
+	vfp = sc->sc_panel.panel_vfp;
+	vsw = sc->sc_panel.panel_vsw - 1;
 
-	height = panel.panel_height - 1;
-	width = panel.panel_width - 1;
+	height = sc->sc_panel.panel_height - 1;
+	width = sc->sc_panel.panel_width - 1;
 
 	/* Horizontal back porch */
 	timing0 |= (hbp & 0xff) << RASTER_TIMING_0_HBP_SHIFT;
@@ -636,22 +686,22 @@ am335x_lcd_attach(device_t dev)
 	    << RASTER_TIMING_2_LPP_B10_SHIFT;
 
 	/* clock signal settings */
-	if (panel.sync_ctrl)
+	if (sc->sc_panel.sync_ctrl)
 		timing2 |= RASTER_TIMING_2_PHSVS;
-	if (panel.sync_edge)
+	if (sc->sc_panel.sync_edge)
 		timing2 |= RASTER_TIMING_2_PHSVS_RISE;
 	else
 		timing2 |= RASTER_TIMING_2_PHSVS_FALL;
-	if (panel.hsync_active == 0)
+	if (sc->sc_panel.hsync_active == 0)
 		timing2 |= RASTER_TIMING_2_IHS;
-	if (panel.vsync_active == 0)
+	if (sc->sc_panel.vsync_active == 0)
 		timing2 |= RASTER_TIMING_2_IVS;
-	if (panel.pixelclk_active == 0)
+	if (sc->sc_panel.pixelclk_active == 0)
 		timing2 |= RASTER_TIMING_2_IPC;
 
 	/* AC bias */
-	timing2 |= (panel.ac_bias << RASTER_TIMING_2_ACB_SHIFT);
-	timing2 |= (panel.ac_bias_intrpt << RASTER_TIMING_2_ACBI_SHIFT);
+	timing2 |= (sc->sc_panel.ac_bias << RASTER_TIMING_2_ACB_SHIFT);
+	timing2 |= (sc->sc_panel.ac_bias_intrpt << RASTER_TIMING_2_ACBI_SHIFT);
 
 	LCD_WRITE4(sc, LCD_RASTER_TIMING_0, timing0); 
 	LCD_WRITE4(sc, LCD_RASTER_TIMING_1, timing1); 
@@ -660,7 +710,7 @@ am335x_lcd_attach(device_t dev)
 	/* DMA settings */
 	reg = LCDDMA_CTRL_FB0_FB1;
 	/* Find power of 2 for current burst size */
-	switch (panel.dma_burst_sz) {
+	switch (sc->sc_panel.dma_burst_sz) {
 	case 1:
 		burst_log = 0;
 		break;
@@ -690,11 +740,11 @@ am335x_lcd_attach(device_t dev)
 
 	/* Enable LCD */
 	reg = RASTER_CTRL_LCDTFT;
-	reg |= (panel.fdd << RASTER_CTRL_REQDLY_SHIFT);
+	reg |= (sc->sc_panel.fdd << RASTER_CTRL_REQDLY_SHIFT);
 	reg |= (PALETTE_DATA_ONLY << RASTER_CTRL_PALMODE_SHIFT);
-	if (panel.bpp >= 24)
+	if (sc->sc_panel.bpp >= 24)
 		reg |= RASTER_CTRL_TFT24;
-	if (panel.bpp == 32)
+	if (sc->sc_panel.bpp == 32)
 		reg |= RASTER_CTRL_TFT24_UNPACKED;
 	LCD_WRITE4(sc, LCD_RASTER_CTRL, reg); 
 
@@ -717,54 +767,241 @@ am335x_lcd_attach(device_t dev)
 	LCD_WRITE4(sc, LCD_SYSCONFIG,
 	    SYSCONFIG_STANDBY_SMART | SYSCONFIG_IDLE_SMART); 
 
-	/* Init backlight interface */
-	ctx = device_get_sysctl_ctx(sc->sc_dev);
-	tree = device_get_sysctl_tree(sc->sc_dev);
-	sc->sc_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
-	    "backlight", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
-	    am335x_lcd_sysctl_backlight, "I", "LCD backlight");
-	sc->sc_backlight = 0;
-	/* Check if eCAS interface is available at this point */
-	if (am335x_pwm_config_ecap(PWM_UNIT,
-	    PWM_PERIOD, PWM_PERIOD) == 0)
-		sc->sc_backlight = 100;
-
 	sc->sc_fb_info.fb_name = device_get_nameunit(sc->sc_dev);
 	sc->sc_fb_info.fb_vbase = (intptr_t)sc->sc_fb_base;
 	sc->sc_fb_info.fb_pbase = sc->sc_fb_phys;
 	sc->sc_fb_info.fb_size = sc->sc_fb_size;
-	sc->sc_fb_info.fb_bpp = sc->sc_fb_info.fb_depth = panel.bpp;
-	sc->sc_fb_info.fb_stride = panel.panel_width*panel.bpp / 8;
-	sc->sc_fb_info.fb_width = panel.panel_width;
-	sc->sc_fb_info.fb_height = panel.panel_height;
+	sc->sc_fb_info.fb_bpp = sc->sc_fb_info.fb_depth = sc->sc_panel.bpp;
+	sc->sc_fb_info.fb_stride = sc->sc_panel.panel_width*sc->sc_panel.bpp / 8;
+	sc->sc_fb_info.fb_width = sc->sc_panel.panel_width;
+	sc->sc_fb_info.fb_height = sc->sc_panel.panel_height;
 
 #ifdef	DEV_SC
-	err = (sc_attach_unit(device_get_unit(dev),
-	    device_get_flags(dev) | SC_AUTODETECT_KBD));
+	err = (sc_attach_unit(device_get_unit(sc->sc_dev),
+	    device_get_flags(sc->sc_dev) | SC_AUTODETECT_KBD));
 
 	if (err) {
-		device_printf(dev, "failed to attach syscons\n");
+		device_printf(sc->sc_dev, "failed to attach syscons\n");
 		goto fail;
 	}
 
 	am335x_lcd_syscons_setup((vm_offset_t)sc->sc_fb_base, sc->sc_fb_phys, &panel);
 #else /* VT */
-	device_t fbd = device_add_child(dev, "fbd",
-	device_get_unit(dev));
-	if (fbd == NULL) {
-		device_printf(dev, "Failed to add fbd child\n");
-		goto fail;
+	device_t fbd = device_add_child(sc->sc_dev, "fbd",
+	device_get_unit(sc->sc_dev));
+	if (fbd != NULL) {
+		if (device_probe_and_attach(fbd) != 0)
+			device_printf(sc->sc_dev, "failed to attach fbd device\n");
+	} else
+		device_printf(sc->sc_dev, "failed to add fbd child\n");
+#endif
+
+done:
+	return (err);
+}
+
+static void
+am335x_lcd_hdmi_event(void *arg)
+{
+	struct am335x_lcd_softc *sc;
+	const struct videomode *videomode;
+	struct videomode hdmi_mode;
+	device_t hdmi_dev;
+	uint8_t *edid;
+	uint32_t edid_len;
+	struct edid_info ei;
+
+	sc = arg;
+
+	/* Nothing to work with */
+	if (!sc->sc_hdmi_framer) {
+		device_printf(sc->sc_dev, "HDMI event without HDMI framer set\n");
+		return;
 	}
-	if (device_probe_and_attach(fbd) != 0) {
-		device_printf(dev, "Failed to attach fbd device\n");
-		goto fail;
+
+	hdmi_dev = OF_device_from_xref(sc->sc_hdmi_framer);
+	if (!hdmi_dev) {
+		device_printf(sc->sc_dev, "no actual device for \"hdmi\" property\n");
+		return;
 	}
+
+	edid = NULL;
+	edid_len = 0;
+	if (HDMI_GET_EDID(hdmi_dev, &edid, &edid_len) != 0) {
+		device_printf(sc->sc_dev, "failed to get EDID info from HDMI framer\n");
+		return;
+	}
+
+	videomode = NULL;
+
+	if (edid_parse(edid, &ei) == 0) {
+		edid_print(&ei);
+		videomode = am335x_lcd_pick_mode(&ei);
+	} else
+		device_printf(sc->sc_dev, "failed to parse EDID\n");
+
+	/* Use standard VGA as fallback */
+	if (videomode == NULL)
+		videomode = pick_mode_by_ref(640, 480, 60);
+
+	if (videomode == NULL) {
+		device_printf(sc->sc_dev, "failed to find usable videomode");
+		return;
+	}
+
+	device_printf(sc->sc_dev, "detected videomode: %dx%d @ %dKHz\n", videomode->hdisplay,
+		videomode->vdisplay, am335x_mode_vrefresh(videomode));
+
+	sc->sc_panel.panel_width = videomode->hdisplay;
+	sc->sc_panel.panel_height = videomode->vdisplay;
+	sc->sc_panel.panel_hfp = videomode->hsync_start - videomode->hdisplay;
+	sc->sc_panel.panel_hbp = videomode->htotal - videomode->hsync_end;
+	sc->sc_panel.panel_hsw = videomode->hsync_end - videomode->hsync_start;
+	sc->sc_panel.panel_vfp = videomode->vsync_start - videomode->vdisplay;
+	sc->sc_panel.panel_vbp = videomode->vtotal - videomode->vsync_end;
+	sc->sc_panel.panel_vsw = videomode->vsync_end - videomode->vsync_start;
+	sc->sc_panel.pixelclk_active = 1;
+
+	/* logic for HSYNC should be reversed */
+	if (videomode->flags & VID_NHSYNC)
+		sc->sc_panel.hsync_active = 1;
+	else
+		sc->sc_panel.hsync_active = 0;
+
+	if (videomode->flags & VID_NVSYNC)
+		sc->sc_panel.vsync_active = 0;
+	else
+		sc->sc_panel.vsync_active = 1;
+
+	sc->sc_panel.panel_pxl_clk = videomode->dot_clock * 1000;
+
+	am335x_lcd_configure(sc);
+
+	memcpy(&hdmi_mode, videomode, sizeof(hdmi_mode));
+	hdmi_mode.hskew = videomode->hsync_end - videomode->hsync_start;
+	hdmi_mode.flags |= VID_HSKEW;
+
+	HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode);
+}
+
+static int
+am335x_lcd_probe(device_t dev)
+{
+#ifdef DEV_SC
+	int err;
 #endif
 
-	return (0);
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
 
-fail:
-	return (err);
+	if (!ofw_bus_is_compatible(dev, "ti,am33xx-tilcdc"))
+		return (ENXIO);
+
+	device_set_desc(dev, "AM335x LCD controller");
+
+#ifdef DEV_SC
+	err = sc_probe_unit(device_get_unit(dev), 
+	    device_get_flags(dev) | SC_AUTODETECT_KBD);
+	if (err != 0)
+		return (err);
+#endif
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+am335x_lcd_attach(device_t dev)
+{
+	struct am335x_lcd_softc *sc;
+
+	int err;
+	int rid;
+	struct sysctl_ctx_list *ctx;
+	struct sysctl_oid *tree;
+	phandle_t root, panel_node;
+
+	err = 0;
+	sc = device_get_softc(dev);
+	sc->sc_dev = dev;
+
+	am335x_read_hdmi_property(dev);
+
+	root = OF_finddevice("/");
+	if (root == 0) {
+		device_printf(dev, "failed to get FDT root node\n");
+		return (ENXIO);
+	}
+
+	sc->sc_panel.ac_bias = 255;
+	sc->sc_panel.ac_bias_intrpt = 0;
+	sc->sc_panel.dma_burst_sz = 16;
+	sc->sc_panel.bpp = 16;
+	sc->sc_panel.fdd = 128;
+	sc->sc_panel.sync_edge = 0;
+	sc->sc_panel.sync_ctrl = 1;
+
+	panel_node = fdt_find_compatible(root, "ti,tilcdc,panel", 1);
+	if (panel_node != 0) {
+		device_printf(dev, "using static panel info\n");
+		if (am335x_read_panel_info(dev, panel_node, &sc->sc_panel)) {
+			device_printf(dev, "failed to read panel info\n");
+			return (ENXIO);
+		}
+
+		if (am335x_read_timing(dev, panel_node, &sc->sc_panel)) {
+			device_printf(dev, "failed to read timings\n");
+			return (ENXIO);
+		}
+	}
+
+	ti_prcm_clk_enable(LCDC_CLK);
+
+	rid = 0;
+	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (!sc->sc_mem_res) {
+		device_printf(dev, "cannot allocate memory window\n");
+		return (ENXIO);
+	}
+
+	rid = 0;
+	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (!sc->sc_irq_res) {
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+		device_printf(dev, "cannot allocate interrupt\n");
+		return (ENXIO);
+	}
+
+	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+			NULL, am335x_lcd_intr, sc,
+			&sc->sc_intr_hl) != 0) {
+		bus_release_resource(dev, SYS_RES_IRQ, rid,
+		    sc->sc_irq_res);
+		bus_release_resource(dev, SYS_RES_MEMORY, rid,
+		    sc->sc_mem_res);
+		device_printf(dev, "Unable to setup the irq handler.\n");
+		return (ENXIO);
+	}
+
+	LCD_LOCK_INIT(sc);
+
+	/* Init backlight interface */
+	ctx = device_get_sysctl_ctx(sc->sc_dev);
+	tree = device_get_sysctl_tree(sc->sc_dev);
+	sc->sc_oid = SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree), OID_AUTO,
+	    "backlight", CTLTYPE_INT | CTLFLAG_RW, sc, 0,
+	    am335x_lcd_sysctl_backlight, "I", "LCD backlight");
+	sc->sc_backlight = 0;
+	/* Check if eCAS interface is available at this point */
+	if (am335x_pwm_config_ecap(PWM_UNIT,
+	    PWM_PERIOD, PWM_PERIOD) == 0)
+		sc->sc_backlight = 100;
+
+	sc->sc_hdmi_evh = EVENTHANDLER_REGISTER(hdmi_event,
+	    am335x_lcd_hdmi_event, sc, 0);
+
+	return (0);
 }
 
 static int

Modified: head/sys/arm/ti/am335x/am335x_lcd.h
==============================================================================
--- head/sys/arm/ti/am335x/am335x_lcd.h	Thu Jun 18 00:22:14 2015	(r284533)
+++ head/sys/arm/ti/am335x/am335x_lcd.h	Thu Jun 18 00:57:52 2015	(r284534)
@@ -29,6 +29,7 @@
 #define __AM335X_LCD_H__
 
 struct panel_info {
+	/* Timing part */
 	uint32_t panel_width;
 	uint32_t panel_height;
 	uint32_t panel_hfp;
@@ -37,16 +38,17 @@ struct panel_info {
 	uint32_t panel_vfp;
 	uint32_t panel_vbp;
 	uint32_t panel_vsw;
+	uint32_t hsync_active;
+	uint32_t vsync_active;
+	uint32_t panel_pxl_clk;
+
 	uint32_t ac_bias;
 	uint32_t ac_bias_intrpt;
 	uint32_t dma_burst_sz;
 	uint32_t bpp;
 	uint32_t fdd;
-	uint32_t hsync_active;
-	uint32_t vsync_active;
 	uint32_t sync_edge;
 	uint32_t sync_ctrl;
-	uint32_t panel_pxl_clk;
 	uint32_t pixelclk_active;
 };
 

Modified: head/sys/arm/ti/am335x/files.am335x
==============================================================================
--- head/sys/arm/ti/am335x/files.am335x	Thu Jun 18 00:22:14 2015	(r284533)
+++ head/sys/arm/ti/am335x/files.am335x	Thu Jun 18 00:57:52 2015	(r284534)
@@ -16,5 +16,8 @@ arm/ti/am335x/am335x_scm_padconf.c	stand
 arm/ti/am335x/am335x_usbss.c		optional	musb fdt
 arm/ti/am335x/am335x_musb.c		optional	musb fdt
 
+arm/ti/am335x/hdmi_if.m			optional	hdmi
+arm/ti/am335x/tda19988.c		optional	hdmi
+
 arm/ti/ti_edma3.c			standard
 arm/ti/cpsw/if_cpsw.c			optional	cpsw

Added: head/sys/arm/ti/am335x/hdmi.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/ti/am335x/hdmi.h	Thu Jun 18 00:57:52 2015	(r284534)
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 2015 Oleksandr Tymoshenko
+ * All rights reserved.
+ *
+ * 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 AUTHOR 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 AUTHOR 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.
+ *
+ *	$FreeBSD$
+ */
+
+#ifndef _HDMI_H_
+#define _HDMI_H_
+
+#include <sys/eventhandler.h>
+
+typedef void (*hdmi_event_hook)(void *, int);
+EVENTHANDLER_DECLARE(hdmi_event, hdmi_event_hook);
+
+#endif	/* !_HDMI_H_ */
+

Added: head/sys/arm/ti/am335x/hdmi_if.m
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/ti/am335x/hdmi_if.m	Thu Jun 18 00:57:52 2015	(r284534)
@@ -0,0 +1,50 @@
+#-
+# Copyright (c) 2015 Oleksandr Tymoshenko <gonzo at freebsd.org>
+# All rights reserved.
+#
+# 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 AUTHOR 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 AUTHOR 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.
+#
+# $FreeBSD$
+#
+
+#include <sys/bus.h>
+#include <dev/videomode/videomode.h>
+#include <dev/videomode/edidvar.h>
+
+INTERFACE hdmi;
+
+#
+# Get EDID info
+#
+METHOD int get_edid {
+	device_t dev;
+	uint8_t **edid;
+	uint32_t *edid_length;
+};
+
+#
+# Set videomode
+#
+METHOD int set_videomode {
+	device_t dev;
+	const struct videomode *videomode;
+};

Modified: head/sys/boot/fdt/dts/arm/beaglebone-black.dts
==============================================================================
--- head/sys/boot/fdt/dts/arm/beaglebone-black.dts	Thu Jun 18 00:22:14 2015	(r284533)
+++ head/sys/boot/fdt/dts/arm/beaglebone-black.dts	Thu Jun 18 00:57:52 2015	(r284534)
@@ -29,3 +29,25 @@
 
 #include "am335x-boneblack.dts"
 #include "beaglebone-common.dtsi"
+
+&i2c0 {
+	tda998x: hdmi-encoder {
+		compatible = "nxp,tda998x";
+		reg = <0x70>;
+
+ 		pinctrl-names = "default", "off";
+ 		pinctrl-0 = <&nxp_hdmi_bonelt_pins>;
+ 		pinctrl-1 = <&nxp_hdmi_bonelt_off_pins>;
+		status = "okay";
+ 	};
+};
+
+&lcdc {
+	hdmi = <&tda998x>;
+};
+
+/ {
+	hdmi {
+		status = "disabled";
+	};
+};


More information about the svn-src-all mailing list