Index: sys/arm/conf/BEAGLEBONE =================================================================== --- sys/arm/conf/BEAGLEBONE (revision 277406) +++ sys/arm/conf/BEAGLEBONE (working copy) @@ -160,4 +160,12 @@ # Flattened Device Tree options FDT # Configure using FDT/DTB data options FDT_DTB_STATIC -makeoptions FDT_DTS_FILE=beaglebone.dts +makeoptions FDT_DTS_FILE=beaglebone-black.dts + +# Comment following lines for boot console on serial port +device vt +device hdmi +device ums +device ukbd +device kbdmux +device tda19988 Index: sys/arm/ti/am335x/am335x_lcd.c =================================================================== --- sys/arm/ti/am335x/am335x_lcd.c (revision 277716) +++ sys/arm/ti/am335x/am335x_lcd.c (working copy) @@ -52,6 +52,10 @@ #include #include +#include +#include +#include + #include #ifdef DEV_SC #include @@ -66,6 +70,7 @@ #include "am335x_pwm.h" #include "fb_if.h" +#include "hdmi_if.h" #define LCD_PID 0x00 #define LCD_CTRL 0x04 @@ -176,11 +181,20 @@ #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,6 +205,9 @@ int sc_backlight; struct sysctl_oid *sc_oid; + struct panel_info sc_panel; + int sc_ref_freq; + /* Framebuffer */ bus_dma_tag_t sc_dma_tag; bus_dmamap_t sc_dma_map; @@ -197,6 +214,10 @@ 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 @@ -251,7 +272,82 @@ 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_panel_property(device_t dev, const char *name, uint32_t *val) { phandle_t node; @@ -269,89 +365,85 @@ return (0); } -static int +static void am335x_read_panel_info(device_t dev, struct panel_info *panel) { - int error; - error = 0; - if ((error = am335x_read_panel_property(dev, - "panel_width", &panel->panel_width))) - goto out; + if (am335x_read_panel_property(dev, + "panel_width", &panel->panel_width)) + panel->panel_width = 0; - if ((error = am335x_read_panel_property(dev, - "panel_height", &panel->panel_height))) - goto out; + if (am335x_read_panel_property(dev, + "panel_height", &panel->panel_height)) + panel->panel_height = 0; - if ((error = am335x_read_panel_property(dev, - "panel_hfp", &panel->panel_hfp))) - goto out; + if (am335x_read_panel_property(dev, + "panel_hfp", &panel->panel_hfp)) + panel->panel_hfp = 0; - if ((error = am335x_read_panel_property(dev, - "panel_hbp", &panel->panel_hbp))) - goto out; + if (am335x_read_panel_property(dev, + "panel_hbp", &panel->panel_hbp)) + panel->panel_hbp = 0; - if ((error = am335x_read_panel_property(dev, - "panel_hsw", &panel->panel_hsw))) - goto out; + if (am335x_read_panel_property(dev, + "panel_hsw", &panel->panel_hsw)) + panel->panel_hsw = 0; - if ((error = am335x_read_panel_property(dev, - "panel_vfp", &panel->panel_vfp))) - goto out; + if (am335x_read_panel_property(dev, + "panel_vfp", &panel->panel_vfp)) + panel->panel_vfp = 0; - if ((error = am335x_read_panel_property(dev, - "panel_vbp", &panel->panel_vbp))) - goto out; + if (am335x_read_panel_property(dev, + "panel_vbp", &panel->panel_vbp)) + panel->panel_vbp = 0; - if ((error = am335x_read_panel_property(dev, - "panel_vsw", &panel->panel_vsw))) - goto out; + if (am335x_read_panel_property(dev, + "panel_vsw", &panel->panel_vsw)) + panel->panel_vsw = 0; - if ((error = am335x_read_panel_property(dev, - "panel_pxl_clk", &panel->panel_pxl_clk))) - goto out; + if (am335x_read_panel_property(dev, + "panel_pxl_clk", &panel->panel_pxl_clk)) + panel->panel_pxl_clk = 0; - if ((error = am335x_read_panel_property(dev, - "panel_invert_pxl_clk", &panel->panel_invert_pxl_clk))) - goto out; + if (am335x_read_panel_property(dev, + "invert_line_clock", &panel->invert_line_clock)) + panel->invert_line_clock = 0; - if ((error = am335x_read_panel_property(dev, - "ac_bias", &panel->ac_bias))) - goto out; + if (am335x_read_panel_property(dev, + "invert_frm_clock", &panel->invert_frm_clock)) + panel->invert_frm_clock = 0; - if ((error = am335x_read_panel_property(dev, - "ac_bias_intrpt", &panel->ac_bias_intrpt))) - goto out; + if (am335x_read_panel_property(dev, + "panel_invert_pxl_clk", &panel->panel_invert_pxl_clk)) + panel->panel_invert_pxl_clk = 0; - if ((error = am335x_read_panel_property(dev, - "dma_burst_sz", &panel->dma_burst_sz))) - goto out; + if (am335x_read_panel_property(dev, + "ac_bias", &panel->ac_bias)) + panel->ac_bias = 255; - if ((error = am335x_read_panel_property(dev, - "bpp", &panel->bpp))) - goto out; + if (am335x_read_panel_property(dev, + "ac_bias_intrpt", &panel->ac_bias_intrpt)) + panel->ac_bias_intrpt = 0; - if ((error = am335x_read_panel_property(dev, - "fdd", &panel->fdd))) - goto out; + if (am335x_read_panel_property(dev, + "dma_burst_sz", &panel->dma_burst_sz)) + panel->dma_burst_sz = 16; - if ((error = am335x_read_panel_property(dev, - "invert_line_clock", &panel->invert_line_clock))) - goto out; + if (am335x_read_panel_property(dev, + "bpp", &panel->bpp)) + panel->bpp = 16; - if ((error = am335x_read_panel_property(dev, - "invert_frm_clock", &panel->invert_frm_clock))) - goto out; + if (am335x_read_panel_property(dev, + "fdd", &panel->fdd)) + panel->fdd = 128; - if ((error = am335x_read_panel_property(dev, - "sync_edge", &panel->sync_edge))) - goto out; + if (am335x_read_panel_property(dev, + "sync_edge", &panel->sync_edge)) + panel->sync_edge = 0; - error = am335x_read_panel_property(dev, - "sync_ctrl", &panel->sync_ctrl); - -out: - return (error); + if (am335x_read_panel_property(dev, + "sync_ctrl", &panel->sync_ctrl)) + panel->sync_ctrl = 0; } static void @@ -413,99 +505,60 @@ 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,am335x-lcd")) - 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; + int err; - sc = device_get_softc(dev); - sc->sc_dev = dev; - - if (am335x_read_panel_info(dev, &panel)) - 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"); - 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 */ @@ -515,14 +568,14 @@ 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, @@ -529,19 +582,19 @@ 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(sc->sc_ref_freq, sc->sc_panel.panel_pxl_clk); reg |= (div << CTRL_DIV_SHIFT); LCD_WRITE4(sc, LCD_CTRL, reg); @@ -548,16 +601,16 @@ /* Set timing */ timing0 = timing1 = timing2 = 0; - hbp = panel.panel_hbp - 1; - hfp = panel.panel_hfp - 1; - hsw = panel.panel_hsw - 1; + hbp = sc->sc_panel.panel_hbp - 1; + hfp = sc->sc_panel.panel_hfp - 1; + hsw = sc->sc_panel.panel_hsw - 1; - vbp = panel.panel_vbp; - vfp = panel.panel_vfp; - vsw = panel.panel_vsw - 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; @@ -587,22 +640,22 @@ << 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.invert_line_clock) + if (sc->sc_panel.invert_line_clock) timing2 |= RASTER_TIMING_2_IHS; - if (panel.invert_frm_clock) + if (sc->sc_panel.invert_frm_clock) timing2 |= RASTER_TIMING_2_IVS; - if (panel.panel_invert_pxl_clk) + if (sc->sc_panel.panel_invert_pxl_clk) 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); @@ -611,7 +664,7 @@ /* 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; @@ -641,11 +694,11 @@ /* 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); @@ -668,57 +721,231 @@ 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_ecas(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; + + if (videomode->flags & VID_NHSYNC) + sc->sc_panel.invert_line_clock = 0; + else + sc->sc_panel.invert_line_clock = 1; + if (videomode->flags & VID_NVSYNC) + sc->sc_panel.invert_frm_clock = 1; + else + sc->sc_panel.invert_frm_clock = 0; + + 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; +#if 0 + if (videomode->flags & VID_NHSYNC) { + hdmi_mode.flags &= ~(VID_NHSYNC); + hdmi_mode.flags |= VID_PHSYNC; + } + else { + hdmi_mode.flags &= ~(VID_PHSYNC); + hdmi_mode.flags |= VID_NHSYNC; + } #endif - return (0); + HDMI_SET_VIDEOMODE(hdmi_dev, &hdmi_mode); +} -fail: - return (err); +static int +am335x_lcd_probe(device_t dev) +{ +#ifdef DEV_SC + int err; +#endif + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "ti,am335x-lcd")) + 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; + + err = 0; + sc = device_get_softc(dev); + sc->sc_dev = dev; + + am335x_read_hdmi_property(dev); + am335x_read_panel_info(dev, &sc->sc_panel); + + ti_prcm_clk_enable(LCDC_CLK); + if (ti_prcm_clk_get_source_freq(LCDC_CLK, &sc->sc_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"); + 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_ecas(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 am335x_lcd_detach(device_t dev) { /* Do not let unload driver */ Index: sys/arm/ti/am335x/am335x_lcd.h =================================================================== --- sys/arm/ti/am335x/am335x_lcd.h (revision 277406) +++ sys/arm/ti/am335x/am335x_lcd.h (working copy) @@ -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 @@ uint32_t panel_vfp; uint32_t panel_vbp; uint32_t panel_vsw; + uint32_t invert_line_clock; + uint32_t invert_frm_clock; + 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 invert_line_clock; - uint32_t invert_frm_clock; uint32_t sync_edge; uint32_t sync_ctrl; - uint32_t panel_pxl_clk; uint32_t panel_invert_pxl_clk; }; Index: sys/boot/fdt/dts/arm/beaglebone-black.dts =================================================================== --- sys/boot/fdt/dts/arm/beaglebone-black.dts (revision 277406) +++ sys/boot/fdt/dts/arm/beaglebone-black.dts (working copy) @@ -110,18 +110,30 @@ "GPMC_BEn1", "gpio1_28", "input_pulldown", "GPMC_CSn0", "gpio1_29", "input_pulldown", "GPMC_CLK", "gpio2_1", "input_pulldown", - "LCD_DATA0", "gpio2_6", "input_pulldown", - "LCD_DATA1", "gpio2_7", "input_pulldown", - "LCD_DATA2", "gpio2_8", "input_pulldown", - "LCD_DATA3", "gpio2_9", "input_pulldown", - "LCD_DATA4", "gpio2_10", "input_pulldown", - "LCD_DATA5", "gpio2_11", "input_pulldown", - "LCD_DATA6", "gpio2_12", "input_pulldown", - "LCD_DATA7", "gpio2_13", "input_pulldown", - "LCD_VSYNC", "gpio2_22", "input_pulldown", - "LCD_HSYNC", "gpio2_23", "input_pulldown", - "LCD_PCLK", "gpio2_24", "input_pulldown", - "LCD_AC_BIAS_EN", "gpio2_25", "input_pulldown", + + /* HDMI */ + "LCD_DATA0", "lcd_data0", "output", + "LCD_DATA1", "lcd_data1", "output", + "LCD_DATA2", "lcd_data2", "output", + "LCD_DATA3", "lcd_data3", "output", + "LCD_DATA4", "lcd_data4", "output", + "LCD_DATA5", "lcd_data5", "output", + "LCD_DATA6", "lcd_data6", "output", + "LCD_DATA7", "lcd_data7", "output", + "LCD_DATA8", "lcd_data8", "output", + "LCD_DATA9", "lcd_data9", "output", + "LCD_DATA10", "lcd_data10", "output", + "LCD_DATA11", "lcd_data11", "output", + "LCD_DATA12", "lcd_data12", "output", + "LCD_DATA13", "lcd_data13", "output", + "LCD_DATA14", "lcd_data14", "output", + "LCD_DATA15", "lcd_data15", "output", + "LCD_VSYNC", "lcd_vsync", "output", + "LCD_HSYNC", "lcd_hsync", "output", + "LCD_PCLK", "lcd_pclk", "output", + "LCD_AC_BIAS_EN", "lcd_ac_bias_en", "output", + "XDMA_EVENT_INTR0", "clkout1", "output", + "MCASP0_FSR", "gpio3_19", "input_pulldown", "MCASP0_AHCLKX", "gpio3_21", "input_pulldown", /* TIMERs */ @@ -139,6 +151,17 @@ "GPMC_AD9", "ehrpwm2B", "output"; }; + lcd@4830e000 { + hdmi = <&hdmi>; + ac_bias = <255>; + ac_bias_intrpt = <0>; + dma_burst_sz = <16>; + sync_edge = <0>; + sync_ctrl = <1>; + bpp = <16>; + fdd = <128>; + }; + mmchs1@481D8000 { bus-width = <8>; status = "okay"; @@ -150,6 +173,11 @@ compatible = "ti,am335x-pmic"; reg = <0x48>; }; + + hdmi: tda19988@50 { + compatible = "nxp,tda19988"; + reg = <0xe0>; + }; }; }; Index: sys/conf/files =================================================================== --- sys/conf/files (revision 277406) +++ sys/conf/files (working copy) @@ -1431,6 +1431,11 @@ dev/hatm/if_hatm_ioctl.c optional hatm pci dev/hatm/if_hatm_rx.c optional hatm pci dev/hatm/if_hatm_tx.c optional hatm pci +dev/hdmi/hdmi_if.m optional hdmi +dev/hdmi/edid.c optional hdmi +dev/hdmi/pickmode.c optional hdmi +dev/hdmi/videomode.c optional hdmi +dev/hdmi/vesagtf.c optional hdmi dev/hifn/hifn7751.c optional hifn dev/hme/if_hme.c optional hme dev/hme/if_hme_pci.c optional hme pci @@ -1452,6 +1457,7 @@ dev/iicbus/ds1374.c optional ds1374 dev/iicbus/ds1672.c optional ds1672 dev/iicbus/icee.c optional icee +dev/iicbus/tda19988.c optional tda19988 dev/iicbus/if_ic.c optional ic dev/iicbus/iic.c optional iic dev/iicbus/iicbb.c optional iicbb Index: sys/dev/hdmi/edid.c =================================================================== --- sys/dev/hdmi/edid.c (revision 0) +++ sys/dev/hdmi/edid.c (working copy) @@ -0,0 +1,641 @@ +/* $NetBSD: edid.c,v 1.13 2014/11/17 00:46:04 jmcneill Exp $ */ + +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Written by Garrett D'Amore for Itronix Inc. + * + * 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. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define EDIDVERBOSE 1 +#define DIVIDE(x,y) (((x) + ((y) / 2)) / (y)) + +/* These are reversed established timing order */ +static const char *_edid_modes[] = { + "1280x1024x75", + "1024x768x75", + "1024x768x70", + "1024x768x60", + "1024x768x87i", + "832x624x74", /* rounding error, 74.55 Hz aka "832x624x75" */ + "800x600x75", + "800x600x72", + "800x600x60", + "800x600x56", + "640x480x75", + "640x480x72", + "640x480x67", + "640x480x60", + "720x400x87", /* rounding error, 87.85 Hz aka "720x400x88" */ + "720x400x70", +}; + +#ifdef EDIDVERBOSE +struct edid_vendor { + const char *vendor; + const char *name; +}; + +struct edid_product { + const char *vendor; + uint16_t product; + const char *name; +}; + +#include +#endif /* EDIDVERBOSE */ + +static const char * +edid_findvendor(const char *vendor) +{ +#ifdef EDIDVERBOSE + int n; + + for (n = 0; n < edid_nvendors; n++) + if (memcmp(edid_vendors[n].vendor, vendor, 3) == 0) + return edid_vendors[n].name; +#endif + return NULL; +} + +static const char * +edid_findproduct(const char *vendor, uint16_t product) +{ +#ifdef EDIDVERBOSE + int n; + + for (n = 0; n < edid_nproducts; n++) + if (edid_products[n].product == product && + memcmp(edid_products[n].vendor, vendor, 3) == 0) + return edid_products[n].name; +#endif /* EDIDVERBOSE */ + return NULL; + +} + +static void +edid_strchomp(char *ptr) +{ + for (;;) { + switch (*ptr) { + case '\0': + return; + case '\r': + case '\n': + *ptr = '\0'; + return; + } + ptr++; + } +} + +int +edid_is_valid(uint8_t *d) +{ + int sum = 0, i; + uint8_t sig[8] = EDID_SIGNATURE; + + if (memcmp(d, sig, 8) != 0) + return EINVAL; + + for (i = 0; i < 128; i++) + sum += d[i]; + if ((sum & 0xff) != 0) + return EINVAL; + + return 0; +} + +void +edid_print(struct edid_info *edid) +{ + int i; + + if (edid == NULL) + return; + printf("Vendor: [%s] %s\n", edid->edid_vendor, edid->edid_vendorname); + printf("Product: [%04X] %s\n", edid->edid_product, + edid->edid_productname); + printf("Serial number: %s\n", edid->edid_serial); + printf("Manufactured %d Week %d\n", + edid->edid_year, edid->edid_week); + printf("EDID Version %d.%d\n", edid->edid_version, + edid->edid_revision); + printf("EDID Comment: %s\n", edid->edid_comment); + + printf("Video Input: %x\n", edid->edid_video_input); + if (edid->edid_video_input & EDID_VIDEO_INPUT_DIGITAL) { + printf("\tDigital"); + if (edid->edid_video_input & EDID_VIDEO_INPUT_DFP1_COMPAT) + printf(" (DFP 1.x compatible)"); + printf("\n"); + } else { + printf("\tAnalog\n"); + switch (EDID_VIDEO_INPUT_LEVEL(edid->edid_video_input)) { + case 0: + printf("\t-0.7, 0.3V\n"); + break; + case 1: + printf("\t-0.714, 0.286V\n"); + break; + case 2: + printf("\t-1.0, 0.4V\n"); + break; + case 3: + printf("\t-0.7, 0.0V\n"); + break; + } + if (edid->edid_video_input & EDID_VIDEO_INPUT_BLANK_TO_BLACK) + printf("\tBlank-to-black setup\n"); + if (edid->edid_video_input & EDID_VIDEO_INPUT_SEPARATE_SYNCS) + printf("\tSeperate syncs\n"); + if (edid->edid_video_input & EDID_VIDEO_INPUT_COMPOSITE_SYNC) + printf("\tComposite sync\n"); + if (edid->edid_video_input & EDID_VIDEO_INPUT_SYNC_ON_GRN) + printf("\tSync on green\n"); + if (edid->edid_video_input & EDID_VIDEO_INPUT_SERRATION) + printf("\tSerration vsync\n"); + } + + printf("Gamma: %d.%02d\n", + edid->edid_gamma / 100, edid->edid_gamma % 100); + + printf("Max Size: %d cm x %d cm\n", + edid->edid_max_hsize, edid->edid_max_vsize); + + printf("Features: %x\n", edid->edid_features); + if (edid->edid_features & EDID_FEATURES_STANDBY) + printf("\tDPMS standby\n"); + if (edid->edid_features & EDID_FEATURES_SUSPEND) + printf("\tDPMS suspend\n"); + if (edid->edid_features & EDID_FEATURES_ACTIVE_OFF) + printf("\tDPMS active-off\n"); + switch (EDID_FEATURES_DISP_TYPE(edid->edid_features)) { + case EDID_FEATURES_DISP_TYPE_MONO: + printf("\tMonochrome\n"); + break; + case EDID_FEATURES_DISP_TYPE_RGB: + printf("\tRGB\n"); + break; + case EDID_FEATURES_DISP_TYPE_NON_RGB: + printf("\tMulticolor\n"); + break; + case EDID_FEATURES_DISP_TYPE_UNDEFINED: + printf("\tUndefined monitor type\n"); + break; + } + if (edid->edid_features & EDID_FEATURES_STD_COLOR) + printf("\tStandard color space\n"); + if (edid->edid_features & EDID_FEATURES_PREFERRED_TIMING) + printf("\tPreferred timing\n"); + if (edid->edid_features & EDID_FEATURES_DEFAULT_GTF) + printf("\tDefault GTF supported\n"); + + printf("Chroma Info:\n"); + printf("\tRed X: 0.%03d\n", edid->edid_chroma.ec_redx); + printf("\tRed Y: 0.%03d\n", edid->edid_chroma.ec_redy); + printf("\tGrn X: 0.%03d\n", edid->edid_chroma.ec_greenx); + printf("\tGrn Y: 0.%03d\n", edid->edid_chroma.ec_greeny); + printf("\tBlu X: 0.%03d\n", edid->edid_chroma.ec_bluex); + printf("\tBlu Y: 0.%03d\n", edid->edid_chroma.ec_bluey); + printf("\tWht X: 0.%03d\n", edid->edid_chroma.ec_whitex); + printf("\tWht Y: 0.%03d\n", edid->edid_chroma.ec_whitey); + + if (edid->edid_have_range) { + printf("Range:\n"); + printf("\tHorizontal: %d - %d kHz\n", + edid->edid_range.er_min_hfreq, + edid->edid_range.er_max_hfreq); + printf("\tVertical: %d - %d Hz\n", + edid->edid_range.er_min_vfreq, + edid->edid_range.er_max_vfreq); + printf("\tMax Dot Clock: %d MHz\n", + edid->edid_range.er_max_clock); + if (edid->edid_range.er_have_gtf2) { + printf("\tGTF2 hfreq: %d\n", + edid->edid_range.er_gtf2_hfreq); + printf("\tGTF2 C: %d\n", edid->edid_range.er_gtf2_c); + printf("\tGTF2 M: %d\n", edid->edid_range.er_gtf2_m); + printf("\tGTF2 J: %d\n", edid->edid_range.er_gtf2_j); + printf("\tGTF2 K: %d\n", edid->edid_range.er_gtf2_k); + } + } + printf("Video modes:\n"); + for (i = 0; i < edid->edid_nmodes; i++) { + printf("\t%dx%d @ %dHz", + edid->edid_modes[i].hdisplay, + edid->edid_modes[i].vdisplay, + DIVIDE(DIVIDE(edid->edid_modes[i].dot_clock * 1000, + edid->edid_modes[i].htotal), edid->edid_modes[i].vtotal)); + printf(" (%d %d %d %d %d %d %d", + edid->edid_modes[i].dot_clock, + edid->edid_modes[i].hsync_start, + edid->edid_modes[i].hsync_end, + edid->edid_modes[i].htotal, + edid->edid_modes[i].vsync_start, + edid->edid_modes[i].vsync_end, + edid->edid_modes[i].vtotal); + printf(" %s%sH %s%sV)\n", + edid->edid_modes[i].flags & VID_PHSYNC ? "+" : "", + edid->edid_modes[i].flags & VID_NHSYNC ? "-" : "", + edid->edid_modes[i].flags & VID_PVSYNC ? "+" : "", + edid->edid_modes[i].flags & VID_NVSYNC ? "-" : ""); + } + if (edid->edid_preferred_mode) + printf("Preferred mode: %dx%d @ %dHz\n", + edid->edid_preferred_mode->hdisplay, + edid->edid_preferred_mode->vdisplay, + DIVIDE(DIVIDE(edid->edid_preferred_mode->dot_clock * 1000, + edid->edid_preferred_mode->htotal), + edid->edid_preferred_mode->vtotal)); + + printf("Number of extension blocks: %d\n", edid->edid_ext_block_count); +} + +static const struct videomode * +edid_mode_lookup_list(const char *name) +{ + int i; + + for (i = 0; i < videomode_count; i++) + if (strcmp(name, videomode_list[i].name) == 0) + return &videomode_list[i]; + return NULL; +} + +static struct videomode * +edid_search_mode(struct edid_info *edid, const struct videomode *mode) +{ + int refresh, i; + + refresh = DIVIDE(DIVIDE(mode->dot_clock * 1000, + mode->htotal), mode->vtotal); + for (i = 0; i < edid->edid_nmodes; i++) { + if (mode->hdisplay == edid->edid_modes[i].hdisplay && + mode->vdisplay == edid->edid_modes[i].vdisplay && + refresh == DIVIDE(DIVIDE( + edid->edid_modes[i].dot_clock * 1000, + edid->edid_modes[i].htotal), edid->edid_modes[i].vtotal)) { + return &edid->edid_modes[i]; + } + } + return NULL; +} + +static int +edid_std_timing(uint8_t *data, struct videomode *vmp) +{ + unsigned x, y, f; + const struct videomode *lookup; + char name[80]; + + if ((data[0] == 1 && data[1] == 1) || + (data[0] == 0 && data[1] == 0) || + (data[0] == 0x20 && data[1] == 0x20)) + return 0; + + x = EDID_STD_TIMING_HRES(data); + switch (EDID_STD_TIMING_RATIO(data)) { + case EDID_STD_TIMING_RATIO_16_10: + y = x * 10 / 16; + break; + case EDID_STD_TIMING_RATIO_4_3: + y = x * 3 / 4; + break; + case EDID_STD_TIMING_RATIO_5_4: + y = x * 4 / 5; + break; + case EDID_STD_TIMING_RATIO_16_9: + default: + y = x * 9 / 16; + break; + } + f = EDID_STD_TIMING_VFREQ(data); + + /* first try to lookup the mode as a DMT timing */ + snprintf(name, sizeof(name), "%dx%dx%d", x, y, f); + if ((lookup = edid_mode_lookup_list(name)) != NULL) { + *vmp = *lookup; + } else { + /* failing that, calculate it using gtf */ + /* + * Hmm. I'm not using alternate GTF timings, which + * could, in theory, be present. + */ + vesagtf_mode(x, y, f, vmp); + } + return 1; +} + +static int +edid_det_timing(uint8_t *data, struct videomode *vmp) +{ + unsigned hactive, hblank, hsyncwid, hsyncoff; + unsigned vactive, vblank, vsyncwid, vsyncoff; + uint8_t flags; + + flags = EDID_DET_TIMING_FLAGS(data); + + /* we don't support stereo modes (for now) */ + if (flags & (EDID_DET_TIMING_FLAG_STEREO | + EDID_DET_TIMING_FLAG_STEREO_MODE)) + return 0; + + vmp->dot_clock = EDID_DET_TIMING_DOT_CLOCK(data) / 1000; + + hactive = EDID_DET_TIMING_HACTIVE(data); + hblank = EDID_DET_TIMING_HBLANK(data); + hsyncwid = EDID_DET_TIMING_HSYNC_WIDTH(data); + hsyncoff = EDID_DET_TIMING_HSYNC_OFFSET(data); + + vactive = EDID_DET_TIMING_VACTIVE(data); + vblank = EDID_DET_TIMING_VBLANK(data); + vsyncwid = EDID_DET_TIMING_VSYNC_WIDTH(data); + vsyncoff = EDID_DET_TIMING_VSYNC_OFFSET(data); + + /* Borders are contained within the blank areas. */ + + vmp->hdisplay = hactive; + vmp->htotal = hactive + hblank; + vmp->hsync_start = hactive + hsyncoff; + vmp->hsync_end = vmp->hsync_start + hsyncwid; + + vmp->vdisplay = vactive; + vmp->vtotal = vactive + vblank; + vmp->vsync_start = vactive + vsyncoff; + vmp->vsync_end = vmp->vsync_start + vsyncwid; + + vmp->flags = 0; + + if (flags & EDID_DET_TIMING_FLAG_INTERLACE) + vmp->flags |= VID_INTERLACE; + if (flags & EDID_DET_TIMING_FLAG_HSYNC_POSITIVE) + vmp->flags |= VID_PHSYNC; + else + vmp->flags |= VID_NHSYNC; + + if (flags & EDID_DET_TIMING_FLAG_VSYNC_POSITIVE) + vmp->flags |= VID_PVSYNC; + else + vmp->flags |= VID_NVSYNC; + + return 1; +} + +static void +edid_block(struct edid_info *edid, uint8_t *data) +{ + int i; + struct videomode mode, *exist_mode; + + if (EDID_BLOCK_IS_DET_TIMING(data)) { + if (!edid_det_timing(data, &mode)) + return; + /* Does this mode already exist? */ + exist_mode = edid_search_mode(edid, &mode); + if (exist_mode != NULL) { + *exist_mode = mode; + if (edid->edid_preferred_mode == NULL) + edid->edid_preferred_mode = exist_mode; + } else { + edid->edid_modes[edid->edid_nmodes] = mode; + if (edid->edid_preferred_mode == NULL) + edid->edid_preferred_mode = + &edid->edid_modes[edid->edid_nmodes]; + edid->edid_nmodes++; + } + return; + } + + switch (EDID_BLOCK_TYPE(data)) { + case EDID_DESC_BLOCK_TYPE_SERIAL: + memcpy(edid->edid_serial, data + EDID_DESC_ASCII_DATA_OFFSET, + EDID_DESC_ASCII_DATA_LEN); + edid->edid_serial[sizeof(edid->edid_serial) - 1] = 0; + break; + + case EDID_DESC_BLOCK_TYPE_ASCII: + memcpy(edid->edid_comment, data + EDID_DESC_ASCII_DATA_OFFSET, + EDID_DESC_ASCII_DATA_LEN); + edid->edid_comment[sizeof(edid->edid_comment) - 1] = 0; + break; + + case EDID_DESC_BLOCK_TYPE_RANGE: + edid->edid_have_range = 1; + edid->edid_range.er_min_vfreq = EDID_DESC_RANGE_MIN_VFREQ(data); + edid->edid_range.er_max_vfreq = EDID_DESC_RANGE_MAX_VFREQ(data); + edid->edid_range.er_min_hfreq = EDID_DESC_RANGE_MIN_HFREQ(data); + edid->edid_range.er_max_hfreq = EDID_DESC_RANGE_MAX_HFREQ(data); + edid->edid_range.er_max_clock = EDID_DESC_RANGE_MAX_CLOCK(data); + if (!EDID_DESC_RANGE_HAVE_GTF2(data)) + break; + edid->edid_range.er_have_gtf2 = 1; + edid->edid_range.er_gtf2_hfreq = + EDID_DESC_RANGE_GTF2_HFREQ(data); + edid->edid_range.er_gtf2_c = EDID_DESC_RANGE_GTF2_C(data); + edid->edid_range.er_gtf2_m = EDID_DESC_RANGE_GTF2_M(data); + edid->edid_range.er_gtf2_j = EDID_DESC_RANGE_GTF2_J(data); + edid->edid_range.er_gtf2_k = EDID_DESC_RANGE_GTF2_K(data); + break; + + case EDID_DESC_BLOCK_TYPE_NAME: + /* copy the product name into place */ + memcpy(edid->edid_productname, + data + EDID_DESC_ASCII_DATA_OFFSET, + EDID_DESC_ASCII_DATA_LEN); + break; + + case EDID_DESC_BLOCK_TYPE_STD_TIMING: + data += EDID_DESC_STD_TIMING_START; + for (i = 0; i < EDID_DESC_STD_TIMING_COUNT; i++) { + if (edid_std_timing(data, &mode)) { + /* Does this mode already exist? */ + exist_mode = edid_search_mode(edid, &mode); + if (exist_mode == NULL) { + edid->edid_modes[edid->edid_nmodes] = + mode; + edid->edid_nmodes++; + } + } + data += 2; + } + break; + + case EDID_DESC_BLOCK_TYPE_COLOR_POINT: + /* XXX: not implemented yet */ + break; + } +} + +/* + * Gets EDID version in BCD, e.g. EDID v1.3 returned as 0x0103 + */ +int +edid_parse(uint8_t *data, struct edid_info *edid) +{ + uint16_t manfid, estmodes; + const struct videomode *vmp; + int i; + const char *name; + int max_dotclock = 0; + int mhz; + + if (edid_is_valid(data) != 0) + return -1; + + /* get product identification */ + manfid = EDID_VENDOR_ID(data); + edid->edid_vendor[0] = EDID_MANFID_0(manfid); + edid->edid_vendor[1] = EDID_MANFID_1(manfid); + edid->edid_vendor[2] = EDID_MANFID_2(manfid); + edid->edid_vendor[3] = 0; /* null terminate for convenience */ + + edid->edid_product = data[EDID_OFFSET_PRODUCT_ID] + + (data[EDID_OFFSET_PRODUCT_ID + 1] << 8); + + name = edid_findvendor(edid->edid_vendor); + if (name != NULL) + strlcpy(edid->edid_vendorname, name, + sizeof(edid->edid_vendorname)); + else + edid->edid_vendorname[0] = '\0'; + + name = edid_findproduct(edid->edid_vendor, edid->edid_product); + if (name != NULL) + strlcpy(edid->edid_productname, name, + sizeof(edid->edid_productname)); + else + edid->edid_productname[0] = '\0'; + + snprintf(edid->edid_serial, sizeof(edid->edid_serial), "%08x", + EDID_SERIAL_NUMBER(data)); + + edid->edid_week = EDID_WEEK(data); + edid->edid_year = EDID_YEAR(data); + + /* get edid revision */ + edid->edid_version = EDID_VERSION(data); + edid->edid_revision = EDID_REVISION(data); + + edid->edid_video_input = EDID_VIDEO_INPUT(data); + edid->edid_max_hsize = EDID_MAX_HSIZE(data); + edid->edid_max_vsize = EDID_MAX_VSIZE(data); + + edid->edid_gamma = EDID_GAMMA(data); + edid->edid_features = EDID_FEATURES(data); + + edid->edid_chroma.ec_redx = EDID_CHROMA_REDX(data); + edid->edid_chroma.ec_redy = EDID_CHROMA_REDX(data); + edid->edid_chroma.ec_greenx = EDID_CHROMA_GREENX(data); + edid->edid_chroma.ec_greeny = EDID_CHROMA_GREENY(data); + edid->edid_chroma.ec_bluex = EDID_CHROMA_BLUEX(data); + edid->edid_chroma.ec_bluey = EDID_CHROMA_BLUEY(data); + edid->edid_chroma.ec_whitex = EDID_CHROMA_WHITEX(data); + edid->edid_chroma.ec_whitey = EDID_CHROMA_WHITEY(data); + + edid->edid_ext_block_count = EDID_EXT_BLOCK_COUNT(data); + + /* lookup established modes */ + edid->edid_nmodes = 0; + edid->edid_preferred_mode = NULL; + estmodes = EDID_EST_TIMING(data); + /* Iterate in esztablished timing order */ + for (i = 15; i >= 0; i--) { + if (estmodes & (1 << i)) { + vmp = edid_mode_lookup_list(_edid_modes[i]); + if (vmp != NULL) { + edid->edid_modes[edid->edid_nmodes] = *vmp; + edid->edid_nmodes++; + } +#ifdef DIAGNOSTIC + else + printf("no data for est. mode %s\n", + _edid_modes[i]); +#endif + } + } + + /* do standard timing section */ + for (i = 0; i < EDID_STD_TIMING_COUNT; i++) { + struct videomode mode, *exist_mode; + if (edid_std_timing(data + EDID_OFFSET_STD_TIMING + i * 2, + &mode)) { + /* Does this mode already exist? */ + exist_mode = edid_search_mode(edid, &mode); + if (exist_mode == NULL) { + edid->edid_modes[edid->edid_nmodes] = mode; + edid->edid_nmodes++; + } + } + } + + /* do detailed timings and descriptors */ + for (i = 0; i < EDID_BLOCK_COUNT; i++) { + edid_block(edid, data + EDID_OFFSET_DESC_BLOCK + + i * EDID_BLOCK_SIZE); + } + + edid_strchomp(edid->edid_vendorname); + edid_strchomp(edid->edid_productname); + edid_strchomp(edid->edid_serial); + edid_strchomp(edid->edid_comment); + + /* + * XXX + * some monitors lie about their maximum supported dot clock + * by claiming to support modes which need a higher dot clock + * than the stated maximum. + * For sanity's sake we bump it to the highest dot clock we find + * in the list of supported modes + */ + for (i = 0; i < edid->edid_nmodes; i++) + if (edid->edid_modes[i].dot_clock > max_dotclock) + max_dotclock = edid->edid_modes[i].dot_clock; + + mhz = (max_dotclock + 999) / 1000; + + if (edid->edid_have_range) { + if (mhz > edid->edid_range.er_max_clock) + edid->edid_range.er_max_clock = mhz; + } else + edid->edid_range.er_max_clock = mhz; + + return 0; +} + Property changes on: sys/dev/hdmi/edid.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/hdmi/ediddevs.h =================================================================== --- sys/dev/hdmi/ediddevs.h (revision 0) +++ sys/dev/hdmi/ediddevs.h (working copy) @@ -0,0 +1,91 @@ +/* $NetBSD: ediddevs.h,v 1.2 2007/03/07 18:47:55 macallan Exp $ */ + +/* + * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT. + * + * generated from: + * NetBSD: ediddevs,v 1.1 2006/05/11 01:49:53 gdamore Exp + */ + +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Written by Garrett D'Amore for Itronix Inc. + * + * 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. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. + */ +#define EDID_VENDOR_AAC "AcerView" +#define EDID_VENDOR_AOC "AOC" +#define EDID_VENDOR_APP "Apple Computer" +#define EDID_VENDOR_AST "AST Research" +#define EDID_VENDOR_CPL "Compal" +#define EDID_VENDOR_CPQ "Compaq" +#define EDID_VENDOR_CTX "CTX" +#define EDID_VENDOR_DEC "DEC" +#define EDID_VENDOR_DEL "Dell" +#define EDID_VENDOR_DPC "Delta" +#define EDID_VENDOR_DWE "Daewoo" +#define EDID_VENDOR_EIZ "EIZO" +#define EDID_VENDOR_ELS "ELSA" +#define EDID_VENDOR_EPI "Envision" +#define EDID_VENDOR_FCM "Funai" +#define EDID_VENDOR_FUJ "Fujitsu" +#define EDID_VENDOR_GSM "LG Electronics" +#define EDID_VENDOR_GWY "Gateway 2000" +#define EDID_VENDOR_HEI "Hyundai" +#define EDID_VENDOR_HIT "Hitachi" +#define EDID_VENDOR_HSL "Hansol" +#define EDID_VENDOR_HTC "Hitachi/Nissei" +#define EDID_VENDOR_HWP "HP" +#define EDID_VENDOR_IBM "IBM" +#define EDID_VENDOR_ICL "Fujitsu ICL" +#define EDID_VENDOR_IVM "Iiyama" +#define EDID_VENDOR_KDS "Korea Data Systems" +#define EDID_VENDOR_MEI "Panasonic" +#define EDID_VENDOR_MEL "Mitsubishi Electronics" +#define EDID_VENDOR_NAN "Nanao" +#define EDID_VENDOR_NEC "NEC" +#define EDID_VENDOR_NOK "Nokia Data" +#define EDID_VENDOR_PHL "Philips" +#define EDID_VENDOR_REL "Relisys" +#define EDID_VENDOR_SAM "Samsung" +#define EDID_VENDOR_SGI "SGI" +#define EDID_VENDOR_SNY "Sony" +#define EDID_VENDOR_SRC "Shamrock" +#define EDID_VENDOR_SUN "Sun Microsystems" +#define EDID_VENDOR_TAT "Tatung" +#define EDID_VENDOR_TOS "Toshiba" +#define EDID_VENDOR_TSB "Toshiba" +#define EDID_VENDOR_VSC "ViewSonic" +#define EDID_VENDOR_ZCM "Zenith" + +/* Dell - this exists for now as a sample. I don't have one of these. */ +#define EDID_PRODUCT_DEL_ULTRASCAN14XE_REVA 0x139A /* Ultrascan 14XE */ +#define EDID_PRODUCT_DEL_ULTRASCAN14XE_REVB 0x139B /* Ultrascan 14XE */ + +/* ViewSonic */ +#define EDID_PRODUCT_VSC_17GS 0x0c00 /* 17GS */ +#define EDID_PRODUCT_VSC_17PS 0x0c0f /* 17PS */ Property changes on: sys/dev/hdmi/ediddevs.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/hdmi/ediddevs_data.h =================================================================== --- sys/dev/hdmi/ediddevs_data.h (revision 0) +++ sys/dev/hdmi/ediddevs_data.h (working copy) @@ -0,0 +1,107 @@ +/* $NetBSD: ediddevs_data.h,v 1.2 2007/03/07 18:47:13 macallan Exp $ */ + +/* + * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT. + * + * generated from: + * NetBSD: ediddevs,v 1.1 2006/05/11 01:49:53 gdamore Exp + */ + +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Written by Garrett D'Amore for Itronix Inc. + * + * 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. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. + */ + +const struct edid_vendor edid_vendors[] = { + { "AAC", EDID_VENDOR_AAC }, + { "AOC", EDID_VENDOR_AOC }, + { "APP", EDID_VENDOR_APP }, + { "AST", EDID_VENDOR_AST }, + { "CPL", EDID_VENDOR_CPL }, + { "CPQ", EDID_VENDOR_CPQ }, + { "CTX", EDID_VENDOR_CTX }, + { "DEC", EDID_VENDOR_DEC }, + { "DEL", EDID_VENDOR_DEL }, + { "DPC", EDID_VENDOR_DPC }, + { "DWE", EDID_VENDOR_DWE }, + { "EIZ", EDID_VENDOR_EIZ }, + { "ELS", EDID_VENDOR_ELS }, + { "EPI", EDID_VENDOR_EPI }, + { "FCM", EDID_VENDOR_FCM }, + { "FUJ", EDID_VENDOR_FUJ }, + { "GSM", EDID_VENDOR_GSM }, + { "GWY", EDID_VENDOR_GWY }, + { "HEI", EDID_VENDOR_HEI }, + { "HIT", EDID_VENDOR_HIT }, + { "HSL", EDID_VENDOR_HSL }, + { "HTC", EDID_VENDOR_HTC }, + { "HWP", EDID_VENDOR_HWP }, + { "IBM", EDID_VENDOR_IBM }, + { "ICL", EDID_VENDOR_ICL }, + { "IVM", EDID_VENDOR_IVM }, + { "KDS", EDID_VENDOR_KDS }, + { "MEI", EDID_VENDOR_MEI }, + { "MEL", EDID_VENDOR_MEL }, + { "NAN", EDID_VENDOR_NAN }, + { "NEC", EDID_VENDOR_NEC }, + { "NOK", EDID_VENDOR_NOK }, + { "PHL", EDID_VENDOR_PHL }, + { "REL", EDID_VENDOR_REL }, + { "SAM", EDID_VENDOR_SAM }, + { "SGI", EDID_VENDOR_SGI }, + { "SNY", EDID_VENDOR_SNY }, + { "SRC", EDID_VENDOR_SRC }, + { "SUN", EDID_VENDOR_SUN }, + { "TAT", EDID_VENDOR_TAT }, + { "TOS", EDID_VENDOR_TOS }, + { "TSB", EDID_VENDOR_TSB }, + { "VSC", EDID_VENDOR_VSC }, + { "ZCM", EDID_VENDOR_ZCM }, +}; +const int edid_nvendors = 44; + +const struct edid_product edid_products[] = { + { + "DEL", EDID_PRODUCT_DEL_ULTRASCAN14XE_REVA, + "Ultrascan 14XE", + }, + { + "DEL", EDID_PRODUCT_DEL_ULTRASCAN14XE_REVB, + "Ultrascan 14XE", + }, + { + "VSC", EDID_PRODUCT_VSC_17GS, + "17GS", + }, + { + "VSC", EDID_PRODUCT_VSC_17PS, + "17PS", + }, +}; +const int edid_nproducts = 4; Property changes on: sys/dev/hdmi/ediddevs_data.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/hdmi/edidreg.h =================================================================== --- sys/dev/hdmi/edidreg.h (revision 0) +++ sys/dev/hdmi/edidreg.h (working copy) @@ -0,0 +1,255 @@ +/* $NetBSD: edidreg.h,v 1.4 2014/11/17 00:46:04 jmcneill Exp $ */ + +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Written by Garrett D'Amore for Itronix Inc. + * + * 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. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. + */ + +#ifndef _DEV_VIDEOMODE_EDIDREG_H +#define _DEV_VIDEOMODE_EDIDREG_H + +#define EDID_OFFSET_SIGNATURE 0x00 +#define EDID_OFFSET_MANUFACTURER_ID 0x08 +#define EDID_OFFSET_PRODUCT_ID 0x0a +#define EDID_OFFSET_SERIAL_NUMBER 0x0c +#define EDID_OFFSET_MANUFACTURE_WEEK 0x10 +#define EDID_OFFSET_MANUFACTURE_YEAR 0x11 +#define EDID_OFFSET_VERSION 0x12 +#define EDID_OFFSET_REVISION 0x13 +#define EDID_OFFSET_VIDEO_INPUT 0x14 +#define EDID_OFFSET_MAX_HSIZE 0x15 /* in cm */ +#define EDID_OFFSET_MAX_VSIZE 0x16 +#define EDID_OFFSET_GAMMA 0x17 +#define EDID_OFFSET_FEATURE 0x18 +#define EDID_OFFSET_CHROMA 0x19 +#define EDID_OFFSET_EST_TIMING_1 0x23 +#define EDID_OFFSET_EST_TIMING_2 0x24 +#define EDID_OFFSET_MFG_TIMING 0x25 +#define EDID_OFFSET_STD_TIMING 0x26 +#define EDID_OFFSET_DESC_BLOCK 0x36 + +#define EDID_SIGNATURE { 0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0 } + +/* assume x is 16-bit value */ +#define EDID_VENDOR_ID(ptr) ((((ptr)[8]) << 8) + ptr[9]) +#define EDID_MANFID_0(x) ((((x) >> 10) & 0x1f) + '@') +#define EDID_MANFID_1(x) ((((x) >> 5) & 0x1f) + '@') +#define EDID_MANFID_2(x) ((((x) >> 0) & 0x1f) + '@') + +/* relative to edid block */ +#define EDID_PRODUCT_ID(ptr) (((ptr)[10]) | (((ptr)[11]) << 8)) +#define EDID_SERIAL_NUMBER(ptr) (((ptr)[12] << 24) + \ + ((ptr)[13] << 16) + \ + ((ptr)[14] << 8) + \ + (ptr)[15]) + +/* relative to edid block */ +#define EDID_WEEK(ptr) ((ptr)[16]) +#define EDID_YEAR(ptr) (((ptr)[17]) + 1990) + +#define EDID_VERSION(ptr) ((ptr)[18]) +#define EDID_REVISION(ptr) ((ptr)[19]) + +#define EDID_VIDEO_INPUT(ptr) ((ptr)[20]) +#define EDID_VIDEO_INPUT_DIGITAL 0x80 +/* if INPUT_BIT_DIGITAL set */ +#define EDID_VIDEO_INPUT_DFP1_COMPAT 0x01 +/* if INPUT_BIT_DIGITAL not set */ +#define EDID_VIDEO_INPUT_BLANK_TO_BLACK 0x10 +#define EDID_VIDEO_INPUT_SEPARATE_SYNCS 0x08 +#define EDID_VIDEO_INPUT_COMPOSITE_SYNC 0x04 +#define EDID_VIDEO_INPUT_SYNC_ON_GRN 0x02 +#define EDID_VIDEO_INPUT_SERRATION 0x01 +#define EDID_VIDEO_INPUT_LEVEL(x) (((x) & 0x60) >> 5) +/* meanings of level bits are as follows, I don't know names */ +/* 0 = 0.7,0.3, 1 = 0.714,0.286, 2 = 1.0,0.4, 3 = 0.7,0.0 */ + +/* relative to edid block */ +#define EDID_MAX_HSIZE(ptr) ((ptr)[21]) /* cm */ +#define EDID_MAX_VSIZE(ptr) ((ptr)[22]) /* cm */ +/* gamma is scaled by 100 (avoid fp), e.g. 213 == 2.13 */ +#define _GAMMA(x) ((x) == 0xff ? 100 : ((x) + 100)) +#define EDID_GAMMA(ptr) _GAMMA(ptr[23]) + +#define EDID_FEATURES(ptr) ((ptr)[24]) +#define EDID_FEATURES_STANDBY 0x80 +#define EDID_FEATURES_SUSPEND 0x40 +#define EDID_FEATURES_ACTIVE_OFF 0x20 +#define EDID_FEATURES_DISP_TYPE(x) (((x) & 0x18) >> 3) +#define EDID_FEATURES_DISP_TYPE_MONO 0 +#define EDID_FEATURES_DISP_TYPE_RGB 1 +#define EDID_FEATURES_DISP_TYPE_NON_RGB 2 +#define EDID_FEATURES_DISP_TYPE_UNDEFINED 3 +#define EDID_FEATURES_STD_COLOR 0x04 +#define EDID_FEATURES_PREFERRED_TIMING 0x02 +#define EDID_FEATURES_DEFAULT_GTF 0x01 + +/* chroma values 0.0 - 0.999 scaled as 0-999 */ +#define _CHLO(byt, shft) (((byt) >> (shft)) & 0x3) +#define _CHHI(byt) ((byt) << 2) +#define _CHHILO(ptr, l, s, h) (_CHLO((ptr)[l], s) | _CHHI((ptr)[h])) +#define _CHROMA(ptr, l, s, h) ((_CHHILO(ptr, l, s, h) * 1000) / 1024) + +#define EDID_CHROMA_REDX(ptr) (_CHROMA(ptr, 25, 6, 27)) +#define EDID_CHROMA_REDY(ptr) (_CHROMA(ptr, 25, 4, 28)) +#define EDID_CHROMA_GREENX(ptr) (_CHROMA(ptr, 25, 2, 29)) +#define EDID_CHROMA_GREENY(ptr) (_CHROMA(ptr, 25, 0, 30)) +#define EDID_CHROMA_BLUEX(ptr) (_CHROMA(ptr, 26, 6, 31)) +#define EDID_CHROMA_BLUEY(ptr) (_CHROMA(ptr, 26, 4, 32)) +#define EDID_CHROMA_WHITEX(ptr) (_CHROMA(ptr, 26, 2, 33)) +#define EDID_CHROMA_WHITEY(ptr) (_CHROMA(ptr, 26, 0, 34)) + +/* relative to edid block */ +#define EDID_EST_TIMING(ptr) (((ptr)[35] << 8) | (ptr)[36]) +#define EDID_EST_TIMING_720_400_70 0x8000 /* 720x400 @ 70Hz */ +#define EDID_EST_TIMING_720_400_88 0x4000 /* 720x400 @ 88Hz */ +#define EDID_EST_TIMING_640_480_60 0x2000 /* 640x480 @ 60Hz */ +#define EDID_EST_TIMING_640_480_67 0x1000 /* 640x480 @ 67Hz */ +#define EDID_EST_TIMING_640_480_72 0x0800 /* 640x480 @ 72Hz */ +#define EDID_EST_TIMING_640_480_75 0x0400 /* 640x480 @ 75Hz */ +#define EDID_EST_TIMING_800_600_56 0x0200 /* 800x600 @ 56Hz */ +#define EDID_EST_TIMING_800_600_60 0x0100 /* 800x600 @ 60Hz */ +#define EDID_EST_TIMING_800_600_72 0x0080 /* 800x600 @ 72Hz */ +#define EDID_EST_TIMING_800_600_75 0x0040 /* 800x600 @ 75Hz */ +#define EDID_EST_TIMING_832_624_75 0x0020 /* 832x624 @ 75Hz */ +#define EDID_EST_TIMING_1024_768_87I 0x0010 /* 1024x768i @ 87Hz */ +#define EDID_EST_TIMING_1024_768_60 0x0008 /* 1024x768 @ 60Hz */ +#define EDID_EST_TIMING_1024_768_70 0x0004 /* 1024x768 @ 70Hz */ +#define EDID_EST_TIMING_1024_768_75 0x0002 /* 1024x768 @ 75Hz */ +#define EDID_EST_TIMING_1280_1024_75 0x0001 /* 1280x1024 @ 75Hz */ + +/* + * N.B.: ptr is relative to standard timing block - used for standard timing + * descriptors as well as standard timings section of edid! + */ +#define EDID_STD_TIMING_HRES(ptr) ((((ptr)[0]) * 8) + 248) +#define EDID_STD_TIMING_VFREQ(ptr) ((((ptr)[1]) & 0x3f) + 60) +#define EDID_STD_TIMING_RATIO(ptr) ((ptr)[1] & 0xc0) +#define EDID_STD_TIMING_RATIO_16_10 0x00 +#define EDID_STD_TIMING_RATIO_4_3 0x40 +#define EDID_STD_TIMING_RATIO_5_4 0x80 +#define EDID_STD_TIMING_RATIO_16_9 0xc0 + +#define EDID_STD_TIMING_SIZE 16 +#define EDID_STD_TIMING_COUNT 8 + +/* + * N.B.: ptr is relative to descriptor block start + */ +#define EDID_BLOCK_SIZE 18 +#define EDID_BLOCK_COUNT 4 + +/* detailed timing block.... what a mess */ +#define EDID_BLOCK_IS_DET_TIMING(ptr) ((ptr)[0] | (ptr)[1]) + +#define EDID_DET_TIMING_DOT_CLOCK(ptr) (((ptr)[0] | ((ptr)[1] << 8)) * 10000) +#define _HACT_LO(ptr) ((ptr)[2]) +#define _HBLK_LO(ptr) ((ptr)[3]) +#define _HACT_HI(ptr) (((ptr)[4] & 0xf0) << 4) +#define _HBLK_HI(ptr) (((ptr)[4] & 0x0f) << 8) +#define EDID_DET_TIMING_HACTIVE(ptr) (_HACT_LO(ptr) | _HACT_HI(ptr)) +#define EDID_DET_TIMING_HBLANK(ptr) (_HBLK_LO(ptr) | _HBLK_HI(ptr)) +#define _VACT_LO(ptr) ((ptr)[5]) +#define _VBLK_LO(ptr) ((ptr)[6]) +#define _VACT_HI(ptr) (((ptr)[7] & 0xf0) << 4) +#define _VBLK_HI(ptr) (((ptr)[7] & 0x0f) << 8) +#define EDID_DET_TIMING_VACTIVE(ptr) (_VACT_LO(ptr) | _VACT_HI(ptr)) +#define EDID_DET_TIMING_VBLANK(ptr) (_VBLK_LO(ptr) | _VBLK_HI(ptr)) +#define _HOFF_LO(ptr) ((ptr)[8]) +#define _HWID_LO(ptr) ((ptr)[9]) +#define _VOFF_LO(ptr) ((ptr)[10] >> 4) +#define _VWID_LO(ptr) ((ptr)[10] & 0xf) +#define _HOFF_HI(ptr) (((ptr)[11] & 0xc0) << 2) +#define _HWID_HI(ptr) (((ptr)[11] & 0x30) << 4) +#define _VOFF_HI(ptr) (((ptr)[11] & 0x0c) << 2) +#define _VWID_HI(ptr) (((ptr)[11] & 0x03) << 4) +#define EDID_DET_TIMING_HSYNC_OFFSET(ptr) (_HOFF_LO(ptr) | _HOFF_HI(ptr)) +#define EDID_DET_TIMING_HSYNC_WIDTH(ptr) (_HWID_LO(ptr) | _HWID_HI(ptr)) +#define EDID_DET_TIMING_VSYNC_OFFSET(ptr) (_VOFF_LO(ptr) | _VOFF_HI(ptr)) +#define EDID_DET_TIMING_VSYNC_WIDTH(ptr) (_VWID_LO(ptr) | _VWID_HI(ptr)) +#define _HSZ_LO(ptr) ((ptr)[12]) +#define _VSZ_LO(ptr) ((ptr)[13]) +#define _HSZ_HI(ptr) (((ptr)[14] & 0xf0) << 4) +#define _VSZ_HI(ptr) (((ptr)[14] & 0x0f) << 8) +#define EDID_DET_TIMING_HSIZE(ptr) (_HSZ_LO(ptr) | _HSZ_HI(ptr)) +#define EDID_DET_TIMING_VSIZE(ptr) (_VSZ_LO(ptr) | _VSZ_HI(ptr)) +#define EDID_DET_TIMING_HBORDER(ptr) ((ptr)[15]) +#define EDID_DET_TIMING_VBORDER(ptr) ((ptr)[16]) +#define EDID_DET_TIMING_FLAGS(ptr) ((ptr)[17]) +#define EDID_DET_TIMING_FLAG_INTERLACE 0x80 +#define EDID_DET_TIMING_FLAG_STEREO 0x60 /* stereo or not */ +#define EDID_DET_TIMING_FLAG_SYNC_SEPARATE 0x18 +#define EDID_DET_TIMING_FLAG_VSYNC_POSITIVE 0x04 +#define EDID_DET_TIMING_FLAG_HSYNC_POSITIVE 0x02 +#define EDID_DET_TIMING_FLAG_STEREO_MODE 0x01 /* stereo mode */ + + +/* N.B.: these tests assume that we already checked for detailed timing! */ +#define EDID_BLOCK_TYPE(ptr) ((ptr)[3]) + +#define EDID_DESC_BLOCK_SIZE 18 +#define EDID_DESC_BLOCK_TYPE_SERIAL 0xFF +#define EDID_DESC_BLOCK_TYPE_ASCII 0xFE +#define EDID_DESC_BLOCK_TYPE_RANGE 0xFD +#define EDID_DESC_BLOCK_TYPE_NAME 0xFC +#define EDID_DESC_BLOCK_TYPE_COLOR_POINT 0xFB +#define EDID_DESC_BLOCK_TYPE_STD_TIMING 0xFA + +/* used for descriptors 0xFF, 0xFE, and 0xFC */ +#define EDID_DESC_ASCII_DATA_OFFSET 5 +#define EDID_DESC_ASCII_DATA_LEN 13 + +#define EDID_DESC_RANGE_MIN_VFREQ(ptr) ((ptr)[5]) /* Hz */ +#define EDID_DESC_RANGE_MAX_VFREQ(ptr) ((ptr)[6]) /* Hz */ +#define EDID_DESC_RANGE_MIN_HFREQ(ptr) ((ptr)[7]) /* kHz */ +#define EDID_DESC_RANGE_MAX_HFREQ(ptr) ((ptr)[8]) /* kHz */ +#define EDID_DESC_RANGE_MAX_CLOCK(ptr) (((ptr)[9]) * 10) /* MHz */ +#define EDID_DESC_RANGE_HAVE_GTF2(ptr) (((ptr)[10]) == 0x02) +#define EDID_DESC_RANGE_GTF2_HFREQ(ptr) (((ptr)[12]) * 2) +#define EDID_DESC_RANGE_GTF2_C(ptr) (((ptr)[13]) / 2) +#define EDID_DESC_RANGE_GTF2_M(ptr) ((ptr)[14] + ((ptr)[15] << 8)) +#define EDID_DESC_RANGE_GTF2_K(ptr) ((ptr)[16]) +#define EDID_DESC_RANGE_GTF2_J(ptr) ((ptr)[17] / 2) + +#define EDID_DESC_COLOR_WHITEX(ptr) +#define EDID_DESC_COLOR_WHITE_INDEX_1(ptr) ((ptr)[5]) +#define EDID_DESC_COLOR_WHITEX_1(ptr) _CHROMA(ptr, 6, 2, 7) +#define EDID_DESC_COLOR_WHITEY_1(ptr) _CHROMA(ptr, 6, 0, 8) +#define EDID_DESC_COLOR_GAMMA_1(ptr) _GAMMA(ptr[9]) +#define EDID_DESC_COLOR_WHITE_INDEX_2(ptr) ((ptr)[10]) +#define EDID_DESC_COLOR_WHITEX_2(ptr) _CHROMA(ptr, 11, 2, 12) +#define EDID_DESC_COLOR_WHITEY_2(ptr) _CHROMA(ptr, 11, 0, 13) +#define EDID_DESC_COLOR_GAMMA_2(ptr) _GAMMA(ptr[14]) + +#define EDID_DESC_STD_TIMING_START 5 +#define EDID_DESC_STD_TIMING_COUNT 6 + +#define EDID_EXT_BLOCK_COUNT(ptr) ((ptr)[126]) + +#endif /* _DEV_VIDEOMODE_EDIDREG_H */ Property changes on: sys/dev/hdmi/edidreg.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/hdmi/edidvar.h =================================================================== --- sys/dev/hdmi/edidvar.h (revision 0) +++ sys/dev/hdmi/edidvar.h (working copy) @@ -0,0 +1,95 @@ +/* $NetBSD: edidvar.h,v 1.3 2014/11/17 00:46:04 jmcneill Exp $ */ + +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Written by Garrett D'Amore for Itronix Inc. + * + * 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. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. + */ + +#ifndef _DEV_VIDEOMODE_EDIDVAR_H +#define _DEV_VIDEOMODE_EDIDVAR_H + +struct edid_chroma { + uint16_t ec_redx; + uint16_t ec_redy; + uint16_t ec_greenx; + uint16_t ec_greeny; + uint16_t ec_bluex; + uint16_t ec_bluey; + uint16_t ec_whitex; + uint16_t ec_whitey; +}; + +struct edid_range { + uint16_t er_min_vfreq; /* Hz */ + uint16_t er_max_vfreq; /* Hz */ + uint16_t er_min_hfreq; /* kHz */ + uint16_t er_max_hfreq; /* kHz */ + uint16_t er_max_clock; /* MHz */ + int er_have_gtf2; + uint16_t er_gtf2_hfreq; + uint16_t er_gtf2_c; + uint16_t er_gtf2_m; + uint16_t er_gtf2_k; + uint16_t er_gtf2_j; +}; + +struct edid_info { + uint8_t edid_vendor[4]; + char edid_vendorname[16]; + char edid_productname[16]; + char edid_comment[16]; + char edid_serial[16]; + uint16_t edid_product; + uint8_t edid_version; + uint8_t edid_revision; + int edid_year; + int edid_week; + uint8_t edid_video_input; /* see edidregs.h */ + uint8_t edid_max_hsize; /* in cm */ + uint8_t edid_max_vsize; /* in cm */ + uint8_t edid_gamma; + uint8_t edid_features; + uint8_t edid_ext_block_count; + + int edid_have_range; + struct edid_range edid_range; + + struct edid_chroma edid_chroma; + + /* parsed modes */ + struct videomode *edid_preferred_mode; + int edid_nmodes; + struct videomode edid_modes[64]; +}; + +int edid_is_valid(uint8_t *); +int edid_parse(uint8_t *, struct edid_info *); +void edid_print(struct edid_info *); + +#endif /* _DEV_VIDEOMODE_EDIDVAR_H */ Property changes on: sys/dev/hdmi/edidvar.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/hdmi/hdmi.h =================================================================== --- sys/dev/hdmi/hdmi.h (revision 0) +++ sys/dev/hdmi/hdmi.h (working copy) @@ -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 + +typedef void (*hdmi_event_hook)(void *, int); +EVENTHANDLER_DECLARE(hdmi_event, hdmi_event_hook); + +#endif /* !_HDMI_H_ */ + Property changes on: sys/dev/hdmi/hdmi.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/hdmi/hdmi_if.m =================================================================== --- sys/dev/hdmi/hdmi_if.m (revision 0) +++ sys/dev/hdmi/hdmi_if.m (working copy) @@ -0,0 +1,50 @@ +#- +# Copyright (c) 2009 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: head/sys/dev/gpio/gpio_if.m 276168 2014-12-24 03:24:50Z loos $ +# + +#include +#include +#include + +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; +}; Index: sys/dev/hdmi/pickmode.c =================================================================== --- sys/dev/hdmi/pickmode.c (revision 0) +++ sys/dev/hdmi/pickmode.c (working copy) @@ -0,0 +1,203 @@ +/* $NetBSD: pickmode.c,v 1.4 2011/04/09 20:53:39 christos Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation + * All rights reserved. + * + * this code was contributed to The NetBSD Foundation by Michael Lorenz + * + * 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 ``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 NETBSD FOUNDATION 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. + */ + +#include + +#include +#include +#include +#include + +#ifdef PICKMODE_DEBUG +#define DPRINTF printf +#else +#define DPRINTF while (0) printf +#endif + +const struct videomode * +pick_mode_by_dotclock(int width, int height, int dotclock) +{ + const struct videomode *this, *best = NULL; + int i; + + DPRINTF("%s: looking for %d x %d at up to %d kHz\n", __func__, width, + height, dotclock); + for (i = 0; i < videomode_count; i++) { + this = &videomode_list[i]; + if ((this->hdisplay != width) || (this->vdisplay != height) || + (this->dot_clock > dotclock)) + continue; + if (best != NULL) { + if (this->dot_clock > best->dot_clock) + best = this; + } else + best = this; + } + if (best != NULL) + DPRINTF("found %s\n", best->name); + + return best; +} + +const struct videomode * +pick_mode_by_ref(int width, int height, int refresh) +{ + const struct videomode *this, *best = NULL; + int mref, closest = 1000, i, diff; + + DPRINTF("%s: looking for %d x %d at up to %d Hz\n", __func__, width, + height, refresh); + for (i = 0; i < videomode_count; i++) { + + this = &videomode_list[i]; + mref = this->dot_clock * 1000 / (this->htotal * this->vtotal); + diff = abs(mref - refresh); + if ((this->hdisplay != width) || (this->vdisplay != height)) + continue; + DPRINTF("%s in %d hz, diff %d\n", this->name, mref, diff); + if (best != NULL) { + if (diff < closest) { + best = this; + closest = diff; + } + } else { + best = this; + closest = diff; + } + } + if (best != NULL) + DPRINTF("found %s %d\n", best->name, best->dot_clock); + + return best; +} + +static inline void +swap_modes(struct videomode *left, struct videomode *right) +{ + struct videomode temp; + + temp = *left; + *left = *right; + *right = temp; +} + +/* + * Sort modes by refresh rate, aspect ratio (*), then resolution. + * Preferred mode or largest mode is first in the list and other modes + * are sorted on closest match to that mode. + * (*) Note that the aspect ratio calculation treats "close" aspect ratios + * (within 12.5%) as the same for this purpose. + */ +#define DIVIDE(x, y) (((x) + ((y) / 2)) / (y)) +void +sort_modes(struct videomode *modes, struct videomode **preferred, int nmodes) +{ + int aspect, refresh, hbest, vbest, abest, atemp, rbest, rtemp; + int i, j; + struct videomode *mtemp = NULL; + + if (nmodes < 2) + return; + + if (*preferred != NULL) { + /* Put the preferred mode first in the list */ + aspect = (*preferred)->hdisplay * 100 / (*preferred)->vdisplay; + refresh = DIVIDE(DIVIDE((*preferred)->dot_clock * 1000, + (*preferred)->htotal), (*preferred)->vtotal); + if (*preferred != modes) { + swap_modes(*preferred, modes); + *preferred = modes; + } + } else { + /* + * Find the largest horizontal and vertical mode and put that + * first in the list. Preferred refresh rate is taken from + * the first mode of this size. + */ + hbest = 0; + vbest = 0; + for (i = 0; i < nmodes; i++) { + if (modes[i].hdisplay > hbest) { + hbest = modes[i].hdisplay; + vbest = modes[i].vdisplay; + mtemp = &modes[i]; + } else if (modes[i].hdisplay == hbest && + modes[i].vdisplay > vbest) { + vbest = modes[i].vdisplay; + mtemp = &modes[i]; + } + } + aspect = mtemp->hdisplay * 100 / mtemp->vdisplay; + refresh = DIVIDE(DIVIDE(mtemp->dot_clock * 1000, + mtemp->htotal), mtemp->vtotal); + if (mtemp != modes) + swap_modes(mtemp, modes); + } + + /* Sort other modes by refresh rate, aspect ratio, then resolution */ + for (j = 1; j < nmodes - 1; j++) { + rbest = 1000; + abest = 1000; + hbest = 0; + vbest = 0; + for (i = j; i < nmodes; i++) { + rtemp = abs(refresh - + DIVIDE(DIVIDE(modes[i].dot_clock * 1000, + modes[i].htotal), modes[i].vtotal)); + atemp = (modes[i].hdisplay * 100 / modes[i].vdisplay); + if (rtemp < rbest) { + rbest = rtemp; + mtemp = &modes[i]; + } + if (rtemp == rbest) { + /* Treat "close" aspect ratios as identical */ + if (abs(abest - atemp) > (abest / 8) && + abs(aspect - atemp) < abs(aspect - abest)) { + abest = atemp; + mtemp = &modes[i]; + } + if (atemp == abest || + abs(abest - atemp) <= (abest / 8)) { + if (modes[i].hdisplay > hbest) { + hbest = modes[i].hdisplay; + mtemp = &modes[i]; + } + if (modes[i].hdisplay == hbest && + modes[i].vdisplay > vbest) { + vbest = modes[i].vdisplay; + mtemp = &modes[i]; + } + } + } + } + if (mtemp != &modes[j]) + swap_modes(mtemp, &modes[j]); + } +} Property changes on: sys/dev/hdmi/pickmode.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/hdmi/vesagtf.c =================================================================== --- sys/dev/hdmi/vesagtf.c (revision 0) +++ sys/dev/hdmi/vesagtf.c (working copy) @@ -0,0 +1,641 @@ +/* $NetBSD: vesagtf.c,v 1.3 2014/03/21 22:00:00 dholland Exp $ */ + +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Written by Garrett D'Amore for Itronix Inc. + * + * 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. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. + */ + +/* + * This was derived from a userland GTF program supplied by NVIDIA. + * NVIDIA's original boilerplate follows. + * + * Note that I have heavily modified the program for use in the EDID + * kernel code for NetBSD, including removing the use of floating + * point operations and making significant adjustments to minimize + * error propagation while operating with integer only math. + * + * This has required the use of 64-bit integers in a few places, but + * the upshot is that for a calculation of 1920x1200x85 (as an + * example), the error deviates by only ~.004% relative to the + * floating point version. This error is *well* within VESA + * tolerances. + */ + +/* + * Copyright (c) 2001, Andy Ritger aritger@nvidia.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * o Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * o 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. + * o Neither the name of NVIDIA nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 REGENTS 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. + * + * + * + * This program is based on the Generalized Timing Formula(GTF TM) + * Standard Version: 1.0, Revision: 1.0 + * + * The GTF Document contains the following Copyright information: + * + * Copyright (c) 1994, 1995, 1996 - Video Electronics Standards + * Association. Duplication of this document within VESA member + * companies for review purposes is permitted. All other rights + * reserved. + * + * While every precaution has been taken in the preparation + * of this standard, the Video Electronics Standards Association and + * its contributors assume no responsibility for errors or omissions, + * and make no warranties, expressed or implied, of functionality + * of suitability for any purpose. The sample code contained within + * this standard may be used without restriction. + * + * + * + * The GTF EXCEL(TM) SPREADSHEET, a sample (and the definitive) + * implementation of the GTF Timing Standard, is available at: + * + * ftp://ftp.vesa.org/pub/GTF/GTF_V1R1.xls + * + * + * + * This program takes a desired resolution and vertical refresh rate, + * and computes mode timings according to the GTF Timing Standard. + * These mode timings can then be formatted as an XFree86 modeline + * or a mode description for use by fbset(8). + * + * + * + * NOTES: + * + * The GTF allows for computation of "margins" (the visible border + * surrounding the addressable video); on most non-overscan type + * systems, the margin period is zero. I've implemented the margin + * computations but not enabled it because 1) I don't really have + * any experience with this, and 2) neither XFree86 modelines nor + * fbset fb.modes provide an obvious way for margin timings to be + * included in their mode descriptions (needs more investigation). + * + * The GTF provides for computation of interlaced mode timings; + * I've implemented the computations but not enabled them, yet. + * I should probably enable and test this at some point. + * + * + * + * TODO: + * + * o Add support for interlaced modes. + * + * o Implement the other portions of the GTF: compute mode timings + * given either the desired pixel clock or the desired horizontal + * frequency. + * + * o It would be nice if this were more general purpose to do things + * outside the scope of the GTF: like generate double scan mode + * timings, for example. + * + * o Printing digits to the right of the decimal point when the + * digits are 0 annoys me. + * + * o Error checking. + * + */ + + +#include + +#include +#include +#include +#include +#include + +#define CELL_GRAN 8 /* assumed character cell granularity */ + +/* C' and M' are part of the Blanking Duty Cycle computation */ +/* + * #define C_PRIME (((C - J) * K/256.0) + J) + * #define M_PRIME (K/256.0 * M) + */ + +/* + * C' and M' multiplied by 256 to give integer math. Make sure to + * scale results using these back down, appropriately. + */ +#define C_PRIME256(p) (((p->C - p->J) * p->K) + (p->J * 256)) +#define M_PRIME256(p) (p->K * p->M) + +#define DIVIDE(x,y) (((x) + ((y) / 2)) / (y)) + +/* + * print_value() - print the result of the named computation; this is + * useful when comparing against the GTF EXCEL spreadsheet. + */ + +#ifdef GTFDEBUG + +static void +print_value(int n, const char *name, unsigned val) +{ + printf("%2d: %-27s: %u\n", n, name, val); +} +#else +#define print_value(n, name, val) +#endif + + +/* + * vert_refresh() - as defined by the GTF Timing Standard, compute the + * Stage 1 Parameters using the vertical refresh frequency. In other + * words: input a desired resolution and desired refresh rate, and + * output the GTF mode timings. + * + * XXX All the code is in place to compute interlaced modes, but I don't + * feel like testing it right now. + * + * XXX margin computations are implemented but not tested (nor used by + * XFree86 of fbset mode descriptions, from what I can tell). + */ + +void +vesagtf_mode_params(unsigned h_pixels, unsigned v_lines, unsigned freq, + struct vesagtf_params *params, int flags, struct videomode *vmp) +{ + unsigned v_field_rqd; + unsigned top_margin; + unsigned bottom_margin; + unsigned interlace; + uint64_t h_period_est; + unsigned vsync_plus_bp; + unsigned v_back_porch __unused; + unsigned total_v_lines; + uint64_t v_field_est; + uint64_t h_period; + unsigned v_field_rate; + unsigned v_frame_rate __unused; + unsigned left_margin; + unsigned right_margin; + unsigned total_active_pixels; + uint64_t ideal_duty_cycle; + unsigned h_blank; + unsigned total_pixels; + unsigned pixel_freq; + + unsigned h_sync; + unsigned h_front_porch; + unsigned v_odd_front_porch_lines; + +#ifdef GTFDEBUG + unsigned h_freq; +#endif + + /* 1. In order to give correct results, the number of horizontal + * pixels requested is first processed to ensure that it is divisible + * by the character size, by rounding it to the nearest character + * cell boundary: + * + * [H PIXELS RND] = ((ROUND([H PIXELS]/[CELL GRAN RND],0))*[CELLGRAN RND]) + */ + + h_pixels = DIVIDE(h_pixels, CELL_GRAN) * CELL_GRAN; + + print_value(1, "[H PIXELS RND]", h_pixels); + + + /* 2. If interlace is requested, the number of vertical lines assumed + * by the calculation must be halved, as the computation calculates + * the number of vertical lines per field. In either case, the + * number of lines is rounded to the nearest integer. + * + * [V LINES RND] = IF([INT RQD?]="y", ROUND([V LINES]/2,0), + * ROUND([V LINES],0)) + */ + + v_lines = (flags & VESAGTF_FLAG_ILACE) ? DIVIDE(v_lines, 2) : v_lines; + + print_value(2, "[V LINES RND]", v_lines); + + + /* 3. Find the frame rate required: + * + * [V FIELD RATE RQD] = IF([INT RQD?]="y", [I/P FREQ RQD]*2, + * [I/P FREQ RQD]) + */ + + v_field_rqd = (flags & VESAGTF_FLAG_ILACE) ? (freq * 2) : (freq); + + print_value(3, "[V FIELD RATE RQD]", v_field_rqd); + + + /* 4. Find number of lines in Top margin: + * 5. Find number of lines in Bottom margin: + * + * [TOP MARGIN (LINES)] = IF([MARGINS RQD?]="Y", + * ROUND(([MARGIN%]/100*[V LINES RND]),0), + * 0) + * + * Ditto for bottom margin. Note that instead of %, we use PPT, which + * is parts per thousand. This helps us with integer math. + */ + + top_margin = bottom_margin = (flags & VESAGTF_FLAG_MARGINS) ? + DIVIDE(v_lines * params->margin_ppt, 1000) : 0; + + print_value(4, "[TOP MARGIN (LINES)]", top_margin); + print_value(5, "[BOT MARGIN (LINES)]", bottom_margin); + + + /* 6. If interlace is required, then set variable [INTERLACE]=0.5: + * + * [INTERLACE]=(IF([INT RQD?]="y",0.5,0)) + * + * To make this integer friendly, we use some special hacks in step + * 7 below. Please read those comments to understand why I am using + * a whole number of 1.0 instead of 0.5 here. + */ + interlace = (flags & VESAGTF_FLAG_ILACE) ? 1 : 0; + + print_value(6, "[2*INTERLACE]", interlace); + + + /* 7. Estimate the Horizontal period + * + * [H PERIOD EST] = ((1/[V FIELD RATE RQD]) - [MIN VSYNC+BP]/1000000) / + * ([V LINES RND] + (2*[TOP MARGIN (LINES)]) + + * [MIN PORCH RND]+[INTERLACE]) * 1000000 + * + * To make it integer friendly, we pre-multiply the 1000000 to get to + * usec. This gives us: + * + * [H PERIOD EST] = ((1000000/[V FIELD RATE RQD]) - [MIN VSYNC+BP]) / + * ([V LINES RND] + (2 * [TOP MARGIN (LINES)]) + + * [MIN PORCH RND]+[INTERLACE]) + * + * The other problem is that the interlace value is wrong. To get + * the interlace to a whole number, we multiply both the numerator and + * divisor by 2, so we can use a value of either 1 or 0 for the interlace + * factor. + * + * This gives us: + * + * [H PERIOD EST] = ((2*((1000000/[V FIELD RATE RQD]) - [MIN VSYNC+BP])) / + * (2*([V LINES RND] + (2*[TOP MARGIN (LINES)]) + + * [MIN PORCH RND]) + [2*INTERLACE])) + * + * Finally we multiply by another 1000, to get value in picosec. + * Why picosec? To minimize rounding errors. Gotta love integer + * math and error propagation. + */ + + h_period_est = DIVIDE(((DIVIDE(2000000000000ULL, v_field_rqd)) - + (2000000 * params->min_vsbp)), + ((2 * (v_lines + (2 * top_margin) + params->min_porch)) + interlace)); + + print_value(7, "[H PERIOD EST (ps)]", h_period_est); + + + /* 8. Find the number of lines in V sync + back porch: + * + * [V SYNC+BP] = ROUND(([MIN VSYNC+BP]/[H PERIOD EST]),0) + * + * But recall that h_period_est is in psec. So multiply by 1000000. + */ + + vsync_plus_bp = DIVIDE(params->min_vsbp * 1000000, h_period_est); + + print_value(8, "[V SYNC+BP]", vsync_plus_bp); + + + /* 9. Find the number of lines in V back porch alone: + * + * [V BACK PORCH] = [V SYNC+BP] - [V SYNC RND] + * + * XXX is "[V SYNC RND]" a typo? should be [V SYNC RQD]? + */ + + v_back_porch = vsync_plus_bp - params->vsync_rqd; + + print_value(9, "[V BACK PORCH]", v_back_porch); + + + /* 10. Find the total number of lines in Vertical field period: + * + * [TOTAL V LINES] = [V LINES RND] + [TOP MARGIN (LINES)] + + * [BOT MARGIN (LINES)] + [V SYNC+BP] + [INTERLACE] + + * [MIN PORCH RND] + */ + + total_v_lines = v_lines + top_margin + bottom_margin + vsync_plus_bp + + interlace + params->min_porch; + + print_value(10, "[TOTAL V LINES]", total_v_lines); + + + /* 11. Estimate the Vertical field frequency: + * + * [V FIELD RATE EST] = 1 / [H PERIOD EST] / [TOTAL V LINES] * 1000000 + * + * Again, we want to pre multiply by 10^9 to convert for nsec, thereby + * making it usable in integer math. + * + * So we get: + * + * [V FIELD RATE EST] = 1000000000 / [H PERIOD EST] / [TOTAL V LINES] + * + * This is all scaled to get the result in uHz. Again, we're trying to + * minimize error propagation. + */ + v_field_est = DIVIDE(DIVIDE(1000000000000000ULL, h_period_est), + total_v_lines); + + print_value(11, "[V FIELD RATE EST(uHz)]", v_field_est); + + + /* 12. Find the actual horizontal period: + * + * [H PERIOD] = [H PERIOD EST] / ([V FIELD RATE RQD] / [V FIELD RATE EST]) + */ + + h_period = DIVIDE(h_period_est * v_field_est, v_field_rqd * 1000); + + print_value(12, "[H PERIOD(ps)]", h_period); + + + /* 13. Find the actual Vertical field frequency: + * + * [V FIELD RATE] = 1 / [H PERIOD] / [TOTAL V LINES] * 1000000 + * + * And again, we convert to nsec ahead of time, giving us: + * + * [V FIELD RATE] = 1000000 / [H PERIOD] / [TOTAL V LINES] + * + * And another rescaling back to mHz. Gotta love it. + */ + + v_field_rate = DIVIDE(1000000000000ULL, h_period * total_v_lines); + + print_value(13, "[V FIELD RATE]", v_field_rate); + + + /* 14. Find the Vertical frame frequency: + * + * [V FRAME RATE] = (IF([INT RQD?]="y", [V FIELD RATE]/2, [V FIELD RATE])) + * + * N.B. that the result here is in mHz. + */ + + v_frame_rate = (flags & VESAGTF_FLAG_ILACE) ? + v_field_rate / 2 : v_field_rate; + + print_value(14, "[V FRAME RATE]", v_frame_rate); + + + /* 15. Find number of pixels in left margin: + * 16. Find number of pixels in right margin: + * + * [LEFT MARGIN (PIXELS)] = (IF( [MARGINS RQD?]="Y", + * (ROUND( ([H PIXELS RND] * [MARGIN%] / 100 / + * [CELL GRAN RND]),0)) * [CELL GRAN RND], + * 0)) + * + * Again, we deal with margin percentages as PPT (parts per thousand). + * And the calculations for left and right are the same. + */ + + left_margin = right_margin = (flags & VESAGTF_FLAG_MARGINS) ? + DIVIDE(DIVIDE(h_pixels * params->margin_ppt, 1000), + CELL_GRAN) * CELL_GRAN : 0; + + print_value(15, "[LEFT MARGIN (PIXELS)]", left_margin); + print_value(16, "[RIGHT MARGIN (PIXELS)]", right_margin); + + + /* 17. Find total number of active pixels in image and left and right + * margins: + * + * [TOTAL ACTIVE PIXELS] = [H PIXELS RND] + [LEFT MARGIN (PIXELS)] + + * [RIGHT MARGIN (PIXELS)] + */ + + total_active_pixels = h_pixels + left_margin + right_margin; + + print_value(17, "[TOTAL ACTIVE PIXELS]", total_active_pixels); + + + /* 18. Find the ideal blanking duty cycle from the blanking duty cycle + * equation: + * + * [IDEAL DUTY CYCLE] = [C'] - ([M']*[H PERIOD]/1000) + * + * However, we have modified values for [C'] as [256*C'] and + * [M'] as [256*M']. Again the idea here is to get good scaling. + * We use 256 as the factor to make the math fast. + * + * Note that this means that we have to scale it appropriately in + * later calculations. + * + * The ending result is that our ideal_duty_cycle is 256000x larger + * than the duty cycle used by VESA. But again, this reduces error + * propagation. + */ + + ideal_duty_cycle = + ((C_PRIME256(params) * 1000) - + (M_PRIME256(params) * h_period / 1000000)); + + print_value(18, "[IDEAL DUTY CYCLE]", ideal_duty_cycle); + + + /* 19. Find the number of pixels in the blanking time to the nearest + * double character cell: + * + * [H BLANK (PIXELS)] = (ROUND(([TOTAL ACTIVE PIXELS] * + * [IDEAL DUTY CYCLE] / + * (100-[IDEAL DUTY CYCLE]) / + * (2*[CELL GRAN RND])), 0)) + * * (2*[CELL GRAN RND]) + * + * Of course, we adjust to make this rounding work in integer math. + */ + + h_blank = DIVIDE(DIVIDE(total_active_pixels * ideal_duty_cycle, + (256000 * 100ULL) - ideal_duty_cycle), + 2 * CELL_GRAN) * (2 * CELL_GRAN); + + print_value(19, "[H BLANK (PIXELS)]", h_blank); + + + /* 20. Find total number of pixels: + * + * [TOTAL PIXELS] = [TOTAL ACTIVE PIXELS] + [H BLANK (PIXELS)] + */ + + total_pixels = total_active_pixels + h_blank; + + print_value(20, "[TOTAL PIXELS]", total_pixels); + + + /* 21. Find pixel clock frequency: + * + * [PIXEL FREQ] = [TOTAL PIXELS] / [H PERIOD] + * + * We calculate this in Hz rather than MHz, to get a value that + * is usable with integer math. Recall that the [H PERIOD] is in + * nsec. + */ + + pixel_freq = DIVIDE(total_pixels * 1000000, DIVIDE(h_period, 1000)); + + print_value(21, "[PIXEL FREQ]", pixel_freq); + + + /* 22. Find horizontal frequency: + * + * [H FREQ] = 1000 / [H PERIOD] + * + * I've ifdef'd this out, because we don't need it for any of + * our calculations. + * We calculate this in Hz rather than kHz, to avoid rounding + * errors. Recall that the [H PERIOD] is in usec. + */ + +#ifdef GTFDEBUG + h_freq = 1000000000 / h_period; + + print_value(22, "[H FREQ]", h_freq); +#endif + + + + /* Stage 1 computations are now complete; I should really pass + the results to another function and do the Stage 2 + computations, but I only need a few more values so I'll just + append the computations here for now */ + + + + /* 17. Find the number of pixels in the horizontal sync period: + * + * [H SYNC (PIXELS)] =(ROUND(([H SYNC%] / 100 * [TOTAL PIXELS] / + * [CELL GRAN RND]),0))*[CELL GRAN RND] + * + * Rewriting for integer math: + * + * [H SYNC (PIXELS)]=(ROUND((H SYNC%] * [TOTAL PIXELS] / 100 / + * [CELL GRAN RND),0))*[CELL GRAN RND] + */ + + h_sync = DIVIDE(((params->hsync_pct * total_pixels) / 100), CELL_GRAN) * + CELL_GRAN; + + print_value(17, "[H SYNC (PIXELS)]", h_sync); + + + /* 18. Find the number of pixels in the horizontal front porch period: + * + * [H FRONT PORCH (PIXELS)] = ([H BLANK (PIXELS)]/2)-[H SYNC (PIXELS)] + * + * Note that h_blank is always an even number of characters (i.e. + * h_blank % (CELL_GRAN * 2) == 0) + */ + + h_front_porch = (h_blank / 2) - h_sync; + + print_value(18, "[H FRONT PORCH (PIXELS)]", h_front_porch); + + + /* 36. Find the number of lines in the odd front porch period: + * + * [V ODD FRONT PORCH(LINES)]=([MIN PORCH RND]+[INTERLACE]) + * + * Adjusting for the fact that the interlace is scaled: + * + * [V ODD FRONT PORCH(LINES)]=(([MIN PORCH RND] * 2) + [2*INTERLACE]) / 2 + */ + + v_odd_front_porch_lines = ((2 * params->min_porch) + interlace) / 2; + + print_value(36, "[V ODD FRONT PORCH(LINES)]", v_odd_front_porch_lines); + + + /* finally, pack the results in the mode struct */ + + vmp->hsync_start = h_pixels + h_front_porch; + vmp->hsync_end = vmp->hsync_start + h_sync; + vmp->htotal = total_pixels; + vmp->hdisplay = h_pixels; + + vmp->vsync_start = v_lines + v_odd_front_porch_lines; + vmp->vsync_end = vmp->vsync_start + params->vsync_rqd; + vmp->vtotal = total_v_lines; + vmp->vdisplay = v_lines; + + vmp->dot_clock = pixel_freq; + +} + +void +vesagtf_mode(unsigned x, unsigned y, unsigned refresh, struct videomode *vmp) +{ + struct vesagtf_params params; + + params.margin_ppt = VESAGTF_MARGIN_PPT; + params.min_porch = VESAGTF_MIN_PORCH; + params.vsync_rqd = VESAGTF_VSYNC_RQD; + params.hsync_pct = VESAGTF_HSYNC_PCT; + params.min_vsbp = VESAGTF_MIN_VSBP; + params.M = VESAGTF_M; + params.C = VESAGTF_C; + params.K = VESAGTF_K; + params.J = VESAGTF_J; + + vesagtf_mode_params(x, y, refresh, ¶ms, 0, vmp); +} Property changes on: sys/dev/hdmi/vesagtf.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/hdmi/vesagtf.h =================================================================== --- sys/dev/hdmi/vesagtf.h (revision 0) +++ sys/dev/hdmi/vesagtf.h (working copy) @@ -0,0 +1,85 @@ +/* $NetBSD: vesagtf.h,v 1.1 2006/05/11 01:49:53 gdamore Exp $ */ + +/*- + * Copyright (c) 2006 Itronix Inc. + * All rights reserved. + * + * Written by Garrett D'Amore for Itronix Inc. + * + * 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. + * 3. The name of Itronix Inc. may not be used to endorse + * or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``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 ITRONIX INC. 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. + */ + +#ifndef _DEV_VIDEOMODE_VESAGTF_H +#define _DEV_VIDEOMODE_VESAGTF_H + +/* + * Use VESA GTF formula to generate a monitor mode, given resolution and + * refresh rates. + */ + +struct vesagtf_params { + unsigned margin_ppt; /* vertical margin size, percent * 10 + * think parts-per-thousand */ + unsigned min_porch; /* minimum front porch */ + unsigned vsync_rqd; /* width of vsync in lines */ + unsigned hsync_pct; /* hsync as % of total width */ + unsigned min_vsbp; /* minimum vsync + back porch (usec) */ + unsigned M; /* blanking formula gradient */ + unsigned C; /* blanking formula offset */ + unsigned K; /* blanking formula scaling factor */ + unsigned J; /* blanking formula scaling factor */ +}; + +/* + * Default values to use for params. + */ +#define VESAGTF_MARGIN_PPT 18 /* 1.8% */ +#define VESAGTF_MIN_PORCH 1 /* minimum front porch */ +#define VESAGTF_VSYNC_RQD 3 /* vsync width in lines */ +#define VESAGTF_HSYNC_PCT 8 /* width of hsync % of total line */ +#define VESAGTF_MIN_VSBP 550 /* min vsync + back porch (usec) */ +#define VESAGTF_M 600 /* blanking formula gradient */ +#define VESAGTF_C 40 /* blanking formula offset */ +#define VESAGTF_K 128 /* blanking formula scaling factor */ +#define VESAGTF_J 20 /* blanking formula scaling factor */ + +/* + * Use VESA GTF formula to generate monitor timings. Assumes default + * GTF parameters, non-interlaced, and no margins. + */ +void vesagtf_mode(unsigned x, unsigned y, unsigned refresh, + struct videomode *); + +/* + * A more complete version, in case we ever want to use alternate GTF + * parameters. EDID 1.3 allows for "secondary GTF parameters". + */ +void vesagtf_mode_params(unsigned x, unsigned y, unsigned refresh, + struct vesagtf_params *, int flags, struct videomode *); + +#define VESAGTF_FLAG_ILACE 0x0001 /* use interlace */ +#define VESAGTF_FLAG_MARGINS 0x0002 /* use margins */ + +#endif /* _DEV_VIDEOMODE_VESAGTF_H */ Property changes on: sys/dev/hdmi/vesagtf.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/hdmi/videomode.c =================================================================== --- sys/dev/hdmi/videomode.c (revision 0) +++ sys/dev/hdmi/videomode.c (working copy) @@ -0,0 +1,125 @@ +/* $NetBSD: videomode.c,v 1.11 2011/03/30 18:46:32 jdc Exp $ */ + +/* + * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT. + * + * generated from: + * NetBSD: modelines,v 1.9 2011/03/30 18:45:04 jdc Exp + */ + +#include + +#include + +/* + * These macros help the modelines below fit on one line. + */ +#define HP VID_PHSYNC +#define HN VID_NHSYNC +#define VP VID_PVSYNC +#define VN VID_NVSYNC +#define I VID_INTERLACE +#define DS VID_DBLSCAN + +#define M(nm,hr,vr,clk,hs,he,ht,vs,ve,vt,f) \ + { clk, hr, hs, he, ht, vr, vs, ve, vt, f, nm } + +const struct videomode videomode_list[] = { +M("640x350x85",640,350,31500,672,736,832,382,385,445,HP|VN), +M("640x400x85",640,400,31500,672,736,832,401,404,445,HN|VP), +M("720x400x70",720,400,28320,738,846,900,412,414,449,HN|VP), +M("720x400x85",720,400,35500,756,828,936,401,404,446,HN|VP), +M("720x400x87",720,400,35500,738,846,900,421,423,449,HN|VN), +M("640x480x60",640,480,25175,656,752,800,490,492,525,HN|VN), +M("640x480x72",640,480,31500,664,704,832,489,492,520,HN|VN), +M("640x480x75",640,480,31500,656,720,840,481,484,500,HN|VN), +M("640x480x85",640,480,36000,696,752,832,481,484,509,HN|VN), +M("800x600x56",800,600,36000,824,896,1024,601,603,625,HP|VP), +M("800x600x60",800,600,40000,840,968,1056,601,605,628,HP|VP), +M("800x600x72",800,600,50000,856,976,1040,637,643,666,HP|VP), +M("800x600x75",800,600,49500,816,896,1056,601,604,625,HP|VP), +M("800x600x85",800,600,56250,832,896,1048,601,604,631,HP|VP), +M("1024x768x87i",1024,768,44900,1032,1208,1264,768,776,817,HP|VP|I), +M("1024x768x60",1024,768,65000,1048,1184,1344,771,777,806,HN|VN), +M("1024x768x70",1024,768,75000,1048,1184,1328,771,777,806,HN|VN), +M("1024x768x75",1024,768,78750,1040,1136,1312,769,772,800,HP|VP), +M("1024x768x85",1024,768,94500,1072,1168,1376,769,772,808,HP|VP), +M("1024x768x89",1024,768,100000,1108,1280,1408,768,780,796,HP|VP), +M("1152x864x75",1152,864,108000,1216,1344,1600,865,868,900,HP|VP), +M("1280x768x75",1280,768,105640,1312,1712,1744,782,792,807,HN|VP), +M("1280x960x60",1280,960,108000,1376,1488,1800,961,964,1000,HP|VP), +M("1280x960x85",1280,960,148500,1344,1504,1728,961,964,1011,HP|VP), +M("1280x1024x60",1280,1024,108000,1328,1440,1688,1025,1028,1066,HP|VP), +M("1280x1024x70",1280,1024,126000,1328,1440,1688,1025,1028,1066,HP|VP), +M("1280x1024x75",1280,1024,135000,1296,1440,1688,1025,1028,1066,HP|VP), +M("1280x1024x85",1280,1024,157500,1344,1504,1728,1025,1028,1072,HP|VP), +M("1600x1200x60",1600,1200,162000,1664,1856,2160,1201,1204,1250,HP|VP), +M("1600x1200x65",1600,1200,175500,1664,1856,2160,1201,1204,1250,HP|VP), +M("1600x1200x70",1600,1200,189000,1664,1856,2160,1201,1204,1250,HP|VP), +M("1600x1200x75",1600,1200,202500,1664,1856,2160,1201,1204,1250,HP|VP), +M("1600x1200x85",1600,1200,229500,1664,1856,2160,1201,1204,1250,HP|VP), +M("1680x1050x60",1680,1050,147140,1784,1968,2256,1051,1054,1087,HP|VP), +M("1792x1344x60",1792,1344,204800,1920,2120,2448,1345,1348,1394,HN|VP), +M("1792x1344x75",1792,1344,261000,1888,2104,2456,1345,1348,1417,HN|VP), +M("1856x1392x60",1856,1392,218300,1952,2176,2528,1393,1396,1439,HN|VP), +M("1856x1392x75",1856,1392,288000,1984,2208,2560,1393,1396,1500,HN|VP), +M("1920x1440x60",1920,1440,234000,2048,2256,2600,1441,1444,1500,HN|VP), +M("1920x1440x75",1920,1440,297000,2064,2288,2640,1441,1444,1500,HN|VP), +M("832x624x74",832,624,57284,864,928,1152,625,628,667,HN|VN), +M("1152x768x54",1152,768,64995,1178,1314,1472,771,777,806,HP|VP), +M("1400x1050x60",1400,1050,122000,1488,1640,1880,1052,1064,1082,HP|VP), +M("1400x1050x74",1400,1050,155800,1464,1784,1912,1052,1064,1090,HP|VP), +M("1152x900x66",1152,900,94500,1192,1320,1528,902,906,937,HN|VN), +M("1152x900x76",1152,900,105560,1168,1280,1472,902,906,943,HN|VN), + +/* Derived Double Scan Modes */ + +M("320x175x85",320,175,15750,336,368,416,191,192,222,HP|VN|DS), +M("320x200x85",320,200,15750,336,368,416,200,202,222,HN|VP|DS), +M("360x200x70",360,200,14160,369,423,450,206,207,224,HN|VP|DS), +M("360x200x85",360,200,17750,378,414,468,200,202,223,HN|VP|DS), +M("360x200x87",360,200,17750,369,423,450,210,211,224,HN|VN|DS), +M("320x240x60",320,240,12587,328,376,400,245,246,262,HN|VN|DS), +M("320x240x72",320,240,15750,332,352,416,244,246,260,HN|VN|DS), +M("320x240x75",320,240,15750,328,360,420,240,242,250,HN|VN|DS), +M("320x240x85",320,240,18000,348,376,416,240,242,254,HN|VN|DS), +M("400x300x56",400,300,18000,412,448,512,300,301,312,HP|VP|DS), +M("400x300x60",400,300,20000,420,484,528,300,302,314,HP|VP|DS), +M("400x300x72",400,300,25000,428,488,520,318,321,333,HP|VP|DS), +M("400x300x75",400,300,24750,408,448,528,300,302,312,HP|VP|DS), +M("400x300x85",400,300,28125,416,448,524,300,302,315,HP|VP|DS), +M("512x384x87i",512,384,22450,516,604,632,384,388,408,HP|VP|DS|I), +M("512x384x60",512,384,32500,524,592,672,385,388,403,HN|VN|DS), +M("512x384x70",512,384,37500,524,592,664,385,388,403,HN|VN|DS), +M("512x384x75",512,384,39375,520,568,656,384,386,400,HP|VP|DS), +M("512x384x85",512,384,47250,536,584,688,384,386,404,HP|VP|DS), +M("512x384x89",512,384,50000,554,640,704,384,390,398,HP|VP|DS), +M("576x432x75",576,432,54000,608,672,800,432,434,450,HP|VP|DS), +M("640x384x75",640,384,52820,656,856,872,391,396,403,HN|VP|DS), +M("640x480x60",640,480,54000,688,744,900,480,482,500,HP|VP|DS), +M("640x480x85",640,480,74250,672,752,864,480,482,505,HP|VP|DS), +M("640x512x60",640,512,54000,664,720,844,512,514,533,HP|VP|DS), +M("640x512x70",640,512,63000,664,720,844,512,514,533,HP|VP|DS), +M("640x512x75",640,512,67500,648,720,844,512,514,533,HP|VP|DS), +M("640x512x85",640,512,78750,672,752,864,512,514,536,HP|VP|DS), +M("800x600x60",800,600,81000,832,928,1080,600,602,625,HP|VP|DS), +M("800x600x65",800,600,87750,832,928,1080,600,602,625,HP|VP|DS), +M("800x600x70",800,600,94500,832,928,1080,600,602,625,HP|VP|DS), +M("800x600x75",800,600,101250,832,928,1080,600,602,625,HP|VP|DS), +M("800x600x85",800,600,114750,832,928,1080,600,602,625,HP|VP|DS), +M("840x525x60",840,525,73570,892,984,1128,525,527,543,HP|VP|DS), +M("896x672x60",896,672,102400,960,1060,1224,672,674,697,HN|VP|DS), +M("896x672x75",896,672,130500,944,1052,1228,672,674,708,HN|VP|DS), +M("928x696x60",928,696,109150,976,1088,1264,696,698,719,HN|VP|DS), +M("928x696x75",928,696,144000,992,1104,1280,696,698,750,HN|VP|DS), +M("960x720x60",960,720,117000,1024,1128,1300,720,722,750,HN|VP|DS), +M("960x720x75",960,720,148500,1032,1144,1320,720,722,750,HN|VP|DS), +M("416x312x74",416,312,28642,432,464,576,312,314,333,HN|VN|DS), +M("576x384x54",576,384,32497,589,657,736,385,388,403,HP|VP|DS), +M("700x525x60",700,525,61000,744,820,940,526,532,541,HP|VP|DS), +M("700x525x74",700,525,77900,732,892,956,526,532,545,HP|VP|DS), +M("576x450x66",576,450,47250,596,660,764,451,453,468,HN|VN|DS), +M("576x450x76",576,450,52780,584,640,736,451,453,471,HN|VN|DS), +}; + +const int videomode_count = 46; Property changes on: sys/dev/hdmi/videomode.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/hdmi/videomode.h =================================================================== --- sys/dev/hdmi/videomode.h (revision 0) +++ sys/dev/hdmi/videomode.h (working copy) @@ -0,0 +1,74 @@ +/* $NetBSD: videomode.h,v 1.3 2011/04/09 18:22:31 jdc Exp $ */ + +/* + * Copyright (c) 2001, 2002 Bang Jun-Young + * 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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. + */ + +#ifndef _DEV_VIDEOMODE_H +#define _DEV_VIDEOMODE_H + +struct videomode { + int dot_clock; /* Dot clock frequency in kHz. */ + int hdisplay; + int hsync_start; + int hsync_end; + int htotal; + int vdisplay; + int vsync_start; + int vsync_end; + int vtotal; + int flags; /* Video mode flags; see below. */ + const char *name; + int hskew; +}; + +/* + * Video mode flags. + */ + +#define VID_PHSYNC 0x0001 +#define VID_NHSYNC 0x0002 +#define VID_PVSYNC 0x0004 +#define VID_NVSYNC 0x0008 +#define VID_INTERLACE 0x0010 +#define VID_DBLSCAN 0x0020 +#define VID_CSYNC 0x0040 +#define VID_PCSYNC 0x0080 +#define VID_NCSYNC 0x0100 +#define VID_HSKEW 0x0200 +#define VID_BCAST 0x0400 +#define VID_PIXMUX 0x1000 +#define VID_DBLCLK 0x2000 +#define VID_CLKDIV2 0x4000 + +extern const struct videomode videomode_list[]; +extern const int videomode_count; + +const struct videomode *pick_mode_by_dotclock(int, int, int); +const struct videomode *pick_mode_by_ref(int, int, int); +void sort_modes(struct videomode *, struct videomode **, int); + +#endif /* _DEV_VIDEOMODE_H */ Property changes on: sys/dev/hdmi/videomode.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: sys/dev/iicbus/tda19988.c =================================================================== --- sys/dev/iicbus/tda19988.c (revision 0) +++ sys/dev/iicbus/tda19988.c (working copy) @@ -0,0 +1,1016 @@ +/*- + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); +/* +* NXP TDA19988 HDMI encoder +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "iicbus_if.h" +#include "hdmi_if.h" + +#define MKREG(page, addr) (((page) << 8) | (addr)) + +#define REGPAGE(reg) (((reg) >> 8) & 0xff) +#define REGADDR(reg) ((reg) & 0xff) + +#define TDA_VERSION MKREG(0x00, 0x00) +#define TDA_MAIN_CNTRL0 MKREG(0x00, 0x01) +#define MAIN_CNTRL0_SR (1 << 0) +#define TDA_VERSION_MSB MKREG(0x00, 0x02) +#define TDA_SOFTRESET MKREG(0x00, 0x0a) +#define SOFTRESET_I2C (1 << 1) +#define SOFTRESET_AUDIO (1 << 0) +#define TDA_DDC_CTRL MKREG(0x00, 0x0b) +#define DDC_ENABLE 0 +#define TDA_CCLK MKREG(0x00, 0x0c) +#define CCLK_ENABLE 1 +#define TDA_INT_FLAGS_2 MKREG(0x00, 0x11) +#define INT_FLAGS_2_EDID_BLK_RD (1 << 1) + +#define TDA_VIP_CNTRL_0 MKREG(0x00, 0x20) +#define TDA_VIP_CNTRL_1 MKREG(0x00, 0x21) +#define TDA_VIP_CNTRL_2 MKREG(0x00, 0x22) +#define TDA_VIP_CNTRL_3 MKREG(0x00, 0x23) +#define VIP_CNTRL_3_SYNC_HS (2 << 4) +#define VIP_CNTRL_3_V_TGL (1 << 2) +#define VIP_CNTRL_3_H_TGL (1 << 1) + +#define TDA_VIP_CNTRL_4 MKREG(0x00, 0x24) +#define VIP_CNTRL_4_BLANKIT_NDE (0 << 2) +#define VIP_CNTRL_4_BLANKIT_HS_VS (1 << 2) +#define VIP_CNTRL_4_BLANKIT_NHS_VS (2 << 2) +#define VIP_CNTRL_4_BLANKIT_HE_VE (3 << 2) +#define VIP_CNTRL_4_BLC_NONE (0 << 0) +#define VIP_CNTRL_4_BLC_RGB444 (1 << 0) +#define VIP_CNTRL_4_BLC_YUV444 (2 << 0) +#define VIP_CNTRL_4_BLC_YUV422 (3 << 0) +#define TDA_VIP_CNTRL_5 MKREG(0x00, 0x25) +#define VIP_CNTRL_5_SP_CNT(n) (((n) & 3) << 1) +#define TDA_MUX_VP_VIP_OUT MKREG(0x00, 0x27) +#define TDA_MAT_CONTRL MKREG(0x00, 0x80) +#define MAT_CONTRL_MAT_BP (1 << 2) +#define TDA_VIDFORMAT MKREG(0x00, 0xa0) +#define TDA_REFPIX_MSB MKREG(0x00, 0xa1) +#define TDA_REFPIX_LSB MKREG(0x00, 0xa2) +#define TDA_REFLINE_MSB MKREG(0x00, 0xa3) +#define TDA_REFLINE_LSB MKREG(0x00, 0xa4) +#define TDA_NPIX_MSB MKREG(0x00, 0xa5) +#define TDA_NPIX_LSB MKREG(0x00, 0xa6) +#define TDA_NLINE_MSB MKREG(0x00, 0xa7) +#define TDA_NLINE_LSB MKREG(0x00, 0xa8) +#define TDA_VS_LINE_STRT_1_MSB MKREG(0x00, 0xa9) +#define TDA_VS_LINE_STRT_1_LSB MKREG(0x00, 0xaa) +#define TDA_VS_PIX_STRT_1_MSB MKREG(0x00, 0xab) +#define TDA_VS_PIX_STRT_1_LSB MKREG(0x00, 0xac) +#define TDA_VS_LINE_END_1_MSB MKREG(0x00, 0xad) +#define TDA_VS_LINE_END_1_LSB MKREG(0x00, 0xae) +#define TDA_VS_PIX_END_1_MSB MKREG(0x00, 0xaf) +#define TDA_VS_PIX_END_1_LSB MKREG(0x00, 0xb0) +#define TDA_VS_LINE_STRT_2_MSB MKREG(0x00, 0xb1) +#define TDA_VS_LINE_STRT_2_LSB MKREG(0x00, 0xb2) +#define TDA_VS_PIX_STRT_2_MSB MKREG(0x00, 0xb3) +#define TDA_VS_PIX_STRT_2_LSB MKREG(0x00, 0xb4) +#define TDA_VS_LINE_END_2_MSB MKREG(0x00, 0xb5) +#define TDA_VS_LINE_END_2_LSB MKREG(0x00, 0xb6) +#define TDA_VS_PIX_END_2_MSB MKREG(0x00, 0xb7) +#define TDA_VS_PIX_END_2_LSB MKREG(0x00, 0xb8) +#define TDA_HS_PIX_START_MSB MKREG(0x00, 0xb9) +#define TDA_HS_PIX_START_LSB MKREG(0x00, 0xba) +#define TDA_HS_PIX_STOP_MSB MKREG(0x00, 0xbb) +#define TDA_HS_PIX_STOP_LSB MKREG(0x00, 0xbc) +#define TDA_VWIN_START_1_MSB MKREG(0x00, 0xbd) +#define TDA_VWIN_START_1_LSB MKREG(0x00, 0xbe) +#define TDA_VWIN_END_1_MSB MKREG(0x00, 0xbf) +#define TDA_VWIN_END_1_LSB MKREG(0x00, 0xc0) +#define TDA_VWIN_START_2_MSB MKREG(0x00, 0xc1) +#define TDA_VWIN_START_2_LSB MKREG(0x00, 0xc2) +#define TDA_VWIN_END_2_MSB MKREG(0x00, 0xc3) +#define TDA_VWIN_END_2_LSB MKREG(0x00, 0xc4) +#define TDA_DE_START_MSB MKREG(0x00, 0xc5) +#define TDA_DE_START_LSB MKREG(0x00, 0xc6) +#define TDA_DE_STOP_MSB MKREG(0x00, 0xc7) +#define TDA_DE_STOP_LSB MKREG(0x00, 0xc8) + +#define TDA_TBG_CNTRL_0 MKREG(0x00, 0xca) +#define TBG_CNTRL_0_SYNC_ONCE (1 << 7) +#define TBG_CNTRL_0_SYNC_MTHD (1 << 6) + +#define TDA_TBG_CNTRL_1 MKREG(0x00, 0xcb) +#define TBG_CNTRL_1_DWIN_DIS (1 << 6) +#define TBG_CNTRL_1_TGL_EN (1 << 2) +#define TBG_CNTRL_1_V_TGL (1 << 1) +#define TBG_CNTRL_1_H_TGL (1 << 0) + +#define TDA_HVF_CNTRL_0 MKREG(0x00, 0xe4) +#define HVF_CNTRL_0_PREFIL_NONE (0 << 2) +#define HVF_CNTRL_0_INTPOL_BYPASS (0 << 0) +#define TDA_HVF_CNTRL_1 MKREG(0x00, 0xe5) +#define HVF_CNTRL_1_VQR(x) (((x) & 3) << 2) +#define HVF_CNTRL_1_VQR_FULL HVF_CNTRL_1_VQR(0) +#define TDA_ENABLE_SPACE MKREG(0x00, 0xd6) +#define TDA_RPT_CNTRL MKREG(0x00, 0xf0) + +#define TDA_PLL_SERIAL_1 MKREG(0x02, 0x00) +#define PLL_SERIAL_1_SRL_MAN_IP (1 << 6) +#define TDA_PLL_SERIAL_2 MKREG(0x02, 0x01) +#define PLL_SERIAL_2_SRL_PR(x) (((x) & 0xf) << 4) +#define PLL_SERIAL_2_SRL_NOSC(x) (((x) & 0x3) << 0) +#define TDA_PLL_SERIAL_3 MKREG(0x02, 0x02) +#define PLL_SERIAL_3_SRL_PXIN_SEL (1 << 4) +#define PLL_SERIAL_3_SRL_DE (1 << 2) +#define PLL_SERIAL_3_SRL_CCIR (1 << 0) +#define TDA_SERIALIZER MKREG(0x02, 0x03) +#define TDA_BUFFER_OUT MKREG(0x02, 0x04) +#define TDA_PLL_SCG1 MKREG(0x02, 0x05) +#define TDA_PLL_SCG2 MKREG(0x02, 0x06) +#define TDA_PLL_SCGN1 MKREG(0x02, 0x07) +#define TDA_PLL_SCGN2 MKREG(0x02, 0x08) +#define TDA_PLL_SCGR1 MKREG(0x02, 0x09) +#define TDA_PLL_SCGR2 MKREG(0x02, 0x0a) + +#define TDA_SEL_CLK MKREG(0x02, 0x11) +#define SEL_CLK_ENA_SC_CLK (1 << 3) +#define SEL_CLK_SEL_VRF_CLK(x) (((x) & 3) << 1) +#define SEL_CLK_SEL_CLK1 (1 << 0) +#define TDA_ANA_GENERAL MKREG(0x02, 0x12) + +#define TDA_EDID_DATA0 MKREG(0x09, 0x00) +#define TDA_EDID_CTRL MKREG(0x09, 0xfa) +#define TDA_DDC_ADDR MKREG(0x09, 0xfb) +#define TDA_DDC_OFFS MKREG(0x09, 0xfc) +#define TDA_DDC_SEGM_ADDR MKREG(0x09, 0xfd) +#define TDA_DDC_SEGM MKREG(0x09, 0xfe) + +#define TDA_IF_VSP MKREG(0x10, 0x20) +#define TDA_IF_AVI MKREG(0x10, 0x40) +#define TDA_IF_SPD MKREG(0x10, 0x60) +#define TDA_IF_AUD MKREG(0x10, 0x80) +#define TDA_IF_MPS MKREG(0x10, 0xa0) + +#define TDA_ENC_CNTRL MKREG(0x11, 0x0d) +#define ENC_CNTRL_DVI_MODE (0 << 2) +#define ENC_CNTRL_HDMI_MODE (1 << 2) +#define TDA_DIP_IF_FLAGS MKREG(0x11, 0x0f) +#define DIP_IF_FLAGS_IF5 (1 << 5) +#define DIP_IF_FLAGS_IF4 (1 << 4) +#define DIP_IF_FLAGS_IF3 (1 << 3) +#define DIP_IF_FLAGS_IF2 (1 << 2) /* AVI IF on page 10h */ +#define DIP_IF_FLAGS_IF1 (1 << 1) + + +#define TDA_TX3 MKREG(0x12, 0x9a) +#define TDA_TX4 MKREG(0x12, 0x9b) +#define TX4_PD_RAM (1 << 1) +#define TDA_HDCP_TX33 MKREG(0x12, 0xb8) +#define HDCP_TX33_HDMI (1 << 1) + +#define TDA_CURPAGE_ADDR 0xff + +#define TDA_CEC_ENAMODS 0xff +#define ENAMODS_RXSENS (1 << 2) +#define ENAMODS_HDMI (1 << 1) +#define TDA_CEC_FRO_IM_CLK_CTRL 0xfb +#define CEC_FRO_IM_CLK_CTRL_GHOST_DIS (1 << 7) +#define CEC_FRO_IM_CLK_CTRL_IMCLK_SEL (1 << 1) + +/* EDID reading */ +#define EDID_LENGTH 0x80 +#define MAX_READ_ATTEMPTS 100 + +/* EDID fields */ +#define EDID_MODES0 35 +#define EDID_MODES1 36 +#define EDID_TIMING_START 38 +#define EDID_TIMING_END 54 +#define EDID_TIMING_X(v) (((v) + 31) * 8) +#define EDID_FREQ(v) (((v) & 0x3f) + 60) +#define EDID_RATIO(v) (((v) >> 6) & 0x3) +#define EDID_RATIO_10x16 0 +#define EDID_RATIO_3x4 1 +#define EDID_RATIO_4x5 2 +#define EDID_RATIO_9x16 3 + +struct drm_display_mode { + char name[32]; + + unsigned int type; + + /* Proposed mode values */ + int clock; /* in kHz */ + int hdisplay; + int hsync_start; + int hsync_end; + int htotal; + int hskew; + int vdisplay; + int vsync_start; + int vsync_end; + int vtotal; + int vscan; + unsigned int flags; +}; + +#define DRM_MODE_FLAG_NHSYNC (1 << 0) +#define DRM_MODE_FLAG_NVSYNC (1 << 1) +#define DRM_MODE_FLAG_INTERLACE (1 << 2) +#define DRM_MODE_FLAG_HSKEW (1 << 3) + +#define DRM_MODE(nm, t, c, hd, hss, hse, ht, hsk, vd, vss, vse, vt, vs, f) \ + .name = nm, .type = (t), .clock = (c), \ + .hdisplay = (hd), .hsync_start = (hss), .hsync_end = (hse), \ + .htotal = (ht), .hskew = (hsk), .vdisplay = (vd), \ + .vsync_start = (vss), .vsync_end = (vse), .vtotal = (vt), \ + .vscan = (vs), .flags = (f) + +/* + * This is standard 640x480@60Hz mode timing with HSKEW to + * compensate for inability of TI LCDC to generate VESA timing: TDA19988 + * detects frame start by deecting rising VSYNC and rising HSYNC signals but + * LCDC can not provide it + */ +static struct drm_display_mode mode_std640x480 = + { DRM_MODE("640x480", 0, 25175, 640, 656, + 752, 800, (752 - 656), 480, 490, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_HSKEW) }; + +#define TDA19988 0x0301 + +struct tda19988_softc { + device_t sc_dev; + uint32_t sc_addr; + uint32_t sc_cec_addr; + uint16_t sc_version; + struct intr_config_hook enum_hook; + int sc_current_page; + uint8_t *sc_edid; + uint32_t sc_edid_len; + // struct edid_info *sc_edid_info; +}; + +static int +tda19988_set_page(struct tda19988_softc *sc, uint8_t page) +{ + uint8_t addr = TDA_CURPAGE_ADDR; + uint8_t cmd[2]; + int result; + struct iic_msg msg[] = { + { sc->sc_addr, IIC_M_WR, 2, cmd }, + }; + + cmd[0] = addr; + cmd[1] = page; + + result = (iicbus_transfer(sc->sc_dev, msg, 1)); + if (result) + printf("tda19988_set_page failed: %d\n", result); + else + sc->sc_current_page = page; + + return (result); +} + +static int +tda19988_cec_read(struct tda19988_softc *sc, uint8_t addr, uint8_t *data) +{ + int result; + struct iic_msg msg[] = { + { sc->sc_cec_addr, IIC_M_WR, 1, &addr }, + { sc->sc_cec_addr, IIC_M_RD, 1, data }, + }; + + result = iicbus_transfer(sc->sc_dev, msg, 2); + if (result) + printf("tda19988_cec_read failed: %d\n", result); + return (result); +} + +static int +tda19988_cec_write(struct tda19988_softc *sc, uint8_t address, uint8_t data) +{ + uint8_t cmd[2]; + int result; + struct iic_msg msg[] = { + { sc->sc_cec_addr, IIC_M_WR, 2, cmd }, + }; + + cmd[0] = address; + cmd[1] = data; + + result = iicbus_transfer(sc->sc_dev, msg, 1); + if (result) + printf("tda19988_cec_write failed: %d\n", result); + return (result); +} + +static int +tda19988_block_read(struct tda19988_softc *sc, uint16_t addr, uint8_t *data, int len) +{ + uint8_t reg; + int result; + struct iic_msg msg[] = { + { sc->sc_addr, IIC_M_WR, 1, ® }, + { sc->sc_addr, IIC_M_RD, len, data }, + }; + + reg = REGADDR(addr); + + if (sc->sc_current_page != REGPAGE(addr)) + tda19988_set_page(sc, REGPAGE(addr)); + + result = (iicbus_transfer(sc->sc_dev, msg, 2)); + if (result) + device_printf(sc->sc_dev, "tda19988_block_read failed: %d\n", result); + return (result); +} + + +static int +tda19988_reg_read(struct tda19988_softc *sc, uint16_t addr, uint8_t *data) +{ + uint8_t reg; + int result; + struct iic_msg msg[] = { + { sc->sc_addr, IIC_M_WR, 1, ® }, + { sc->sc_addr, IIC_M_RD, 1, data }, + }; + + reg = REGADDR(addr); + + if (sc->sc_current_page != REGPAGE(addr)) + tda19988_set_page(sc, REGPAGE(addr)); + + result = (iicbus_transfer(sc->sc_dev, msg, 2)); + if (result) + device_printf(sc->sc_dev, "tda19988_reg_read failed: %d\n", result); + return (result); +} + +static int +tda19988_reg_write(struct tda19988_softc *sc, uint16_t address, uint8_t data) +{ + uint8_t cmd[2]; + int result; + struct iic_msg msg[] = { + { sc->sc_addr, IIC_M_WR, 2, cmd }, + }; + + cmd[0] = REGADDR(address); + cmd[1] = data; + + if (sc->sc_current_page != REGPAGE(address)) + tda19988_set_page(sc, REGPAGE(address)); + + result = iicbus_transfer(sc->sc_dev, msg, 1); + if (result) + device_printf(sc->sc_dev, "tda19988_reg_write failed: %d\n", result); + + return (result); +} + +static int +tda19988_reg_write2(struct tda19988_softc *sc, uint16_t address, uint16_t data) +{ + uint8_t cmd[3]; + int result; + struct iic_msg msg[] = { + { sc->sc_addr, IIC_M_WR, 3, cmd }, + }; + + cmd[0] = REGADDR(address); + cmd[1] = (data >> 8); + cmd[2] = (data & 0xff); + + if (sc->sc_current_page != REGPAGE(address)) + tda19988_set_page(sc, REGPAGE(address)); + + result = iicbus_transfer(sc->sc_dev, msg, 1); + if (result) + device_printf(sc->sc_dev, "tda19988_reg_write2 failed: %d\n", result); + + return (result); +} + +static void +tda19988_reg_set(struct tda19988_softc *sc, uint16_t addr, uint8_t flags) +{ + uint8_t data; + + tda19988_reg_read(sc, addr, &data); + data |= flags; + tda19988_reg_write(sc, addr, data); +} + +static void +tda19988_reg_clear(struct tda19988_softc *sc, uint16_t addr, uint8_t flags) +{ + uint8_t data; + + tda19988_reg_read(sc, addr, &data); + data &= ~flags; + tda19988_reg_write(sc, addr, data); +} + +static int +tda19988_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "nxp,tda19988")) + return (ENXIO); + + return (BUS_PROBE_DEFAULT); +} + +static void +tda19988_init_encoder(struct tda19988_softc *sc, struct drm_display_mode *mode) +{ + uint16_t ref_pix, ref_line, n_pix, n_line; + uint16_t hs_pix_start, hs_pix_stop; + uint16_t vs1_pix_start, vs1_pix_stop; + uint16_t vs1_line_start, vs1_line_end; + uint16_t vs2_pix_start, vs2_pix_stop; + uint16_t vs2_line_start, vs2_line_end; + uint16_t vwin1_line_start, vwin1_line_end; + uint16_t vwin2_line_start, vwin2_line_end; + uint16_t de_start, de_stop; + uint8_t reg, div; + + n_pix = mode->htotal; + n_line = mode->vtotal; + + hs_pix_stop = mode->hsync_end - mode->hdisplay; + hs_pix_start = mode->hsync_start - mode->hdisplay; + + de_stop = mode->htotal; + de_start = mode->htotal - mode->hdisplay; + ref_pix = hs_pix_start + 3; + + if (mode->flags & DRM_MODE_FLAG_HSKEW) + ref_pix += mode->hskew; + + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) == 0) { + ref_line = 1 + mode->vsync_start - mode->vdisplay; + vwin1_line_start = mode->vtotal - mode->vdisplay - 1; + vwin1_line_end = vwin1_line_start + mode->vdisplay; + + vs1_pix_start = vs1_pix_stop = hs_pix_start; + vs1_line_start = mode->vsync_start - mode->vdisplay; + vs1_line_end = vs1_line_start + mode->vsync_end - mode->vsync_start; + + vwin2_line_start = vwin2_line_end = 0; + vs2_pix_start = vs2_pix_stop = 0; + vs2_line_start = vs2_line_end = 0; + } else { + ref_line = 1 + (mode->vsync_start - mode->vdisplay)/2; + vwin1_line_start = (mode->vtotal - mode->vdisplay)/2; + vwin1_line_end = vwin1_line_start + mode->vdisplay/2; + + vs1_pix_start = vs1_pix_stop = hs_pix_start; + vs1_line_start = (mode->vsync_start - mode->vdisplay)/2; + vs1_line_end = vs1_line_start + (mode->vsync_end - mode->vsync_start)/2; + + vwin2_line_start = vwin1_line_start + mode->vtotal/2; + vwin2_line_end = vwin2_line_start + mode->vdisplay/2; + + vs2_pix_start = vs2_pix_stop = hs_pix_start + mode->htotal/2; + vs2_line_start = vs1_line_start + mode->vtotal/2 ; + vs2_line_end = vs2_line_start + (mode->vsync_end - mode->vsync_start)/2; + } + + div = 148500 / mode->clock; + if (div != 0) { + div--; + if (div > 3) + div = 3; + } + + /* set HDMI HDCP mode off */ + tda19988_reg_set(sc, TDA_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS); + tda19988_reg_clear(sc, TDA_HDCP_TX33, HDCP_TX33_HDMI); + tda19988_reg_write(sc, TDA_ENC_CNTRL, ENC_CNTRL_DVI_MODE); + + /* no pre-filter or interpolator */ + tda19988_reg_write(sc, TDA_HVF_CNTRL_0, + HVF_CNTRL_0_INTPOL_BYPASS | HVF_CNTRL_0_PREFIL_NONE); + tda19988_reg_write(sc, TDA_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0)); + tda19988_reg_write(sc, TDA_VIP_CNTRL_4, + VIP_CNTRL_4_BLANKIT_NDE | VIP_CNTRL_4_BLC_NONE); + + tda19988_reg_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR); + tda19988_reg_clear(sc, TDA_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IP); + tda19988_reg_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_DE); + tda19988_reg_write(sc, TDA_SERIALIZER, 0); + tda19988_reg_write(sc, TDA_HVF_CNTRL_1, HVF_CNTRL_1_VQR_FULL); + + tda19988_reg_write(sc, TDA_RPT_CNTRL, 0); + tda19988_reg_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) | + SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK); + + tda19988_reg_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) | + PLL_SERIAL_2_SRL_PR(0)); + + tda19988_reg_set(sc, TDA_MAT_CONTRL, MAT_CONTRL_MAT_BP); + + tda19988_reg_write(sc, TDA_ANA_GENERAL, 0x09); + + tda19988_reg_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_MTHD); + + /* + * Sync on rising HSYNC/VSYNC + */ + reg = VIP_CNTRL_3_SYNC_HS; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + reg |= VIP_CNTRL_3_H_TGL; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + reg |= VIP_CNTRL_3_V_TGL; + tda19988_reg_write(sc, TDA_VIP_CNTRL_3, reg); + + reg = TBG_CNTRL_1_TGL_EN; + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + reg |= TBG_CNTRL_1_H_TGL; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + reg |= TBG_CNTRL_1_V_TGL; + tda19988_reg_write(sc, TDA_TBG_CNTRL_1, reg); + + /* Program timing */ + tda19988_reg_write(sc, TDA_VIDFORMAT, 0x00); + + tda19988_reg_write2(sc, TDA_REFPIX_MSB, ref_pix); + tda19988_reg_write2(sc, TDA_REFLINE_MSB, ref_line); + tda19988_reg_write2(sc, TDA_NPIX_MSB, n_pix); + tda19988_reg_write2(sc, TDA_NLINE_MSB, n_line); + + tda19988_reg_write2(sc, TDA_VS_LINE_STRT_1_MSB, vs1_line_start); + tda19988_reg_write2(sc, TDA_VS_PIX_STRT_1_MSB, vs1_pix_start); + tda19988_reg_write2(sc, TDA_VS_LINE_END_1_MSB, vs1_line_end); + tda19988_reg_write2(sc, TDA_VS_PIX_END_1_MSB, vs1_pix_stop); + tda19988_reg_write2(sc, TDA_VS_LINE_STRT_2_MSB, vs2_line_start); + tda19988_reg_write2(sc, TDA_VS_PIX_STRT_2_MSB, vs2_pix_start); + tda19988_reg_write2(sc, TDA_VS_LINE_END_2_MSB, vs2_line_end); + tda19988_reg_write2(sc, TDA_VS_PIX_END_2_MSB, vs2_pix_stop); + tda19988_reg_write2(sc, TDA_HS_PIX_START_MSB, hs_pix_start); + tda19988_reg_write2(sc, TDA_HS_PIX_STOP_MSB, hs_pix_stop); + tda19988_reg_write2(sc, TDA_VWIN_START_1_MSB, vwin1_line_start); + tda19988_reg_write2(sc, TDA_VWIN_END_1_MSB, vwin1_line_end); + tda19988_reg_write2(sc, TDA_VWIN_START_2_MSB, vwin2_line_start); + tda19988_reg_write2(sc, TDA_VWIN_END_2_MSB, vwin2_line_end); + tda19988_reg_write2(sc, TDA_DE_START_MSB, de_start); + tda19988_reg_write2(sc, TDA_DE_STOP_MSB, de_stop); + + if (sc->sc_version == TDA19988) + tda19988_reg_write(sc, TDA_ENABLE_SPACE, 0x00); + + /* must be last register set */ + tda19988_reg_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE); +} + + +static void +tda19988_init_encoder_videomode(struct tda19988_softc *sc, const struct videomode *mode) +{ + uint16_t ref_pix, ref_line, n_pix, n_line; + uint16_t hs_pix_start, hs_pix_stop; + uint16_t vs1_pix_start, vs1_pix_stop; + uint16_t vs1_line_start, vs1_line_end; + uint16_t vs2_pix_start, vs2_pix_stop; + uint16_t vs2_line_start, vs2_line_end; + uint16_t vwin1_line_start, vwin1_line_end; + uint16_t vwin2_line_start, vwin2_line_end; + uint16_t de_start, de_stop; + uint8_t reg, div; + + n_pix = mode->htotal; + n_line = mode->vtotal; + + hs_pix_stop = mode->hsync_end - mode->hdisplay; + hs_pix_start = mode->hsync_start - mode->hdisplay; + + de_stop = mode->htotal; + de_start = mode->htotal - mode->hdisplay; + ref_pix = hs_pix_start + 3; + + if (mode->flags & VID_HSKEW) + ref_pix += mode->hskew; + + if ((mode->flags & VID_INTERLACE) == 0) { + ref_line = 1 + mode->vsync_start - mode->vdisplay; + vwin1_line_start = mode->vtotal - mode->vdisplay - 1; + vwin1_line_end = vwin1_line_start + mode->vdisplay; + + vs1_pix_start = vs1_pix_stop = hs_pix_start; + vs1_line_start = mode->vsync_start - mode->vdisplay; + vs1_line_end = vs1_line_start + mode->vsync_end - mode->vsync_start; + + vwin2_line_start = vwin2_line_end = 0; + vs2_pix_start = vs2_pix_stop = 0; + vs2_line_start = vs2_line_end = 0; + } else { + ref_line = 1 + (mode->vsync_start - mode->vdisplay)/2; + vwin1_line_start = (mode->vtotal - mode->vdisplay)/2; + vwin1_line_end = vwin1_line_start + mode->vdisplay/2; + + vs1_pix_start = vs1_pix_stop = hs_pix_start; + vs1_line_start = (mode->vsync_start - mode->vdisplay)/2; + vs1_line_end = vs1_line_start + (mode->vsync_end - mode->vsync_start)/2; + + vwin2_line_start = vwin1_line_start + mode->vtotal/2; + vwin2_line_end = vwin2_line_start + mode->vdisplay/2; + + vs2_pix_start = vs2_pix_stop = hs_pix_start + mode->htotal/2; + vs2_line_start = vs1_line_start + mode->vtotal/2 ; + vs2_line_end = vs2_line_start + (mode->vsync_end - mode->vsync_start)/2; + } + + div = 148500 / mode->dot_clock; + if (div != 0) { + div--; + if (div > 3) + div = 3; + } + + /* set HDMI HDCP mode off */ + tda19988_reg_set(sc, TDA_TBG_CNTRL_1, TBG_CNTRL_1_DWIN_DIS); + tda19988_reg_clear(sc, TDA_HDCP_TX33, HDCP_TX33_HDMI); + tda19988_reg_write(sc, TDA_ENC_CNTRL, ENC_CNTRL_DVI_MODE); + + /* no pre-filter or interpolator */ + tda19988_reg_write(sc, TDA_HVF_CNTRL_0, + HVF_CNTRL_0_INTPOL_BYPASS | HVF_CNTRL_0_PREFIL_NONE); + tda19988_reg_write(sc, TDA_VIP_CNTRL_5, VIP_CNTRL_5_SP_CNT(0)); + tda19988_reg_write(sc, TDA_VIP_CNTRL_4, + VIP_CNTRL_4_BLANKIT_NDE | VIP_CNTRL_4_BLC_NONE); + + tda19988_reg_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_CCIR); + tda19988_reg_clear(sc, TDA_PLL_SERIAL_1, PLL_SERIAL_1_SRL_MAN_IP); + tda19988_reg_clear(sc, TDA_PLL_SERIAL_3, PLL_SERIAL_3_SRL_DE); + tda19988_reg_write(sc, TDA_SERIALIZER, 0); + tda19988_reg_write(sc, TDA_HVF_CNTRL_1, HVF_CNTRL_1_VQR_FULL); + + tda19988_reg_write(sc, TDA_RPT_CNTRL, 0); + tda19988_reg_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_VRF_CLK(0) | + SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK); + + tda19988_reg_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(div) | + PLL_SERIAL_2_SRL_PR(0)); + + tda19988_reg_set(sc, TDA_MAT_CONTRL, MAT_CONTRL_MAT_BP); + + tda19988_reg_write(sc, TDA_ANA_GENERAL, 0x09); + + tda19988_reg_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_MTHD); + + /* + * Sync on rising HSYNC/VSYNC + */ + reg = VIP_CNTRL_3_SYNC_HS; + if (mode->flags & VID_NHSYNC) + reg |= VIP_CNTRL_3_H_TGL; + if (mode->flags & VID_NVSYNC) + reg |= VIP_CNTRL_3_V_TGL; + tda19988_reg_write(sc, TDA_VIP_CNTRL_3, reg); + + reg = TBG_CNTRL_1_TGL_EN; + if (mode->flags & VID_NHSYNC) + reg |= TBG_CNTRL_1_H_TGL; + if (mode->flags & VID_NVSYNC) + reg |= TBG_CNTRL_1_V_TGL; + tda19988_reg_write(sc, TDA_TBG_CNTRL_1, reg); + + /* Program timing */ + tda19988_reg_write(sc, TDA_VIDFORMAT, 0x00); + + tda19988_reg_write2(sc, TDA_REFPIX_MSB, ref_pix); + tda19988_reg_write2(sc, TDA_REFLINE_MSB, ref_line); + tda19988_reg_write2(sc, TDA_NPIX_MSB, n_pix); + tda19988_reg_write2(sc, TDA_NLINE_MSB, n_line); + + tda19988_reg_write2(sc, TDA_VS_LINE_STRT_1_MSB, vs1_line_start); + tda19988_reg_write2(sc, TDA_VS_PIX_STRT_1_MSB, vs1_pix_start); + tda19988_reg_write2(sc, TDA_VS_LINE_END_1_MSB, vs1_line_end); + tda19988_reg_write2(sc, TDA_VS_PIX_END_1_MSB, vs1_pix_stop); + tda19988_reg_write2(sc, TDA_VS_LINE_STRT_2_MSB, vs2_line_start); + tda19988_reg_write2(sc, TDA_VS_PIX_STRT_2_MSB, vs2_pix_start); + tda19988_reg_write2(sc, TDA_VS_LINE_END_2_MSB, vs2_line_end); + tda19988_reg_write2(sc, TDA_VS_PIX_END_2_MSB, vs2_pix_stop); + tda19988_reg_write2(sc, TDA_HS_PIX_START_MSB, hs_pix_start); + tda19988_reg_write2(sc, TDA_HS_PIX_STOP_MSB, hs_pix_stop); + tda19988_reg_write2(sc, TDA_VWIN_START_1_MSB, vwin1_line_start); + tda19988_reg_write2(sc, TDA_VWIN_END_1_MSB, vwin1_line_end); + tda19988_reg_write2(sc, TDA_VWIN_START_2_MSB, vwin2_line_start); + tda19988_reg_write2(sc, TDA_VWIN_END_2_MSB, vwin2_line_end); + tda19988_reg_write2(sc, TDA_DE_START_MSB, de_start); + tda19988_reg_write2(sc, TDA_DE_STOP_MSB, de_stop); + + if (sc->sc_version == TDA19988) + tda19988_reg_write(sc, TDA_ENABLE_SPACE, 0x00); + + /* must be last register set */ + tda19988_reg_clear(sc, TDA_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE); +} + + + +static int +tda19988_read_edid_block(struct tda19988_softc *sc, uint8_t *buf, int block) +{ + int attempt, err; + uint8_t data; + + err = 0; + + tda19988_reg_set(sc, TDA_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); + + /* Block 0 */ + tda19988_reg_write(sc, TDA_DDC_ADDR, 0xa0); + tda19988_reg_write(sc, TDA_DDC_OFFS, (block % 2) ? 128 : 0); + tda19988_reg_write(sc, TDA_DDC_SEGM_ADDR, 0x60); + tda19988_reg_write(sc, TDA_DDC_SEGM, block / 2); + + tda19988_reg_write(sc, TDA_EDID_CTRL, 1); + tda19988_reg_write(sc, TDA_EDID_CTRL, 0); + + data = 0; + for (attempt = 0; attempt < MAX_READ_ATTEMPTS; attempt++) { + tda19988_reg_read(sc, TDA_INT_FLAGS_2, &data); + if (data & INT_FLAGS_2_EDID_BLK_RD) + break; + pause("EDID", 1); + } + + if (attempt == MAX_READ_ATTEMPTS) { + err = -1; + goto done; + } + + if (tda19988_block_read(sc, TDA_EDID_DATA0, buf, EDID_LENGTH) != 0) { + err = -1; + goto done; + } + +done: + tda19988_reg_clear(sc, TDA_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); + + return (err); +} + +static int +tda19988_read_edid(struct tda19988_softc *sc) +{ + int err; + int blocks, i; + uint8_t *buf; + + err = 0; + if (sc->sc_version == TDA19988) + tda19988_reg_clear(sc, TDA_TX4, TX4_PD_RAM); + + err = tda19988_read_edid_block(sc, sc->sc_edid, 0); + if (err) + goto done; + + blocks = sc->sc_edid[0x7e]; + if (blocks > 0) { + sc->sc_edid = realloc(sc->sc_edid, + EDID_LENGTH*(blocks+1), M_DEVBUF, M_WAITOK); + sc->sc_edid_len = EDID_LENGTH*(blocks+1); + for (i = 0; i < blocks; i++) { + /* TODO: check validity */ + buf = sc->sc_edid + EDID_LENGTH*(i+1); + err = tda19988_read_edid_block(sc, buf, i); + if (err) + goto done; + } + } + +#if 0 + for (i = 0; i < EDID_LENGTH; i++) { + printf("%02x ", sc->sc_edid[i]); + if ((i % 16) == 15) { + printf("\n"); + } + } +#endif + + EVENTHANDLER_INVOKE(hdmi_event, 0); + +done: + if (sc->sc_version == TDA19988) + tda19988_reg_set(sc, TDA_TX4, TX4_PD_RAM); + + return (err); +} + +static void +tda19988_start(void *xdev) +{ + struct tda19988_softc *sc; + device_t dev = (device_t)xdev; + uint8_t data; + uint16_t version; + + sc = device_get_softc(dev); + + tda19988_cec_write(sc, TDA_CEC_ENAMODS, ENAMODS_RXSENS | ENAMODS_HDMI); + DELAY(1000); + tda19988_cec_read(sc, 0xfe, &data); + + /* Reset core */ + tda19988_reg_set(sc, TDA_SOFTRESET, 3); + DELAY(100); + tda19988_reg_clear(sc, TDA_SOFTRESET, 3); + DELAY(100); + + /* reset transmitter: */ + tda19988_reg_set(sc, TDA_MAIN_CNTRL0, MAIN_CNTRL0_SR); + tda19988_reg_clear(sc, TDA_MAIN_CNTRL0, MAIN_CNTRL0_SR); + + /* PLL registers common configuration */ + tda19988_reg_write(sc, TDA_PLL_SERIAL_1, 0x00); + tda19988_reg_write(sc, TDA_PLL_SERIAL_2, PLL_SERIAL_2_SRL_NOSC(1)); + tda19988_reg_write(sc, TDA_PLL_SERIAL_3, 0x00); + tda19988_reg_write(sc, TDA_SERIALIZER, 0x00); + tda19988_reg_write(sc, TDA_BUFFER_OUT, 0x00); + tda19988_reg_write(sc, TDA_PLL_SCG1, 0x00); + tda19988_reg_write(sc, TDA_SEL_CLK, SEL_CLK_SEL_CLK1 | SEL_CLK_ENA_SC_CLK); + tda19988_reg_write(sc, TDA_PLL_SCGN1, 0xfa); + tda19988_reg_write(sc, TDA_PLL_SCGN2, 0x00); + tda19988_reg_write(sc, TDA_PLL_SCGR1, 0x5b); + tda19988_reg_write(sc, TDA_PLL_SCGR2, 0x00); + tda19988_reg_write(sc, TDA_PLL_SCG2, 0x10); + + /* Write the default value MUX register */ + tda19988_reg_write(sc, TDA_MUX_VP_VIP_OUT, 0x24); + + version = 0; + tda19988_reg_read(sc, TDA_VERSION, &data); + version |= data; + tda19988_reg_read(sc, TDA_VERSION_MSB, &data); + version |= (data << 8); + + /* Clear feature bits */ + sc->sc_version = version & ~0x30; + switch (sc->sc_version) { + case TDA19988: + device_printf(dev, "TDA19988\n"); + break; + default: + device_printf(dev, "Unknown device: %04x\n", sc->sc_version); + goto done; + } + + tda19988_reg_write(sc, TDA_DDC_CTRL, DDC_ENABLE); + tda19988_reg_write(sc, TDA_TX3, 39); + + tda19988_cec_write(sc, TDA_CEC_FRO_IM_CLK_CTRL, + CEC_FRO_IM_CLK_CTRL_GHOST_DIS | CEC_FRO_IM_CLK_CTRL_IMCLK_SEL); + + if (tda19988_read_edid(sc) < 0) { + device_printf(dev, "failed to read EDID\n"); + goto done; + } + + if (0) + tda19988_init_encoder(sc, &mode_std640x480); + + /* Default values for RGB 4:4:4 mapping */ + tda19988_reg_write(sc, TDA_VIP_CNTRL_0, 0x23); + tda19988_reg_write(sc, TDA_VIP_CNTRL_1, 0x45); + tda19988_reg_write(sc, TDA_VIP_CNTRL_2, 0x01); + +done: + config_intrhook_disestablish(&sc->enum_hook); +} + +static int +tda19988_attach(device_t dev) +{ + struct tda19988_softc *sc; + phandle_t node; + + sc = device_get_softc(dev); + + sc->sc_dev = dev; + sc->sc_addr = iicbus_get_addr(dev); + sc->sc_cec_addr = (0x34 << 1); /* hardcoded */ + sc->sc_edid = malloc(EDID_LENGTH, M_DEVBUF, M_WAITOK | M_ZERO); + sc->sc_edid_len = EDID_LENGTH; + + device_set_desc(dev, "NXP TDA19988 HDMI transmitter"); + + sc->enum_hook.ich_func = tda19988_start; + sc->enum_hook.ich_arg = dev; + + if (config_intrhook_establish(&sc->enum_hook) != 0) + return (ENOMEM); + + node = ofw_bus_get_node(dev); + OF_device_register_xref(OF_xref_from_node(node), dev); + + return (0); +} + +static int +tda19988_detach(device_t dev) +{ + + /* XXX: Do not let unload drive */ + return (EBUSY); +} + +static int +tda19988_get_edid(device_t dev, uint8_t **edid, uint32_t *edid_len) +{ + struct tda19988_softc *sc; + + sc = device_get_softc(dev); + + if (sc->sc_edid) { + *edid = sc->sc_edid; + *edid_len = sc->sc_edid_len; + } else + return (ENXIO); + + return (0); +} + + +static int +tda19988_set_videomode(device_t dev, const struct videomode *mode) +{ + struct tda19988_softc *sc; + + sc = device_get_softc(dev); + + tda19988_init_encoder_videomode(sc, mode); + + return (0); +} + +static device_method_t tda_methods[] = { + DEVMETHOD(device_probe, tda19988_probe), + DEVMETHOD(device_attach, tda19988_attach), + DEVMETHOD(device_detach, tda19988_detach), + + /* HDMI methods */ + DEVMETHOD(hdmi_get_edid, tda19988_get_edid), + DEVMETHOD(hdmi_set_videomode, tda19988_set_videomode), + {0, 0}, +}; + +static driver_t tda_driver = { + "tda", + tda_methods, + sizeof(struct tda19988_softc), +}; + +static devclass_t tda_devclass; + +DRIVER_MODULE(tda, iicbus, tda_driver, tda_devclass, 0, 0); +MODULE_VERSION(tda, 1); +MODULE_DEPEND(tda, iicbus, 1, 1, 1); Property changes on: sys/dev/iicbus/tda19988.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property