blob: 680ea47998da6aec8f28d5ab55ba55a196248acd [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>
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010015#include <lcd.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060016#include <log.h>
Dario Binacchi295ab882020-05-03 21:27:48 +020017#include <asm/arch/clock.h>
18#include <asm/arch/hardware.h>
19#include <asm/arch/omap.h>
20#include <asm/arch/sys_proto.h>
Simon Glass401d1c42020-10-30 21:38:53 -060021#include <asm/global_data.h>
Dario Binacchi295ab882020-05-03 21:27:48 +020022#include <asm/io.h>
Simon Glassc05ed002020-05-10 11:40:11 -060023#include <linux/delay.h>
Dario Binacchi295ab882020-05-03 21:27:48 +020024#include <linux/err.h>
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010025#include "am335x-fb.h"
26
Hannes Schmelzer8a094f52018-01-09 19:01:34 +010027#define LCDC_FMAX 200000000
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010028
29/* LCD Control Register */
Dario Binacchia9df3c52020-02-22 14:05:44 +010030#define LCDC_CTRL_CLK_DIVISOR_MASK GENMASK(15, 8)
Dario Binacchi41f76a02020-02-22 14:05:41 +010031#define LCDC_CTRL_RASTER_MODE BIT(0)
32#define LCDC_CTRL_CLK_DIVISOR(x) (((x) & GENMASK(7, 0)) << 8)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010033/* LCD Clock Enable Register */
Dario Binacchi41f76a02020-02-22 14:05:41 +010034#define LCDC_CLKC_ENABLE_CORECLKEN BIT(0)
35#define LCDC_CLKC_ENABLE_LIDDCLKEN BIT(1)
36#define LCDC_CLKC_ENABLE_DMACLKEN BIT(2)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010037/* LCD DMA Control Register */
Dario Binacchi41f76a02020-02-22 14:05:41 +010038#define LCDC_DMA_CTRL_BURST_SIZE(x) (((x) & GENMASK(2, 0)) << 4)
39#define LCDC_DMA_CTRL_BURST_1 0x0
40#define LCDC_DMA_CTRL_BURST_2 0x1
41#define LCDC_DMA_CTRL_BURST_4 0x2
42#define LCDC_DMA_CTRL_BURST_8 0x3
43#define LCDC_DMA_CTRL_BURST_16 0x4
Dario Binacchi96b109b2020-02-22 14:05:45 +010044#define LCDC_DMA_CTRL_FIFO_TH(x) (((x) & GENMASK(2, 0)) << 8)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010045/* LCD Timing_0 Register */
Dario Binacchi0aff8e22020-02-22 14:05:48 +010046#define LCDC_RASTER_TIMING_0_HORMSB(x) ((((x) - 1) & BIT(10)) >> 7)
Dario Binacchi41f76a02020-02-22 14:05:41 +010047#define LCDC_RASTER_TIMING_0_HORLSB(x) (((((x) >> 4) - 1) & GENMASK(5, 0)) << 4)
48#define LCDC_RASTER_TIMING_0_HSWLSB(x) ((((x) - 1) & GENMASK(5, 0)) << 10)
49#define LCDC_RASTER_TIMING_0_HFPLSB(x) ((((x) - 1) & GENMASK(7, 0)) << 16)
50#define LCDC_RASTER_TIMING_0_HBPLSB(x) ((((x) - 1) & GENMASK(7, 0)) << 24)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010051/* LCD Timing_1 Register */
Dario Binacchi41f76a02020-02-22 14:05:41 +010052#define LCDC_RASTER_TIMING_1_VERLSB(x) (((x) - 1) & GENMASK(9, 0))
53#define LCDC_RASTER_TIMING_1_VSW(x) ((((x) - 1) & GENMASK(5, 0)) << 10)
54#define LCDC_RASTER_TIMING_1_VFP(x) (((x) & GENMASK(7, 0)) << 16)
55#define LCDC_RASTER_TIMING_1_VBP(x) (((x) & GENMASK(7, 0)) << 24)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010056/* LCD Timing_2 Register */
Dario Binacchi41f76a02020-02-22 14:05:41 +010057#define LCDC_RASTER_TIMING_2_HFPMSB(x) ((((x) - 1) & GENMASK(9, 8)) >> 8)
58#define LCDC_RASTER_TIMING_2_HBPMSB(x) ((((x) - 1) & GENMASK(9, 8)) >> 4)
Dario Binacchi96b109b2020-02-22 14:05:45 +010059#define LCDC_RASTER_TIMING_2_ACB(x) (((x) & GENMASK(7, 0)) << 8)
60#define LCDC_RASTER_TIMING_2_ACBI(x) (((x) & GENMASK(3, 0)) << 16)
61#define LCDC_RASTER_TIMING_2_VSYNC_INVERT BIT(20)
62#define LCDC_RASTER_TIMING_2_HSYNC_INVERT BIT(21)
63#define LCDC_RASTER_TIMING_2_PXCLK_INVERT BIT(22)
64#define LCDC_RASTER_TIMING_2_DE_INVERT BIT(23)
65#define LCDC_RASTER_TIMING_2_HSVS_RISEFALL BIT(24)
66#define LCDC_RASTER_TIMING_2_HSVS_CONTROL BIT(25)
Dario Binacchi41f76a02020-02-22 14:05:41 +010067#define LCDC_RASTER_TIMING_2_VERMSB(x) ((((x) - 1) & BIT(10)) << 16)
68#define LCDC_RASTER_TIMING_2_HSWMSB(x) ((((x) - 1) & GENMASK(9, 6)) << 21)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010069/* LCD Raster Ctrl Register */
Dario Binacchi41f76a02020-02-22 14:05:41 +010070#define LCDC_RASTER_CTRL_ENABLE BIT(0)
71#define LCDC_RASTER_CTRL_TFT_MODE BIT(7)
Dario Binacchi96b109b2020-02-22 14:05:45 +010072#define LCDC_RASTER_CTRL_DATA_ORDER BIT(8)
73#define LCDC_RASTER_CTRL_REQDLY(x) (((x) & GENMASK(7, 0)) << 12)
Dario Binacchi41f76a02020-02-22 14:05:41 +010074#define LCDC_RASTER_CTRL_PALMODE_RAWDATA (0x02 << 20)
Dario Binacchi96b109b2020-02-22 14:05:45 +010075#define LCDC_RASTER_CTRL_TFT_ALT_ENABLE BIT(23)
Dario Binacchi41f76a02020-02-22 14:05:41 +010076#define LCDC_RASTER_CTRL_TFT_24BPP_MODE BIT(25)
77#define LCDC_RASTER_CTRL_TFT_24BPP_UNPACK BIT(26)
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010078
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +010079struct am335x_lcdhw {
80 unsigned int pid; /* 0x00 */
81 unsigned int ctrl; /* 0x04 */
82 unsigned int gap0; /* 0x08 */
83 unsigned int lidd_ctrl; /* 0x0C */
84 unsigned int lidd_cs0_conf; /* 0x10 */
85 unsigned int lidd_cs0_addr; /* 0x14 */
86 unsigned int lidd_cs0_data; /* 0x18 */
87 unsigned int lidd_cs1_conf; /* 0x1C */
88 unsigned int lidd_cs1_addr; /* 0x20 */
89 unsigned int lidd_cs1_data; /* 0x24 */
90 unsigned int raster_ctrl; /* 0x28 */
91 unsigned int raster_timing0; /* 0x2C */
92 unsigned int raster_timing1; /* 0x30 */
93 unsigned int raster_timing2; /* 0x34 */
94 unsigned int raster_subpanel; /* 0x38 */
95 unsigned int raster_subpanel2; /* 0x3C */
96 unsigned int lcddma_ctrl; /* 0x40 */
97 unsigned int lcddma_fb0_base; /* 0x44 */
98 unsigned int lcddma_fb0_ceiling; /* 0x48 */
99 unsigned int lcddma_fb1_base; /* 0x4C */
100 unsigned int lcddma_fb1_ceiling; /* 0x50 */
101 unsigned int sysconfig; /* 0x54 */
102 unsigned int irqstatus_raw; /* 0x58 */
103 unsigned int irqstatus; /* 0x5C */
104 unsigned int irqenable_set; /* 0x60 */
105 unsigned int irqenable_clear; /* 0x64 */
106 unsigned int gap1; /* 0x68 */
107 unsigned int clkc_enable; /* 0x6C */
108 unsigned int clkc_reset; /* 0x70 */
109};
110
Dario Binacchi91337f52020-12-30 00:16:29 +0100111DECLARE_GLOBAL_DATA_PTR;
112
Dario Binacchi91337f52020-12-30 00:16:29 +0100113#if !defined(LCD_CNTL_BASE)
114#error "hw-base address of LCD-Controller (LCD_CNTL_BASE) not defined!"
115#endif
116
117/* Macro definitions */
118#define FBSIZE(x) (((x)->hactive * (x)->vactive * (x)->bpp) >> 3)
119
120#define LCDC_RASTER_TIMING_2_INVMASK(x) ((x) & GENMASK(25, 20))
121
122static struct am335x_lcdhw *lcdhw = (void *)LCD_CNTL_BASE;
123
124int lcd_get_size(int *line_length)
125{
126 *line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8;
127 return *line_length * panel_info.vl_row + 0x20;
128}
129
Dario Binacchia9df3c52020-02-22 14:05:44 +0100130struct dpll_data {
131 unsigned long rounded_rate;
132 u16 rounded_m;
133 u8 rounded_n;
134 u8 rounded_div;
135};
136
Dario Binacchia9df3c52020-02-22 14:05:44 +0100137/**
138 * am335x_dpll_round_rate() - Round a target rate for an OMAP DPLL
139 *
140 * @dpll_data: struct dpll_data pointer for the DPLL
141 * @rate: New DPLL clock rate
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100142 * Return: rounded rate and the computed m, n and div values in the dpll_data
Dario Binacchia9df3c52020-02-22 14:05:44 +0100143 * structure, or -ve error code.
144 */
145static ulong am335x_dpll_round_rate(struct dpll_data *dd, ulong rate)
146{
147 unsigned int m, n, d;
148 unsigned long rounded_rate;
149 int err, err_r;
150
151 dd->rounded_rate = -EFAULT;
152 err = rate;
153 err_r = err;
154
155 for (d = 2; err && d < 255; d++) {
156 for (m = 2; m < 2047; m++) {
157 if ((V_OSCK * m) < (rate * d))
158 continue;
159
160 n = (V_OSCK * m) / (rate * d);
161 if (n > 127)
162 break;
163
164 if (((V_OSCK * m) / n) > LCDC_FMAX)
165 break;
166
167 rounded_rate = (V_OSCK * m) / n / d;
168 err = abs(rounded_rate - rate);
169 if (err < err_r) {
170 err_r = err;
171 dd->rounded_rate = rounded_rate;
172 dd->rounded_m = m;
173 dd->rounded_n = n;
174 dd->rounded_div = d;
175 if (err == 0)
176 break;
177 }
178 }
179 }
180
181 debug("DPLL display: best error %d Hz (M %d, N %d, DIV %d)\n",
182 err_r, dd->rounded_m, dd->rounded_n, dd->rounded_div);
183
184 return dd->rounded_rate;
185}
186
187/**
188 * am335x_fb_set_pixel_clk_rate() - Set pixel clock rate.
189 *
190 * @am335x_lcdhw: Base address of the LCD controller registers.
191 * @rate: New clock rate in Hz.
Heinrich Schuchardt185f8122022-01-19 18:05:50 +0100192 * Return: new rate, or -ve error code.
Dario Binacchia9df3c52020-02-22 14:05:44 +0100193 */
194static ulong am335x_fb_set_pixel_clk_rate(struct am335x_lcdhw *regs, ulong rate)
195{
196 struct dpll_params dpll_disp = { 1, 0, 1, -1, -1, -1, -1 };
197 struct dpll_data dd;
198 ulong round_rate;
199 u32 reg;
200
201 round_rate = am335x_dpll_round_rate(&dd, rate);
202 if (IS_ERR_VALUE(round_rate))
203 return round_rate;
204
205 dpll_disp.m = dd.rounded_m;
206 dpll_disp.n = dd.rounded_n;
207 do_setup_dpll(&dpll_disp_regs, &dpll_disp);
208
209 reg = readl(&regs->ctrl) & ~LCDC_CTRL_CLK_DIVISOR_MASK;
210 reg |= LCDC_CTRL_CLK_DIVISOR(dd.rounded_div);
211 writel(reg, &regs->ctrl);
212 return round_rate;
213}
214
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100215int am335xfb_init(struct am335x_lcdpanel *panel)
216{
Martin Pietryka7d045172016-04-27 21:39:15 +0200217 u32 raster_ctrl = 0;
Hannes Schmelzer8a094f52018-01-09 19:01:34 +0100218 struct cm_dpll *const cmdpll = (struct cm_dpll *)CM_DPLL;
Dario Binacchia9df3c52020-02-22 14:05:44 +0100219 ulong rate;
220 u32 reg;
Hannes Schmelzer8a094f52018-01-09 19:01:34 +0100221
Hannes Schmelzer0d8a7d62018-01-09 19:01:33 +0100222 if (gd->fb_base == 0) {
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100223 printf("ERROR: no valid fb_base stored in GLOBAL_DATA_PTR!\n");
224 return -1;
225 }
Hannes Schmelzer0d8a7d62018-01-09 19:01:33 +0100226 if (panel == NULL) {
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100227 printf("ERROR: missing ptr to am335x_lcdpanel!\n");
228 return -1;
229 }
230
Martin Pietryka7d045172016-04-27 21:39:15 +0200231 /* We can already set the bits for the raster_ctrl in this check */
232 switch (panel->bpp) {
233 case 16:
234 break;
235 case 32:
Dario Binacchi41f76a02020-02-22 14:05:41 +0100236 raster_ctrl |= LCDC_RASTER_CTRL_TFT_24BPP_UNPACK;
Martin Pietryka7d045172016-04-27 21:39:15 +0200237 /* fallthrough */
238 case 24:
Dario Binacchi41f76a02020-02-22 14:05:41 +0100239 raster_ctrl |= LCDC_RASTER_CTRL_TFT_24BPP_MODE;
Martin Pietryka7d045172016-04-27 21:39:15 +0200240 break;
241 default:
Masahiro Yamada9b643e32017-09-16 14:10:41 +0900242 pr_err("am335x-fb: invalid bpp value: %d\n", panel->bpp);
Martin Pietryka7d045172016-04-27 21:39:15 +0200243 return -1;
244 }
245
Hannes Schmelzer8a094f52018-01-09 19:01:34 +0100246 /* check given clock-frequency */
247 if (panel->pxl_clk > (LCDC_FMAX / 2)) {
248 pr_err("am335x-fb: requested pxl-clk: %d not supported!\n",
249 panel->pxl_clk);
250 return -1;
251 }
252
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100253 debug("setting up LCD-Controller for %dx%dx%d (hfp=%d,hbp=%d,hsw=%d / ",
254 panel->hactive, panel->vactive, panel->bpp,
255 panel->hfp, panel->hbp, panel->hsw);
Hannes Schmelzer8a094f52018-01-09 19:01:34 +0100256 debug("vfp=%d,vbp=%d,vsw=%d / clk=%d)\n",
257 panel->vfp, panel->vfp, panel->vsw, panel->pxl_clk);
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100258 debug("using frambuffer at 0x%08x with size %d.\n",
259 (unsigned int)gd->fb_base, FBSIZE(panel));
260
Dario Binacchia9df3c52020-02-22 14:05:44 +0100261 rate = am335x_fb_set_pixel_clk_rate(lcdhw, panel->pxl_clk);
262 if (IS_ERR_VALUE(rate))
263 return rate;
Hannes Schmelzer8a094f52018-01-09 19:01:34 +0100264
265 /* clock source for LCDC from dispPLL M2 */
266 writel(0x0, &cmdpll->clklcdcpixelclk);
267
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100268 /* palette default entry */
269 memset((void *)gd->fb_base, 0, 0x20);
270 *(unsigned int *)gd->fb_base = 0x4000;
Martin Pietryka3d47b2d2016-04-27 21:39:16 +0200271 /* point fb behind palette */
272 gd->fb_base += 0x20;
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100273
Hannes Petermaier3b4e16e2015-02-03 13:22:23 +0100274 /* turn ON display through powercontrol function if accessible */
Hannes Schmelzer0d8a7d62018-01-09 19:01:33 +0100275 if (panel->panel_power_ctrl != NULL)
Hannes Petermaier3b4e16e2015-02-03 13:22:23 +0100276 panel->panel_power_ctrl(1);
277
278 debug("am335x-fb: wait for stable power ...\n");
279 mdelay(panel->pup_delay);
Dario Binacchi41f76a02020-02-22 14:05:41 +0100280 lcdhw->clkc_enable = LCDC_CLKC_ENABLE_CORECLKEN |
281 LCDC_CLKC_ENABLE_LIDDCLKEN | LCDC_CLKC_ENABLE_DMACLKEN;
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100282 lcdhw->raster_ctrl = 0;
Dario Binacchia9df3c52020-02-22 14:05:44 +0100283
284 reg = lcdhw->ctrl & LCDC_CTRL_CLK_DIVISOR_MASK;
285 reg |= LCDC_CTRL_RASTER_MODE;
286 lcdhw->ctrl = reg;
287
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100288 lcdhw->lcddma_fb0_base = gd->fb_base;
Martin Pietryka3d47b2d2016-04-27 21:39:16 +0200289 lcdhw->lcddma_fb0_ceiling = gd->fb_base + FBSIZE(panel);
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100290 lcdhw->lcddma_fb1_base = gd->fb_base;
Martin Pietryka3d47b2d2016-04-27 21:39:16 +0200291 lcdhw->lcddma_fb1_ceiling = gd->fb_base + FBSIZE(panel);
Dario Binacchi41f76a02020-02-22 14:05:41 +0100292 lcdhw->lcddma_ctrl = LCDC_DMA_CTRL_BURST_SIZE(LCDC_DMA_CTRL_BURST_16);
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100293
Dario Binacchi41f76a02020-02-22 14:05:41 +0100294 lcdhw->raster_timing0 = LCDC_RASTER_TIMING_0_HORLSB(panel->hactive) |
295 LCDC_RASTER_TIMING_0_HORMSB(panel->hactive) |
296 LCDC_RASTER_TIMING_0_HFPLSB(panel->hfp) |
297 LCDC_RASTER_TIMING_0_HBPLSB(panel->hbp) |
298 LCDC_RASTER_TIMING_0_HSWLSB(panel->hsw);
299 lcdhw->raster_timing1 = LCDC_RASTER_TIMING_1_VBP(panel->vbp) |
300 LCDC_RASTER_TIMING_1_VFP(panel->vfp) |
301 LCDC_RASTER_TIMING_1_VSW(panel->vsw) |
302 LCDC_RASTER_TIMING_1_VERLSB(panel->vactive);
303 lcdhw->raster_timing2 = LCDC_RASTER_TIMING_2_HSWMSB(panel->hsw) |
304 LCDC_RASTER_TIMING_2_VERMSB(panel->vactive) |
305 LCDC_RASTER_TIMING_2_INVMASK(panel->pol) |
306 LCDC_RASTER_TIMING_2_HBPMSB(panel->hbp) |
307 LCDC_RASTER_TIMING_2_HFPMSB(panel->hfp) |
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100308 0x0000FF00; /* clk cycles for ac-bias */
Martin Pietryka7d045172016-04-27 21:39:15 +0200309 lcdhw->raster_ctrl = raster_ctrl |
Dario Binacchi41f76a02020-02-22 14:05:41 +0100310 LCDC_RASTER_CTRL_PALMODE_RAWDATA |
311 LCDC_RASTER_CTRL_TFT_MODE |
312 LCDC_RASTER_CTRL_ENABLE;
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100313
Hannes Petermaier3b4e16e2015-02-03 13:22:23 +0100314 debug("am335x-fb: waiting picture to be stable.\n.");
315 mdelay(panel->pon_delay);
Hannes Petermaier3c5fabd2014-03-06 14:39:06 +0100316
317 return 0;
318}