summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arm/freescale/imx/imx6_ccm.c75
-rw-r--r--sys/arm/freescale/imx/imx6_ccmreg.h16
-rw-r--r--sys/arm/freescale/imx/imx6_ipu.c129
-rw-r--r--sys/arm/freescale/imx/imx_ccmvar.h2
4 files changed, 182 insertions, 40 deletions
diff --git a/sys/arm/freescale/imx/imx6_ccm.c b/sys/arm/freescale/imx/imx6_ccm.c
index 939ece04ac5b..94c0420e6737 100644
--- a/sys/arm/freescale/imx/imx6_ccm.c
+++ b/sys/arm/freescale/imx/imx6_ccm.c
@@ -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,6 +453,24 @@ 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
@@ -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
diff --git a/sys/arm/freescale/imx/imx6_ccmreg.h b/sys/arm/freescale/imx/imx6_ccmreg.h
index 69e028debae6..cbe107f1fba7 100644
--- a/sys/arm/freescale/imx/imx6_ccmreg.h
+++ b/sys/arm/freescale/imx/imx6_ccmreg.h
@@ -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)
diff --git a/sys/arm/freescale/imx/imx6_ipu.c b/sys/arm/freescale/imx/imx6_ipu.c
index 831a13988bba..417f8f64a28c 100644
--- a/sys/arm/freescale/imx/imx6_ipu.c
+++ b/sys/arm/freescale/imx/imx6_ipu.c
@@ -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 di, int map)
}
}
+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;
+
+ freq = sc->sc_mode->dot_clock * 1000;
- /* TODO: check mode restrictions / fixup */
- /* TODO: enable timers, get divisors */
- div = 1;
+ 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);
}
diff --git a/sys/arm/freescale/imx/imx_ccmvar.h b/sys/arm/freescale/imx/imx_ccmvar.h
index 5636729ace68..f15c61bd96e7 100644
--- a/sys/arm/freescale/imx/imx_ccmvar.h
+++ b/sys/arm/freescale/imx/imx_ccmvar.h
@@ -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);