2 * Timing controller driver for Allwinner SoCs.
4 * (C) Copyright 2013-2014 Luc Verhaegen <libv@skynet.be>
5 * (C) Copyright 2014-2015 Hans de Goede <hdegoede@redhat.com>
6 * (C) Copyright 2017 Jernej Skrabec <jernej.skrabec@siol.net>
8 * SPDX-License-Identifier: GPL-2.0+
13 #include <asm/arch/clock.h>
14 #include <asm/arch/lcdc.h>
17 static int lcdc_get_clk_delay(const struct display_timing
*mode
, int tcon
)
21 delay
= mode
->vfront_porch
.typ
+ mode
->vsync_len
.typ
+
22 mode
->vback_porch
.typ
;
23 if (mode
->flags
& DISPLAY_FLAGS_INTERLACED
)
28 return (delay
> 30) ? 30 : delay
;
31 void lcdc_init(struct sunxi_lcdc_reg
* const lcdc
)
34 writel(0, &lcdc
->ctrl
); /* Disable tcon */
35 writel(0, &lcdc
->int0
); /* Disable all interrupts */
37 /* Disable tcon0 dot clock */
38 clrbits_le32(&lcdc
->tcon0_dclk
, SUNXI_LCDC_TCON0_DCLK_ENABLE
);
40 /* Set all io lines to tristate */
41 writel(0xffffffff, &lcdc
->tcon0_io_tristate
);
42 writel(0xffffffff, &lcdc
->tcon1_io_tristate
);
45 void lcdc_enable(struct sunxi_lcdc_reg
* const lcdc
, int depth
)
47 setbits_le32(&lcdc
->ctrl
, SUNXI_LCDC_CTRL_TCON_ENABLE
);
48 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
49 setbits_le32(&lcdc
->tcon0_lvds_intf
, SUNXI_LCDC_TCON0_LVDS_INTF_ENABLE
);
50 setbits_le32(&lcdc
->lvds_ana0
, SUNXI_LCDC_LVDS_ANA0
);
51 #ifdef CONFIG_SUNXI_GEN_SUN6I
52 udelay(2); /* delay at least 1200 ns */
53 setbits_le32(&lcdc
->lvds_ana0
, SUNXI_LCDC_LVDS_ANA0_EN_MB
);
54 udelay(2); /* delay at least 1200 ns */
55 setbits_le32(&lcdc
->lvds_ana0
, SUNXI_LCDC_LVDS_ANA0_DRVC
);
57 setbits_le32(&lcdc
->lvds_ana0
, SUNXI_LCDC_LVDS_ANA0_DRVD(0x7));
59 setbits_le32(&lcdc
->lvds_ana0
, SUNXI_LCDC_LVDS_ANA0_DRVD(0xf));
61 setbits_le32(&lcdc
->lvds_ana0
, SUNXI_LCDC_LVDS_ANA0_UPDATE
);
62 udelay(2); /* delay at least 1200 ns */
63 setbits_le32(&lcdc
->lvds_ana1
, SUNXI_LCDC_LVDS_ANA1_INIT1
);
64 udelay(1); /* delay at least 120 ns */
65 setbits_le32(&lcdc
->lvds_ana1
, SUNXI_LCDC_LVDS_ANA1_INIT2
);
66 setbits_le32(&lcdc
->lvds_ana0
, SUNXI_LCDC_LVDS_ANA0_UPDATE
);
71 void lcdc_tcon0_mode_set(struct sunxi_lcdc_reg
* const lcdc
,
72 const struct display_timing
*mode
,
73 int clk_div
, bool for_ext_vga_dac
,
74 int depth
, int dclk_phase
)
76 int bp
, clk_delay
, total
, val
;
78 #ifndef CONFIG_SUNXI_DE2
80 clrsetbits_le32(&lcdc
->ctrl
, SUNXI_LCDC_CTRL_IO_MAP_MASK
,
81 SUNXI_LCDC_CTRL_IO_MAP_TCON0
);
84 clk_delay
= lcdc_get_clk_delay(mode
, 0);
85 writel(SUNXI_LCDC_TCON0_CTRL_ENABLE
|
86 SUNXI_LCDC_TCON0_CTRL_CLK_DELAY(clk_delay
), &lcdc
->tcon0_ctrl
);
88 writel(SUNXI_LCDC_TCON0_DCLK_ENABLE
|
89 SUNXI_LCDC_TCON0_DCLK_DIV(clk_div
), &lcdc
->tcon0_dclk
);
91 writel(SUNXI_LCDC_X(mode
->hactive
.typ
) |
92 SUNXI_LCDC_Y(mode
->vactive
.typ
), &lcdc
->tcon0_timing_active
);
94 bp
= mode
->hsync_len
.typ
+ mode
->hback_porch
.typ
;
95 total
= mode
->hactive
.typ
+ mode
->hfront_porch
.typ
+ bp
;
96 writel(SUNXI_LCDC_TCON0_TIMING_H_TOTAL(total
) |
97 SUNXI_LCDC_TCON0_TIMING_H_BP(bp
), &lcdc
->tcon0_timing_h
);
99 bp
= mode
->vsync_len
.typ
+ mode
->vback_porch
.typ
;
100 total
= mode
->vactive
.typ
+ mode
->vfront_porch
.typ
+ bp
;
101 writel(SUNXI_LCDC_TCON0_TIMING_V_TOTAL(total
) |
102 SUNXI_LCDC_TCON0_TIMING_V_BP(bp
), &lcdc
->tcon0_timing_v
);
104 #if defined(CONFIG_VIDEO_LCD_IF_PARALLEL) || defined(CONFIG_VIDEO_DE2)
105 writel(SUNXI_LCDC_X(mode
->hsync_len
.typ
) |
106 SUNXI_LCDC_Y(mode
->vsync_len
.typ
), &lcdc
->tcon0_timing_sync
);
108 writel(0, &lcdc
->tcon0_hv_intf
);
109 writel(0, &lcdc
->tcon0_cpu_intf
);
111 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
112 val
= (depth
== 18) ? 1 : 0;
113 writel(SUNXI_LCDC_TCON0_LVDS_INTF_BITWIDTH(val
) |
114 SUNXI_LCDC_TCON0_LVDS_CLK_SEL_TCON0
, &lcdc
->tcon0_lvds_intf
);
117 if (depth
== 18 || depth
== 16) {
118 writel(SUNXI_LCDC_TCON0_FRM_SEED
, &lcdc
->tcon0_frm_seed
[0]);
119 writel(SUNXI_LCDC_TCON0_FRM_SEED
, &lcdc
->tcon0_frm_seed
[1]);
120 writel(SUNXI_LCDC_TCON0_FRM_SEED
, &lcdc
->tcon0_frm_seed
[2]);
121 writel(SUNXI_LCDC_TCON0_FRM_SEED
, &lcdc
->tcon0_frm_seed
[3]);
122 writel(SUNXI_LCDC_TCON0_FRM_SEED
, &lcdc
->tcon0_frm_seed
[4]);
123 writel(SUNXI_LCDC_TCON0_FRM_SEED
, &lcdc
->tcon0_frm_seed
[5]);
124 writel(SUNXI_LCDC_TCON0_FRM_TAB0
, &lcdc
->tcon0_frm_table
[0]);
125 writel(SUNXI_LCDC_TCON0_FRM_TAB1
, &lcdc
->tcon0_frm_table
[1]);
126 writel(SUNXI_LCDC_TCON0_FRM_TAB2
, &lcdc
->tcon0_frm_table
[2]);
127 writel(SUNXI_LCDC_TCON0_FRM_TAB3
, &lcdc
->tcon0_frm_table
[3]);
128 writel(((depth
== 18) ?
129 SUNXI_LCDC_TCON0_FRM_CTRL_RGB666
:
130 SUNXI_LCDC_TCON0_FRM_CTRL_RGB565
),
131 &lcdc
->tcon0_frm_ctrl
);
134 val
= SUNXI_LCDC_TCON0_IO_POL_DCLK_PHASE(dclk_phase
);
135 if (mode
->flags
& DISPLAY_FLAGS_HSYNC_LOW
)
136 val
|= SUNXI_LCDC_TCON_HSYNC_MASK
;
137 if (mode
->flags
& DISPLAY_FLAGS_VSYNC_LOW
)
138 val
|= SUNXI_LCDC_TCON_VSYNC_MASK
;
140 #ifdef CONFIG_VIDEO_VGA_VIA_LCD_FORCE_SYNC_ACTIVE_HIGH
144 writel(val
, &lcdc
->tcon0_io_polarity
);
146 writel(0, &lcdc
->tcon0_io_tristate
);
149 void lcdc_tcon1_mode_set(struct sunxi_lcdc_reg
* const lcdc
,
150 const struct display_timing
*mode
,
151 bool ext_hvsync
, bool is_composite
)
153 int bp
, clk_delay
, total
, val
, yres
;
155 #ifndef CONFIG_SUNXI_DE2
157 clrsetbits_le32(&lcdc
->ctrl
, SUNXI_LCDC_CTRL_IO_MAP_MASK
,
158 SUNXI_LCDC_CTRL_IO_MAP_TCON1
);
161 clk_delay
= lcdc_get_clk_delay(mode
, 1);
162 writel(SUNXI_LCDC_TCON1_CTRL_ENABLE
|
163 ((mode
->flags
& DISPLAY_FLAGS_INTERLACED
) ?
164 SUNXI_LCDC_TCON1_CTRL_INTERLACE_ENABLE
: 0) |
165 SUNXI_LCDC_TCON1_CTRL_CLK_DELAY(clk_delay
), &lcdc
->tcon1_ctrl
);
167 yres
= mode
->vactive
.typ
;
168 if (mode
->flags
& DISPLAY_FLAGS_INTERLACED
)
170 writel(SUNXI_LCDC_X(mode
->hactive
.typ
) | SUNXI_LCDC_Y(yres
),
171 &lcdc
->tcon1_timing_source
);
172 writel(SUNXI_LCDC_X(mode
->hactive
.typ
) | SUNXI_LCDC_Y(yres
),
173 &lcdc
->tcon1_timing_scale
);
174 writel(SUNXI_LCDC_X(mode
->hactive
.typ
) | SUNXI_LCDC_Y(yres
),
175 &lcdc
->tcon1_timing_out
);
177 bp
= mode
->hsync_len
.typ
+ mode
->hback_porch
.typ
;
178 total
= mode
->hactive
.typ
+ mode
->hfront_porch
.typ
+ bp
;
179 writel(SUNXI_LCDC_TCON1_TIMING_H_TOTAL(total
) |
180 SUNXI_LCDC_TCON1_TIMING_H_BP(bp
), &lcdc
->tcon1_timing_h
);
182 bp
= mode
->vsync_len
.typ
+ mode
->vback_porch
.typ
;
183 total
= mode
->vactive
.typ
+ mode
->vfront_porch
.typ
+ bp
;
184 if (!(mode
->flags
& DISPLAY_FLAGS_INTERLACED
))
186 writel(SUNXI_LCDC_TCON1_TIMING_V_TOTAL(total
) |
187 SUNXI_LCDC_TCON1_TIMING_V_BP(bp
), &lcdc
->tcon1_timing_v
);
189 writel(SUNXI_LCDC_X(mode
->hsync_len
.typ
) |
190 SUNXI_LCDC_Y(mode
->vsync_len
.typ
), &lcdc
->tcon1_timing_sync
);
194 if (mode
->flags
& DISPLAY_FLAGS_HSYNC_HIGH
)
195 val
|= SUNXI_LCDC_TCON_HSYNC_MASK
;
196 if (mode
->flags
& DISPLAY_FLAGS_VSYNC_HIGH
)
197 val
|= SUNXI_LCDC_TCON_VSYNC_MASK
;
198 writel(val
, &lcdc
->tcon1_io_polarity
);
200 clrbits_le32(&lcdc
->tcon1_io_tristate
,
201 SUNXI_LCDC_TCON_VSYNC_MASK
|
202 SUNXI_LCDC_TCON_HSYNC_MASK
);
205 #ifdef CONFIG_MACH_SUN5I
207 clrsetbits_le32(&lcdc
->mux_ctrl
, SUNXI_LCDC_MUX_CTRL_SRC0_MASK
,
208 SUNXI_LCDC_MUX_CTRL_SRC0(1));
212 void lcdc_pll_set(struct sunxi_ccm_reg
*ccm
, int tcon
, int dotclock
,
213 int *clk_div
, int *clk_double
, bool is_composite
)
215 int value
, n
, m
, min_m
, max_m
, diff
;
216 int best_n
= 0, best_m
= 0, best_diff
= 0x0FFFFFFF;
218 bool use_mipi_pll
= false;
221 #if defined(CONFIG_VIDEO_LCD_IF_PARALLEL) || defined(CONFIG_SUNXI_DE2)
225 #ifdef CONFIG_VIDEO_LCD_IF_LVDS
235 * Find the lowest divider resulting in a matching clock, if there
236 * is no match, pick the closest lower clock, as monitors tend to
237 * not sync to higher frequencies.
239 for (m
= min_m
; m
<= max_m
; m
++) {
240 #ifndef CONFIG_SUNXI_DE2
241 n
= (m
* dotclock
) / 3000;
243 if ((n
>= 9) && (n
<= 127)) {
244 value
= (3000 * n
) / m
;
245 diff
= dotclock
- value
;
246 if (diff
< best_diff
) {
254 /* These are just duplicates */
259 /* No double clock on DE2 */
260 n
= (m
* dotclock
) / 6000;
261 if ((n
>= 9) && (n
<= 127)) {
262 value
= (6000 * n
) / m
;
263 diff
= dotclock
- value
;
264 if (diff
< best_diff
) {
273 #ifdef CONFIG_MACH_SUN6I
275 * Use the MIPI pll if we've been unable to find any matching setting
276 * for PLL3, this happens with high dotclocks because of min_m = 6.
278 if (tcon
== 0 && best_n
== 0) {
280 best_m
= 6; /* Minimum m for tcon0 */
284 clock_set_pll3(297000000); /* Fix the video pll at 297 MHz */
285 clock_set_mipi_pll(best_m
* dotclock
* 1000);
286 debug("dotclock: %dkHz = %dkHz via mipi pll\n",
287 dotclock
, clock_get_mipi_pll() / best_m
/ 1000);
291 clock_set_pll3(best_n
* 3000000);
292 debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n",
294 (best_double
+ 1) * clock_get_pll3() / best_m
/ 1000,
295 best_double
+ 1, best_n
, best_m
);
302 pll
= CCM_LCD_CH0_CTRL_MIPI_PLL
;
303 else if (best_double
)
304 pll
= CCM_LCD_CH0_CTRL_PLL3_2X
;
306 pll
= CCM_LCD_CH0_CTRL_PLL3
;
307 #ifndef CONFIG_SUNXI_DE2
308 writel(CCM_LCD_CH0_CTRL_GATE
| CCM_LCD_CH0_CTRL_RST
| pll
,
309 &ccm
->lcd0_ch0_clk_cfg
);
311 writel(CCM_LCD_CH0_CTRL_GATE
| CCM_LCD_CH0_CTRL_RST
| pll
,
315 #ifndef CONFIG_SUNXI_DE2
317 writel(CCM_LCD_CH1_CTRL_GATE
|
318 (best_double
? CCM_LCD_CH1_CTRL_PLL3_2X
:
319 CCM_LCD_CH1_CTRL_PLL3
) |
320 CCM_LCD_CH1_CTRL_M(best_m
), &ccm
->lcd0_ch1_clk_cfg
);
322 setbits_le32(&ccm
->lcd0_ch1_clk_cfg
,
323 CCM_LCD_CH1_CTRL_HALF_SCLK1
);
328 *clk_double
= best_double
;