blob: 5a8576ea2b06de152b01465633b3dfaa3b0662b5 [file] [log] [blame]
York Sunb7145172007-10-29 13:58:39 -05001/*
2 * Copyright 2007 Freescale Semiconductor, Inc.
3 * York Sun <yorksun@freescale.com>
4 *
5 * FSL DIU Framebuffer driver
6 *
7 * See file CREDITS for list of people who contributed to this
8 * project.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License as
12 * published by the Free Software Foundation; either version 2 of
13 * the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
23 * MA 02111-1307 USA
24 */
25
York Sunb7145172007-10-29 13:58:39 -050026#include <common.h>
27#include <i2c.h>
28#include <malloc.h>
29
30#ifdef CONFIG_FSL_DIU_FB
31
32#include "fsl_diu_fb.h"
33
York Sunb7145172007-10-29 13:58:39 -050034#ifdef DEBUG
35#define DPRINTF(fmt, args...) printf("%s: " fmt,__FUNCTION__,## args)
36#else
37#define DPRINTF(fmt, args...)
38#endif
39
York Sunb7145172007-10-29 13:58:39 -050040struct fb_videomode {
41 const char *name; /* optional */
42 unsigned int refresh; /* optional */
43 unsigned int xres;
44 unsigned int yres;
45 unsigned int pixclock;
46 unsigned int left_margin;
47 unsigned int right_margin;
48 unsigned int upper_margin;
49 unsigned int lower_margin;
50 unsigned int hsync_len;
51 unsigned int vsync_len;
52 unsigned int sync;
53 unsigned int vmode;
54 unsigned int flag;
55};
56
57#define FB_SYNC_VERT_HIGH_ACT 2 /* vertical sync high active */
58#define FB_SYNC_COMP_HIGH_ACT 8 /* composite sync high active */
59#define FB_VMODE_NONINTERLACED 0 /* non interlaced */
60
61/*
62 * These parameters give default parameters
63 * for video output 1024x768,
64 * FIXME - change timing to proper amounts
65 * hsync 31.5kHz, vsync 60Hz
66 */
67static struct fb_videomode fsl_diu_mode_1024 = {
68 .refresh = 60,
69 .xres = 1024,
70 .yres = 768,
71 .pixclock = 15385,
72 .left_margin = 160,
73 .right_margin = 24,
74 .upper_margin = 29,
75 .lower_margin = 3,
76 .hsync_len = 136,
77 .vsync_len = 6,
78 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
79 .vmode = FB_VMODE_NONINTERLACED
80};
81
82static struct fb_videomode fsl_diu_mode_1280 = {
83 .name = "1280x1024-60",
84 .refresh = 60,
85 .xres = 1280,
86 .yres = 1024,
87 .pixclock = 9375,
88 .left_margin = 38,
89 .right_margin = 128,
90 .upper_margin = 2,
91 .lower_margin = 7,
92 .hsync_len = 216,
93 .vsync_len = 37,
94 .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
95 .vmode = FB_VMODE_NONINTERLACED
96};
97
98/*
99 * These are the fields of area descriptor(in DDR memory) for every plane
100 */
101struct diu_ad {
102 /* Word 0(32-bit) in DDR memory */
103 unsigned int pix_fmt; /* hard coding pixel format */
104 /* Word 1(32-bit) in DDR memory */
105 unsigned int addr;
106 /* Word 2(32-bit) in DDR memory */
107 unsigned int src_size_g_alpha;
108 /* Word 3(32-bit) in DDR memory */
109 unsigned int aoi_size;
110 /* Word 4(32-bit) in DDR memory */
111 unsigned int offset_xyi;
112 /* Word 5(32-bit) in DDR memory */
113 unsigned int offset_xyd;
114 /* Word 6(32-bit) in DDR memory */
115 unsigned int ckmax_r:8;
116 unsigned int ckmax_g:8;
117 unsigned int ckmax_b:8;
118 unsigned int res9:8;
119 /* Word 7(32-bit) in DDR memory */
120 unsigned int ckmin_r:8;
121 unsigned int ckmin_g:8;
122 unsigned int ckmin_b:8;
123 unsigned int res10:8;
124 /* Word 8(32-bit) in DDR memory */
125 unsigned int next_ad;
126 /* Word 9(32-bit) in DDR memory, just for 64-bit aligned */
127 unsigned int res1;
128 unsigned int res2;
129 unsigned int res3;
130}__attribute__ ((packed));
131
132/*
133 * DIU register map
134 */
135struct diu {
136 unsigned int desc[3];
137 unsigned int gamma;
138 unsigned int pallete;
139 unsigned int cursor;
140 unsigned int curs_pos;
141 unsigned int diu_mode;
142 unsigned int bgnd;
143 unsigned int bgnd_wb;
144 unsigned int disp_size;
145 unsigned int wb_size;
146 unsigned int wb_mem_addr;
147 unsigned int hsyn_para;
148 unsigned int vsyn_para;
149 unsigned int syn_pol;
150 unsigned int thresholds;
151 unsigned int int_status;
152 unsigned int int_mask;
153 unsigned int colorbar[8];
154 unsigned int filling;
155 unsigned int plut;
156} __attribute__ ((packed));
157
158struct diu_hw {
159 struct diu *diu_reg;
160 volatile unsigned int mode; /* DIU operation mode */
161};
162
163struct diu_addr {
164 unsigned char * paddr; /* Virtual address */
165 unsigned int offset;
166};
167
168#define FSL_DIU_BASE_OFFSET 0x2C000 /* Offset of Display Interface Unit */
169
170/*
171 * Modes of operation of DIU
172 */
173#define MFB_MODE0 0 /* DIU off */
174#define MFB_MODE1 1 /* All three planes output to display */
175#define MFB_MODE2 2 /* Plane 1 to display,
176 * planes 2+3 written back to memory */
177#define MFB_MODE3 3 /* All three planes written back to memory */
178#define MFB_MODE4 4 /* Color bar generation */
179
180#define MAX_CURS 32
181
York Sunb7145172007-10-29 13:58:39 -0500182static struct fb_info fsl_fb_info;
183static struct diu_addr gamma, cursor;
184static struct diu_ad fsl_diu_fb_ad __attribute__ ((aligned(32)));
185static struct diu_ad dummy_ad __attribute__ ((aligned(32)));
186static unsigned char *dummy_fb;
187static struct diu_hw dr = {
188 .mode = MFB_MODE1,
189};
190
191int fb_enabled = 0;
192int fb_initialized = 0;
193const int default_xres = 1280;
194const int default_pixel_format = 0x88882317;
195
196static int map_video_memory(struct fb_info *info, unsigned long bytes_align);
197static void enable_lcdc(void);
198static void disable_lcdc(void);
199static int fsl_diu_enable_panel(struct fb_info *info);
200static int fsl_diu_disable_panel(struct fb_info *info);
201static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align);
202static u32 get_busfreq(void);
203
York Sunb7145172007-10-29 13:58:39 -0500204int fsl_diu_init(int xres,
205 unsigned int pixel_format,
206 int gamma_fix,
207 unsigned char *splash_bmp)
208{
209 struct fb_videomode *fsl_diu_mode_db;
210 struct diu_ad *ad = &fsl_diu_fb_ad;
211 struct diu *hw;
212 struct fb_info *info = &fsl_fb_info;
213 struct fb_var_screeninfo *var = &info->var;
214 volatile immap_t *immap = (immap_t *)CFG_IMMR;
215 volatile ccsr_gur_t *gur = &immap->im_gur;
216 volatile unsigned int *guts_clkdvdr = &gur->clkdvdr;
217 unsigned char *gamma_table_base;
218 unsigned int i, j;
219 unsigned long speed_ccb, temp, pixval;
220
221 DPRINTF("Enter fsl_diu_init\n");
222 dr.diu_reg = (struct diu *) (CFG_IMMR + FSL_DIU_BASE_OFFSET);
223 hw = (struct diu *) dr.diu_reg;
224
225 disable_lcdc();
226
227 if (xres == 1280) {
228 fsl_diu_mode_db = &fsl_diu_mode_1280;
229 } else {
230 fsl_diu_mode_db = &fsl_diu_mode_1024;
231 }
232
233 if (0 == fb_initialized) {
234 allocate_buf(&gamma, 768, 32);
235 DPRINTF("gamma is allocated @ 0x%x\n",
236 (unsigned int)gamma.paddr);
237 allocate_buf(&cursor, MAX_CURS * MAX_CURS * 2, 32);
238 DPRINTF("curosr is allocated @ 0x%x\n",
239 (unsigned int)cursor.paddr);
240
241 /* create a dummy fb and dummy ad */
242 dummy_fb = malloc(64);
243 if (NULL == dummy_fb) {
244 printf("Cannot allocate dummy fb\n");
245 return -1;
246 }
247 dummy_ad.addr = cpu_to_le32((unsigned int)dummy_fb);
248 dummy_ad.pix_fmt = 0x88882317;
249 dummy_ad.src_size_g_alpha = 0x04400000; /* alpha = 0 */
250 dummy_ad.aoi_size = 0x02000400;
251 dummy_ad.offset_xyi = 0;
252 dummy_ad.offset_xyd = 0;
253 dummy_ad.next_ad = 0;
254 /* Memory allocation for framebuffer */
255 if (map_video_memory(info, 32)) {
256 printf("Unable to allocate fb memory 1\n");
257 return -1;
258 }
259 } else {
260 memset(info->screen_base, 0, info->smem_len);
261 }
262
263 dr.diu_reg->desc[0] = (unsigned int) &dummy_ad;
264 dr.diu_reg->desc[1] = (unsigned int) &dummy_ad;
265 dr.diu_reg->desc[2] = (unsigned int) &dummy_ad;
266 DPRINTF("dummy dr.diu_reg->desc[0] = 0x%x\n", dr.diu_reg->desc[0]);
267 DPRINTF("dummy desc[0] = 0x%x\n", hw->desc[0]);
268
269 /* read mode info */
270 var->xres = fsl_diu_mode_db->xres;
271 var->yres = fsl_diu_mode_db->yres;
272 var->bits_per_pixel = 32;
273 var->pixclock = fsl_diu_mode_db->pixclock;
274 var->left_margin = fsl_diu_mode_db->left_margin;
275 var->right_margin = fsl_diu_mode_db->right_margin;
276 var->upper_margin = fsl_diu_mode_db->upper_margin;
277 var->lower_margin = fsl_diu_mode_db->lower_margin;
278 var->hsync_len = fsl_diu_mode_db->hsync_len;
279 var->vsync_len = fsl_diu_mode_db->vsync_len;
280 var->sync = fsl_diu_mode_db->sync;
281 var->vmode = fsl_diu_mode_db->vmode;
282 info->line_length = var->xres * var->bits_per_pixel / 8;
283 info->logo_size = 0;
284 info->logo_height = 0;
285
286 ad->pix_fmt = pixel_format;
287 ad->addr = cpu_to_le32((unsigned int)info->screen_base);
288 ad->src_size_g_alpha
289 = cpu_to_le32((var->yres << 12) | var->xres);
290 /* fix me. AOI should not be greater than display size */
291 ad->aoi_size = cpu_to_le32(( var->yres << 16) | var->xres);
292 ad->offset_xyi = 0;
293 ad->offset_xyd = 0;
294
295 /* Disable chroma keying function */
296 ad->ckmax_r = 0;
297 ad->ckmax_g = 0;
298 ad->ckmax_b = 0;
299
300 ad->ckmin_r = 255;
301 ad->ckmin_g = 255;
302 ad->ckmin_b = 255;
303
304 gamma_table_base = gamma.paddr;
305 DPRINTF("gamma_table_base is allocated @ 0x%x\n",
306 (unsigned int)gamma_table_base);
307
308 /* Prep for DIU init - gamma table */
309
310 for (i = 0; i <= 2; i++)
311 for (j = 0; j <= 255; j++)
312 *gamma_table_base++ = j;
313
314 if (gamma_fix == 1) { /* fix the gamma */
315 DPRINTF("Fix gamma table\n");
316 gamma_table_base = gamma.paddr;
317 for (i = 0; i < 256*3; i++) {
318 gamma_table_base[i] = (gamma_table_base[i] << 2)
319 | ((gamma_table_base[i] >> 6) & 0x03);
320 }
321 }
322
323 DPRINTF("update-lcdc: HW - %p\n Disabling DIU\n", hw);
324
325 /* Program DIU registers */
326
327 hw->gamma = (unsigned int) gamma.paddr;
328 hw->cursor= (unsigned int) cursor.paddr;
329 hw->bgnd = 0x007F7F7F; /* BGND */
330 hw->bgnd_wb = 0; /* BGND_WB */
331 hw->disp_size = var->yres << 16 | var->xres; /* DISP SIZE */
332 hw->wb_size = 0; /* WB SIZE */
333 hw->wb_mem_addr = 0; /* WB MEM ADDR */
334 hw->hsyn_para = var->left_margin << 22 | /* BP_H */
335 var->hsync_len << 11 | /* PW_H */
336 var->right_margin; /* FP_H */
337 hw->vsyn_para = var->upper_margin << 22 | /* BP_V */
338 var->vsync_len << 11 | /* PW_V */
339 var->lower_margin; /* FP_V */
340
341 /* Pixel Clock configuration */
342 DPRINTF("DIU: Bus Frequency = %d\n", get_busfreq());
343 speed_ccb = get_busfreq();
344
345 DPRINTF("DIU pixclock in ps - %d\n", var->pixclock);
346 temp = 1;
347 temp *= 1000000000;
348 temp /= var->pixclock;
349 temp *= 1000;
350 pixval = speed_ccb / temp;
351 DPRINTF("DIU pixval = %lu\n", pixval);
352
353 hw->syn_pol = 0; /* SYNC SIGNALS POLARITY */
354 hw->thresholds = 0x00037800; /* The Thresholds */
355 hw->int_status = 0; /* INTERRUPT STATUS */
356 hw->int_mask = 0; /* INT MASK */
357 hw->plut = 0x01F5F666;
358
359 /* Modify PXCLK in GUTS CLKDVDR */
360 DPRINTF("DIU: Current value of CLKDVDR = 0x%08x\n", *guts_clkdvdr);
361 temp = *guts_clkdvdr & 0x2000FFFF;
362 *guts_clkdvdr = temp; /* turn off clock */
363 *guts_clkdvdr = temp | 0x80000000 | ((pixval & 0x1F) << 16);
364 DPRINTF("DIU: Modified value of CLKDVDR = 0x%08x\n", *guts_clkdvdr);
365
366 fb_initialized = 1;
367
368 if (splash_bmp) {
369 info->logo_height = fsl_diu_display_bmp(splash_bmp, 0, 0, 0);
370 info->logo_size = info->logo_height * info->line_length;
371 DPRINTF("logo height %d, logo_size 0x%x\n",
372 info->logo_height,info->logo_size);
373 }
374
375 /* Enable the DIU */
376 fsl_diu_enable_panel(info);
377 enable_lcdc();
378
379 return 0;
380}
381
382char *fsl_fb_open(struct fb_info **info)
383{
384 *info = &fsl_fb_info;
385 return (char *) ((unsigned int)(*info)->screen_base
386 + (*info)->logo_size);
387}
388
389void fsl_diu_close(void)
390{
391 struct fb_info *info = &fsl_fb_info;
392 fsl_diu_disable_panel(info);
393}
394
395static int fsl_diu_enable_panel(struct fb_info *info)
396{
397 struct diu *hw = dr.diu_reg;
398 struct diu_ad *ad = &fsl_diu_fb_ad;
399
400 DPRINTF("Entered: enable_panel\n");
401 if (hw->desc[0] != (unsigned int)ad)
402 hw->desc[0] = (unsigned int)ad;
403 DPRINTF("desc[0] = 0x%x\n", hw->desc[0]);
404 return 0;
405}
406
407static int fsl_diu_disable_panel(struct fb_info *info)
408{
409 struct diu *hw = dr.diu_reg;
410
411 DPRINTF("Entered: disable_panel\n");
412 if (hw->desc[0] != (unsigned int)&dummy_ad)
413 hw->desc[0] = (unsigned int)&dummy_ad;
414 return 0;
415}
416
417static int map_video_memory(struct fb_info *info, unsigned long bytes_align)
418{
419 unsigned long offset;
420 unsigned long mask;
421
422 DPRINTF("Entered: map_video_memory\n");
423 /* allocate maximum 1280*1024 with 32bpp */
424 info->smem_len = 1280 * 4 *1024 + bytes_align;
425 DPRINTF("MAP_VIDEO_MEMORY: smem_len = %d\n", info->smem_len);
426 info->screen_base = malloc(info->smem_len);
427 if (info->screen_base == NULL) {
428 printf("Unable to allocate fb memory\n");
429 return -1;
430 }
431 info->smem_start = (unsigned int) info->screen_base;
432 mask = bytes_align - 1;
433 offset = (unsigned long)info->screen_base & mask;
434 if (offset) {
435 info->screen_base += offset;
436 info->smem_len = info->smem_len - (bytes_align - offset);
437 } else
438 info->smem_len = info->smem_len - bytes_align;
439
440 info->screen_size = info->smem_len;
441
442 DPRINTF("Allocated fb @ 0x%08lx, size=%d.\n",
443 info->smem_start, info->smem_len);
444
445 return 0;
446}
447
448static void enable_lcdc(void)
449{
450 struct diu *hw = dr.diu_reg;
451
452 DPRINTF("Entered: enable_lcdc, fb_enabled = %d\n", fb_enabled);
453 if (!fb_enabled) {
454 hw->diu_mode = dr.mode;
455 fb_enabled++;
456 }
457 DPRINTF("diu_mode = %d\n", hw->diu_mode);
458}
459
460static void disable_lcdc(void)
461{
462 struct diu *hw = dr.diu_reg;
463
464 DPRINTF("Entered: disable_lcdc, fb_enabled = %d\n", fb_enabled);
465 if (fb_enabled) {
466 hw->diu_mode = 0;
467 fb_enabled = 0;
468 }
469}
470
471static u32 get_busfreq(void)
472{
473 u32 fs_busfreq = 0;
474
475 fs_busfreq = get_bus_freq(0);
476 return fs_busfreq;
477}
478
479/*
480 * Align to 64-bit(8-byte), 32-byte, etc.
481 */
482static int allocate_buf(struct diu_addr *buf, u32 size, u32 bytes_align)
483{
484 u32 offset, ssize;
485 u32 mask;
486
487 DPRINTF("Entered: allocate_buf\n");
488 ssize = size + bytes_align;
489 buf->paddr = malloc(ssize);
490 if (!buf->paddr)
491 return -1;
492
493 memset(buf->paddr, 0, ssize);
494 mask = bytes_align - 1;
495 offset = (u32)buf->paddr & mask;
496 if (offset) {
497 buf->offset = bytes_align - offset;
498 buf->paddr = (unsigned char *) ((u32)buf->paddr + offset);
499 } else
500 buf->offset = 0;
501 return 0;
502}
503
504int fsl_diu_display_bmp(unsigned char *bmp,
505 int xoffset,
506 int yoffset,
507 int transpar)
508{
509 struct fb_info *info = &fsl_fb_info;
510 unsigned char r, g, b;
511 unsigned int *fb_t, val;
512 unsigned char *bitmap;
513 unsigned int palette[256];
514 int width, height, bpp, ncolors, raster, offset, x, y, i, k, cpp;
515
516 if (!bmp) {
517 printf("Must supply a bitmap address\n");
518 return 0;
519 }
520
521 raster = bmp[10] + (bmp[11] << 8) + (bmp[12] << 16) + (bmp[13] << 24);
522 width = (bmp[21] << 24) | (bmp[20] << 16) | (bmp[19] << 8) | bmp[18];
523 height = (bmp[25] << 24) | (bmp[24] << 16) | (bmp[23] << 8) | bmp[22];
524 bpp = (bmp[29] << 8) | (bmp[28]);
525 ncolors = bmp[46] + (bmp[47] << 8) + (bmp[48] << 16) + (bmp[49] << 24);
526 bitmap = bmp + raster;
527 cpp = info->var.bits_per_pixel / 8;
528
529 DPRINTF("bmp = 0x%08x\n", (unsigned int)bmp);
530 DPRINTF("bitmap = 0x%08x\n", (unsigned int)bitmap);
531 DPRINTF("width = %d\n", width);
532 DPRINTF("height = %d\n", height);
533 DPRINTF("bpp = %d\n", bpp);
534 DPRINTF("ncolors = %d\n", ncolors);
535
536 DPRINTF("xres = %d\n", info->var.xres);
537 DPRINTF("yres = %d\n", info->var.yres);
538 DPRINTF("Screen_base = 0x%x\n", (unsigned int)info->screen_base);
539
540 if (((width+xoffset) > info->var.xres) ||
541 ((height+yoffset) > info->var.yres)) {
542 printf("bitmap is out of range, image too large or too much offset\n");
543 return 0;
544 }
545 if (bpp < 24) {
546 for (i = 0, offset = 54; i < ncolors; i++, offset += 4)
547 palette[i] = (bmp[offset+2] << 16)
548 + (bmp[offset+1] << 8) + bmp[offset];
549 }
550
551 switch (bpp) {
552 case 1:
553 for (y = height - 1; y >= 0; y--) {
554 fb_t = (unsigned int *) ((unsigned int)info->screen_base + (((y+yoffset) * info->var.xres) + xoffset)*cpp);
555 for (x = 0; x < width; x += 8) {
556 b = *bitmap++;
557 for (k = 0; k < 8; k++) {
558 if (b & 0x80)
559 *fb_t = palette[1];
560 else
561 *fb_t = palette[0];
562 b = b << 1;
563 }
564 }
565 for (i = (width / 2) % 4; i > 0; i--)
566 bitmap++;
567 }
568 break;
569 case 4:
570 for (y = height - 1; y >= 0; y--) {
571 fb_t = (unsigned int *) ((unsigned int)info->screen_base + (((y+yoffset) * info->var.xres) + xoffset)*cpp);
572 for (x = 0; x < width; x += 2) {
573 b = *bitmap++;
574 r = (b >> 4) & 0x0F;
575 g = b & 0x0F;
576 *fb_t++ = palette[r];
577 *fb_t++ = palette[g];
578 }
579 for (i = (width / 2) % 4; i > 0; i--)
580 bitmap++;
581 }
582 break;
583 case 8:
584 for (y = height - 1; y >= 0; y--) {
585 fb_t = (unsigned int *) ((unsigned int)info->screen_base + (((y+yoffset) * info->var.xres) + xoffset)*cpp);
586 for (x = 0; x < width; x++) {
587 *fb_t++ = palette[ *bitmap++ ];
588 }
589 for (i = (width / 2) % 4; i > 0; i--)
590 bitmap++;
591 }
592 break;
593 case 24:
594 for (y = height - 1; y >= 0; y--) {
595 fb_t = (unsigned int *) ((unsigned int)info->screen_base + (((y+yoffset) * info->var.xres) + xoffset)*cpp);
596 for (x = 0; x < width; x++) {
597 b = *bitmap++;
598 g = *bitmap++;
599 r = *bitmap++;
600 val = (r << 16) + (g << 8) + b;
601 *fb_t++ = val;
602 }
603 for (; (x % 4) != 0; x++) /* 4-byte alignment */
604 bitmap++;
605 }
606 break;
607 }
608
609 return height;
610}
611
612void fsl_diu_clear_screen(void)
613{
614 struct fb_info *info = &fsl_fb_info;
615
616 memset(info->screen_base, 0, info->smem_len);
617}
618#endif /* CONFIG_FSL_DIU_FB */