Author: gonzo Date: Mon Jun 29 00:28:11 2020 New Revision: 362743 URL: https://svnweb.freebsd.org/changeset/base/362743
Log: MFC r362029-r362030 r362029: Fix reading EDID on TVs/monitors without E-DCC support Writing segment id to I2C device 0x30 only required if the segment is non-zero. On the devices without E-DCC support writing to that address fails and whole transaction then fails too. To avoid this do not attempt write to the segment selection device unless required. r362030: Add mode selection to iMX6 IPU driver - Configure ipu1_di0 tob e sourced from the VIDEO_PLL(PLL5) and hardcode frequency to (455000000/3)Mhz. This value, further divided, can yield frequencies close enough to support 1080p, 720p, 1024x768, and 640x480 modes. This is not ideal but it's an improvement comparing to the only hardcoded 1024x768 mode. - Fix memory leaks if attach method failed - Print EDID when -v passed to the kernel Modified: stable/12/sys/arm/freescale/imx/imx6_ccm.c stable/12/sys/arm/freescale/imx/imx6_ccmreg.h stable/12/sys/arm/freescale/imx/imx6_ipu.c stable/12/sys/arm/freescale/imx/imx_ccmvar.h stable/12/sys/dev/hdmi/dwc_hdmi.c Directory Properties: stable/12/ (props changed) Modified: stable/12/sys/arm/freescale/imx/imx6_ccm.c ============================================================================== --- stable/12/sys/arm/freescale/imx/imx6_ccm.c Sun Jun 28 22:04:52 2020 (r362742) +++ stable/12/sys/arm/freescale/imx/imx6_ccm.c Mon Jun 29 00:28:11 2020 (r362743) @@ -393,6 +393,53 @@ imx_ccm_ahb_hz(void) return (132000000); } +int +imx_ccm_pll_video_enable(void) +{ + uint32_t reg; + int timeout; + + /* Power down PLL */ + reg = RD4(ccm_sc, CCM_ANALOG_PLL_VIDEO); + reg &= ~CCM_ANALOG_PLL_VIDEO_POWERDOWN; + WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg); + + /* + * Fvideo = Fref * (37 + 11/12) / 2 + * Fref = 24MHz, Fvideo = 455MHz + */ + reg &= ~CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_MASK; + reg |= CCM_ANALOG_PLL_VIDEO_POST_DIV_2; + reg &= ~CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK; + reg |= 37 << CCM_ANALOG_PLL_VIDEO_DIV_SELECT_SHIFT; + WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg); + + WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO_NUM, 11); + WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO_DENOM, 12); + + /* Power up and wait for PLL lock down */ + reg = RD4(ccm_sc, CCM_ANALOG_PLL_VIDEO); + reg &= ~CCM_ANALOG_PLL_VIDEO_POWERDOWN; + WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg); + + for (timeout = 100000; timeout > 0; timeout--) { + if (RD4(ccm_sc, CCM_ANALOG_PLL_VIDEO) & + CCM_ANALOG_PLL_VIDEO_LOCK) { + break; + } + } + if (timeout <= 0) { + return ETIMEDOUT; + } + + /* Enable the PLL */ + reg |= CCM_ANALOG_PLL_VIDEO_ENABLE; + reg &= ~CCM_ANALOG_PLL_VIDEO_BYPASS; + WR4(ccm_sc, CCM_ANALOG_PLL_VIDEO, reg); + + return (0); +} + void imx_ccm_ipu_enable(int ipu) { @@ -406,8 +453,26 @@ imx_ccm_ipu_enable(int ipu) else reg |= CCGR3_IPU2_IPU | CCGR3_IPU2_DI0; WR4(sc, CCM_CCGR3, reg); + + /* Set IPU1_DI0 clock to source from PLL5 and divide it by 3 */ + reg = RD4(sc, CCM_CHSCCDR); + reg &= ~(CHSCCDR_IPU1_DI0_PRE_CLK_SEL_MASK | + CHSCCDR_IPU1_DI0_PODF_MASK | CHSCCDR_IPU1_DI0_CLK_SEL_MASK); + reg |= (CHSCCDR_PODF_DIVIDE_BY_3 << CHSCCDR_IPU1_DI0_PODF_SHIFT); + reg |= (CHSCCDR_IPU_PRE_CLK_PLL5 << CHSCCDR_IPU1_DI0_PRE_CLK_SEL_SHIFT); + WR4(sc, CCM_CHSCCDR, reg); + + reg |= (CHSCCDR_CLK_SEL_PREMUXED << CHSCCDR_IPU1_DI0_CLK_SEL_SHIFT); + WR4(sc, CCM_CHSCCDR, reg); } +uint32_t +imx_ccm_ipu_hz(void) +{ + + return (455000000 / 3); +} + void imx_ccm_hdmi_enable(void) { @@ -418,16 +483,6 @@ imx_ccm_hdmi_enable(void) reg = RD4(sc, CCM_CCGR2); reg |= CCGR2_HDMI_TX | CCGR2_HDMI_TX_ISFR; WR4(sc, CCM_CCGR2, reg); - - /* Set HDMI clock to 280MHz */ - reg = RD4(sc, CCM_CHSCCDR); - reg &= ~(CHSCCDR_IPU1_DI0_PRE_CLK_SEL_MASK | - CHSCCDR_IPU1_DI0_PODF_MASK | CHSCCDR_IPU1_DI0_CLK_SEL_MASK); - reg |= (CHSCCDR_PODF_DIVIDE_BY_3 << CHSCCDR_IPU1_DI0_PODF_SHIFT); - reg |= (CHSCCDR_IPU_PRE_CLK_540M_PFD << CHSCCDR_IPU1_DI0_PRE_CLK_SEL_SHIFT); - WR4(sc, CCM_CHSCCDR, reg); - reg |= (CHSCCDR_CLK_SEL_LDB_DI0 << CHSCCDR_IPU1_DI0_CLK_SEL_SHIFT); - WR4(sc, CCM_CHSCCDR, reg); } uint32_t Modified: stable/12/sys/arm/freescale/imx/imx6_ccmreg.h ============================================================================== --- stable/12/sys/arm/freescale/imx/imx6_ccmreg.h Sun Jun 28 22:04:52 2020 (r362742) +++ stable/12/sys/arm/freescale/imx/imx6_ccmreg.h Mon Jun 29 00:28:11 2020 (r362743) @@ -64,9 +64,12 @@ #define CHSCCDR_IPU1_DI0_PODF_SHIFT 3 #define CHSCCDR_IPU1_DI0_CLK_SEL_MASK (0x7) #define CHSCCDR_IPU1_DI0_CLK_SEL_SHIFT 0 +#define CHSCCDR_CLK_SEL_PREMUXED 0 #define CHSCCDR_CLK_SEL_LDB_DI0 3 #define CHSCCDR_PODF_DIVIDE_BY_3 2 +#define CHSCCDR_PODF_DIVIDE_BY_1 0 #define CHSCCDR_IPU_PRE_CLK_540M_PFD 5 +#define CHSCCDR_IPU_PRE_CLK_PLL5 2 #define CCM_CSCDR2 0x038 #define CCM_CLPCR 0x054 #define CCM_CLPCR_LPM_MASK 0x03 @@ -138,6 +141,19 @@ #define CCGR6_USDHC3 (0x3 << 6) #define CCGR6_USDHC4 (0x3 << 8) #define CCM_CMEOR 0x088 + +#define CCM_ANALOG_PLL_VIDEO 0x000040a0 +#define CCM_ANALOG_PLL_VIDEO_LOCK (1u << 31) +#define CCM_ANALOG_PLL_VIDEO_BYPASS (1u << 16) +#define CCM_ANALOG_PLL_VIDEO_ENABLE (1u << 13) +#define CCM_ANALOG_PLL_VIDEO_POWERDOWN (1u << 12) +#define CCM_ANALOG_PLL_VIDEO_POST_DIV_SELECT_MASK (3u << 19) +#define CCM_ANALOG_PLL_VIDEO_POST_DIV_2 (1u << 19) +#define CCM_ANALOG_PLL_VIDEO_DIV_SELECT_MASK (0x7f << 0) +#define CCM_ANALOG_PLL_VIDEO_DIV_SELECT_SHIFT 0 + +#define CCM_ANALOG_PLL_VIDEO_NUM 0x000040b0 +#define CCM_ANALOG_PLL_VIDEO_DENOM 0x000040c0 #define CCM_ANALOG_PLL_ENET 0x000040e0 #define CCM_ANALOG_PLL_ENET_LOCK (1u << 31) Modified: stable/12/sys/arm/freescale/imx/imx6_ipu.c ============================================================================== --- stable/12/sys/arm/freescale/imx/imx6_ipu.c Sun Jun 28 22:04:52 2020 (r362742) +++ stable/12/sys/arm/freescale/imx/imx6_ipu.c Mon Jun 29 00:28:11 2020 (r362743) @@ -60,12 +60,8 @@ __FBSDID("$FreeBSD$"); #include "fb_if.h" #include "hdmi_if.h" -#define EDID_DEBUG_not - static int have_ipu = 0; -#define LDB_CLOCK_RATE 280000000 - #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) @@ -76,11 +72,6 @@ static int have_ipu = 0; #define MODE_BPP 16 #define MODE_PIXEL_CLOCK_INVERT 1 -#define M(nm,hr,vr,clk,hs,he,ht,vs,ve,vt,f) \ - { clk, hr, hs, he, ht, vr, vs, ve, vt, f, nm } - -static struct videomode mode1024x768 = M("1024x768x60",1024,768,65000,1048,1184,1344,771,777,806,VID_NHSYNC|VID_PHSYNC); - #define DMA_CHANNEL 23 #define DC_CHAN5 5 #define DI_PORT 0 @@ -383,7 +374,7 @@ struct ipu_softc { void *sc_intr_hl; struct mtx sc_mtx; struct fb_info sc_fb_info; - struct videomode *sc_mode; + const struct videomode *sc_mode; /* Framebuffer */ bus_dma_tag_t sc_dma_tag; @@ -633,10 +624,30 @@ ipu_init_microcode_template(struct ipu_softc *sc, int } } +static uint32_t +ipu_calc_divisor(uint32_t reference, uint32_t freq) +{ + uint32_t div, i; + uint32_t delta, min_delta; + + min_delta = freq; + div = 255; + + for (i = 1; i < 255; i++) { + delta = abs(reference/i - freq); + if (delta < min_delta) { + div = i; + min_delta = delta; + } + } + + return (div); +} + static void ipu_config_timing(struct ipu_softc *sc, int di) { - int div; + uint32_t div; uint32_t di_scr_conf; uint32_t gen_offset, gen; uint32_t as_gen_offset, as_gen; @@ -644,10 +655,11 @@ ipu_config_timing(struct ipu_softc *sc, int di) uint32_t dw_set_offset, dw_set; uint32_t bs_clkgen_offset; int map; + uint32_t freq; - /* TODO: check mode restrictions / fixup */ - /* TODO: enable timers, get divisors */ - div = 1; + freq = sc->sc_mode->dot_clock * 1000; + + div = ipu_calc_divisor(imx_ccm_ipu_hz(), freq); map = 0; bs_clkgen_offset = di ? IPU_DI1_BS_CLKGEN0 : IPU_DI0_BS_CLKGEN0; @@ -655,14 +667,6 @@ ipu_config_timing(struct ipu_softc *sc, int di) /* half of the divider */ IPU_WRITE4(sc, bs_clkgen_offset + 4, DI_BS_CLKGEN1_DOWN(div / 2, div % 2)); - /* - * TODO: Configure LLDB clock by changing following fields - * in CCM fields: - * CS2CDR_LDB_DI0_CLK_SEL - * CSCMR2_LDB_DI0_IPU_DIV - * CBCDR_MMDC_CH1_AXI_PODF - */ - /* Setup wave generator */ dw_gen_offset = di ? IPU_DI1_DW_GEN_0 : IPU_DI0_DW_GEN_0; dw_gen = DW_GEN_DI_ACCESS_SIZE(div - 1) | DW_GEN_DI_COMPONENT_SIZE(div - 1); @@ -767,8 +771,6 @@ ipu_dc_enable(struct ipu_softc *sc) conf &= ~WRITE_CH_CONF_PROG_CHAN_TYP_MASK; conf |= WRITE_CH_CONF_PROG_CHAN_NORMAL; IPU_WRITE4(sc, DC_WRITE_CH_CONF_5, conf); - - /* TODO: enable clock */ } static void @@ -1062,15 +1064,55 @@ fail: return (err); } +static int +ipu_mode_is_valid(const struct videomode *mode) +{ + if ((mode->dot_clock < 13500) || (mode->dot_clock > 216000)) + return (0); + + return (1); +} + +static const struct videomode * +ipu_pick_mode(struct edid_info *ei) +{ + const struct videomode *videomode; + const struct videomode *m; + int n; + + videomode = NULL; + + /* + * Pick a mode. + */ + if (ei->edid_preferred_mode != NULL) { + if (ipu_mode_is_valid(ei->edid_preferred_mode)) + videomode = ei->edid_preferred_mode; + } + + if (videomode == NULL) { + m = ei->edid_modes; + + sort_modes(ei->edid_modes, + &ei->edid_preferred_mode, + ei->edid_nmodes); + for (n = 0; n < ei->edid_nmodes; n++) + if (ipu_mode_is_valid(&m[n])) { + videomode = &m[n]; + break; + } + } + + return videomode; +} + static void ipu_hdmi_event(void *arg, device_t hdmi_dev) { struct ipu_softc *sc; uint8_t *edid; uint32_t edid_len; -#ifdef EDID_DEBUG struct edid_info ei; -#endif const struct videomode *videomode; sc = arg; @@ -1083,14 +1125,28 @@ ipu_hdmi_event(void *arg, device_t hdmi_dev) videomode = NULL; -#ifdef EDID_DEBUG if ( edid && (edid_parse(edid, &ei) == 0)) { - edid_print(&ei); + if (bootverbose) + edid_print(&ei); + videomode = ipu_pick_mode(&ei); } else device_printf(sc->sc_dev, "failed to parse EDID\n"); -#endif - sc->sc_mode = &mode1024x768; + /* 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\n"); + return; + } + + sc->sc_mode = videomode; + + if (bootverbose) + device_printf(sc->sc_dev, "detected videomode: %dx%d\n", + videomode->hdisplay, videomode->vdisplay); + ipu_init(sc); HDMI_SET_VIDEOMODE(hdmi_dev, sc->sc_mode); @@ -1144,9 +1200,22 @@ ipu_attach(device_t dev) } /* Enable IPU1 */ + if (imx_ccm_pll_video_enable() != 0) { + bus_release_resource(dev, SYS_RES_MEMORY, + sc->sc_mem_rid, sc->sc_mem_res); + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_irq_rid, sc->sc_irq_res); + device_printf(dev, "failed to set up video PLL\n"); + return (ENXIO); + } + imx_ccm_ipu_enable(1); if (src_reset_ipu() != 0) { + bus_release_resource(dev, SYS_RES_MEMORY, + sc->sc_mem_rid, sc->sc_mem_res); + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_irq_rid, sc->sc_irq_res); device_printf(dev, "failed to reset IPU\n"); return (ENXIO); } Modified: stable/12/sys/arm/freescale/imx/imx_ccmvar.h ============================================================================== --- stable/12/sys/arm/freescale/imx/imx_ccmvar.h Sun Jun 28 22:04:52 2020 (r362742) +++ stable/12/sys/arm/freescale/imx/imx_ccmvar.h Mon Jun 29 00:28:11 2020 (r362743) @@ -49,10 +49,12 @@ uint32_t imx_ccm_perclk_hz(void); uint32_t imx_ccm_sdhci_hz(void); uint32_t imx_ccm_uart_hz(void); uint32_t imx_ccm_ahb_hz(void); +uint32_t imx_ccm_ipu_hz(void); void imx_ccm_usb_enable(device_t _usbdev); void imx_ccm_usbphy_enable(device_t _phydev); void imx_ccm_ssi_configure(device_t _ssidev); +int imx_ccm_pll_video_enable(void); void imx_ccm_hdmi_enable(void); void imx_ccm_ipu_enable(int ipu); int imx6_ccm_sata_enable(void); Modified: stable/12/sys/dev/hdmi/dwc_hdmi.c ============================================================================== --- stable/12/sys/dev/hdmi/dwc_hdmi.c Sun Jun 28 22:04:52 2020 (r362742) +++ stable/12/sys/dev/hdmi/dwc_hdmi.c Mon Jun 29 00:28:11 2020 (r362743) @@ -657,6 +657,11 @@ hdmi_edid_read(struct dwc_hdmi_softc *sc, int block, u int result; uint8_t addr = block & 1 ? EDID_LENGTH : 0; uint8_t segment = block >> 1; + /* + * Some devices do not support E-DDC so attempt + * writing segment address only if it's neccessary + */ + unsigned char xfers = segment ? 3 : 2; struct iic_msg msg[] = { { I2C_DDC_SEGADDR, IIC_M_WR, 1, &segment }, { I2C_DDC_ADDR, IIC_M_WR, 1, &addr }, @@ -686,7 +691,7 @@ hdmi_edid_read(struct dwc_hdmi_softc *sc, int block, u return (result); } - result = iicbus_transfer(i2c_dev, msg, 3); + result = iicbus_transfer(i2c_dev, &msg[3 - xfers], xfers); iicbus_release_bus(i2c_dev, sc->sc_dev); if (result) { _______________________________________________ svn-src-all@freebsd.org mailing list https://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"