1 /*
2  *
3  * Copyright (c) 2009 Nuvoton technology corporation
4  * All rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  *  Description:
12  *    Nuvoton LCD Controller Driver
13  *  Author:
14  *    Wang Qiang (rurality.linux@gmail.com) 2009/12/11
15  */
16 #include <linux/module.h>
17 #include <linux/kernel.h>
18 #include <linux/err.h>
19 #include <linux/errno.h>
20 #include <linux/string.h>
21 #include <linux/mm.h>
22 #include <linux/tty.h>
23 #include <linux/slab.h>
24 #include <linux/delay.h>
25 #include <linux/fb.h>
26 #include <linux/init.h>
27 #include <linux/dma-mapping.h>
28 #include <linux/interrupt.h>
29 #include <linux/workqueue.h>
30 #include <linux/wait.h>
31 #include <linux/platform_device.h>
32 #include <linux/clk.h>
33 #include <linux/cpufreq.h>
34 #include <linux/io.h>
35 #include <linux/pm.h>
36 #include <linux/device.h>
37 
38 #include <mach/map.h>
39 #include <mach/regs-clock.h>
40 #include <mach/regs-ldm.h>
41 #include <linux/platform_data/video-nuc900fb.h>
42 
43 #include "nuc900fb.h"
44 
45 
46 /*
47  *  Initialize the nuc900 video (dual) buffer address
48  */
nuc900fb_set_lcdaddr(struct fb_info * info)49 static void nuc900fb_set_lcdaddr(struct fb_info *info)
50 {
51 	struct nuc900fb_info *fbi = info->par;
52 	void __iomem *regs = fbi->io;
53 	unsigned long vbaddr1, vbaddr2;
54 
55 	vbaddr1  = info->fix.smem_start;
56 	vbaddr2  = info->fix.smem_start;
57 	vbaddr2 += info->fix.line_length * info->var.yres;
58 
59 	/* set frambuffer start phy addr*/
60 	writel(vbaddr1, regs + REG_LCM_VA_BADDR0);
61 	writel(vbaddr2, regs + REG_LCM_VA_BADDR1);
62 
63 	writel(fbi->regs.lcd_va_fbctrl, regs + REG_LCM_VA_FBCTRL);
64 	writel(fbi->regs.lcd_va_scale, regs + REG_LCM_VA_SCALE);
65 }
66 
67 /*
68  *	calculate divider for lcd div
69  */
nuc900fb_calc_pixclk(struct nuc900fb_info * fbi,unsigned long pixclk)70 static unsigned int nuc900fb_calc_pixclk(struct nuc900fb_info *fbi,
71 					 unsigned long pixclk)
72 {
73 	unsigned long clk = fbi->clk_rate;
74 	unsigned long long div;
75 
76 	/* pixclk is in picseconds. our clock is in Hz*/
77 	/* div = (clk * pixclk)/10^12 */
78 	div = (unsigned long long)clk * pixclk;
79 	div >>= 12;
80 	do_div(div, 625 * 625UL * 625);
81 
82 	dev_dbg(fbi->dev, "pixclk %ld, divisor is %lld\n", pixclk, div);
83 
84 	return div;
85 }
86 
87 /*
88  *	Check the video params of 'var'.
89  */
nuc900fb_check_var(struct fb_var_screeninfo * var,struct fb_info * info)90 static int nuc900fb_check_var(struct fb_var_screeninfo *var,
91 			       struct fb_info *info)
92 {
93 	struct nuc900fb_info *fbi = info->par;
94 	struct nuc900fb_mach_info *mach_info = dev_get_platdata(fbi->dev);
95 	struct nuc900fb_display *display = NULL;
96 	struct nuc900fb_display *default_display = mach_info->displays +
97 						   mach_info->default_display;
98 	int i;
99 
100 	dev_dbg(fbi->dev, "check_var(var=%p, info=%p)\n", var, info);
101 
102 	/* validate x/y resolution */
103 	/* choose default mode if possible */
104 	if (var->xres == default_display->xres &&
105 	    var->yres == default_display->yres &&
106 	    var->bits_per_pixel == default_display->bpp)
107 		display = default_display;
108 	else
109 		for (i = 0; i < mach_info->num_displays; i++)
110 			if (var->xres == mach_info->displays[i].xres &&
111 			    var->yres == mach_info->displays[i].yres &&
112 			    var->bits_per_pixel == mach_info->displays[i].bpp) {
113 				display = mach_info->displays + i;
114 				break;
115 			}
116 
117 	if (display == NULL) {
118 		printk(KERN_ERR "wrong resolution or depth %dx%d at %d bit per pixel\n",
119 			var->xres, var->yres, var->bits_per_pixel);
120 		return -EINVAL;
121 	}
122 
123 	/* it should be the same size as the display */
124 	var->xres_virtual	= display->xres;
125 	var->yres_virtual	= display->yres;
126 	var->height		= display->height;
127 	var->width		= display->width;
128 
129 	/* copy lcd settings */
130 	var->pixclock		= display->pixclock;
131 	var->left_margin	= display->left_margin;
132 	var->right_margin	= display->right_margin;
133 	var->upper_margin	= display->upper_margin;
134 	var->lower_margin	= display->lower_margin;
135 	var->vsync_len		= display->vsync_len;
136 	var->hsync_len		= display->hsync_len;
137 
138 	var->transp.offset	= 0;
139 	var->transp.length	= 0;
140 
141 	fbi->regs.lcd_dccs = display->dccs;
142 	fbi->regs.lcd_device_ctrl = display->devctl;
143 	fbi->regs.lcd_va_fbctrl = display->fbctrl;
144 	fbi->regs.lcd_va_scale = display->scale;
145 
146 	/* set R/G/B possions */
147 	switch (var->bits_per_pixel) {
148 	case 1:
149 	case 2:
150 	case 4:
151 	case 8:
152 	default:
153 		var->red.offset 	= 0;
154 		var->red.length 	= var->bits_per_pixel;
155 		var->green 		= var->red;
156 		var->blue		= var->red;
157 		break;
158 	case 12:
159 		var->red.length		= 4;
160 		var->green.length	= 4;
161 		var->blue.length	= 4;
162 		var->red.offset		= 8;
163 		var->green.offset	= 4;
164 		var->blue.offset	= 0;
165 		break;
166 	case 16:
167 		var->red.length		= 5;
168 		var->green.length	= 6;
169 		var->blue.length	= 5;
170 		var->red.offset		= 11;
171 		var->green.offset	= 5;
172 		var->blue.offset	= 0;
173 		break;
174 	case 18:
175 		var->red.length		= 6;
176 		var->green.length	= 6;
177 		var->blue.length	= 6;
178 		var->red.offset		= 12;
179 		var->green.offset	= 6;
180 		var->blue.offset	= 0;
181 		break;
182 	case 32:
183 		var->red.length		= 8;
184 		var->green.length	= 8;
185 		var->blue.length	= 8;
186 		var->red.offset		= 16;
187 		var->green.offset	= 8;
188 		var->blue.offset	= 0;
189 		break;
190 	}
191 
192 	return 0;
193 }
194 
195 /*
196  *	Calculate lcd register values from var setting & save into hw
197  */
nuc900fb_calculate_lcd_regs(const struct fb_info * info,struct nuc900fb_hw * regs)198 static void nuc900fb_calculate_lcd_regs(const struct fb_info *info,
199 					struct nuc900fb_hw *regs)
200 {
201 	const struct fb_var_screeninfo *var = &info->var;
202 	int vtt = var->height + var->upper_margin + var->lower_margin;
203 	int htt = var->width + var->left_margin + var->right_margin;
204 	int hsync = var->width + var->right_margin;
205 	int vsync = var->height + var->lower_margin;
206 
207 	regs->lcd_crtc_size = LCM_CRTC_SIZE_VTTVAL(vtt) |
208 			      LCM_CRTC_SIZE_HTTVAL(htt);
209 	regs->lcd_crtc_dend = LCM_CRTC_DEND_VDENDVAL(var->height) |
210 			      LCM_CRTC_DEND_HDENDVAL(var->width);
211 	regs->lcd_crtc_hr = LCM_CRTC_HR_EVAL(var->width + 5) |
212 			    LCM_CRTC_HR_SVAL(var->width + 1);
213 	regs->lcd_crtc_hsync = LCM_CRTC_HSYNC_EVAL(hsync + var->hsync_len) |
214 			       LCM_CRTC_HSYNC_SVAL(hsync);
215 	regs->lcd_crtc_vr = LCM_CRTC_VR_EVAL(vsync + var->vsync_len) |
216 			    LCM_CRTC_VR_SVAL(vsync);
217 
218 }
219 
220 /*
221  *	Activate (set) the controller from the given framebuffer
222  *	information
223  */
nuc900fb_activate_var(struct fb_info * info)224 static void nuc900fb_activate_var(struct fb_info *info)
225 {
226 	struct nuc900fb_info *fbi = info->par;
227 	void __iomem *regs = fbi->io;
228 	struct fb_var_screeninfo *var = &info->var;
229 	int clkdiv;
230 
231 	clkdiv = nuc900fb_calc_pixclk(fbi, var->pixclock) - 1;
232 	if (clkdiv < 0)
233 		clkdiv = 0;
234 
235 	nuc900fb_calculate_lcd_regs(info, &fbi->regs);
236 
237 	/* set the new lcd registers*/
238 
239 	dev_dbg(fbi->dev, "new lcd register set:\n");
240 	dev_dbg(fbi->dev, "dccs       = 0x%08x\n", fbi->regs.lcd_dccs);
241 	dev_dbg(fbi->dev, "dev_ctl    = 0x%08x\n", fbi->regs.lcd_device_ctrl);
242 	dev_dbg(fbi->dev, "crtc_size  = 0x%08x\n", fbi->regs.lcd_crtc_size);
243 	dev_dbg(fbi->dev, "crtc_dend  = 0x%08x\n", fbi->regs.lcd_crtc_dend);
244 	dev_dbg(fbi->dev, "crtc_hr    = 0x%08x\n", fbi->regs.lcd_crtc_hr);
245 	dev_dbg(fbi->dev, "crtc_hsync = 0x%08x\n", fbi->regs.lcd_crtc_hsync);
246 	dev_dbg(fbi->dev, "crtc_vr    = 0x%08x\n", fbi->regs.lcd_crtc_vr);
247 
248 	writel(fbi->regs.lcd_device_ctrl, regs + REG_LCM_DEV_CTRL);
249 	writel(fbi->regs.lcd_crtc_size, regs + REG_LCM_CRTC_SIZE);
250 	writel(fbi->regs.lcd_crtc_dend, regs + REG_LCM_CRTC_DEND);
251 	writel(fbi->regs.lcd_crtc_hr, regs + REG_LCM_CRTC_HR);
252 	writel(fbi->regs.lcd_crtc_hsync, regs + REG_LCM_CRTC_HSYNC);
253 	writel(fbi->regs.lcd_crtc_vr, regs + REG_LCM_CRTC_VR);
254 
255 	/* set lcd address pointers */
256 	nuc900fb_set_lcdaddr(info);
257 
258 	writel(fbi->regs.lcd_dccs, regs + REG_LCM_DCCS);
259 }
260 
261 /*
262  *      Alters the hardware state.
263  *
264  */
nuc900fb_set_par(struct fb_info * info)265 static int nuc900fb_set_par(struct fb_info *info)
266 {
267 	struct fb_var_screeninfo *var = &info->var;
268 
269 	switch (var->bits_per_pixel) {
270 	case 32:
271 	case 24:
272 	case 18:
273 	case 16:
274 	case 12:
275 		info->fix.visual = FB_VISUAL_TRUECOLOR;
276 		break;
277 	case 1:
278 		info->fix.visual = FB_VISUAL_MONO01;
279 		break;
280 	default:
281 		info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
282 		break;
283 	}
284 
285 	info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
286 
287 	/* activate this new configuration */
288 	nuc900fb_activate_var(info);
289 	return 0;
290 }
291 
chan_to_field(unsigned int chan,struct fb_bitfield * bf)292 static inline unsigned int chan_to_field(unsigned int chan,
293 					 struct fb_bitfield *bf)
294 {
295 	chan &= 0xffff;
296 	chan >>= 16 - bf->length;
297 	return chan << bf->offset;
298 }
299 
nuc900fb_setcolreg(unsigned regno,unsigned red,unsigned green,unsigned blue,unsigned transp,struct fb_info * info)300 static int nuc900fb_setcolreg(unsigned regno,
301 			       unsigned red, unsigned green, unsigned blue,
302 			       unsigned transp, struct fb_info *info)
303 {
304 	unsigned int val;
305 
306 	switch (info->fix.visual) {
307 	case FB_VISUAL_TRUECOLOR:
308 		/* true-colour, use pseuo-palette */
309 		if (regno < 16) {
310 			u32 *pal = info->pseudo_palette;
311 
312 			val  = chan_to_field(red, &info->var.red);
313 			val |= chan_to_field(green, &info->var.green);
314 			val |= chan_to_field(blue, &info->var.blue);
315 			pal[regno] = val;
316 		}
317 		break;
318 
319 	default:
320 		return 1;   /* unknown type */
321 	}
322 	return 0;
323 }
324 
325 /**
326  *      nuc900fb_blank
327  *
328  */
nuc900fb_blank(int blank_mode,struct fb_info * info)329 static int nuc900fb_blank(int blank_mode, struct fb_info *info)
330 {
331 
332 	return 0;
333 }
334 
335 static struct fb_ops nuc900fb_ops = {
336 	.owner			= THIS_MODULE,
337 	.fb_check_var		= nuc900fb_check_var,
338 	.fb_set_par		= nuc900fb_set_par,
339 	.fb_blank		= nuc900fb_blank,
340 	.fb_setcolreg		= nuc900fb_setcolreg,
341 	.fb_fillrect		= cfb_fillrect,
342 	.fb_copyarea		= cfb_copyarea,
343 	.fb_imageblit		= cfb_imageblit,
344 };
345 
346 
modify_gpio(void __iomem * reg,unsigned long set,unsigned long mask)347 static inline void modify_gpio(void __iomem *reg,
348 			       unsigned long set, unsigned long mask)
349 {
350 	unsigned long tmp;
351 	tmp = readl(reg) & ~mask;
352 	writel(tmp | set, reg);
353 }
354 
355 /*
356  * Initialise LCD-related registers
357  */
nuc900fb_init_registers(struct fb_info * info)358 static int nuc900fb_init_registers(struct fb_info *info)
359 {
360 	struct nuc900fb_info *fbi = info->par;
361 	struct nuc900fb_mach_info *mach_info = dev_get_platdata(fbi->dev);
362 	void __iomem *regs = fbi->io;
363 
364 	/*reset the display engine*/
365 	writel(0, regs + REG_LCM_DCCS);
366 	writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_ENG_RST,
367 	       regs + REG_LCM_DCCS);
368 	ndelay(100);
369 	writel(readl(regs + REG_LCM_DCCS) & (~LCM_DCCS_ENG_RST),
370 	       regs + REG_LCM_DCCS);
371 	ndelay(100);
372 
373 	writel(0, regs + REG_LCM_DEV_CTRL);
374 
375 	/* config gpio output */
376 	modify_gpio(W90X900_VA_GPIO + 0x54, mach_info->gpio_dir,
377 		    mach_info->gpio_dir_mask);
378 	modify_gpio(W90X900_VA_GPIO + 0x58, mach_info->gpio_data,
379 		    mach_info->gpio_data_mask);
380 
381 	return 0;
382 }
383 
384 
385 /*
386  *    Alloc the SDRAM region of NUC900 for the frame buffer.
387  *    The buffer should be a non-cached, non-buffered, memory region
388  *    to allow palette and pixel writes without flushing the cache.
389  */
nuc900fb_map_video_memory(struct fb_info * info)390 static int nuc900fb_map_video_memory(struct fb_info *info)
391 {
392 	struct nuc900fb_info *fbi = info->par;
393 	dma_addr_t map_dma;
394 	unsigned long map_size = PAGE_ALIGN(info->fix.smem_len);
395 
396 	dev_dbg(fbi->dev, "nuc900fb_map_video_memory(fbi=%p) map_size %lu\n",
397 		fbi, map_size);
398 
399 	info->screen_base = dma_alloc_wc(fbi->dev, map_size, &map_dma,
400 					 GFP_KERNEL);
401 
402 	if (!info->screen_base)
403 		return -ENOMEM;
404 
405 	memset(info->screen_base, 0x00, map_size);
406 	info->fix.smem_start = map_dma;
407 
408 	return 0;
409 }
410 
nuc900fb_unmap_video_memory(struct fb_info * info)411 static inline void nuc900fb_unmap_video_memory(struct fb_info *info)
412 {
413 	struct nuc900fb_info *fbi = info->par;
414 	dma_free_wc(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
415 		    info->screen_base, info->fix.smem_start);
416 }
417 
nuc900fb_irqhandler(int irq,void * dev_id)418 static irqreturn_t nuc900fb_irqhandler(int irq, void *dev_id)
419 {
420 	struct nuc900fb_info *fbi = dev_id;
421 	void __iomem *regs = fbi->io;
422 	void __iomem *irq_base = fbi->irq_base;
423 	unsigned long lcdirq = readl(regs + REG_LCM_INT_CS);
424 
425 	if (lcdirq & LCM_INT_CS_DISP_F_STATUS) {
426 		writel(readl(irq_base) | 1<<30, irq_base);
427 
428 		/* wait VA_EN low */
429 		if ((readl(regs + REG_LCM_DCCS) &
430 		    LCM_DCCS_SINGLE) == LCM_DCCS_SINGLE)
431 			while ((readl(regs + REG_LCM_DCCS) &
432 			       LCM_DCCS_VA_EN) == LCM_DCCS_VA_EN)
433 				;
434 		/* display_out-enable */
435 		writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_DISP_OUT_EN,
436 			regs + REG_LCM_DCCS);
437 		/* va-enable*/
438 		writel(readl(regs + REG_LCM_DCCS) | LCM_DCCS_VA_EN,
439 			regs + REG_LCM_DCCS);
440 	} else if (lcdirq & LCM_INT_CS_UNDERRUN_INT) {
441 		writel(readl(irq_base) | LCM_INT_CS_UNDERRUN_INT, irq_base);
442 	} else if (lcdirq & LCM_INT_CS_BUS_ERROR_INT) {
443 		writel(readl(irq_base) | LCM_INT_CS_BUS_ERROR_INT, irq_base);
444 	}
445 
446 	return IRQ_HANDLED;
447 }
448 
449 #ifdef CONFIG_CPU_FREQ
450 
nuc900fb_cpufreq_transition(struct notifier_block * nb,unsigned long val,void * data)451 static int nuc900fb_cpufreq_transition(struct notifier_block *nb,
452 				       unsigned long val, void *data)
453 {
454 	struct nuc900fb_info *info;
455 	struct fb_info *fbinfo;
456 	long delta_f;
457 	info = container_of(nb, struct nuc900fb_info, freq_transition);
458 	fbinfo = platform_get_drvdata(to_platform_device(info->dev));
459 
460 	delta_f = info->clk_rate - clk_get_rate(info->clk);
461 
462 	if ((val == CPUFREQ_POSTCHANGE && delta_f > 0) ||
463 	   (val == CPUFREQ_PRECHANGE && delta_f < 0)) {
464 		info->clk_rate = clk_get_rate(info->clk);
465 		nuc900fb_activate_var(fbinfo);
466 	}
467 
468 	return 0;
469 }
470 
nuc900fb_cpufreq_register(struct nuc900fb_info * fbi)471 static inline int nuc900fb_cpufreq_register(struct nuc900fb_info *fbi)
472 {
473 	fbi->freq_transition.notifier_call = nuc900fb_cpufreq_transition;
474 	return cpufreq_register_notifier(&fbi->freq_transition,
475 				  CPUFREQ_TRANSITION_NOTIFIER);
476 }
477 
nuc900fb_cpufreq_deregister(struct nuc900fb_info * fbi)478 static inline void nuc900fb_cpufreq_deregister(struct nuc900fb_info *fbi)
479 {
480 	cpufreq_unregister_notifier(&fbi->freq_transition,
481 				    CPUFREQ_TRANSITION_NOTIFIER);
482 }
483 #else
nuc900fb_cpufreq_transition(struct notifier_block * nb,unsigned long val,void * data)484 static inline int nuc900fb_cpufreq_transition(struct notifier_block *nb,
485 				       unsigned long val, void *data)
486 {
487 	return 0;
488 }
489 
nuc900fb_cpufreq_register(struct nuc900fb_info * fbi)490 static inline int nuc900fb_cpufreq_register(struct nuc900fb_info *fbi)
491 {
492 	return 0;
493 }
494 
nuc900fb_cpufreq_deregister(struct nuc900fb_info * info)495 static inline void nuc900fb_cpufreq_deregister(struct nuc900fb_info *info)
496 {
497 }
498 #endif
499 
500 static char driver_name[] = "nuc900fb";
501 
nuc900fb_probe(struct platform_device * pdev)502 static int nuc900fb_probe(struct platform_device *pdev)
503 {
504 	struct nuc900fb_info *fbi;
505 	struct nuc900fb_display *display;
506 	struct fb_info	   *fbinfo;
507 	struct nuc900fb_mach_info *mach_info;
508 	struct resource *res;
509 	int ret;
510 	int irq;
511 	int i;
512 	int size;
513 
514 	dev_dbg(&pdev->dev, "devinit\n");
515 	mach_info = dev_get_platdata(&pdev->dev);
516 	if (mach_info == NULL) {
517 		dev_err(&pdev->dev,
518 			"no platform data for lcd, cannot attach\n");
519 		return -EINVAL;
520 	}
521 
522 	if (mach_info->default_display > mach_info->num_displays) {
523 		dev_err(&pdev->dev,
524 			"default display No. is %d but only %d displays \n",
525 			mach_info->default_display, mach_info->num_displays);
526 		return -EINVAL;
527 	}
528 
529 
530 	display = mach_info->displays + mach_info->default_display;
531 
532 	irq = platform_get_irq(pdev, 0);
533 	if (irq < 0) {
534 		dev_err(&pdev->dev, "no irq for device\n");
535 		return -ENOENT;
536 	}
537 
538 	fbinfo = framebuffer_alloc(sizeof(struct nuc900fb_info), &pdev->dev);
539 	if (!fbinfo)
540 		return -ENOMEM;
541 
542 	platform_set_drvdata(pdev, fbinfo);
543 
544 	fbi = fbinfo->par;
545 	fbi->dev = &pdev->dev;
546 
547 #ifdef CONFIG_CPU_NUC950
548 	fbi->drv_type = LCDDRV_NUC950;
549 #endif
550 
551 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
552 
553 	size = resource_size(res);
554 	fbi->mem = request_mem_region(res->start, size, pdev->name);
555 	if (fbi->mem == NULL) {
556 		dev_err(&pdev->dev, "failed to alloc memory region\n");
557 		ret = -ENOENT;
558 		goto free_fb;
559 	}
560 
561 	fbi->io = ioremap(res->start, size);
562 	if (fbi->io == NULL) {
563 		dev_err(&pdev->dev, "ioremap() of lcd registers failed\n");
564 		ret = -ENXIO;
565 		goto release_mem_region;
566 	}
567 
568 	fbi->irq_base = fbi->io + REG_LCM_INT_CS;
569 
570 
571 	/* Stop the LCD */
572 	writel(0, fbi->io + REG_LCM_DCCS);
573 
574 	/* fill the fbinfo*/
575 	strcpy(fbinfo->fix.id, driver_name);
576 	fbinfo->fix.type		= FB_TYPE_PACKED_PIXELS;
577 	fbinfo->fix.type_aux		= 0;
578 	fbinfo->fix.xpanstep		= 0;
579 	fbinfo->fix.ypanstep		= 0;
580 	fbinfo->fix.ywrapstep		= 0;
581 	fbinfo->fix.accel		= FB_ACCEL_NONE;
582 	fbinfo->var.nonstd		= 0;
583 	fbinfo->var.activate		= FB_ACTIVATE_NOW;
584 	fbinfo->var.accel_flags		= 0;
585 	fbinfo->var.vmode		= FB_VMODE_NONINTERLACED;
586 	fbinfo->fbops			= &nuc900fb_ops;
587 	fbinfo->flags			= FBINFO_FLAG_DEFAULT;
588 	fbinfo->pseudo_palette		= &fbi->pseudo_pal;
589 
590 	ret = request_irq(irq, nuc900fb_irqhandler, 0, pdev->name, fbi);
591 	if (ret) {
592 		dev_err(&pdev->dev, "cannot register irq handler %d -err %d\n",
593 			irq, ret);
594 		ret = -EBUSY;
595 		goto release_regs;
596 	}
597 
598 	fbi->clk = clk_get(&pdev->dev, NULL);
599 	if (IS_ERR(fbi->clk)) {
600 		printk(KERN_ERR "nuc900-lcd:failed to get lcd clock source\n");
601 		ret = PTR_ERR(fbi->clk);
602 		goto release_irq;
603 	}
604 
605 	clk_enable(fbi->clk);
606 	dev_dbg(&pdev->dev, "got and enabled clock\n");
607 
608 	fbi->clk_rate = clk_get_rate(fbi->clk);
609 
610 	/* calutate the video buffer size */
611 	for (i = 0; i < mach_info->num_displays; i++) {
612 		unsigned long smem_len = mach_info->displays[i].xres;
613 		smem_len *= mach_info->displays[i].yres;
614 		smem_len *= mach_info->displays[i].bpp;
615 		smem_len >>= 3;
616 		if (fbinfo->fix.smem_len < smem_len)
617 			fbinfo->fix.smem_len = smem_len;
618 	}
619 
620 	/* Initialize Video Memory */
621 	ret = nuc900fb_map_video_memory(fbinfo);
622 	if (ret) {
623 		printk(KERN_ERR "Failed to allocate video RAM: %x\n", ret);
624 		goto release_clock;
625 	}
626 
627 	dev_dbg(&pdev->dev, "got video memory\n");
628 
629 	fbinfo->var.xres = display->xres;
630 	fbinfo->var.yres = display->yres;
631 	fbinfo->var.bits_per_pixel = display->bpp;
632 
633 	nuc900fb_init_registers(fbinfo);
634 
635 	nuc900fb_check_var(&fbinfo->var, fbinfo);
636 
637 	ret = nuc900fb_cpufreq_register(fbi);
638 	if (ret < 0) {
639 		dev_err(&pdev->dev, "Failed to register cpufreq\n");
640 		goto free_video_memory;
641 	}
642 
643 	ret = register_framebuffer(fbinfo);
644 	if (ret) {
645 		printk(KERN_ERR "failed to register framebuffer device: %d\n",
646 			ret);
647 		goto free_cpufreq;
648 	}
649 
650 	fb_info(fbinfo, "%s frame buffer device\n", fbinfo->fix.id);
651 
652 	return 0;
653 
654 free_cpufreq:
655 	nuc900fb_cpufreq_deregister(fbi);
656 free_video_memory:
657 	nuc900fb_unmap_video_memory(fbinfo);
658 release_clock:
659 	clk_disable(fbi->clk);
660 	clk_put(fbi->clk);
661 release_irq:
662 	free_irq(irq, fbi);
663 release_regs:
664 	iounmap(fbi->io);
665 release_mem_region:
666 	release_mem_region(res->start, size);
667 free_fb:
668 	framebuffer_release(fbinfo);
669 	return ret;
670 }
671 
672 /*
673  * shutdown the lcd controller
674  */
nuc900fb_stop_lcd(struct fb_info * info)675 static void nuc900fb_stop_lcd(struct fb_info *info)
676 {
677 	struct nuc900fb_info *fbi = info->par;
678 	void __iomem *regs = fbi->io;
679 
680 	writel((~LCM_DCCS_DISP_INT_EN) | (~LCM_DCCS_VA_EN) | (~LCM_DCCS_OSD_EN),
681 		regs + REG_LCM_DCCS);
682 }
683 
684 /*
685  *  Cleanup
686  */
nuc900fb_remove(struct platform_device * pdev)687 static int nuc900fb_remove(struct platform_device *pdev)
688 {
689 	struct fb_info *fbinfo = platform_get_drvdata(pdev);
690 	struct nuc900fb_info *fbi = fbinfo->par;
691 	int irq;
692 
693 	nuc900fb_stop_lcd(fbinfo);
694 	msleep(1);
695 
696 	unregister_framebuffer(fbinfo);
697 	nuc900fb_cpufreq_deregister(fbi);
698 	nuc900fb_unmap_video_memory(fbinfo);
699 
700 	iounmap(fbi->io);
701 
702 	irq = platform_get_irq(pdev, 0);
703 	free_irq(irq, fbi);
704 
705 	release_resource(fbi->mem);
706 	kfree(fbi->mem);
707 
708 	framebuffer_release(fbinfo);
709 
710 	return 0;
711 }
712 
713 #ifdef CONFIG_PM
714 
715 /*
716  *	suspend and resume support for the lcd controller
717  */
718 
nuc900fb_suspend(struct platform_device * dev,pm_message_t state)719 static int nuc900fb_suspend(struct platform_device *dev, pm_message_t state)
720 {
721 	struct fb_info	   *fbinfo = platform_get_drvdata(dev);
722 	struct nuc900fb_info *info = fbinfo->par;
723 
724 	nuc900fb_stop_lcd(fbinfo);
725 	msleep(1);
726 	clk_disable(info->clk);
727 	return 0;
728 }
729 
nuc900fb_resume(struct platform_device * dev)730 static int nuc900fb_resume(struct platform_device *dev)
731 {
732 	struct fb_info	   *fbinfo = platform_get_drvdata(dev);
733 	struct nuc900fb_info *fbi = fbinfo->par;
734 
735 	printk(KERN_INFO "nuc900fb resume\n");
736 
737 	clk_enable(fbi->clk);
738 	msleep(1);
739 
740 	nuc900fb_init_registers(fbinfo);
741 	nuc900fb_activate_var(fbinfo);
742 
743 	return 0;
744 }
745 
746 #else
747 #define nuc900fb_suspend NULL
748 #define nuc900fb_resume  NULL
749 #endif
750 
751 static struct platform_driver nuc900fb_driver = {
752 	.probe		= nuc900fb_probe,
753 	.remove		= nuc900fb_remove,
754 	.suspend	= nuc900fb_suspend,
755 	.resume		= nuc900fb_resume,
756 	.driver		= {
757 		.name	= "nuc900-lcd",
758 	},
759 };
760 
761 module_platform_driver(nuc900fb_driver);
762 
763 MODULE_DESCRIPTION("Framebuffer driver for the NUC900");
764 MODULE_LICENSE("GPL");
765