sunxi: video: split out PLL configuration code
It will be reused in new DM LCD driver. Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
This commit is contained in:
committed by
Anatolij Gustschin
parent
491041c749
commit
79f285ddeb
@@ -124,5 +124,8 @@ void lcdc_tcon0_mode_set(struct sunxi_lcdc_reg * const lcdc,
|
|||||||
void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc,
|
void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc,
|
||||||
const struct display_timing *mode,
|
const struct display_timing *mode,
|
||||||
bool ext_hvsync, bool is_composite);
|
bool ext_hvsync, bool is_composite);
|
||||||
|
void lcdc_pll_set(struct sunxi_ccm_reg * const ccm, int tcon,
|
||||||
|
int dotclock, int *clk_div, int *clk_double,
|
||||||
|
bool is_composite);
|
||||||
|
|
||||||
#endif /* _LCDC_H */
|
#endif /* _LCDC_H */
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
|
|
||||||
|
#include <asm/arch/clock.h>
|
||||||
#include <asm/arch/lcdc.h>
|
#include <asm/arch/lcdc.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ void lcdc_tcon0_mode_set(struct sunxi_lcdc_reg * const lcdc,
|
|||||||
writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
|
writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total) |
|
||||||
SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
|
SUNXI_LCDC_TCON0_TIMING_V_BP(bp), &lcdc->tcon0_timing_v);
|
||||||
|
|
||||||
#ifdef CONFIG_VIDEO_LCD_IF_PARALLEL
|
#if defined(CONFIG_VIDEO_LCD_IF_PARALLEL) || defined(CONFIG_VIDEO_DE2)
|
||||||
writel(SUNXI_LCDC_X(mode->hsync_len.typ) |
|
writel(SUNXI_LCDC_X(mode->hsync_len.typ) |
|
||||||
SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon0_timing_sync);
|
SUNXI_LCDC_Y(mode->vsync_len.typ), &lcdc->tcon0_timing_sync);
|
||||||
|
|
||||||
@@ -207,3 +208,122 @@ void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg * const lcdc,
|
|||||||
SUNXI_LCDC_MUX_CTRL_SRC0(1));
|
SUNXI_LCDC_MUX_CTRL_SRC0(1));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lcdc_pll_set(struct sunxi_ccm_reg *ccm, int tcon, int dotclock,
|
||||||
|
int *clk_div, int *clk_double, bool is_composite)
|
||||||
|
{
|
||||||
|
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) {
|
||||||
|
#if defined(CONFIG_VIDEO_LCD_IF_PARALLEL) || defined(CONFIG_SUNXI_DE2)
|
||||||
|
min_m = 6;
|
||||||
|
max_m = 127;
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_VIDEO_LCD_IF_LVDS
|
||||||
|
min_m = 7;
|
||||||
|
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++) {
|
||||||
|
#ifndef CONFIG_SUNXI_DE2
|
||||||
|
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;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* No double clock on DE2 */
|
||||||
|
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;
|
||||||
|
#ifndef CONFIG_SUNXI_DE2
|
||||||
|
writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll,
|
||||||
|
&ccm->lcd0_ch0_clk_cfg);
|
||||||
|
#else
|
||||||
|
writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll,
|
||||||
|
&ccm->lcd0_clk_cfg);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifndef CONFIG_SUNXI_DE2
|
||||||
|
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 (is_composite)
|
||||||
|
setbits_le32(&ccm->lcd0_ch1_clk_cfg,
|
||||||
|
CCM_LCD_CH1_CTRL_HALF_SCLK1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
*clk_div = best_m;
|
||||||
|
*clk_double = best_double;
|
||||||
|
}
|
||||||
|
|||||||
@@ -516,119 +516,6 @@ static void sunxi_composer_enable(void)
|
|||||||
setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_START);
|
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)
|
static void sunxi_lcdc_init(void)
|
||||||
{
|
{
|
||||||
struct sunxi_ccm_reg * const ccm =
|
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 * const lcdc =
|
||||||
(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
|
(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;
|
int clk_div, clk_double, pin;
|
||||||
struct display_timing timing;
|
struct display_timing timing;
|
||||||
|
|
||||||
@@ -774,7 +663,8 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode,
|
|||||||
#endif
|
#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);
|
sunxi_ctfb_mode_to_display_timing(mode, &timing);
|
||||||
lcdc_tcon0_mode_set(lcdc, &timing, clk_div, for_ext_vga_dac,
|
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 * const lcdc =
|
||||||
(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
|
(struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE;
|
||||||
|
struct sunxi_ccm_reg * const ccm =
|
||||||
|
(struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
|
||||||
struct display_timing timing;
|
struct display_timing timing;
|
||||||
|
|
||||||
sunxi_ctfb_mode_to_display_timing(mode, &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_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 */
|
#endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user