Author: gonzo
Date: Wed Jun 10 22:00:31 2020
New Revision: 362030
URL: https://svnweb.freebsd.org/changeset/base/362030

Log:
  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:
  head/sys/arm/freescale/imx/imx6_ccm.c
  head/sys/arm/freescale/imx/imx6_ccmreg.h
  head/sys/arm/freescale/imx/imx6_ipu.c
  head/sys/arm/freescale/imx/imx_ccmvar.h

Modified: head/sys/arm/freescale/imx/imx6_ccm.c
==============================================================================
--- head/sys/arm/freescale/imx/imx6_ccm.c       Wed Jun 10 21:38:35 2020        
(r362029)
+++ head/sys/arm/freescale/imx/imx6_ccm.c       Wed Jun 10 22:00:31 2020        
(r362030)
@@ -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: head/sys/arm/freescale/imx/imx6_ccmreg.h
==============================================================================
--- head/sys/arm/freescale/imx/imx6_ccmreg.h    Wed Jun 10 21:38:35 2020        
(r362029)
+++ head/sys/arm/freescale/imx/imx6_ccmreg.h    Wed Jun 10 22:00:31 2020        
(r362030)
@@ -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: head/sys/arm/freescale/imx/imx6_ipu.c
==============================================================================
--- head/sys/arm/freescale/imx/imx6_ipu.c       Wed Jun 10 21:38:35 2020        
(r362029)
+++ head/sys/arm/freescale/imx/imx6_ipu.c       Wed Jun 10 22:00:31 2020        
(r362030)
@@ -61,12 +61,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)
@@ -77,11 +73,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
@@ -384,7 +375,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;
@@ -634,10 +625,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;
@@ -645,10 +656,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;
@@ -656,14 +668,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);
@@ -768,8 +772,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
@@ -1063,15 +1065,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;
@@ -1084,14 +1126,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);
@@ -1145,9 +1201,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: head/sys/arm/freescale/imx/imx_ccmvar.h
==============================================================================
--- head/sys/arm/freescale/imx/imx_ccmvar.h     Wed Jun 10 21:38:35 2020        
(r362029)
+++ head/sys/arm/freescale/imx/imx_ccmvar.h     Wed Jun 10 22:00:31 2020        
(r362030)
@@ -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);
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to