static int sunxi_hdmi_hpd_detect(int hpd_delay) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; struct sunxi_hdmi_reg * const hdmi = (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; unsigned long tmo = timer_get_us() + hpd_delay * 1000; /* Set pll3 to 300MHz */ clock_set_pll3(300000000); /* Set hdmi parent to pll3 */ clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK, CCM_HDMI_CTRL_PLL3); /* Set ahb gating to pass */ #ifdef CONFIG_SUNXI_GEN_SUN6I setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI); #endif setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI); /* Clock on */ setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE); writel(SUNXI_HDMI_CTRL_ENABLE, &hdmi->ctrl); writel(SUNXI_HDMI_PAD_CTRL0_HDP, &hdmi->pad_ctrl0); while (timer_get_us() < tmo) { if (readl(&hdmi->hpd) & SUNXI_HDMI_HPD_DETECT) return 1; } return 0; }
static void sunxi_hdmi_shutdown(void) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; struct sunxi_hdmi_reg * const hdmi = (struct sunxi_hdmi_reg *)SUNXI_HDMI_BASE; clrbits_le32(&hdmi->ctrl, SUNXI_HDMI_CTRL_ENABLE); clrbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE); clrbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI); #ifdef CONFIG_SUNXI_GEN_SUN6I clrbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI); #endif clock_set_pll3(0); }
static int sunxi_dw_hdmi_probe(struct udevice *dev) { struct display_plat *uc_plat = dev_get_uclass_platdata(dev); struct sunxi_dw_hdmi_priv *priv = dev_get_priv(dev); struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; int ret; /* Set pll3 to 297 MHz */ clock_set_pll3(297000000); /* Set hdmi parent to pll3 */ clrsetbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_PLL_MASK, CCM_HDMI_CTRL_PLL3); /* Set ahb gating to pass */ setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI); setbits_le32(&ccm->ahb_reset1_cfg, 1 << AHB_RESET_OFFSET_HDMI2); setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_HDMI); setbits_le32(&ccm->hdmi_slow_clk_cfg, CCM_HDMI_SLOW_CTRL_DDC_GATE); /* Clock on */ setbits_le32(&ccm->hdmi_clk_cfg, CCM_HDMI_CTRL_GATE); sunxi_dw_hdmi_phy_init(); ret = sunxi_dw_hdmi_wait_for_hpd(); if (ret < 0) { debug("hdmi can not get hpd signal\n"); return -1; } priv->hdmi.ioaddr = SUNXI_HDMI_BASE; priv->hdmi.i2c_clk_high = 0xd8; priv->hdmi.i2c_clk_low = 0xfe; priv->hdmi.reg_io_width = 1; priv->hdmi.phy_set = sunxi_dw_hdmi_phy_cfg; priv->mux = uc_plat->source_id; dw_hdmi_init(&priv->hdmi); return 0; }
/* * LCDC, what allwinner calls a CRTC, so timing controller and serializer. */ static void sunxi_lcdc_pll_set(int tcon, int dotclock, int *clk_div, int *clk_double) { struct sunxi_ccm_reg * const ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; int value, n, m, min_m, max_m, diff; int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF; int best_double = 0; bool use_mipi_pll = false; if (tcon == 0) { #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL min_m = 6; max_m = 127; #endif #ifdef CONFIG_VIDEO_LCD_IF_LVDS min_m = max_m = 7; #endif } else { min_m = 1; max_m = 15; } /* * Find the lowest divider resulting in a matching clock, if there * is no match, pick the closest lower clock, as monitors tend to * not sync to higher frequencies. */ for (m = min_m; m <= max_m; m++) { n = (m * dotclock) / 3000; if ((n >= 9) && (n <= 127)) { value = (3000 * n) / m; diff = dotclock - value; if (diff < best_diff) { best_diff = diff; best_m = m; best_n = n; best_double = 0; } } /* These are just duplicates */ if (!(m & 1)) continue; n = (m * dotclock) / 6000; if ((n >= 9) && (n <= 127)) { value = (6000 * n) / m; diff = dotclock - value; if (diff < best_diff) { best_diff = diff; best_m = m; best_n = n; best_double = 1; } } } #ifdef CONFIG_MACH_SUN6I /* * Use the MIPI pll if we've been unable to find any matching setting * for PLL3, this happens with high dotclocks because of min_m = 6. */ if (tcon == 0 && best_n == 0) { use_mipi_pll = true; best_m = 6; /* Minimum m for tcon0 */ } if (use_mipi_pll) { clock_set_pll3(297000000); /* Fix the video pll at 297 MHz */ clock_set_mipi_pll(best_m * dotclock * 1000); debug("dotclock: %dkHz = %dkHz via mipi pll\n", dotclock, clock_get_mipi_pll() / best_m / 1000); } else #endif { clock_set_pll3(best_n * 3000000); debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n", dotclock, (best_double + 1) * clock_get_pll3() / best_m / 1000, best_double + 1, best_n, best_m); } if (tcon == 0) { u32 pll; if (use_mipi_pll) pll = CCM_LCD_CH0_CTRL_MIPI_PLL; else if (best_double) pll = CCM_LCD_CH0_CTRL_PLL3_2X; else pll = CCM_LCD_CH0_CTRL_PLL3; writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll, &ccm->lcd0_ch0_clk_cfg); } else { writel(CCM_LCD_CH1_CTRL_GATE | (best_double ? CCM_LCD_CH1_CTRL_PLL3_2X : CCM_LCD_CH1_CTRL_PLL3) | CCM_LCD_CH1_CTRL_M(best_m), &ccm->lcd0_ch1_clk_cfg); if (sunxi_is_composite()) setbits_le32(&ccm->lcd0_ch1_clk_cfg, CCM_LCD_CH1_CTRL_HALF_SCLK1); } *clk_div = best_m; *clk_double = best_double; }