|
|
|
@ -516,119 +516,6 @@ static void sunxi_composer_enable(void) |
|
|
|
|
setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void sunxi_lcdc_init(void) |
|
|
|
|
{ |
|
|
|
|
struct sunxi_ccm_reg * const ccm = |
|
|
|
@ -755,6 +642,8 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, |
|
|
|
|
{ |
|
|
|
|
struct sunxi_lcdc_reg * const lcdc = |
|
|
|
|
(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; |
|
|
|
|
struct sunxi_ccm_reg * const ccm = |
|
|
|
|
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
|
|
|
|
int clk_div, clk_double, pin; |
|
|
|
|
struct display_timing timing; |
|
|
|
|
|
|
|
|
@ -774,7 +663,8 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double); |
|
|
|
|
lcdc_pll_set(ccm, 0, mode->pixclock_khz, &clk_div, &clk_double, |
|
|
|
|
sunxi_is_composite()); |
|
|
|
|
|
|
|
|
|
sunxi_ctfb_mode_to_display_timing(mode, &timing); |
|
|
|
|
lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac, |
|
|
|
@ -788,6 +678,8 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, |
|
|
|
|
{ |
|
|
|
|
struct sunxi_lcdc_reg * const lcdc = |
|
|
|
|
(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; |
|
|
|
|
struct sunxi_ccm_reg * const ccm = |
|
|
|
|
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE; |
|
|
|
|
struct display_timing timing; |
|
|
|
|
|
|
|
|
|
sunxi_ctfb_mode_to_display_timing(mode, &timing); |
|
|
|
@ -799,7 +691,8 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, |
|
|
|
|
sunxi_gpio_set_cfgpin(SUNXI_GPD(27), SUNXI_GPD_LCD0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double); |
|
|
|
|
lcdc_pll_set(ccm, 1, mode->pixclock_khz, clk_div, clk_double, |
|
|
|
|
sunxi_is_composite()); |
|
|
|
|
} |
|
|
|
|
#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */ |
|
|
|
|
|
|
|
|
|