svn commit: r362030 - head/sys/arm/freescale/imx

Oleksandr Tymoshenko gonzo at FreeBSD.org
Wed Jun 10 22:00:32 UTC 2020


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);


More information about the svn-src-head mailing list