Date: Thu, 18 Jun 2015 00:57:52 +0000 (UTC) From: Oleksandr Tymoshenko <gonzo@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r284534 - in head/sys: arm/conf arm/ti/am335x boot/fdt/dts/arm Message-ID: <201506180057.t5I0vq68045741@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
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@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"; + }; +};
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201506180057.t5I0vq68045741>