]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/video/mxsfb.c
TX6 Release 2013-04-22
[karo-tx-uboot.git] / drivers / video / mxsfb.c
1 /*
2  * Copyright (C) 2012 Lothar Waßmann <LW@KARO-electronics.de>
3  *
4  * LCD driver for i.MXS
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * version 2 as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <common.h>
18 #include <errno.h>
19 #include <lcd.h>
20 #include <malloc.h>
21 #include <asm/io.h>
22 #include <asm/arch/imx-regs.h>
23 #include <asm/arch/mxsfb.h>
24 #include <asm/arch/sys_proto.h>
25
26 vidinfo_t panel_info = {
27         /* set to max. size supported by SoC */
28         .vl_col = 800,
29         .vl_row = 480,
30
31         .vl_bpix = LCD_COLOR24,    /* Bits per pixel, 0: 1bpp, 1: 2bpp, 2: 4bpp, 3: 8bpp ... */
32 };
33
34 static int bits_per_pixel;
35 static int color_depth;
36 static uint32_t pix_fmt;
37 static struct fb_var_screeninfo mxsfb_var;
38
39 static struct mxs_lcdif_regs *lcd_regs = (void *)MXS_LCDIF_BASE;
40
41 void *lcd_base;                 /* Start of framebuffer memory  */
42 void *lcd_console_address;      /* Start of console buffer      */
43
44 int lcd_line_length;
45 int lcd_color_fg;
46 int lcd_color_bg;
47
48 short console_col;
49 short console_row;
50
51 void lcd_initcolregs(void)
52 {
53 }
54
55 void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
56 {
57 }
58
59 #define fourcc_str(fourcc)      ((fourcc) >> 0) & 0xff, \
60                 ((fourcc) >> 8) & 0xff,                 \
61                 ((fourcc) >> 16) & 0xff,                \
62                 ((fourcc) >> 24) & 0xff
63
64 #define LCD_CTRL_DEFAULT        (LCDIF_CTRL_LCDIF_MASTER |      \
65                                 LCDIF_CTRL_BYPASS_COUNT |       \
66                                 LCDIF_CTRL_DOTCLK_MODE)
67
68 #define LCD_CTRL1_DEFAULT       0
69 #define LCD_CTRL2_DEFAULT       LCDIF_CTRL2_OUTSTANDING_REQS_REQ_2
70
71 #define LCD_VDCTRL0_DEFAULT     (LCDIF_VDCTRL0_ENABLE_PRESENT |         \
72                                 LCDIF_VDCTRL0_VSYNC_PERIOD_UNIT |       \
73                                 LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH_UNIT)
74 #define LCD_VDCTRL1_DEFAULT     0
75 #define LCD_VDCTRL2_DEFAULT     0
76 #define LCD_VDCTRL3_DEFAULT     0
77 #define LCD_VDCTRL4_DEFAULT     LCDIF_VDCTRL4_SYNC_SIGNALS_ON
78
79 void video_hw_init(void *lcdbase)
80 {
81         int ret;
82         unsigned int div = 0, best = 0, pix_clk;
83         u32 frac1;
84         const unsigned long lcd_clk = 480000000;
85         u32 lcd_ctrl = LCD_CTRL_DEFAULT | LCDIF_CTRL_RUN;
86         u32 lcd_ctrl1 = LCD_CTRL1_DEFAULT, lcd_ctrl2 = LCD_CTRL2_DEFAULT;
87         u32 lcd_vdctrl0 = LCD_VDCTRL0_DEFAULT;
88         u32 lcd_vdctrl1 = LCD_VDCTRL1_DEFAULT;
89         u32 lcd_vdctrl2 = LCD_VDCTRL2_DEFAULT;
90         u32 lcd_vdctrl3 = LCD_VDCTRL3_DEFAULT;
91         u32 lcd_vdctrl4 = LCD_VDCTRL4_DEFAULT;
92         struct mxs_clkctrl_regs *clk_regs = (void *)MXS_CLKCTRL_BASE;
93         char buf1[16], buf2[16];
94
95         /* pixel format in memory */
96         switch (color_depth) {
97         case 8:
98                 lcd_ctrl |= LCDIF_CTRL_WORD_LENGTH_8BIT;
99                 lcd_ctrl1 |= LCDIF_CTRL1_BYTE_PACKING_FORMAT(1);
100                 break;
101
102         case 16:
103                 lcd_ctrl |= LCDIF_CTRL_WORD_LENGTH_16BIT;
104                 lcd_ctrl1 |= LCDIF_CTRL1_BYTE_PACKING_FORMAT(3);
105                 break;
106
107         case 18:
108                 lcd_ctrl |= LCDIF_CTRL_WORD_LENGTH_18BIT;
109                 lcd_ctrl1 |= LCDIF_CTRL1_BYTE_PACKING_FORMAT(7);
110                 break;
111
112         case 24:
113                 lcd_ctrl |= LCDIF_CTRL_WORD_LENGTH_24BIT;
114                 lcd_ctrl1 |= LCDIF_CTRL1_BYTE_PACKING_FORMAT(7);
115                 break;
116
117         default:
118                 printf("Invalid bpp: %d\n", color_depth);
119                 return;
120         }
121
122         /* pixel format on the LCD data pins */
123         switch (pix_fmt) {
124         case PIX_FMT_RGB332:
125                 lcd_ctrl |= LCDIF_CTRL_LCD_DATABUS_WIDTH_8BIT;
126                 break;
127
128         case PIX_FMT_RGB565:
129                 lcd_ctrl |= LCDIF_CTRL_LCD_DATABUS_WIDTH_16BIT;
130                 break;
131
132         case PIX_FMT_BGR666:
133                 lcd_ctrl |= 1 << LCDIF_CTRL_INPUT_DATA_SWIZZLE_OFFSET;
134                 /* fallthru */
135         case PIX_FMT_RGB666:
136                 lcd_ctrl |= LCDIF_CTRL_LCD_DATABUS_WIDTH_18BIT;
137                 break;
138
139         case PIX_FMT_BGR24:
140                 lcd_ctrl |= 1 << LCDIF_CTRL_INPUT_DATA_SWIZZLE_OFFSET;
141                 /* fallthru */
142         case PIX_FMT_RGB24:
143                 lcd_ctrl |= LCDIF_CTRL_LCD_DATABUS_WIDTH_24BIT;
144                 break;
145
146         default:
147                 printf("Invalid pixel format: %c%c%c%c\n", fourcc_str(pix_fmt));
148                 return;
149         }
150
151         pix_clk = PICOS2KHZ(mxsfb_var.pixclock);
152         debug("designated pix_clk: %sMHz\n", strmhz(buf1, pix_clk * 1000));
153
154         for (frac1 = 18; frac1 < 36; frac1++) {
155                 static unsigned int err = ~0;
156                 unsigned long clk = lcd_clk / 1000 * 18 / frac1;
157                 unsigned int d = (clk + pix_clk - 1) / pix_clk;
158                 unsigned int diff = abs(clk / d - pix_clk);
159
160                 debug("frac1=%u div=%u lcd_clk=%-8sMHz pix_clk=%-8sMHz diff=%u err=%u\n",
161                         frac1, d, strmhz(buf1, clk * 1000), strmhz(buf2, clk * 1000 / d),
162                         diff, err);
163
164                 if (clk < pix_clk)
165                         break;
166                 if (d > 255)
167                         continue;
168
169                 if (diff < err) {
170                         best = frac1;
171                         div = d;
172                         err = diff;
173                         if (err == 0)
174                                 break;
175                 }
176         }
177         if (div == 0) {
178                 printf("Requested pixel clock %sMHz out of range\n",
179                         strmhz(buf1, pix_clk * 1000));
180                 return;
181         }
182
183         debug("div=%lu(%u*%u/18) for pixel clock %sMHz with base clock %sMHz\n",
184                 lcd_clk / pix_clk / 1000, best, div,
185                 strmhz(buf1, lcd_clk / div * 18 / best),
186                 strmhz(buf2, lcd_clk));
187
188         frac1 = (readl(&clk_regs->hw_clkctrl_frac1_reg) & ~0xff) | best;
189         writel(frac1, &clk_regs->hw_clkctrl_frac1_reg);
190         writel(1 << 14, &clk_regs->hw_clkctrl_clkseq_clr);
191
192         /* enable LCD clk and fractional divider */
193         writel(div, &clk_regs->hw_clkctrl_lcdif_reg);
194         while (readl(&clk_regs->hw_clkctrl_lcdif_reg) & (1 << 29))
195                 ;
196
197         ret = mxs_reset_block(&lcd_regs->hw_lcdif_ctrl_reg);
198         if (ret) {
199                 printf("Failed to reset LCD controller: LCDIF_CTRL: %08x CLKCTRL_LCDIF: %08x\n",
200                         readl(&lcd_regs->hw_lcdif_ctrl_reg),
201                         readl(&clk_regs->hw_clkctrl_lcdif_reg));
202                 return;
203         }
204
205         if (mxsfb_var.sync & FB_SYNC_HOR_HIGH_ACT)
206                 lcd_vdctrl0 |= LCDIF_VDCTRL0_HSYNC_POL;
207
208         if (mxsfb_var.sync & FB_SYNC_VERT_HIGH_ACT)
209                 lcd_vdctrl0 |= LCDIF_VDCTRL0_HSYNC_POL;
210
211         if (mxsfb_var.sync & FB_SYNC_DATA_ENABLE_HIGH_ACT)
212                 lcd_vdctrl0 |= LCDIF_VDCTRL0_ENABLE_POL;
213
214         if (mxsfb_var.sync & FB_SYNC_DOTCLK_FALLING_ACT)
215                 lcd_vdctrl0 |= LCDIF_VDCTRL0_DOTCLK_POL;
216
217         lcd_vdctrl0 |= LCDIF_VDCTRL0_VSYNC_PULSE_WIDTH(mxsfb_var.vsync_len);
218         lcd_vdctrl1 |= LCDIF_VDCTRL1_VSYNC_PERIOD(mxsfb_var.vsync_len +
219                                                 mxsfb_var.upper_margin +
220                                                 mxsfb_var.lower_margin +
221                                                 mxsfb_var.yres);
222         lcd_vdctrl2 |= LCDIF_VDCTRL2_HSYNC_PULSE_WIDTH(mxsfb_var.hsync_len);
223         lcd_vdctrl2 |= LCDIF_VDCTRL2_HSYNC_PERIOD(mxsfb_var.hsync_len +
224                                                 mxsfb_var.left_margin +
225                                                 mxsfb_var.right_margin +
226                                                 mxsfb_var.xres);
227
228         lcd_vdctrl3 |= LCDIF_VDCTRL3_HORIZONTAL_WAIT_CNT(mxsfb_var.left_margin +
229                                                         mxsfb_var.hsync_len);
230         lcd_vdctrl3 |= LCDIF_VDCTRL3_VERTICAL_WAIT_CNT(mxsfb_var.upper_margin +
231                                                         mxsfb_var.vsync_len);
232
233         lcd_vdctrl4 |= LCDIF_VDCTRL4_DOTCLK_H_VALID_DATA_CNT(mxsfb_var.xres);
234
235         writel((u32)lcdbase, &lcd_regs->hw_lcdif_next_buf_reg);
236         writel(LCDIF_TRANSFER_COUNT_H_COUNT(mxsfb_var.xres) |
237                 LCDIF_TRANSFER_COUNT_V_COUNT(mxsfb_var.yres),
238                 &lcd_regs->hw_lcdif_transfer_count_reg);
239
240         writel(lcd_vdctrl0, &lcd_regs->hw_lcdif_vdctrl0_reg);
241         writel(lcd_vdctrl1, &lcd_regs->hw_lcdif_vdctrl1_reg);
242         writel(lcd_vdctrl2, &lcd_regs->hw_lcdif_vdctrl2_reg);
243         writel(lcd_vdctrl3, &lcd_regs->hw_lcdif_vdctrl3_reg);
244         writel(lcd_vdctrl4, &lcd_regs->hw_lcdif_vdctrl4_reg);
245
246         writel(lcd_ctrl1, &lcd_regs->hw_lcdif_ctrl1_reg);
247         writel(lcd_ctrl2, &lcd_regs->hw_lcdif_ctrl2_reg);
248
249         writel(lcd_ctrl, &lcd_regs->hw_lcdif_ctrl_reg);
250
251         debug("mxsfb framebuffer driver initialized\n");
252 }
253
254 void mxsfb_disable(void)
255 {
256         u32 lcd_ctrl = readl(&lcd_regs->hw_lcdif_ctrl_reg);
257
258         writel(lcd_ctrl & ~LCDIF_CTRL_RUN, &lcd_regs->hw_lcdif_ctrl_reg);
259 }
260
261 int mxsfb_init(struct fb_videomode *mode, uint32_t pixfmt, int bpp)
262 {
263         switch (bpp) {
264         case 8:
265                 bits_per_pixel = 8;
266                 panel_info.vl_bpix = LCD_COLOR8;
267                 break;
268
269         case 16:
270                 bits_per_pixel = 16;
271                 panel_info.vl_bpix = LCD_COLOR16;
272                 break;
273
274         case 18:
275                 bits_per_pixel = 32;
276                 panel_info.vl_bpix = LCD_COLOR24;
277                 break;
278
279         case 24:
280                 bits_per_pixel = 32;
281                 panel_info.vl_bpix = LCD_COLOR24;
282                 break;
283
284         default:
285                 return -EINVAL;
286         }
287
288         pix_fmt = pixfmt;
289         color_depth = bpp;
290
291         lcd_line_length = bits_per_pixel / 8 * mode->xres;
292
293         mxsfb_var.xres = mode->xres;
294         mxsfb_var.yres = mode->yres;
295         mxsfb_var.xres_virtual = mode->xres;
296         mxsfb_var.yres_virtual = mode->yres;
297         mxsfb_var.pixclock = mode->pixclock;
298         mxsfb_var.left_margin = mode->left_margin;
299         mxsfb_var.right_margin = mode->right_margin;
300         mxsfb_var.upper_margin = mode->upper_margin;
301         mxsfb_var.lower_margin = mode->lower_margin;
302         mxsfb_var.hsync_len = mode->hsync_len;
303         mxsfb_var.vsync_len = mode->vsync_len;
304         mxsfb_var.sync = mode->sync;
305
306         panel_info.vl_col = mode->xres;
307         panel_info.vl_row = mode->yres;
308
309         return 0;
310 }