aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/imxfb.c
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2005-05-01 11:59:24 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-05-01 11:59:24 -0400
commit7c2f891cb64b0b9c8d389da97c221ee4288f1307 (patch)
tree6826d42679d15654c0a0661e56bcbe8fb3f6db86 /drivers/video/imxfb.c
parentdb9f1d9daa2f775a0f7d1a0d2ca4722c1da50158 (diff)
[PATCH] imxfb: Add Freescale i.MX framebuffer driver
This patch adds support for the framebuffer on the freescale i.MX SOC architecture. The driver has been tested on the mx1ads board, the pimx1 board and another custom board with different displays. Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de> Signed-off-by: Antonino Daplas <adaplas@pol.net> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/video/imxfb.c')
-rw-r--r--drivers/video/imxfb.c695
1 files changed, 695 insertions, 0 deletions
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
new file mode 100644
index 000000000000..8fe1c12a17bd
--- /dev/null
+++ b/drivers/video/imxfb.c
@@ -0,0 +1,695 @@
1/*
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
19//#define DEBUG 1
20
21#include <linux/config.h>
22#include <linux/module.h>
23#include <linux/kernel.h>
24#include <linux/sched.h>
25#include <linux/errno.h>
26#include <linux/string.h>
27#include <linux/interrupt.h>
28#include <linux/slab.h>
29#include <linux/fb.h>
30#include <linux/delay.h>
31#include <linux/init.h>
32#include <linux/ioport.h>
33#include <linux/cpufreq.h>
34#include <linux/device.h>
35#include <linux/dma-mapping.h>
36
37#include <asm/hardware.h>
38#include <asm/io.h>
39#include <asm/mach-types.h>
40#include <asm/uaccess.h>
41#include <asm/arch/imxfb.h>
42
43/*
44 * Complain if VAR is out of range.
45 */
46#define DEBUG_VAR 1
47
48#include "imxfb.h"
49
50static struct imxfb_rgb def_rgb_16 = {
51 .red = { .offset = 8, .length = 4, },
52 .green = { .offset = 4, .length = 4, },
53 .blue = { .offset = 0, .length = 4, },
54 .transp = { .offset = 0, .length = 0, },
55};
56
57static struct imxfb_rgb def_rgb_8 = {
58 .red = { .offset = 0, .length = 8, },
59 .green = { .offset = 0, .length = 8, },
60 .blue = { .offset = 0, .length = 8, },
61 .transp = { .offset = 0, .length = 0, },
62};
63
64static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info);
65
66static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
67{
68 chan &= 0xffff;
69 chan >>= 16 - bf->length;
70 return chan << bf->offset;
71}
72
73#define LCDC_PALETTE(x) __REG2(IMX_LCDC_BASE+0x800, (x)<<2)
74static int
75imxfb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
76 u_int trans, struct fb_info *info)
77{
78 struct imxfb_info *fbi = info->par;
79 u_int val, ret = 1;
80
81#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
82 if (regno < fbi->palette_size) {
83 val = (CNVT_TOHW(red, 4) << 8) |
84 (CNVT_TOHW(green,4) << 4) |
85 CNVT_TOHW(blue, 4);
86
87 LCDC_PALETTE(regno) = val;
88 ret = 0;
89 }
90 return ret;
91}
92
93static int
94imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
95 u_int trans, struct fb_info *info)
96{
97 struct imxfb_info *fbi = info->par;
98 unsigned int val;
99 int ret = 1;
100
101 /*
102 * If inverse mode was selected, invert all the colours
103 * rather than the register number. The register number
104 * is what you poke into the framebuffer to produce the
105 * colour you requested.
106 */
107 if (fbi->cmap_inverse) {
108 red = 0xffff - red;
109 green = 0xffff - green;
110 blue = 0xffff - blue;
111 }
112
113 /*
114 * If greyscale is true, then we convert the RGB value
115 * to greyscale no mater what visual we are using.
116 */
117 if (info->var.grayscale)
118 red = green = blue = (19595 * red + 38470 * green +
119 7471 * blue) >> 16;
120
121 switch (info->fix.visual) {
122 case FB_VISUAL_TRUECOLOR:
123 /*
124 * 12 or 16-bit True Colour. We encode the RGB value
125 * according to the RGB bitfield information.
126 */
127 if (regno < 16) {
128 u32 *pal = info->pseudo_palette;
129
130 val = chan_to_field(red, &info->var.red);
131 val |= chan_to_field(green, &info->var.green);
132 val |= chan_to_field(blue, &info->var.blue);
133
134 pal[regno] = val;
135 ret = 0;
136 }
137 break;
138
139 case FB_VISUAL_STATIC_PSEUDOCOLOR:
140 case FB_VISUAL_PSEUDOCOLOR:
141 ret = imxfb_setpalettereg(regno, red, green, blue, trans, info);
142 break;
143 }
144
145 return ret;
146}
147
148/*
149 * imxfb_check_var():
150 * Round up in the following order: bits_per_pixel, xres,
151 * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
152 * bitfields, horizontal timing, vertical timing.
153 */
154static int
155imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
156{
157 struct imxfb_info *fbi = info->par;
158 int rgbidx;
159
160 if (var->xres < MIN_XRES)
161 var->xres = MIN_XRES;
162 if (var->yres < MIN_YRES)
163 var->yres = MIN_YRES;
164 if (var->xres > fbi->max_xres)
165 var->xres = fbi->max_xres;
166 if (var->yres > fbi->max_yres)
167 var->yres = fbi->max_yres;
168 var->xres_virtual = max(var->xres_virtual, var->xres);
169 var->yres_virtual = max(var->yres_virtual, var->yres);
170
171 pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel);
172 switch (var->bits_per_pixel) {
173 case 16:
174 rgbidx = RGB_16;
175 break;
176 case 8:
177 rgbidx = RGB_8;
178 break;
179 default:
180 rgbidx = RGB_16;
181 }
182
183 /*
184 * Copy the RGB parameters for this display
185 * from the machine specific parameters.
186 */
187 var->red = fbi->rgb[rgbidx]->red;
188 var->green = fbi->rgb[rgbidx]->green;
189 var->blue = fbi->rgb[rgbidx]->blue;
190 var->transp = fbi->rgb[rgbidx]->transp;
191
192 pr_debug("RGBT length = %d:%d:%d:%d\n",
193 var->red.length, var->green.length, var->blue.length,
194 var->transp.length);
195
196 pr_debug("RGBT offset = %d:%d:%d:%d\n",
197 var->red.offset, var->green.offset, var->blue.offset,
198 var->transp.offset);
199
200 return 0;
201}
202
203/*
204 * imxfb_set_par():
205 * Set the user defined part of the display for the specified console
206 */
207static int imxfb_set_par(struct fb_info *info)
208{
209 struct imxfb_info *fbi = info->par;
210 struct fb_var_screeninfo *var = &info->var;
211
212 pr_debug("set_par\n");
213
214 if (var->bits_per_pixel == 16)
215 info->fix.visual = FB_VISUAL_TRUECOLOR;
216 else if (!fbi->cmap_static)
217 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
218 else {
219 /*
220 * Some people have weird ideas about wanting static
221 * pseudocolor maps. I suspect their user space
222 * applications are broken.
223 */
224 info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
225 }
226
227 info->fix.line_length = var->xres_virtual *
228 var->bits_per_pixel / 8;
229 fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
230
231 imxfb_activate_var(var, info);
232
233 return 0;
234}
235
236static void imxfb_enable_controller(struct imxfb_info *fbi)
237{
238 pr_debug("Enabling LCD controller\n");
239
240 /* initialize LCDC */
241 LCDC_RMCR &= ~RMCR_LCDC_EN; /* just to be safe... */
242
243 LCDC_SSA = fbi->screen_dma;
244 /* physical screen start address */
245 LCDC_VPW = VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4);
246
247 LCDC_POS = 0x00000000; /* panning offset 0 (0 pixel offset) */
248
249 /* disable hardware cursor */
250 LCDC_CPOS &= ~(CPOS_CC0 | CPOS_CC1);
251
252 /* fixed burst length (see erratum 11) */
253 LCDC_DMACR = DMACR_BURST | DMACR_HM(8) | DMACR_TM(2);
254
255 LCDC_RMCR = RMCR_LCDC_EN;
256
257 if(fbi->backlight_power)
258 fbi->backlight_power(1);
259 if(fbi->lcd_power)
260 fbi->lcd_power(1);
261}
262
263static void imxfb_disable_controller(struct imxfb_info *fbi)
264{
265 pr_debug("Disabling LCD controller\n");
266
267 if(fbi->backlight_power)
268 fbi->backlight_power(0);
269 if(fbi->lcd_power)
270 fbi->lcd_power(0);
271
272 LCDC_RMCR = 0;
273}
274
275static int imxfb_blank(int blank, struct fb_info *info)
276{
277 struct imxfb_info *fbi = info->par;
278
279 pr_debug("imxfb_blank: blank=%d\n", blank);
280
281 switch (blank) {
282 case FB_BLANK_POWERDOWN:
283 case FB_BLANK_VSYNC_SUSPEND:
284 case FB_BLANK_HSYNC_SUSPEND:
285 case FB_BLANK_NORMAL:
286 imxfb_disable_controller(fbi);
287 break;
288
289 case FB_BLANK_UNBLANK:
290 imxfb_enable_controller(fbi);
291 break;
292 }
293 return 0;
294}
295
296static struct fb_ops imxfb_ops = {
297 .owner = THIS_MODULE,
298 .fb_check_var = imxfb_check_var,
299 .fb_set_par = imxfb_set_par,
300 .fb_setcolreg = imxfb_setcolreg,
301 .fb_fillrect = cfb_fillrect,
302 .fb_copyarea = cfb_copyarea,
303 .fb_imageblit = cfb_imageblit,
304 .fb_blank = imxfb_blank,
305 .fb_cursor = soft_cursor, /* FIXME: i.MX can do hardware cursor */
306};
307
308/*
309 * imxfb_activate_var():
310 * Configures LCD Controller based on entries in var parameter. Settings are
311 * only written to the controller if changes were made.
312 */
313static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info)
314{
315 struct imxfb_info *fbi = info->par;
316 pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n",
317 var->xres, var->hsync_len,
318 var->left_margin, var->right_margin);
319 pr_debug("var: yres=%d vslen=%d um=%d bm=%d\n",
320 var->yres, var->vsync_len,
321 var->upper_margin, var->lower_margin);
322
323#if DEBUG_VAR
324 if (var->xres < 16 || var->xres > 1024)
325 printk(KERN_ERR "%s: invalid xres %d\n",
326 info->fix.id, var->xres);
327 if (var->hsync_len < 1 || var->hsync_len > 64)
328 printk(KERN_ERR "%s: invalid hsync_len %d\n",
329 info->fix.id, var->hsync_len);
330 if (var->left_margin > 255)
331 printk(KERN_ERR "%s: invalid left_margin %d\n",
332 info->fix.id, var->left_margin);
333 if (var->right_margin > 255)
334 printk(KERN_ERR "%s: invalid right_margin %d\n",
335 info->fix.id, var->right_margin);
336 if (var->yres < 1 || var->yres > 511)
337 printk(KERN_ERR "%s: invalid yres %d\n",
338 info->fix.id, var->yres);
339 if (var->vsync_len > 100)
340 printk(KERN_ERR "%s: invalid vsync_len %d\n",
341 info->fix.id, var->vsync_len);
342 if (var->upper_margin > 63)
343 printk(KERN_ERR "%s: invalid upper_margin %d\n",
344 info->fix.id, var->upper_margin);
345 if (var->lower_margin > 255)
346 printk(KERN_ERR "%s: invalid lower_margin %d\n",
347 info->fix.id, var->lower_margin);
348#endif
349
350 LCDC_HCR = HCR_H_WIDTH(var->hsync_len) |
351 HCR_H_WAIT_1(var->left_margin) |
352 HCR_H_WAIT_2(var->right_margin);
353
354 LCDC_VCR = VCR_V_WIDTH(var->vsync_len) |
355 VCR_V_WAIT_1(var->upper_margin) |
356 VCR_V_WAIT_2(var->lower_margin);
357
358 LCDC_SIZE = SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres);
359 LCDC_PCR = fbi->pcr;
360 LCDC_PWMR = fbi->pwmr;
361 LCDC_LSCR1 = fbi->lscr1;
362
363 return 0;
364}
365
366static void imxfb_setup_gpio(struct imxfb_info *fbi)
367{
368 int width;
369
370 LCDC_RMCR &= ~(RMCR_LCDC_EN | RMCR_SELF_REF);
371
372 if( fbi->pcr & PCR_TFT )
373 width = 16;
374 else
375 width = 1 << ((fbi->pcr >> 28) & 0x3);
376
377 switch(width) {
378 case 16:
379 imx_gpio_mode(PD30_PF_LD15);
380 imx_gpio_mode(PD29_PF_LD14);
381 imx_gpio_mode(PD28_PF_LD13);
382 imx_gpio_mode(PD27_PF_LD12);
383 imx_gpio_mode(PD26_PF_LD11);
384 imx_gpio_mode(PD25_PF_LD10);
385 imx_gpio_mode(PD24_PF_LD9);
386 imx_gpio_mode(PD23_PF_LD8);
387 case 8:
388 imx_gpio_mode(PD22_PF_LD7);
389 imx_gpio_mode(PD21_PF_LD6);
390 imx_gpio_mode(PD20_PF_LD5);
391 imx_gpio_mode(PD19_PF_LD4);
392 case 4:
393 imx_gpio_mode(PD18_PF_LD3);
394 imx_gpio_mode(PD17_PF_LD2);
395 case 2:
396 imx_gpio_mode(PD16_PF_LD1);
397 case 1:
398 imx_gpio_mode(PD15_PF_LD0);
399 }
400
401 /* initialize GPIOs */
402 imx_gpio_mode(PD6_PF_LSCLK);
403 imx_gpio_mode(PD10_PF_SPL_SPR);
404 imx_gpio_mode(PD11_PF_CONTRAST);
405 imx_gpio_mode(PD14_PF_FLM_VSYNC);
406 imx_gpio_mode(PD13_PF_LP_HSYNC);
407 imx_gpio_mode(PD7_PF_REV);
408 imx_gpio_mode(PD8_PF_CLS);
409
410#ifndef CONFIG_MACH_PIMX1
411 /* on PiMX1 used as buffers enable signal
412 */
413 imx_gpio_mode(PD9_PF_PS);
414#endif
415
416#ifndef CONFIG_MACH_MX1FS2
417 /* on mx1fs2 this pin is used to (de)activate the display, so we need
418 * it as a normal gpio
419 */
420 imx_gpio_mode(PD12_PF_ACD_OE);
421#endif
422
423}
424
425#ifdef CONFIG_PM
426/*
427 * Power management hooks. Note that we won't be called from IRQ context,
428 * unlike the blank functions above, so we may sleep.
429 */
430static int imxfb_suspend(struct device *dev, u32 state, u32 level)
431{
432 struct imxfb_info *fbi = dev_get_drvdata(dev);
433 pr_debug("%s\n",__FUNCTION__);
434
435 if (level == SUSPEND_DISABLE || level == SUSPEND_POWER_DOWN)
436 imxfb_disable_controller(fbi);
437 return 0;
438}
439
440static int imxfb_resume(struct device *dev, u32 level)
441{
442 struct imxfb_info *fbi = dev_get_drvdata(dev);
443 pr_debug("%s\n",__FUNCTION__);
444
445 if (level == RESUME_ENABLE)
446 imxfb_enable_controller(fbi);
447 return 0;
448}
449#else
450#define imxfb_suspend NULL
451#define imxfb_resume NULL
452#endif
453
454static int __init imxfb_init_fbinfo(struct device *dev)
455{
456 struct imxfb_mach_info *inf = dev->platform_data;
457 struct fb_info *info = dev_get_drvdata(dev);
458 struct imxfb_info *fbi = info->par;
459
460 pr_debug("%s\n",__FUNCTION__);
461
462 info->pseudo_palette = kmalloc( sizeof(u32) * 16, GFP_KERNEL);
463 if (!info->pseudo_palette)
464 return -ENOMEM;
465
466 memset(fbi, 0, sizeof(struct imxfb_info));
467 fbi->dev = dev;
468
469 strlcpy(info->fix.id, IMX_NAME, sizeof(info->fix.id));
470
471 info->fix.type = FB_TYPE_PACKED_PIXELS;
472 info->fix.type_aux = 0;
473 info->fix.xpanstep = 0;
474 info->fix.ypanstep = 0;
475 info->fix.ywrapstep = 0;
476 info->fix.accel = FB_ACCEL_NONE;
477
478 info->var.nonstd = 0;
479 info->var.activate = FB_ACTIVATE_NOW;
480 info->var.height = -1;
481 info->var.width = -1;
482 info->var.accel_flags = 0;
483 info->var.vmode = FB_VMODE_NONINTERLACED;
484
485 info->fbops = &imxfb_ops;
486 info->flags = FBINFO_FLAG_DEFAULT;
487 info->pseudo_palette = (fbi + 1);
488
489 fbi->rgb[RGB_16] = &def_rgb_16;
490 fbi->rgb[RGB_8] = &def_rgb_8;
491
492 fbi->max_xres = inf->xres;
493 info->var.xres = inf->xres;
494 info->var.xres_virtual = inf->xres;
495 fbi->max_yres = inf->yres;
496 info->var.yres = inf->yres;
497 info->var.yres_virtual = inf->yres;
498 fbi->max_bpp = inf->bpp;
499 info->var.bits_per_pixel = inf->bpp;
500 info->var.pixclock = inf->pixclock;
501 info->var.hsync_len = inf->hsync_len;
502 info->var.left_margin = inf->left_margin;
503 info->var.right_margin = inf->right_margin;
504 info->var.vsync_len = inf->vsync_len;
505 info->var.upper_margin = inf->upper_margin;
506 info->var.lower_margin = inf->lower_margin;
507 info->var.sync = inf->sync;
508 info->var.grayscale = inf->cmap_greyscale;
509 fbi->cmap_inverse = inf->cmap_inverse;
510 fbi->pcr = inf->pcr;
511 fbi->lscr1 = inf->lscr1;
512 fbi->pwmr = inf->pwmr;
513 fbi->lcd_power = inf->lcd_power;
514 fbi->backlight_power = inf->backlight_power;
515 info->fix.smem_len = fbi->max_xres * fbi->max_yres *
516 fbi->max_bpp / 8;
517
518 return 0;
519}
520
521/*
522 * Allocates the DRAM memory for the frame buffer. This buffer is
523 * remapped into a non-cached, non-buffered, memory region to
524 * allow pixel writes to occur without flushing the cache.
525 * Once this area is remapped, all virtual memory access to the
526 * video memory should occur at the new region.
527 */
528static int __init imxfb_map_video_memory(struct fb_info *info)
529{
530 struct imxfb_info *fbi = info->par;
531
532 fbi->map_size = PAGE_ALIGN(info->fix.smem_len);
533 fbi->map_cpu = dma_alloc_writecombine(fbi->dev, fbi->map_size,
534 &fbi->map_dma,GFP_KERNEL);
535
536 if (fbi->map_cpu) {
537 info->screen_base = fbi->map_cpu;
538 fbi->screen_cpu = fbi->map_cpu;
539 fbi->screen_dma = fbi->map_dma;
540 info->fix.smem_start = fbi->screen_dma;
541 }
542
543 return fbi->map_cpu ? 0 : -ENOMEM;
544}
545
546static int __init imxfb_probe(struct device *dev)
547{
548 struct platform_device *pdev = to_platform_device(dev);
549 struct imxfb_info *fbi;
550 struct fb_info *info;
551 struct imxfb_mach_info *inf;
552 struct resource *res;
553 int ret;
554
555 printk("i.MX Framebuffer driver\n");
556
557 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
558 if(!res)
559 return -ENODEV;
560
561 inf = dev->platform_data;
562 if(!inf) {
563 dev_err(dev,"No platform_data available\n");
564 return -ENOMEM;
565 }
566
567 info = framebuffer_alloc(sizeof(struct imxfb_info), dev);
568 if(!info)
569 return -ENOMEM;
570
571 fbi = info->par;
572
573 dev_set_drvdata(dev, info);
574
575 ret = imxfb_init_fbinfo(dev);
576 if( ret < 0 )
577 goto failed_init;
578
579 res = request_mem_region(res->start, res->end - res->start + 1, "IMXFB");
580 if (!res) {
581 ret = -EBUSY;
582 goto failed_regs;
583 }
584
585 if (!inf->fixed_screen_cpu) {
586 ret = imxfb_map_video_memory(info);
587 if (ret) {
588 dev_err(dev, "Failed to allocate video RAM: %d\n", ret);
589 ret = -ENOMEM;
590 goto failed_map;
591 }
592 } else {
593 /* Fixed framebuffer mapping enables location of the screen in eSRAM */
594 fbi->map_cpu = inf->fixed_screen_cpu;
595 fbi->map_dma = inf->fixed_screen_dma;
596 info->screen_base = fbi->map_cpu;
597 fbi->screen_cpu = fbi->map_cpu;
598 fbi->screen_dma = fbi->map_dma;
599 info->fix.smem_start = fbi->screen_dma;
600 }
601
602 /*
603 * This makes sure that our colour bitfield
604 * descriptors are correctly initialised.
605 */
606 imxfb_check_var(&info->var, info);
607
608 ret = fb_alloc_cmap(&info->cmap, 1<<info->var.bits_per_pixel, 0);
609 if (ret < 0)
610 goto failed_cmap;
611
612 imxfb_setup_gpio(fbi);
613
614 imxfb_set_par(info);
615 ret = register_framebuffer(info);
616 if (ret < 0) {
617 dev_err(dev, "failed to register framebuffer\n");
618 goto failed_register;
619 }
620
621 imxfb_enable_controller(fbi);
622
623 return 0;
624
625failed_register:
626 fb_dealloc_cmap(&info->cmap);
627failed_cmap:
628 if (!inf->fixed_screen_cpu)
629 dma_free_writecombine(dev,fbi->map_size,fbi->map_cpu,
630 fbi->map_dma);
631failed_map:
632 kfree(info->pseudo_palette);
633failed_regs:
634 release_mem_region(res->start, res->end - res->start);
635failed_init:
636 dev_set_drvdata(dev, NULL);
637 framebuffer_release(info);
638 return ret;
639}
640
641static int imxfb_remove(struct device *dev)
642{
643 struct platform_device *pdev = to_platform_device(dev);
644 struct fb_info *info = dev_get_drvdata(dev);
645 struct resource *res;
646
647 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
648
649 /* disable LCD controller */
650 LCDC_RMCR &= ~RMCR_LCDC_EN;
651
652 unregister_framebuffer(info);
653
654 fb_dealloc_cmap(&info->cmap);
655 kfree(info->pseudo_palette);
656 framebuffer_release(info);
657
658 release_mem_region(res->start, res->end - res->start + 1);
659 dev_set_drvdata(dev, NULL);
660
661 return 0;
662}
663
664void imxfb_shutdown(struct device * dev)
665{
666 /* disable LCD Controller */
667 LCDC_RMCR &= ~RMCR_LCDC_EN;
668}
669
670static struct device_driver imxfb_driver = {
671 .name = "imx-fb",
672 .bus = &platform_bus_type,
673 .probe = imxfb_probe,
674 .suspend = imxfb_suspend,
675 .resume = imxfb_resume,
676 .remove = imxfb_remove,
677 .shutdown = imxfb_shutdown,
678};
679
680int __init imxfb_init(void)
681{
682 return driver_register(&imxfb_driver);
683}
684
685static void __exit imxfb_cleanup(void)
686{
687 driver_unregister(&imxfb_driver);
688}
689
690module_init(imxfb_init);
691module_exit(imxfb_cleanup);
692
693MODULE_DESCRIPTION("Motorola i.MX framebuffer driver");
694MODULE_AUTHOR("Sascha Hauer, Pengutronix");
695MODULE_LICENSE("GPL");