blob: 7b3400cfae6d506dc96902cf2e33c585709f4999 [file] [log] [blame]
Sascha Hauer7c2f891c2005-05-01 08:59:24 -07001/*
2 * linux/drivers/video/imxfb.c
3 *
4 * Freescale i.MX Frame Buffer device driver
5 *
6 * Copyright (C) 2004 Sascha Hauer, Pengutronix
7 * Based on acornfb.c Copyright (C) Russell King.
8 *
9 * This file is subject to the terms and conditions of the GNU General Public
10 * License. See the file COPYING in the main directory of this archive for
11 * more details.
12 *
13 * Please direct your questions and comments on this driver to the following
14 * email address:
15 *
16 * linux-arm-kernel@lists.arm.linux.org.uk
17 */
18
Sascha Hauer7c2f891c2005-05-01 08:59:24 -070019
Sascha Hauer7c2f891c2005-05-01 08:59:24 -070020#include <linux/module.h>
21#include <linux/kernel.h>
Sascha Hauer7c2f891c2005-05-01 08:59:24 -070022#include <linux/errno.h>
23#include <linux/string.h>
24#include <linux/interrupt.h>
25#include <linux/slab.h>
Andrea Righi27ac7922008-07-23 21:28:13 -070026#include <linux/mm.h>
Sascha Hauer7c2f891c2005-05-01 08:59:24 -070027#include <linux/fb.h>
28#include <linux/delay.h>
29#include <linux/init.h>
30#include <linux/ioport.h>
31#include <linux/cpufreq.h>
Russell Kingd052d1b2005-10-29 19:07:23 +010032#include <linux/platform_device.h>
Sascha Hauer7c2f891c2005-05-01 08:59:24 -070033#include <linux/dma-mapping.h>
Juergen Beisert72330b02008-12-16 11:44:07 +010034#include <linux/io.h>
Sascha Hauer7c2f891c2005-05-01 08:59:24 -070035
Russell Kinga09e64f2008-08-05 16:14:15 +010036#include <mach/imxfb.h>
Sascha Hauer7c2f891c2005-05-01 08:59:24 -070037
38/*
39 * Complain if VAR is out of range.
40 */
41#define DEBUG_VAR 1
42
Juergen Beisert72330b02008-12-16 11:44:07 +010043#define DRIVER_NAME "imx-fb"
44
45#define LCDC_SSA 0x00
46
47#define LCDC_SIZE 0x04
48#define SIZE_XMAX(x) ((((x) >> 4) & 0x3f) << 20)
49#define SIZE_YMAX(y) ((y) & 0x1ff)
50
51#define LCDC_VPW 0x08
52#define VPW_VPW(x) ((x) & 0x3ff)
53
54#define LCDC_CPOS 0x0C
55#define CPOS_CC1 (1<<31)
56#define CPOS_CC0 (1<<30)
57#define CPOS_OP (1<<28)
58#define CPOS_CXP(x) (((x) & 3ff) << 16)
59#define CPOS_CYP(y) ((y) & 0x1ff)
60
61#define LCDC_LCWHB 0x10
62#define LCWHB_BK_EN (1<<31)
63#define LCWHB_CW(w) (((w) & 0x1f) << 24)
64#define LCWHB_CH(h) (((h) & 0x1f) << 16)
65#define LCWHB_BD(x) ((x) & 0xff)
66
67#define LCDC_LCHCC 0x14
68#define LCHCC_CUR_COL_R(r) (((r) & 0x1f) << 11)
69#define LCHCC_CUR_COL_G(g) (((g) & 0x3f) << 5)
70#define LCHCC_CUR_COL_B(b) ((b) & 0x1f)
71
72#define LCDC_PCR 0x18
73
74#define LCDC_HCR 0x1C
75#define HCR_H_WIDTH(x) (((x) & 0x3f) << 26)
76#define HCR_H_WAIT_1(x) (((x) & 0xff) << 8)
77#define HCR_H_WAIT_2(x) ((x) & 0xff)
78
79#define LCDC_VCR 0x20
80#define VCR_V_WIDTH(x) (((x) & 0x3f) << 26)
81#define VCR_V_WAIT_1(x) (((x) & 0xff) << 8)
82#define VCR_V_WAIT_2(x) ((x) & 0xff)
83
84#define LCDC_POS 0x24
85#define POS_POS(x) ((x) & 1f)
86
87#define LCDC_LSCR1 0x28
88/* bit fields in imxfb.h */
89
90#define LCDC_PWMR 0x2C
91/* bit fields in imxfb.h */
92
93#define LCDC_DMACR 0x30
94/* bit fields in imxfb.h */
95
96#define LCDC_RMCR 0x34
97#define RMCR_LCDC_EN (1<<1)
98#define RMCR_SELF_REF (1<<0)
99
100#define LCDC_LCDICR 0x38
101#define LCDICR_INT_SYN (1<<2)
102#define LCDICR_INT_CON (1)
103
104#define LCDC_LCDISR 0x40
105#define LCDISR_UDR_ERR (1<<3)
106#define LCDISR_ERR_RES (1<<2)
107#define LCDISR_EOF (1<<1)
108#define LCDISR_BOF (1<<0)
109
Sascha Hauer24b9baf2008-12-16 11:44:08 +0100110/*
111 * These are the bitfields for each
112 * display depth that we support.
113 */
114struct imxfb_rgb {
115 struct fb_bitfield red;
116 struct fb_bitfield green;
117 struct fb_bitfield blue;
118 struct fb_bitfield transp;
119};
120
121#define RGB_16 (0)
122#define RGB_8 (1)
123#define NR_RGB 2
124
125struct imxfb_info {
126 struct platform_device *pdev;
127 void __iomem *regs;
128
129 struct imxfb_rgb *rgb[NR_RGB];
130
131 u_int max_bpp;
132 u_int max_xres;
133 u_int max_yres;
134
135 /*
136 * These are the addresses we mapped
137 * the framebuffer memory region to.
138 */
139 dma_addr_t map_dma;
140 u_char *map_cpu;
141 u_int map_size;
142
143 u_char *screen_cpu;
144 dma_addr_t screen_dma;
145 u_int palette_size;
146
147 dma_addr_t dbar1;
148 dma_addr_t dbar2;
149
150 u_int pcr;
151 u_int pwmr;
152 u_int lscr1;
153 u_int dmacr;
154 u_int cmap_inverse:1,
155 cmap_static:1,
156 unused:30;
157
158 void (*lcd_power)(int);
159 void (*backlight_power)(int);
160};
161
162#define IMX_NAME "IMX"
163
164/*
165 * Minimum X and Y resolutions
166 */
167#define MIN_XRES 64
168#define MIN_YRES 64
169
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700170static struct imxfb_rgb def_rgb_16 = {
171 .red = { .offset = 8, .length = 4, },
172 .green = { .offset = 4, .length = 4, },
173 .blue = { .offset = 0, .length = 4, },
174 .transp = { .offset = 0, .length = 0, },
175};
176
177static struct imxfb_rgb def_rgb_8 = {
178 .red = { .offset = 0, .length = 8, },
179 .green = { .offset = 0, .length = 8, },
180 .blue = { .offset = 0, .length = 8, },
181 .transp = { .offset = 0, .length = 0, },
182};
183
184static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info);
185
186static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
187{
188 chan &= 0xffff;
189 chan >>= 16 - bf->length;
190 return chan << bf->offset;
191}
192
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700193static int
194imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
195 u_int trans, struct fb_info *info)
196{
197 struct imxfb_info *fbi = info->par;
198 u_int val, ret = 1;
199
200#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
201 if (regno < fbi->palette_size) {
202 val = (CNVT_TOHW(red, 4) << 8) |
203 (CNVT_TOHW(green,4) << 4) |
204 CNVT_TOHW(blue, 4);
205
Juergen Beisert72330b02008-12-16 11:44:07 +0100206 writel(val, fbi->regs + 0x800 + (regno << 2));
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700207 ret = 0;
208 }
209 return ret;
210}
211
212static int
213imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
214 u_int trans, struct fb_info *info)
215{
216 struct imxfb_info *fbi = info->par;
217 unsigned int val;
218 int ret = 1;
219
220 /*
221 * If inverse mode was selected, invert all the colours
222 * rather than the register number. The register number
223 * is what you poke into the framebuffer to produce the
224 * colour you requested.
225 */
226 if (fbi->cmap_inverse) {
227 red = 0xffff - red;
228 green = 0xffff - green;
229 blue = 0xffff - blue;
230 }
231
232 /*
233 * If greyscale is true, then we convert the RGB value
234 * to greyscale no mater what visual we are using.
235 */
236 if (info->var.grayscale)
237 red = green = blue = (19595 * red + 38470 * green +
238 7471 * blue) >> 16;
239
240 switch (info->fix.visual) {
241 case FB_VISUAL_TRUECOLOR:
242 /*
243 * 12 or 16-bit True Colour. We encode the RGB value
244 * according to the RGB bitfield information.
245 */
246 if (regno < 16) {
247 u32 *pal = info->pseudo_palette;
248
249 val = chan_to_field(red, &info->var.red);
250 val |= chan_to_field(green, &info->var.green);
251 val |= chan_to_field(blue, &info->var.blue);
252
253 pal[regno] = val;
254 ret = 0;
255 }
256 break;
257
258 case FB_VISUAL_STATIC_PSEUDOCOLOR:
259 case FB_VISUAL_PSEUDOCOLOR:
260 ret = imxfb_setpalettereg(regno, red, green, blue, trans, info);
261 break;
262 }
263
264 return ret;
265}
266
267/*
268 * imxfb_check_var():
269 * Round up in the following order: bits_per_pixel, xres,
270 * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
271 * bitfields, horizontal timing, vertical timing.
272 */
273static int
274imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
275{
276 struct imxfb_info *fbi = info->par;
277 int rgbidx;
278
279 if (var->xres < MIN_XRES)
280 var->xres = MIN_XRES;
281 if (var->yres < MIN_YRES)
282 var->yres = MIN_YRES;
283 if (var->xres > fbi->max_xres)
284 var->xres = fbi->max_xres;
285 if (var->yres > fbi->max_yres)
286 var->yres = fbi->max_yres;
287 var->xres_virtual = max(var->xres_virtual, var->xres);
288 var->yres_virtual = max(var->yres_virtual, var->yres);
289
290 pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel);
291 switch (var->bits_per_pixel) {
292 case 16:
293 rgbidx = RGB_16;
294 break;
295 case 8:
296 rgbidx = RGB_8;
297 break;
298 default:
299 rgbidx = RGB_16;
300 }
301
302 /*
303 * Copy the RGB parameters for this display
304 * from the machine specific parameters.
305 */
306 var->red = fbi->rgb[rgbidx]->red;
307 var->green = fbi->rgb[rgbidx]->green;
308 var->blue = fbi->rgb[rgbidx]->blue;
309 var->transp = fbi->rgb[rgbidx]->transp;
310
311 pr_debug("RGBT length = %d:%d:%d:%d\n",
312 var->red.length, var->green.length, var->blue.length,
313 var->transp.length);
314
315 pr_debug("RGBT offset = %d:%d:%d:%d\n",
316 var->red.offset, var->green.offset, var->blue.offset,
317 var->transp.offset);
318
319 return 0;
320}
321
322/*
323 * imxfb_set_par():
324 * Set the user defined part of the display for the specified console
325 */
326static int imxfb_set_par(struct fb_info *info)
327{
328 struct imxfb_info *fbi = info->par;
329 struct fb_var_screeninfo *var = &info->var;
330
331 pr_debug("set_par\n");
332
333 if (var->bits_per_pixel == 16)
334 info->fix.visual = FB_VISUAL_TRUECOLOR;
335 else if (!fbi->cmap_static)
336 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
337 else {
338 /*
339 * Some people have weird ideas about wanting static
340 * pseudocolor maps. I suspect their user space
341 * applications are broken.
342 */
343 info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
344 }
345
346 info->fix.line_length = var->xres_virtual *
347 var->bits_per_pixel / 8;
348 fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
349
350 imxfb_activate_var(var, info);
351
352 return 0;
353}
354
355static void imxfb_enable_controller(struct imxfb_info *fbi)
356{
357 pr_debug("Enabling LCD controller\n");
358
359 /* initialize LCDC */
Juergen Beisert72330b02008-12-16 11:44:07 +0100360 writel(readl(fbi->regs + LCDC_RMCR) & ~RMCR_LCDC_EN,
361 fbi->regs + LCDC_RMCR); /* just to be safe... */
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700362
Juergen Beisert72330b02008-12-16 11:44:07 +0100363 writel(fbi->screen_dma, fbi->regs + LCDC_SSA);
364
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700365 /* physical screen start address */
Juergen Beisert72330b02008-12-16 11:44:07 +0100366 writel(VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4),
367 fbi->regs + LCDC_VPW);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700368
Juergen Beisert72330b02008-12-16 11:44:07 +0100369 /* panning offset 0 (0 pixel offset) */
370 writel(0x00000000, fbi->regs + LCDC_POS);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700371
372 /* disable hardware cursor */
Juergen Beisert72330b02008-12-16 11:44:07 +0100373 writel(readl(fbi->regs + LCDC_CPOS) & ~(CPOS_CC0 | CPOS_CC1),
374 fbi->regs + LCDC_CPOS);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700375
Juergen Beisert72330b02008-12-16 11:44:07 +0100376 writel(RMCR_LCDC_EN, fbi->regs + LCDC_RMCR);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700377
378 if(fbi->backlight_power)
379 fbi->backlight_power(1);
380 if(fbi->lcd_power)
381 fbi->lcd_power(1);
382}
383
384static void imxfb_disable_controller(struct imxfb_info *fbi)
385{
386 pr_debug("Disabling LCD controller\n");
387
388 if(fbi->backlight_power)
389 fbi->backlight_power(0);
390 if(fbi->lcd_power)
391 fbi->lcd_power(0);
392
Juergen Beisert72330b02008-12-16 11:44:07 +0100393 writel(0, fbi->regs + LCDC_RMCR);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700394}
395
396static int imxfb_blank(int blank, struct fb_info *info)
397{
398 struct imxfb_info *fbi = info->par;
399
400 pr_debug("imxfb_blank: blank=%d\n", blank);
401
402 switch (blank) {
403 case FB_BLANK_POWERDOWN:
404 case FB_BLANK_VSYNC_SUSPEND:
405 case FB_BLANK_HSYNC_SUSPEND:
406 case FB_BLANK_NORMAL:
407 imxfb_disable_controller(fbi);
408 break;
409
410 case FB_BLANK_UNBLANK:
411 imxfb_enable_controller(fbi);
412 break;
413 }
414 return 0;
415}
416
417static struct fb_ops imxfb_ops = {
418 .owner = THIS_MODULE,
419 .fb_check_var = imxfb_check_var,
420 .fb_set_par = imxfb_set_par,
421 .fb_setcolreg = imxfb_setcolreg,
422 .fb_fillrect = cfb_fillrect,
423 .fb_copyarea = cfb_copyarea,
424 .fb_imageblit = cfb_imageblit,
425 .fb_blank = imxfb_blank,
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700426};
427
428/*
429 * imxfb_activate_var():
430 * Configures LCD Controller based on entries in var parameter. Settings are
431 * only written to the controller if changes were made.
432 */
433static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info)
434{
435 struct imxfb_info *fbi = info->par;
436 pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n",
437 var->xres, var->hsync_len,
438 var->left_margin, var->right_margin);
439 pr_debug("var: yres=%d vslen=%d um=%d bm=%d\n",
440 var->yres, var->vsync_len,
441 var->upper_margin, var->lower_margin);
442
443#if DEBUG_VAR
444 if (var->xres < 16 || var->xres > 1024)
445 printk(KERN_ERR "%s: invalid xres %d\n",
446 info->fix.id, var->xres);
447 if (var->hsync_len < 1 || var->hsync_len > 64)
448 printk(KERN_ERR "%s: invalid hsync_len %d\n",
449 info->fix.id, var->hsync_len);
450 if (var->left_margin > 255)
451 printk(KERN_ERR "%s: invalid left_margin %d\n",
452 info->fix.id, var->left_margin);
453 if (var->right_margin > 255)
454 printk(KERN_ERR "%s: invalid right_margin %d\n",
455 info->fix.id, var->right_margin);
456 if (var->yres < 1 || var->yres > 511)
457 printk(KERN_ERR "%s: invalid yres %d\n",
458 info->fix.id, var->yres);
459 if (var->vsync_len > 100)
460 printk(KERN_ERR "%s: invalid vsync_len %d\n",
461 info->fix.id, var->vsync_len);
462 if (var->upper_margin > 63)
463 printk(KERN_ERR "%s: invalid upper_margin %d\n",
464 info->fix.id, var->upper_margin);
465 if (var->lower_margin > 255)
466 printk(KERN_ERR "%s: invalid lower_margin %d\n",
467 info->fix.id, var->lower_margin);
468#endif
469
Juergen Beisert72330b02008-12-16 11:44:07 +0100470 writel(HCR_H_WIDTH(var->hsync_len) |
Sascha Hauerd6ed5752008-12-16 11:44:08 +0100471 HCR_H_WAIT_1(var->right_margin) |
472 HCR_H_WAIT_2(var->left_margin),
Juergen Beisert72330b02008-12-16 11:44:07 +0100473 fbi->regs + LCDC_HCR);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700474
Juergen Beisert72330b02008-12-16 11:44:07 +0100475 writel(VCR_V_WIDTH(var->vsync_len) |
Sascha Hauerd6ed5752008-12-16 11:44:08 +0100476 VCR_V_WAIT_1(var->lower_margin) |
477 VCR_V_WAIT_2(var->upper_margin),
Juergen Beisert72330b02008-12-16 11:44:07 +0100478 fbi->regs + LCDC_VCR);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700479
Juergen Beisert72330b02008-12-16 11:44:07 +0100480 writel(SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres),
481 fbi->regs + LCDC_SIZE);
482 writel(fbi->pcr, fbi->regs + LCDC_PCR);
483 writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
484 writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
485 writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700486
487 return 0;
488}
489
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700490#ifdef CONFIG_PM
491/*
492 * Power management hooks. Note that we won't be called from IRQ context,
493 * unlike the blank functions above, so we may sleep.
494 */
Russell King3ae5eae2005-11-09 22:32:44 +0000495static int imxfb_suspend(struct platform_device *dev, pm_message_t state)
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700496{
Russell King3ae5eae2005-11-09 22:32:44 +0000497 struct imxfb_info *fbi = platform_get_drvdata(dev);
Harvey Harrison5ae12172008-04-28 02:15:47 -0700498 pr_debug("%s\n",__func__);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700499
Russell King9480e302005-10-28 09:52:56 -0700500 imxfb_disable_controller(fbi);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700501 return 0;
502}
503
Russell King3ae5eae2005-11-09 22:32:44 +0000504static int imxfb_resume(struct platform_device *dev)
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700505{
Russell King3ae5eae2005-11-09 22:32:44 +0000506 struct imxfb_info *fbi = platform_get_drvdata(dev);
Harvey Harrison5ae12172008-04-28 02:15:47 -0700507 pr_debug("%s\n",__func__);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700508
Russell King9480e302005-10-28 09:52:56 -0700509 imxfb_enable_controller(fbi);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700510 return 0;
511}
512#else
513#define imxfb_suspend NULL
514#define imxfb_resume NULL
515#endif
516
Juergen Beisert72330b02008-12-16 11:44:07 +0100517static int __init imxfb_init_fbinfo(struct platform_device *pdev)
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700518{
Juergen Beisert72330b02008-12-16 11:44:07 +0100519 struct imxfb_mach_info *inf = pdev->dev.platform_data;
520 struct fb_info *info = dev_get_drvdata(&pdev->dev);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700521 struct imxfb_info *fbi = info->par;
522
Harvey Harrison5ae12172008-04-28 02:15:47 -0700523 pr_debug("%s\n",__func__);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700524
525 info->pseudo_palette = kmalloc( sizeof(u32) * 16, GFP_KERNEL);
526 if (!info->pseudo_palette)
527 return -ENOMEM;
528
529 memset(fbi, 0, sizeof(struct imxfb_info));
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700530
531 strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id));
532
533 info->fix.type = FB_TYPE_PACKED_PIXELS;
534 info->fix.type_aux = 0;
535 info->fix.xpanstep = 0;
536 info->fix.ypanstep = 0;
537 info->fix.ywrapstep = 0;
538 info->fix.accel = FB_ACCEL_NONE;
539
540 info->var.nonstd = 0;
541 info->var.activate = FB_ACTIVATE_NOW;
542 info->var.height = -1;
543 info->var.width = -1;
544 info->var.accel_flags = 0;
545 info->var.vmode = FB_VMODE_NONINTERLACED;
546
547 info->fbops = &imxfb_ops;
Pavel Pisa9da505d2007-10-16 01:29:44 -0700548 info->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST;
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700549
550 fbi->rgb[RGB_16] = &def_rgb_16;
551 fbi->rgb[RGB_8] = &def_rgb_8;
552
553 fbi->max_xres = inf->xres;
554 info->var.xres = inf->xres;
555 info->var.xres_virtual = inf->xres;
556 fbi->max_yres = inf->yres;
557 info->var.yres = inf->yres;
558 info->var.yres_virtual = inf->yres;
559 fbi->max_bpp = inf->bpp;
560 info->var.bits_per_pixel = inf->bpp;
Pavel Pisa9da505d2007-10-16 01:29:44 -0700561 info->var.nonstd = inf->nonstd;
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700562 info->var.pixclock = inf->pixclock;
563 info->var.hsync_len = inf->hsync_len;
564 info->var.left_margin = inf->left_margin;
565 info->var.right_margin = inf->right_margin;
566 info->var.vsync_len = inf->vsync_len;
567 info->var.upper_margin = inf->upper_margin;
568 info->var.lower_margin = inf->lower_margin;
569 info->var.sync = inf->sync;
570 info->var.grayscale = inf->cmap_greyscale;
571 fbi->cmap_inverse = inf->cmap_inverse;
Sascha Hauer90e16dd2007-05-23 13:57:51 -0700572 fbi->cmap_static = inf->cmap_static;
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700573 fbi->pcr = inf->pcr;
574 fbi->lscr1 = inf->lscr1;
Sascha Hauer772a9e62005-07-17 20:15:36 +0100575 fbi->dmacr = inf->dmacr;
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700576 fbi->pwmr = inf->pwmr;
577 fbi->lcd_power = inf->lcd_power;
578 fbi->backlight_power = inf->backlight_power;
579 info->fix.smem_len = fbi->max_xres * fbi->max_yres *
580 fbi->max_bpp / 8;
581
582 return 0;
583}
584
Russell King3ae5eae2005-11-09 22:32:44 +0000585static int __init imxfb_probe(struct platform_device *pdev)
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700586{
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700587 struct imxfb_info *fbi;
588 struct fb_info *info;
589 struct imxfb_mach_info *inf;
590 struct resource *res;
591 int ret;
592
593 printk("i.MX Framebuffer driver\n");
594
595 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
596 if(!res)
597 return -ENODEV;
598
Russell King3ae5eae2005-11-09 22:32:44 +0000599 inf = pdev->dev.platform_data;
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700600 if(!inf) {
Pavel Pisaf99c8922006-01-07 10:44:32 +0000601 dev_err(&pdev->dev,"No platform_data available\n");
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700602 return -ENOMEM;
603 }
604
Russell King3ae5eae2005-11-09 22:32:44 +0000605 info = framebuffer_alloc(sizeof(struct imxfb_info), &pdev->dev);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700606 if(!info)
607 return -ENOMEM;
608
609 fbi = info->par;
610
Russell King3ae5eae2005-11-09 22:32:44 +0000611 platform_set_drvdata(pdev, info);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700612
Juergen Beisert72330b02008-12-16 11:44:07 +0100613 ret = imxfb_init_fbinfo(pdev);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700614 if( ret < 0 )
615 goto failed_init;
616
Juergen Beisert72330b02008-12-16 11:44:07 +0100617 res = request_mem_region(res->start, resource_size(res),
618 DRIVER_NAME);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700619 if (!res) {
620 ret = -EBUSY;
Juergen Beisert72330b02008-12-16 11:44:07 +0100621 goto failed_req;
622 }
623
624 fbi->regs = ioremap(res->start, resource_size(res));
625 if (fbi->regs == NULL) {
626 printk(KERN_ERR"Cannot map frame buffer registers\n");
627 goto failed_ioremap;
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700628 }
629
630 if (!inf->fixed_screen_cpu) {
Juergen Beisert72330b02008-12-16 11:44:07 +0100631 fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
632 fbi->map_cpu = dma_alloc_writecombine(&pdev->dev,
633 fbi->map_size, &fbi->map_dma, GFP_KERNEL);
634
635 if (!fbi->map_cpu) {
Pavel Pisaf99c8922006-01-07 10:44:32 +0000636 dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700637 ret = -ENOMEM;
638 goto failed_map;
639 }
Juergen Beisert72330b02008-12-16 11:44:07 +0100640
641 info->screen_base = fbi->map_cpu;
642 fbi->screen_cpu = fbi->map_cpu;
643 fbi->screen_dma = fbi->map_dma;
644 info->fix.smem_start = fbi->screen_dma;
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700645 } else {
646 /* Fixed framebuffer mapping enables location of the screen in eSRAM */
647 fbi->map_cpu = inf->fixed_screen_cpu;
648 fbi->map_dma = inf->fixed_screen_dma;
649 info->screen_base = fbi->map_cpu;
650 fbi->screen_cpu = fbi->map_cpu;
651 fbi->screen_dma = fbi->map_dma;
652 info->fix.smem_start = fbi->screen_dma;
653 }
654
655 /*
656 * This makes sure that our colour bitfield
657 * descriptors are correctly initialised.
658 */
659 imxfb_check_var(&info->var, info);
660
661 ret = fb_alloc_cmap(&info->cmap, 1<<info->var.bits_per_pixel, 0);
662 if (ret < 0)
663 goto failed_cmap;
664
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700665 imxfb_set_par(info);
666 ret = register_framebuffer(info);
667 if (ret < 0) {
Pavel Pisaf99c8922006-01-07 10:44:32 +0000668 dev_err(&pdev->dev, "failed to register framebuffer\n");
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700669 goto failed_register;
670 }
671
672 imxfb_enable_controller(fbi);
673
674 return 0;
675
676failed_register:
677 fb_dealloc_cmap(&info->cmap);
678failed_cmap:
679 if (!inf->fixed_screen_cpu)
Russell King3ae5eae2005-11-09 22:32:44 +0000680 dma_free_writecombine(&pdev->dev,fbi->map_size,fbi->map_cpu,
Juergen Beisert72330b02008-12-16 11:44:07 +0100681 fbi->map_dma);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700682failed_map:
Juergen Beisert72330b02008-12-16 11:44:07 +0100683 iounmap(fbi->regs);
684failed_ioremap:
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700685 release_mem_region(res->start, res->end - res->start);
Juergen Beisert72330b02008-12-16 11:44:07 +0100686failed_req:
687 kfree(info->pseudo_palette);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700688failed_init:
Russell King3ae5eae2005-11-09 22:32:44 +0000689 platform_set_drvdata(pdev, NULL);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700690 framebuffer_release(info);
691 return ret;
692}
693
Juergen Beisert72330b02008-12-16 11:44:07 +0100694static int __devexit imxfb_remove(struct platform_device *pdev)
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700695{
Russell King3ae5eae2005-11-09 22:32:44 +0000696 struct fb_info *info = platform_get_drvdata(pdev);
Sascha Hauer772a9e62005-07-17 20:15:36 +0100697 struct imxfb_info *fbi = info->par;
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700698 struct resource *res;
699
700 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
701
Sascha Hauer772a9e62005-07-17 20:15:36 +0100702 imxfb_disable_controller(fbi);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700703
704 unregister_framebuffer(info);
705
706 fb_dealloc_cmap(&info->cmap);
707 kfree(info->pseudo_palette);
708 framebuffer_release(info);
709
Juergen Beisert72330b02008-12-16 11:44:07 +0100710 iounmap(fbi->regs);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700711 release_mem_region(res->start, res->end - res->start + 1);
Russell King3ae5eae2005-11-09 22:32:44 +0000712 platform_set_drvdata(pdev, NULL);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700713
714 return 0;
715}
716
Russell King3ae5eae2005-11-09 22:32:44 +0000717void imxfb_shutdown(struct platform_device * dev)
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700718{
Russell King3ae5eae2005-11-09 22:32:44 +0000719 struct fb_info *info = platform_get_drvdata(dev);
Sascha Hauer772a9e62005-07-17 20:15:36 +0100720 struct imxfb_info *fbi = info->par;
721 imxfb_disable_controller(fbi);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700722}
723
Russell King3ae5eae2005-11-09 22:32:44 +0000724static struct platform_driver imxfb_driver = {
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700725 .suspend = imxfb_suspend,
726 .resume = imxfb_resume,
Juergen Beisert72330b02008-12-16 11:44:07 +0100727 .remove = __devexit_p(imxfb_remove),
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700728 .shutdown = imxfb_shutdown,
Russell King3ae5eae2005-11-09 22:32:44 +0000729 .driver = {
Juergen Beisert72330b02008-12-16 11:44:07 +0100730 .name = DRIVER_NAME,
Russell King3ae5eae2005-11-09 22:32:44 +0000731 },
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700732};
733
734int __init imxfb_init(void)
735{
Juergen Beisert72330b02008-12-16 11:44:07 +0100736 return platform_driver_probe(&imxfb_driver, imxfb_probe);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700737}
738
739static void __exit imxfb_cleanup(void)
740{
Russell King3ae5eae2005-11-09 22:32:44 +0000741 platform_driver_unregister(&imxfb_driver);
Sascha Hauer7c2f891c2005-05-01 08:59:24 -0700742}
743
744module_init(imxfb_init);
745module_exit(imxfb_cleanup);
746
747MODULE_DESCRIPTION("Motorola i.MX framebuffer driver");
748MODULE_AUTHOR("Sascha Hauer, Pengutronix");
749MODULE_LICENSE("GPL");