blob: a31f403a841e895136e5f0bd15f38ac6924767ce [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +01002/*
Hannes Schmelzere880a5e2018-01-09 19:01:32 +01003 * Copyright (C) 2013-2018 Hannes Schmelzer <oe5hpm@oevsv.at>
4 * B&R Industrial Automation GmbH - http://www.br-automation.com
Dario Binacchi96b109b2020-02-22 14:05:45 +01005 * Copyright (C) 2020 Dario Binacchi <dariobin@libero.it>
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +01006 *
7 * minimal framebuffer driver for TI's AM335x SoC to be compatible with
8 * Wolfgang Denk's LCD-Framework (CONFIG_LCD, common/lcd.c)
9 *
Martin Pietryka7d045172016-04-27 21:39:15 +020010 * - supporting 16/24/32bit RGB/TFT raster Mode (not using palette)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010011 * - sets up LCD controller as in 'am335x_lcdpanel' struct given
12 * - starts output DMA from gd->fb_base buffer
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010013 */
14#include <common.h>
Dario Binacchi91337f52020-12-30 00:16:29 +010015#include <clk.h>
Dario Binacchi96b109b2020-02-22 14:05:45 +010016#include <dm.h>
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010017#include <lcd.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060018#include <log.h>
Dario Binacchiff94c152020-12-30 00:16:27 +010019#include <panel.h>
Dario Binacchi96b109b2020-02-22 14:05:45 +010020#include <video.h>
Dario Binacchi295ab882020-05-03 21:27:48 +020021#include <asm/arch/clock.h>
22#include <asm/arch/hardware.h>
23#include <asm/arch/omap.h>
24#include <asm/arch/sys_proto.h>
25#include <asm/io.h>
26#include <asm/utils.h>
Simon Glassc05ed002020-05-10 11:40:11 -060027#include <linux/delay.h>
Dario Binacchi295ab882020-05-03 21:27:48 +020028#include <linux/err.h>
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010029#include "am335x-fb.h"
Dario Binacchiff94c152020-12-30 00:16:27 +010030#include "tilcdc-panel.h"
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010031
Hannes Schmelzer8a094f52018-01-09 19:01:34 +010032#define LCDC_FMAX 200000000
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010033
34/* LCD Control Register */
Dario Binacchia9df3c52020-02-22 14:05:44 +010035#define LCDC_CTRL_CLK_DIVISOR_MASK GENMASK(15, 8)
Dario Binacchi41f76a02020-02-22 14:05:41 +010036#define LCDC_CTRL_RASTER_MODE BIT(0)
37#define LCDC_CTRL_CLK_DIVISOR(x) (((x) & GENMASK(7, 0)) << 8)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010038/* LCD Clock Enable Register */
Dario Binacchi41f76a02020-02-22 14:05:41 +010039#define LCDC_CLKC_ENABLE_CORECLKEN BIT(0)
40#define LCDC_CLKC_ENABLE_LIDDCLKEN BIT(1)
41#define LCDC_CLKC_ENABLE_DMACLKEN BIT(2)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010042/* LCD DMA Control Register */
Dario Binacchi41f76a02020-02-22 14:05:41 +010043#define LCDC_DMA_CTRL_BURST_SIZE(x) (((x) & GENMASK(2, 0)) << 4)
44#define LCDC_DMA_CTRL_BURST_1 0x0
45#define LCDC_DMA_CTRL_BURST_2 0x1
46#define LCDC_DMA_CTRL_BURST_4 0x2
47#define LCDC_DMA_CTRL_BURST_8 0x3
48#define LCDC_DMA_CTRL_BURST_16 0x4
Dario Binacchi96b109b2020-02-22 14:05:45 +010049#define LCDC_DMA_CTRL_FIFO_TH(x) (((x) & GENMASK(2, 0)) << 8)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010050/* LCD Timing_0 Register */
Dario Binacchi0aff8e22020-02-22 14:05:48 +010051#define LCDC_RASTER_TIMING_0_HORMSB(x) ((((x) - 1) & BIT(10)) >> 7)
Dario Binacchi41f76a02020-02-22 14:05:41 +010052#define LCDC_RASTER_TIMING_0_HORLSB(x) (((((x) >> 4) - 1) & GENMASK(5, 0)) << 4)
53#define LCDC_RASTER_TIMING_0_HSWLSB(x) ((((x) - 1) & GENMASK(5, 0)) << 10)
54#define LCDC_RASTER_TIMING_0_HFPLSB(x) ((((x) - 1) & GENMASK(7, 0)) << 16)
55#define LCDC_RASTER_TIMING_0_HBPLSB(x) ((((x) - 1) & GENMASK(7, 0)) << 24)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010056/* LCD Timing_1 Register */
Dario Binacchi41f76a02020-02-22 14:05:41 +010057#define LCDC_RASTER_TIMING_1_VERLSB(x) (((x) - 1) & GENMASK(9, 0))
58#define LCDC_RASTER_TIMING_1_VSW(x) ((((x) - 1) & GENMASK(5, 0)) << 10)
59#define LCDC_RASTER_TIMING_1_VFP(x) (((x) & GENMASK(7, 0)) << 16)
60#define LCDC_RASTER_TIMING_1_VBP(x) (((x) & GENMASK(7, 0)) << 24)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010061/* LCD Timing_2 Register */
Dario Binacchi41f76a02020-02-22 14:05:41 +010062#define LCDC_RASTER_TIMING_2_HFPMSB(x) ((((x) - 1) & GENMASK(9, 8)) >> 8)
63#define LCDC_RASTER_TIMING_2_HBPMSB(x) ((((x) - 1) & GENMASK(9, 8)) >> 4)
Dario Binacchi96b109b2020-02-22 14:05:45 +010064#define LCDC_RASTER_TIMING_2_ACB(x) (((x) & GENMASK(7, 0)) << 8)
65#define LCDC_RASTER_TIMING_2_ACBI(x) (((x) & GENMASK(3, 0)) << 16)
66#define LCDC_RASTER_TIMING_2_VSYNC_INVERT BIT(20)
67#define LCDC_RASTER_TIMING_2_HSYNC_INVERT BIT(21)
68#define LCDC_RASTER_TIMING_2_PXCLK_INVERT BIT(22)
69#define LCDC_RASTER_TIMING_2_DE_INVERT BIT(23)
70#define LCDC_RASTER_TIMING_2_HSVS_RISEFALL BIT(24)
71#define LCDC_RASTER_TIMING_2_HSVS_CONTROL BIT(25)
Dario Binacchi41f76a02020-02-22 14:05:41 +010072#define LCDC_RASTER_TIMING_2_VERMSB(x) ((((x) - 1) & BIT(10)) << 16)
73#define LCDC_RASTER_TIMING_2_HSWMSB(x) ((((x) - 1) & GENMASK(9, 6)) << 21)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010074/* LCD Raster Ctrl Register */
Dario Binacchi41f76a02020-02-22 14:05:41 +010075#define LCDC_RASTER_CTRL_ENABLE BIT(0)
76#define LCDC_RASTER_CTRL_TFT_MODE BIT(7)
Dario Binacchi96b109b2020-02-22 14:05:45 +010077#define LCDC_RASTER_CTRL_DATA_ORDER BIT(8)
78#define LCDC_RASTER_CTRL_REQDLY(x) (((x) & GENMASK(7, 0)) << 12)
Dario Binacchi41f76a02020-02-22 14:05:41 +010079#define LCDC_RASTER_CTRL_PALMODE_RAWDATA (0x02 << 20)
Dario Binacchi96b109b2020-02-22 14:05:45 +010080#define LCDC_RASTER_CTRL_TFT_ALT_ENABLE BIT(23)
Dario Binacchi41f76a02020-02-22 14:05:41 +010081#define LCDC_RASTER_CTRL_TFT_24BPP_MODE BIT(25)
82#define LCDC_RASTER_CTRL_TFT_24BPP_UNPACK BIT(26)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010083
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010084struct am335x_lcdhw {
85 unsigned int pid; /* 0x00 */
86 unsigned int ctrl; /* 0x04 */
87 unsigned int gap0; /* 0x08 */
88 unsigned int lidd_ctrl; /* 0x0C */
89 unsigned int lidd_cs0_conf; /* 0x10 */
90 unsigned int lidd_cs0_addr; /* 0x14 */
91 unsigned int lidd_cs0_data; /* 0x18 */
92 unsigned int lidd_cs1_conf; /* 0x1C */
93 unsigned int lidd_cs1_addr; /* 0x20 */
94 unsigned int lidd_cs1_data; /* 0x24 */
95 unsigned int raster_ctrl; /* 0x28 */
96 unsigned int raster_timing0; /* 0x2C */
97 unsigned int raster_timing1; /* 0x30 */
98 unsigned int raster_timing2; /* 0x34 */
99 unsigned int raster_subpanel; /* 0x38 */
100 unsigned int raster_subpanel2; /* 0x3C */
101 unsigned int lcddma_ctrl; /* 0x40 */
102 unsigned int lcddma_fb0_base; /* 0x44 */
103 unsigned int lcddma_fb0_ceiling; /* 0x48 */
104 unsigned int lcddma_fb1_base; /* 0x4C */
105 unsigned int lcddma_fb1_ceiling; /* 0x50 */
106 unsigned int sysconfig; /* 0x54 */
107 unsigned int irqstatus_raw; /* 0x58 */
108 unsigned int irqstatus; /* 0x5C */
109 unsigned int irqenable_set; /* 0x60 */
110 unsigned int irqenable_clear; /* 0x64 */
111 unsigned int gap1; /* 0x68 */
112 unsigned int clkc_enable; /* 0x6C */
113 unsigned int clkc_reset; /* 0x70 */
114};
115
Dario Binacchi91337f52020-12-30 00:16:29 +0100116DECLARE_GLOBAL_DATA_PTR;
117
118#if !CONFIG_IS_ENABLED(DM_VIDEO)
119
120#if !defined(LCD_CNTL_BASE)
121#error "hw-base address of LCD-Controller (LCD_CNTL_BASE) not defined!"
122#endif
123
124/* Macro definitions */
125#define FBSIZE(x) (((x)->hactive * (x)->vactive * (x)->bpp) >> 3)
126
127#define LCDC_RASTER_TIMING_2_INVMASK(x) ((x) & GENMASK(25, 20))
128
129static struct am335x_lcdhw *lcdhw = (void *)LCD_CNTL_BASE;
130
131int lcd_get_size(int *line_length)
132{
133 *line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8;
134 return *line_length * panel_info.vl_row + 0x20;
135}
136
Dario Binacchia9df3c52020-02-22 14:05:44 +0100137struct dpll_data {
138 unsigned long rounded_rate;
139 u16 rounded_m;
140 u8 rounded_n;
141 u8 rounded_div;
142};
143
Dario Binacchia9df3c52020-02-22 14:05:44 +0100144/**
145 * am335x_dpll_round_rate() - Round a target rate for an OMAP DPLL
146 *
147 * @dpll_data: struct dpll_data pointer for the DPLL
148 * @rate: New DPLL clock rate
149 * @return rounded rate and the computed m, n and div values in the dpll_data
150 * structure, or -ve error code.
151 */
152static ulong am335x_dpll_round_rate(struct dpll_data *dd, ulong rate)
153{
154 unsigned int m, n, d;
155 unsigned long rounded_rate;
156 int err, err_r;
157
158 dd->rounded_rate = -EFAULT;
159 err = rate;
160 err_r = err;
161
162 for (d = 2; err && d < 255; d++) {
163 for (m = 2; m < 2047; m++) {
164 if ((V_OSCK * m) < (rate * d))
165 continue;
166
167 n = (V_OSCK * m) / (rate * d);
168 if (n > 127)
169 break;
170
171 if (((V_OSCK * m) / n) > LCDC_FMAX)
172 break;
173
174 rounded_rate = (V_OSCK * m) / n / d;
175 err = abs(rounded_rate - rate);
176 if (err < err_r) {
177 err_r = err;
178 dd->rounded_rate = rounded_rate;
179 dd->rounded_m = m;
180 dd->rounded_n = n;
181 dd->rounded_div = d;
182 if (err == 0)
183 break;
184 }
185 }
186 }
187
188 debug("DPLL display: best error %d Hz (M %d, N %d, DIV %d)\n",
189 err_r, dd->rounded_m, dd->rounded_n, dd->rounded_div);
190
191 return dd->rounded_rate;
192}
193
194/**
195 * am335x_fb_set_pixel_clk_rate() - Set pixel clock rate.
196 *
197 * @am335x_lcdhw: Base address of the LCD controller registers.
198 * @rate: New clock rate in Hz.
199 * @return new rate, or -ve error code.
200 */
201static ulong am335x_fb_set_pixel_clk_rate(struct am335x_lcdhw *regs, ulong rate)
202{
203 struct dpll_params dpll_disp = { 1, 0, 1, -1, -1, -1, -1 };
204 struct dpll_data dd;
205 ulong round_rate;
206 u32 reg;
207
208 round_rate = am335x_dpll_round_rate(&dd, rate);
209 if (IS_ERR_VALUE(round_rate))
210 return round_rate;
211
212 dpll_disp.m = dd.rounded_m;
213 dpll_disp.n = dd.rounded_n;
214 do_setup_dpll(&dpll_disp_regs, &dpll_disp);
215
216 reg = readl(&regs->ctrl) & ~LCDC_CTRL_CLK_DIVISOR_MASK;
217 reg |= LCDC_CTRL_CLK_DIVISOR(dd.rounded_div);
218 writel(reg, &regs->ctrl);
219 return round_rate;
220}
221
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100222int am335xfb_init(struct am335x_lcdpanel *panel)
223{
Martin Pietryka7d045172016-04-27 21:39:15 +0200224 u32 raster_ctrl = 0;
Hannes Schmelzer8a094f52018-01-09 19:01:34 +0100225 struct cm_dpll *const cmdpll = (struct cm_dpll *)CM_DPLL;
Dario Binacchia9df3c52020-02-22 14:05:44 +0100226 ulong rate;
227 u32 reg;
Hannes Schmelzer8a094f52018-01-09 19:01:34 +0100228
Hannes Schmelzer0d8a7d62018-01-09 19:01:33 +0100229 if (gd->fb_base == 0) {
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100230 printf("ERROR: no valid fb_base stored in GLOBAL_DATA_PTR!\n");
231 return -1;
232 }
Hannes Schmelzer0d8a7d62018-01-09 19:01:33 +0100233 if (panel == NULL) {
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100234 printf("ERROR: missing ptr to am335x_lcdpanel!\n");
235 return -1;
236 }
237
Martin Pietryka7d045172016-04-27 21:39:15 +0200238 /* We can already set the bits for the raster_ctrl in this check */
239 switch (panel->bpp) {
240 case 16:
241 break;
242 case 32:
Dario Binacchi41f76a02020-02-22 14:05:41 +0100243 raster_ctrl |= LCDC_RASTER_CTRL_TFT_24BPP_UNPACK;
Martin Pietryka7d045172016-04-27 21:39:15 +0200244 /* fallthrough */
245 case 24:
Dario Binacchi41f76a02020-02-22 14:05:41 +0100246 raster_ctrl |= LCDC_RASTER_CTRL_TFT_24BPP_MODE;
Martin Pietryka7d045172016-04-27 21:39:15 +0200247 break;
248 default:
Masahiro Yamada9b643e32017-09-16 14:10:41 +0900249 pr_err("am335x-fb: invalid bpp value: %d\n", panel->bpp);
Martin Pietryka7d045172016-04-27 21:39:15 +0200250 return -1;
251 }
252
Hannes Schmelzer8a094f52018-01-09 19:01:34 +0100253 /* check given clock-frequency */
254 if (panel->pxl_clk > (LCDC_FMAX / 2)) {
255 pr_err("am335x-fb: requested pxl-clk: %d not supported!\n",
256 panel->pxl_clk);
257 return -1;
258 }
259
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100260 debug("setting up LCD-Controller for %dx%dx%d (hfp=%d,hbp=%d,hsw=%d / ",
261 panel->hactive, panel->vactive, panel->bpp,
262 panel->hfp, panel->hbp, panel->hsw);
Hannes Schmelzer8a094f52018-01-09 19:01:34 +0100263 debug("vfp=%d,vbp=%d,vsw=%d / clk=%d)\n",
264 panel->vfp, panel->vfp, panel->vsw, panel->pxl_clk);
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100265 debug("using frambuffer at 0x%08x with size %d.\n",
266 (unsigned int)gd->fb_base, FBSIZE(panel));
267
Dario Binacchia9df3c52020-02-22 14:05:44 +0100268 rate = am335x_fb_set_pixel_clk_rate(lcdhw, panel->pxl_clk);
269 if (IS_ERR_VALUE(rate))
270 return rate;
Hannes Schmelzer8a094f52018-01-09 19:01:34 +0100271
272 /* clock source for LCDC from dispPLL M2 */
273 writel(0x0, &cmdpll->clklcdcpixelclk);
274
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100275 /* palette default entry */
276 memset((void *)gd->fb_base, 0, 0x20);
277 *(unsigned int *)gd->fb_base = 0x4000;
Martin Pietryka3d47b2d2016-04-27 21:39:16 +0200278 /* point fb behind palette */
279 gd->fb_base += 0x20;
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100280
Hannes Petermaier3b4e16e2015-02-03 13:22:23 +0100281 /* turn ON display through powercontrol function if accessible */
Hannes Schmelzer0d8a7d62018-01-09 19:01:33 +0100282 if (panel->panel_power_ctrl != NULL)
Hannes Petermaier3b4e16e2015-02-03 13:22:23 +0100283 panel->panel_power_ctrl(1);
284
285 debug("am335x-fb: wait for stable power ...\n");
286 mdelay(panel->pup_delay);
Dario Binacchi41f76a02020-02-22 14:05:41 +0100287 lcdhw->clkc_enable = LCDC_CLKC_ENABLE_CORECLKEN |
288 LCDC_CLKC_ENABLE_LIDDCLKEN | LCDC_CLKC_ENABLE_DMACLKEN;
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100289 lcdhw->raster_ctrl = 0;
Dario Binacchia9df3c52020-02-22 14:05:44 +0100290
291 reg = lcdhw->ctrl & LCDC_CTRL_CLK_DIVISOR_MASK;
292 reg |= LCDC_CTRL_RASTER_MODE;
293 lcdhw->ctrl = reg;
294
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100295 lcdhw->lcddma_fb0_base = gd->fb_base;
Martin Pietryka3d47b2d2016-04-27 21:39:16 +0200296 lcdhw->lcddma_fb0_ceiling = gd->fb_base + FBSIZE(panel);
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100297 lcdhw->lcddma_fb1_base = gd->fb_base;
Martin Pietryka3d47b2d2016-04-27 21:39:16 +0200298 lcdhw->lcddma_fb1_ceiling = gd->fb_base + FBSIZE(panel);
Dario Binacchi41f76a02020-02-22 14:05:41 +0100299 lcdhw->lcddma_ctrl = LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_16);
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100300
Dario Binacchi41f76a02020-02-22 14:05:41 +0100301 lcdhw->raster_timing0 = LCDC_RASTER_TIMING_0_HORLSB(panel->hactive) |
302 LCDC_RASTER_TIMING_0_HORMSB(panel->hactive) |
303 LCDC_RASTER_TIMING_0_HFPLSB(panel->hfp) |
304 LCDC_RASTER_TIMING_0_HBPLSB(panel->hbp) |
305 LCDC_RASTER_TIMING_0_HSWLSB(panel->hsw);
306 lcdhw->raster_timing1 = LCDC_RASTER_TIMING_1_VBP(panel->vbp) |
307 LCDC_RASTER_TIMING_1_VFP(panel->vfp) |
308 LCDC_RASTER_TIMING_1_VSW(panel->vsw) |
309 LCDC_RASTER_TIMING_1_VERLSB(panel->vactive);
310 lcdhw->raster_timing2 = LCDC_RASTER_TIMING_2_HSWMSB(panel->hsw) |
311 LCDC_RASTER_TIMING_2_VERMSB(panel->vactive) |
312 LCDC_RASTER_TIMING_2_INVMASK(panel->pol) |
313 LCDC_RASTER_TIMING_2_HBPMSB(panel->hbp) |
314 LCDC_RASTER_TIMING_2_HFPMSB(panel->hfp) |
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100315 0x0000FF00; /* clk cycles for ac-bias */
Martin Pietryka7d045172016-04-27 21:39:15 +0200316 lcdhw->raster_ctrl = raster_ctrl |
Dario Binacchi41f76a02020-02-22 14:05:41 +0100317 LCDC_RASTER_CTRL_PALMODE_RAWDATA |
318 LCDC_RASTER_CTRL_TFT_MODE |
319 LCDC_RASTER_CTRL_ENABLE;
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100320
Hannes Petermaier3b4e16e2015-02-03 13:22:23 +0100321 debug("am335x-fb: waiting picture to be stable.\n.");
322 mdelay(panel->pon_delay);
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100323
324 return 0;
325}
Dario Binacchi96b109b2020-02-22 14:05:45 +0100326
327#else /* CONFIG_DM_VIDEO */
328
Dario Binacchiff94c152020-12-30 00:16:27 +0100329#define FBSIZE(t, p) (((t).hactive.typ * (t).vactive.typ * (p).bpp) >> 3)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100330
331enum {
332 LCD_MAX_WIDTH = 2048,
333 LCD_MAX_HEIGHT = 2048,
334 LCD_MAX_LOG2_BPP = VIDEO_BPP32,
335};
336
Dario Binacchi96b109b2020-02-22 14:05:45 +0100337struct am335x_fb_priv {
338 struct am335x_lcdhw *regs;
Dario Binacchi91337f52020-12-30 00:16:29 +0100339 struct clk gclk;
340 struct clk dpll_m2_clk;
Dario Binacchi96b109b2020-02-22 14:05:45 +0100341};
342
Dario Binacchi91337f52020-12-30 00:16:29 +0100343static ulong tilcdc_set_pixel_clk_rate(struct udevice *dev, ulong rate)
344{
345 struct am335x_fb_priv *priv = dev_get_priv(dev);
346 struct am335x_lcdhw *regs = priv->regs;
347 ulong mult_rate, mult_round_rate, best_err, err;
348 u32 v;
349 int div, i;
350
351 best_err = rate;
352 div = 0;
353 for (i = 2; i <= 255; i++) {
354 mult_rate = rate * i;
355 mult_round_rate = clk_round_rate(&priv->gclk, mult_rate);
356 if (IS_ERR_VALUE(mult_round_rate))
357 return mult_round_rate;
358
359 err = mult_rate - mult_round_rate;
360 if (err < best_err) {
361 best_err = err;
362 div = i;
363 if (err == 0)
364 break;
365 }
366 }
367
368 if (div == 0) {
369 dev_err(dev, "failed to find a divisor\n");
370 return -EFAULT;
371 }
372
373 mult_rate = clk_set_rate(&priv->gclk, rate * div);
374 v = readl(&regs->ctrl) & ~LCDC_CTRL_CLK_DIVISOR_MASK;
375 v |= LCDC_CTRL_CLK_DIVISOR(div);
376 writel(v, &regs->ctrl);
377 rate = mult_rate / div;
378 dev_dbg(dev, "rate=%ld, div=%d, err=%ld\n", rate, div, err);
379 return rate;
380}
381
Dario Binacchi96b109b2020-02-22 14:05:45 +0100382static int am335x_fb_remove(struct udevice *dev)
383{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700384 struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
Dario Binacchi91337f52020-12-30 00:16:29 +0100385 struct am335x_fb_priv *priv = dev_get_priv(dev);
Dario Binacchi96b109b2020-02-22 14:05:45 +0100386
387 uc_plat->base -= 0x20;
388 uc_plat->size += 0x20;
Dario Binacchi91337f52020-12-30 00:16:29 +0100389 clk_release_all(&priv->gclk, 1);
390 clk_release_all(&priv->dpll_m2_clk, 1);
Dario Binacchi96b109b2020-02-22 14:05:45 +0100391 return 0;
392}
393
394static int am335x_fb_probe(struct udevice *dev)
395{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700396 struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
Dario Binacchi96b109b2020-02-22 14:05:45 +0100397 struct video_priv *uc_priv = dev_get_uclass_priv(dev);
398 struct am335x_fb_priv *priv = dev_get_priv(dev);
399 struct am335x_lcdhw *regs = priv->regs;
Dario Binacchi91337f52020-12-30 00:16:29 +0100400 struct udevice *panel, *clk_dev;
Dario Binacchiff94c152020-12-30 00:16:27 +0100401 struct tilcdc_panel_info info;
402 struct display_timing timing;
Dario Binacchi91337f52020-12-30 00:16:29 +0100403 ulong rate;
Dario Binacchi96b109b2020-02-22 14:05:45 +0100404 u32 reg;
Dario Binacchiff94c152020-12-30 00:16:27 +0100405 int err;
Dario Binacchi96b109b2020-02-22 14:05:45 +0100406
407 /* Before relocation we don't need to do anything */
408 if (!(gd->flags & GD_FLG_RELOC))
409 return 0;
410
Dario Binacchiff94c152020-12-30 00:16:27 +0100411 err = uclass_get_device(UCLASS_PANEL, 0, &panel);
412 if (err) {
413 dev_err(dev, "failed to get panel\n");
414 return err;
415 }
416
417 err = panel_get_display_timing(panel, &timing);
418 if (err) {
419 dev_err(dev, "failed to get display timing\n");
420 return err;
421 }
422
423 if (timing.pixelclock.typ > (LCDC_FMAX / 2)) {
424 dev_err(dev, "invalid display clock-frequency: %d Hz\n",
425 timing.pixelclock.typ);
426 return -EINVAL;
427 }
428
429 if (timing.hactive.typ > LCD_MAX_WIDTH)
430 timing.hactive.typ = LCD_MAX_WIDTH;
431
432 if (timing.vactive.typ > LCD_MAX_HEIGHT)
433 timing.vactive.typ = LCD_MAX_HEIGHT;
434
435 err = tilcdc_panel_get_display_info(panel, &info);
436 if (err) {
437 dev_err(dev, "failed to get panel info\n");
438 return err;
439 }
440
441 switch (info.bpp) {
442 case 16:
443 case 24:
444 case 32:
445 break;
446 default:
447 dev_err(dev, "invalid seting, bpp: %d\n", info.bpp);
448 return -EINVAL;
449 }
450
451 switch (info.dma_burst_sz) {
452 case 1:
453 case 2:
454 case 4:
455 case 8:
456 case 16:
457 break;
458 default:
459 dev_err(dev, "invalid setting, dma-burst-sz: %d\n",
460 info.dma_burst_sz);
461 return -EINVAL;
462 }
463
Dario Binacchi91337f52020-12-30 00:16:29 +0100464 err = uclass_get_device_by_name(UCLASS_CLK, "lcd_gclk@534", &clk_dev);
465 if (err) {
466 dev_err(dev, "failed to get lcd_gclk device\n");
467 return err;
468 }
Dario Binacchi96b109b2020-02-22 14:05:45 +0100469
Dario Binacchi91337f52020-12-30 00:16:29 +0100470 err = clk_request(clk_dev, &priv->gclk);
471 if (err) {
472 dev_err(dev, "failed to get %s clock\n", clk_dev->name);
473 return err;
474 }
475
476 rate = tilcdc_set_pixel_clk_rate(dev, timing.pixelclock.typ);
477 if (IS_ERR_VALUE(rate)) {
478 dev_err(dev, "failed to set pixel clock rate\n");
479 return rate;
480 }
481
482 err = uclass_get_device_by_name(UCLASS_CLK, "dpll_disp_m2_ck@4a4", &clk_dev);
483 if (err) {
484 dev_err(dev, "failed to get dpll_disp_m2 clock device\n");
485 return err;
486 }
487
488 err = clk_request(clk_dev, &priv->dpll_m2_clk);
489 if (err) {
490 dev_err(dev, "failed to get %s clock\n", clk_dev->name);
491 return err;
492 }
493
494 err = clk_set_parent(&priv->gclk, &priv->dpll_m2_clk);
495 if (err) {
496 dev_err(dev, "failed to set %s clock as %s's parent\n",
497 priv->dpll_m2_clk.dev->name, priv->gclk.dev->name);
498 return err;
499 }
Dario Binacchi96b109b2020-02-22 14:05:45 +0100500
501 /* palette default entry */
502 memset((void *)uc_plat->base, 0, 0x20);
503 *(unsigned int *)uc_plat->base = 0x4000;
504 /* point fb behind palette */
505 uc_plat->base += 0x20;
506 uc_plat->size -= 0x20;
507
508 writel(LCDC_CLKC_ENABLE_CORECLKEN | LCDC_CLKC_ENABLE_LIDDCLKEN |
509 LCDC_CLKC_ENABLE_DMACLKEN, &regs->clkc_enable);
510 writel(0, &regs->raster_ctrl);
511
512 reg = readl(&regs->ctrl) & LCDC_CTRL_CLK_DIVISOR_MASK;
513 reg |= LCDC_CTRL_RASTER_MODE;
514 writel(reg, &regs->ctrl);
515
516 writel(uc_plat->base, &regs->lcddma_fb0_base);
Dario Binacchiff94c152020-12-30 00:16:27 +0100517 writel(uc_plat->base + FBSIZE(timing, info),
Dario Binacchi96b109b2020-02-22 14:05:45 +0100518 &regs->lcddma_fb0_ceiling);
519 writel(uc_plat->base, &regs->lcddma_fb1_base);
Dario Binacchiff94c152020-12-30 00:16:27 +0100520 writel(uc_plat->base + FBSIZE(timing, info),
Dario Binacchi96b109b2020-02-22 14:05:45 +0100521 &regs->lcddma_fb1_ceiling);
522
Dario Binacchiff94c152020-12-30 00:16:27 +0100523 reg = LCDC_DMA_CTRL_FIFO_TH(info.fifo_th);
524 switch (info.dma_burst_sz) {
Dario Binacchi96b109b2020-02-22 14:05:45 +0100525 case 1:
526 reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_1);
527 break;
528 case 2:
529 reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_2);
530 break;
531 case 4:
532 reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_4);
533 break;
534 case 8:
535 reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_8);
536 break;
537 case 16:
538 reg |= LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_16);
539 break;
540 }
541
542 writel(reg, &regs->lcddma_ctrl);
543
Dario Binacchiff94c152020-12-30 00:16:27 +0100544 writel(LCDC_RASTER_TIMING_0_HORLSB(timing.hactive.typ) |
545 LCDC_RASTER_TIMING_0_HORMSB(timing.hactive.typ) |
546 LCDC_RASTER_TIMING_0_HFPLSB(timing.hfront_porch.typ) |
547 LCDC_RASTER_TIMING_0_HBPLSB(timing.hback_porch.typ) |
548 LCDC_RASTER_TIMING_0_HSWLSB(timing.hsync_len.typ),
Dario Binacchi96b109b2020-02-22 14:05:45 +0100549 &regs->raster_timing0);
550
Dario Binacchiff94c152020-12-30 00:16:27 +0100551 writel(LCDC_RASTER_TIMING_1_VBP(timing.vback_porch.typ) |
552 LCDC_RASTER_TIMING_1_VFP(timing.vfront_porch.typ) |
553 LCDC_RASTER_TIMING_1_VSW(timing.vsync_len.typ) |
554 LCDC_RASTER_TIMING_1_VERLSB(timing.vactive.typ),
Dario Binacchi96b109b2020-02-22 14:05:45 +0100555 &regs->raster_timing1);
556
Dario Binacchiff94c152020-12-30 00:16:27 +0100557 reg = LCDC_RASTER_TIMING_2_ACB(info.ac_bias) |
558 LCDC_RASTER_TIMING_2_ACBI(info.ac_bias_intrpt) |
559 LCDC_RASTER_TIMING_2_HSWMSB(timing.hsync_len.typ) |
560 LCDC_RASTER_TIMING_2_VERMSB(timing.vactive.typ) |
561 LCDC_RASTER_TIMING_2_HBPMSB(timing.hback_porch.typ) |
562 LCDC_RASTER_TIMING_2_HFPMSB(timing.hfront_porch.typ);
Dario Binacchi96b109b2020-02-22 14:05:45 +0100563
Dario Binacchiff94c152020-12-30 00:16:27 +0100564 if (timing.flags & DISPLAY_FLAGS_VSYNC_LOW)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100565 reg |= LCDC_RASTER_TIMING_2_VSYNC_INVERT;
566
Dario Binacchiff94c152020-12-30 00:16:27 +0100567 if (timing.flags & DISPLAY_FLAGS_HSYNC_LOW)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100568 reg |= LCDC_RASTER_TIMING_2_HSYNC_INVERT;
569
Dario Binacchiff94c152020-12-30 00:16:27 +0100570 if (info.invert_pxl_clk)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100571 reg |= LCDC_RASTER_TIMING_2_PXCLK_INVERT;
572
Dario Binacchiff94c152020-12-30 00:16:27 +0100573 if (info.sync_edge)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100574 reg |= LCDC_RASTER_TIMING_2_HSVS_RISEFALL;
575
Dario Binacchiff94c152020-12-30 00:16:27 +0100576 if (info.sync_ctrl)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100577 reg |= LCDC_RASTER_TIMING_2_HSVS_CONTROL;
578
579 writel(reg, &regs->raster_timing2);
580
581 reg = LCDC_RASTER_CTRL_PALMODE_RAWDATA | LCDC_RASTER_CTRL_TFT_MODE |
Dario Binacchiff94c152020-12-30 00:16:27 +0100582 LCDC_RASTER_CTRL_ENABLE | LCDC_RASTER_CTRL_REQDLY(info.fdd);
Dario Binacchi96b109b2020-02-22 14:05:45 +0100583
Dario Binacchiff94c152020-12-30 00:16:27 +0100584 if (info.tft_alt_mode)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100585 reg |= LCDC_RASTER_CTRL_TFT_ALT_ENABLE;
586
Dario Binacchiff94c152020-12-30 00:16:27 +0100587 if (info.bpp == 24)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100588 reg |= LCDC_RASTER_CTRL_TFT_24BPP_MODE;
Dario Binacchiff94c152020-12-30 00:16:27 +0100589 else if (info.bpp == 32)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100590 reg |= LCDC_RASTER_CTRL_TFT_24BPP_MODE |
591 LCDC_RASTER_CTRL_TFT_24BPP_UNPACK;
592
Dario Binacchiff94c152020-12-30 00:16:27 +0100593 if (info.raster_order)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100594 reg |= LCDC_RASTER_CTRL_DATA_ORDER;
595
596 writel(reg, &regs->raster_ctrl);
597
Dario Binacchiff94c152020-12-30 00:16:27 +0100598 uc_priv->xsize = timing.hactive.typ;
599 uc_priv->ysize = timing.vactive.typ;
600 uc_priv->bpix = log_2_n_round_up(info.bpp);
601
602 err = panel_enable_backlight(panel);
603 if (err) {
604 dev_err(dev, "failed to enable panel backlight\n");
605 return err;
606 }
607
Dario Binacchi96b109b2020-02-22 14:05:45 +0100608 return 0;
609}
610
Dario Binacchiff94c152020-12-30 00:16:27 +0100611static int am335x_fb_ofdata_to_platdata(struct udevice *dev)
Dario Binacchi96b109b2020-02-22 14:05:45 +0100612{
613 struct am335x_fb_priv *priv = dev_get_priv(dev);
Dario Binacchi96b109b2020-02-22 14:05:45 +0100614
Dario Binacchiff94c152020-12-30 00:16:27 +0100615 priv->regs = (struct am335x_lcdhw *)dev_read_addr(dev);
616 if ((fdt_addr_t)priv->regs == FDT_ADDR_T_NONE) {
617 dev_err(dev, "failed to get base address\n");
618 return -EINVAL;
Dario Binacchi96b109b2020-02-22 14:05:45 +0100619 }
620
Dario Binacchi96b109b2020-02-22 14:05:45 +0100621 dev_dbg(dev, "LCD: base address=0x%x\n", (unsigned int)priv->regs);
Dario Binacchi96b109b2020-02-22 14:05:45 +0100622 return 0;
623}
624
625static int am335x_fb_bind(struct udevice *dev)
626{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700627 struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
Dario Binacchi96b109b2020-02-22 14:05:45 +0100628
629 uc_plat->size = ((LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
630 (1 << LCD_MAX_LOG2_BPP)) >> 3) + 0x20;
631
632 dev_dbg(dev, "frame buffer size 0x%x\n", uc_plat->size);
633 return 0;
634}
635
636static const struct udevice_id am335x_fb_ids[] = {
Dario Binacchiff94c152020-12-30 00:16:27 +0100637 { .compatible = "ti,am33xx-tilcdc" },
Dario Binacchi96b109b2020-02-22 14:05:45 +0100638 { }
639};
640
641U_BOOT_DRIVER(am335x_fb) = {
642 .name = "am335x_fb",
643 .id = UCLASS_VIDEO,
644 .of_match = am335x_fb_ids,
645 .bind = am335x_fb_bind,
Simon Glassd1998a92020-12-03 16:55:21 -0700646 .of_to_plat = am335x_fb_of_to_plat,
Dario Binacchi96b109b2020-02-22 14:05:45 +0100647 .probe = am335x_fb_probe,
648 .remove = am335x_fb_remove,
Simon Glass41575d82020-12-03 16:55:17 -0700649 .priv_auto = sizeof(struct am335x_fb_priv),
Dario Binacchi96b109b2020-02-22 14:05:45 +0100650};
651
652#endif /* CONFIG_DM_VIDEO */