Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
s3c2410fb.c
Go to the documentation of this file.
1 /* linux/drivers/video/s3c2410fb.c
2  * Copyright (c) 2004,2005 Arnaud Patard
3  * Copyright (c) 2004-2008 Ben Dooks
4  *
5  * S3C2410 LCD Framebuffer Driver
6  *
7  * This file is subject to the terms and conditions of the GNU General Public
8  * License. See the file COPYING in the main directory of this archive for
9  * more details.
10  *
11  * Driver based on skeletonfb.c, sa1100fb.c and others.
12 */
13 
14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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/slab.h>
23 #include <linux/delay.h>
24 #include <linux/fb.h>
25 #include <linux/init.h>
26 #include <linux/dma-mapping.h>
27 #include <linux/interrupt.h>
28 #include <linux/platform_device.h>
29 #include <linux/clk.h>
30 #include <linux/cpufreq.h>
31 #include <linux/io.h>
32 
33 #include <asm/div64.h>
34 
35 #include <asm/mach/map.h>
36 #include <mach/regs-lcd.h>
37 #include <mach/regs-gpio.h>
38 #include <mach/fb.h>
39 
40 #ifdef CONFIG_PM
41 #include <linux/pm.h>
42 #endif
43 
44 #include "s3c2410fb.h"
45 
46 /* Debugging stuff */
47 #ifdef CONFIG_FB_S3C2410_DEBUG
48 static int debug = 1;
49 #else
50 static int debug;
51 #endif
52 
53 #define dprintk(msg...) \
54 do { \
55  if (debug) \
56  pr_debug(msg); \
57 } while (0)
58 
59 /* useful functions */
60 
61 static int is_s3c2412(struct s3c2410fb_info *fbi)
62 {
63  return (fbi->drv_type == DRV_S3C2412);
64 }
65 
66 /* s3c2410fb_set_lcdaddr
67  *
68  * initialise lcd controller address pointers
69  */
70 static void s3c2410fb_set_lcdaddr(struct fb_info *info)
71 {
72  unsigned long saddr1, saddr2, saddr3;
73  struct s3c2410fb_info *fbi = info->par;
74  void __iomem *regs = fbi->io;
75 
76  saddr1 = info->fix.smem_start >> 1;
77  saddr2 = info->fix.smem_start;
78  saddr2 += info->fix.line_length * info->var.yres;
79  saddr2 >>= 1;
80 
81  saddr3 = S3C2410_OFFSIZE(0) |
82  S3C2410_PAGEWIDTH((info->fix.line_length / 2) & 0x3ff);
83 
84  dprintk("LCDSADDR1 = 0x%08lx\n", saddr1);
85  dprintk("LCDSADDR2 = 0x%08lx\n", saddr2);
86  dprintk("LCDSADDR3 = 0x%08lx\n", saddr3);
87 
88  writel(saddr1, regs + S3C2410_LCDSADDR1);
89  writel(saddr2, regs + S3C2410_LCDSADDR2);
90  writel(saddr3, regs + S3C2410_LCDSADDR3);
91 }
92 
93 /* s3c2410fb_calc_pixclk()
94  *
95  * calculate divisor for clk->pixclk
96  */
97 static unsigned int s3c2410fb_calc_pixclk(struct s3c2410fb_info *fbi,
98  unsigned long pixclk)
99 {
100  unsigned long clk = fbi->clk_rate;
101  unsigned long long div;
102 
103  /* pixclk is in picoseconds, our clock is in Hz
104  *
105  * Hz -> picoseconds is / 10^-12
106  */
107 
108  div = (unsigned long long)clk * pixclk;
109  div >>= 12; /* div / 2^12 */
110  do_div(div, 625 * 625UL * 625); /* div / 5^12 */
111 
112  dprintk("pixclk %ld, divisor is %ld\n", pixclk, (long)div);
113  return div;
114 }
115 
116 /*
117  * s3c2410fb_check_var():
118  * Get the video params out of 'var'. If a value doesn't fit, round it up,
119  * if it's too big, return -EINVAL.
120  *
121  */
122 static int s3c2410fb_check_var(struct fb_var_screeninfo *var,
123  struct fb_info *info)
124 {
125  struct s3c2410fb_info *fbi = info->par;
126  struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
127  struct s3c2410fb_display *display = NULL;
128  struct s3c2410fb_display *default_display = mach_info->displays +
129  mach_info->default_display;
130  int type = default_display->type;
131  unsigned i;
132 
133  dprintk("check_var(var=%p, info=%p)\n", var, info);
134 
135  /* validate x/y resolution */
136  /* choose default mode if possible */
137  if (var->yres == default_display->yres &&
138  var->xres == default_display->xres &&
139  var->bits_per_pixel == default_display->bpp)
140  display = default_display;
141  else
142  for (i = 0; i < mach_info->num_displays; i++)
143  if (type == mach_info->displays[i].type &&
144  var->yres == mach_info->displays[i].yres &&
145  var->xres == mach_info->displays[i].xres &&
146  var->bits_per_pixel == mach_info->displays[i].bpp) {
147  display = mach_info->displays + i;
148  break;
149  }
150 
151  if (!display) {
152  dprintk("wrong resolution or depth %dx%d at %d bpp\n",
153  var->xres, var->yres, var->bits_per_pixel);
154  return -EINVAL;
155  }
156 
157  /* it is always the size as the display */
158  var->xres_virtual = display->xres;
159  var->yres_virtual = display->yres;
160  var->height = display->height;
161  var->width = display->width;
162 
163  /* copy lcd settings */
164  var->pixclock = display->pixclock;
165  var->left_margin = display->left_margin;
166  var->right_margin = display->right_margin;
167  var->upper_margin = display->upper_margin;
168  var->lower_margin = display->lower_margin;
169  var->vsync_len = display->vsync_len;
170  var->hsync_len = display->hsync_len;
171 
172  fbi->regs.lcdcon5 = display->lcdcon5;
173  /* set display type */
174  fbi->regs.lcdcon1 = display->type;
175 
176  var->transp.offset = 0;
177  var->transp.length = 0;
178  /* set r/g/b positions */
179  switch (var->bits_per_pixel) {
180  case 1:
181  case 2:
182  case 4:
183  var->red.offset = 0;
184  var->red.length = var->bits_per_pixel;
185  var->green = var->red;
186  var->blue = var->red;
187  break;
188  case 8:
189  if (display->type != S3C2410_LCDCON1_TFT) {
190  /* 8 bpp 332 */
191  var->red.length = 3;
192  var->red.offset = 5;
193  var->green.length = 3;
194  var->green.offset = 2;
195  var->blue.length = 2;
196  var->blue.offset = 0;
197  } else {
198  var->red.offset = 0;
199  var->red.length = 8;
200  var->green = var->red;
201  var->blue = var->red;
202  }
203  break;
204  case 12:
205  /* 12 bpp 444 */
206  var->red.length = 4;
207  var->red.offset = 8;
208  var->green.length = 4;
209  var->green.offset = 4;
210  var->blue.length = 4;
211  var->blue.offset = 0;
212  break;
213 
214  default:
215  case 16:
216  if (display->lcdcon5 & S3C2410_LCDCON5_FRM565) {
217  /* 16 bpp, 565 format */
218  var->red.offset = 11;
219  var->green.offset = 5;
220  var->blue.offset = 0;
221  var->red.length = 5;
222  var->green.length = 6;
223  var->blue.length = 5;
224  } else {
225  /* 16 bpp, 5551 format */
226  var->red.offset = 11;
227  var->green.offset = 6;
228  var->blue.offset = 1;
229  var->red.length = 5;
230  var->green.length = 5;
231  var->blue.length = 5;
232  }
233  break;
234  case 32:
235  /* 24 bpp 888 and 8 dummy */
236  var->red.length = 8;
237  var->red.offset = 16;
238  var->green.length = 8;
239  var->green.offset = 8;
240  var->blue.length = 8;
241  var->blue.offset = 0;
242  break;
243  }
244  return 0;
245 }
246 
247 /* s3c2410fb_calculate_stn_lcd_regs
248  *
249  * calculate register values from var settings
250  */
251 static void s3c2410fb_calculate_stn_lcd_regs(const struct fb_info *info,
252  struct s3c2410fb_hw *regs)
253 {
254  const struct s3c2410fb_info *fbi = info->par;
255  const struct fb_var_screeninfo *var = &info->var;
256  int type = regs->lcdcon1 & ~S3C2410_LCDCON1_TFT;
257  int hs = var->xres >> 2;
258  unsigned wdly = (var->left_margin >> 4) - 1;
259  unsigned wlh = (var->hsync_len >> 4) - 1;
260 
261  if (type != S3C2410_LCDCON1_STN4)
262  hs >>= 1;
263 
264  switch (var->bits_per_pixel) {
265  case 1:
267  break;
268  case 2:
270  break;
271  case 4:
273  break;
274  case 8:
276  hs *= 3;
277  break;
278  case 12:
280  hs *= 3;
281  break;
282 
283  default:
284  /* invalid pixel depth */
285  dev_err(fbi->dev, "invalid bpp %d\n",
286  var->bits_per_pixel);
287  }
288  /* update X/Y info */
289  dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
290  var->left_margin, var->right_margin, var->hsync_len);
291 
292  regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1);
293 
294  if (wdly > 3)
295  wdly = 3;
296 
297  if (wlh > 3)
298  wlh = 3;
299 
300  regs->lcdcon3 = S3C2410_LCDCON3_WDLY(wdly) |
302  S3C2410_LCDCON3_HOZVAL(hs - 1);
303 
304  regs->lcdcon4 = S3C2410_LCDCON4_WLH(wlh);
305 }
306 
307 /* s3c2410fb_calculate_tft_lcd_regs
308  *
309  * calculate register values from var settings
310  */
311 static void s3c2410fb_calculate_tft_lcd_regs(const struct fb_info *info,
312  struct s3c2410fb_hw *regs)
313 {
314  const struct s3c2410fb_info *fbi = info->par;
315  const struct fb_var_screeninfo *var = &info->var;
316 
317  switch (var->bits_per_pixel) {
318  case 1:
320  break;
321  case 2:
323  break;
324  case 4:
326  break;
327  case 8:
329  regs->lcdcon5 |= S3C2410_LCDCON5_BSWP |
331  regs->lcdcon5 &= ~S3C2410_LCDCON5_HWSWP;
332  break;
333  case 16:
335  regs->lcdcon5 &= ~S3C2410_LCDCON5_BSWP;
337  break;
338  case 32:
340  regs->lcdcon5 &= ~(S3C2410_LCDCON5_BSWP |
343  break;
344  default:
345  /* invalid pixel depth */
346  dev_err(fbi->dev, "invalid bpp %d\n",
347  var->bits_per_pixel);
348  }
349  /* update X/Y info */
350  dprintk("setting vert: up=%d, low=%d, sync=%d\n",
351  var->upper_margin, var->lower_margin, var->vsync_len);
352 
353  dprintk("setting horz: lft=%d, rt=%d, sync=%d\n",
354  var->left_margin, var->right_margin, var->hsync_len);
355 
356  regs->lcdcon2 = S3C2410_LCDCON2_LINEVAL(var->yres - 1) |
360 
361  regs->lcdcon3 = S3C2410_LCDCON3_HBPD(var->right_margin - 1) |
363  S3C2410_LCDCON3_HOZVAL(var->xres - 1);
364 
365  regs->lcdcon4 = S3C2410_LCDCON4_HSPW(var->hsync_len - 1);
366 }
367 
368 /* s3c2410fb_activate_var
369  *
370  * activate (set) the controller from the given framebuffer
371  * information
372  */
373 static void s3c2410fb_activate_var(struct fb_info *info)
374 {
375  struct s3c2410fb_info *fbi = info->par;
376  void __iomem *regs = fbi->io;
377  int type = fbi->regs.lcdcon1 & S3C2410_LCDCON1_TFT;
378  struct fb_var_screeninfo *var = &info->var;
379  int clkdiv;
380 
381  clkdiv = DIV_ROUND_UP(s3c2410fb_calc_pixclk(fbi, var->pixclock), 2);
382 
383  dprintk("%s: var->xres = %d\n", __func__, var->xres);
384  dprintk("%s: var->yres = %d\n", __func__, var->yres);
385  dprintk("%s: var->bpp = %d\n", __func__, var->bits_per_pixel);
386 
387  if (type == S3C2410_LCDCON1_TFT) {
388  s3c2410fb_calculate_tft_lcd_regs(info, &fbi->regs);
389  --clkdiv;
390  if (clkdiv < 0)
391  clkdiv = 0;
392  } else {
393  s3c2410fb_calculate_stn_lcd_regs(info, &fbi->regs);
394  if (clkdiv < 2)
395  clkdiv = 2;
396  }
397 
398  fbi->regs.lcdcon1 |= S3C2410_LCDCON1_CLKVAL(clkdiv);
399 
400  /* write new registers */
401 
402  dprintk("new register set:\n");
403  dprintk("lcdcon[1] = 0x%08lx\n", fbi->regs.lcdcon1);
404  dprintk("lcdcon[2] = 0x%08lx\n", fbi->regs.lcdcon2);
405  dprintk("lcdcon[3] = 0x%08lx\n", fbi->regs.lcdcon3);
406  dprintk("lcdcon[4] = 0x%08lx\n", fbi->regs.lcdcon4);
407  dprintk("lcdcon[5] = 0x%08lx\n", fbi->regs.lcdcon5);
408 
409  writel(fbi->regs.lcdcon1 & ~S3C2410_LCDCON1_ENVID,
410  regs + S3C2410_LCDCON1);
411  writel(fbi->regs.lcdcon2, regs + S3C2410_LCDCON2);
412  writel(fbi->regs.lcdcon3, regs + S3C2410_LCDCON3);
413  writel(fbi->regs.lcdcon4, regs + S3C2410_LCDCON4);
414  writel(fbi->regs.lcdcon5, regs + S3C2410_LCDCON5);
415 
416  /* set lcd address pointers */
417  s3c2410fb_set_lcdaddr(info);
418 
419  fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID,
420  writel(fbi->regs.lcdcon1, regs + S3C2410_LCDCON1);
421 }
422 
423 /*
424  * s3c2410fb_set_par - Alters the hardware state.
425  * @info: frame buffer structure that represents a single frame buffer
426  *
427  */
428 static int s3c2410fb_set_par(struct fb_info *info)
429 {
430  struct fb_var_screeninfo *var = &info->var;
431 
432  switch (var->bits_per_pixel) {
433  case 32:
434  case 16:
435  case 12:
436  info->fix.visual = FB_VISUAL_TRUECOLOR;
437  break;
438  case 1:
439  info->fix.visual = FB_VISUAL_MONO01;
440  break;
441  default:
442  info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
443  break;
444  }
445 
446  info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
447 
448  /* activate this new configuration */
449 
450  s3c2410fb_activate_var(info);
451  return 0;
452 }
453 
454 static void schedule_palette_update(struct s3c2410fb_info *fbi,
455  unsigned int regno, unsigned int val)
456 {
457  unsigned long flags;
458  unsigned long irqen;
459  void __iomem *irq_base = fbi->irq_base;
460 
461  local_irq_save(flags);
462 
463  fbi->palette_buffer[regno] = val;
464 
465  if (!fbi->palette_ready) {
466  fbi->palette_ready = 1;
467 
468  /* enable IRQ */
469  irqen = readl(irq_base + S3C24XX_LCDINTMSK);
470  irqen &= ~S3C2410_LCDINT_FRSYNC;
471  writel(irqen, irq_base + S3C24XX_LCDINTMSK);
472  }
473 
474  local_irq_restore(flags);
475 }
476 
477 /* from pxafb.c */
478 static inline unsigned int chan_to_field(unsigned int chan,
479  struct fb_bitfield *bf)
480 {
481  chan &= 0xffff;
482  chan >>= 16 - bf->length;
483  return chan << bf->offset;
484 }
485 
486 static int s3c2410fb_setcolreg(unsigned regno,
487  unsigned red, unsigned green, unsigned blue,
488  unsigned transp, struct fb_info *info)
489 {
490  struct s3c2410fb_info *fbi = info->par;
491  void __iomem *regs = fbi->io;
492  unsigned int val;
493 
494  /* dprintk("setcol: regno=%d, rgb=%d,%d,%d\n",
495  regno, red, green, blue); */
496 
497  switch (info->fix.visual) {
498  case FB_VISUAL_TRUECOLOR:
499  /* true-colour, use pseudo-palette */
500 
501  if (regno < 16) {
502  u32 *pal = info->pseudo_palette;
503 
504  val = chan_to_field(red, &info->var.red);
505  val |= chan_to_field(green, &info->var.green);
506  val |= chan_to_field(blue, &info->var.blue);
507 
508  pal[regno] = val;
509  }
510  break;
511 
513  if (regno < 256) {
514  /* currently assume RGB 5-6-5 mode */
515 
516  val = (red >> 0) & 0xf800;
517  val |= (green >> 5) & 0x07e0;
518  val |= (blue >> 11) & 0x001f;
519 
520  writel(val, regs + S3C2410_TFTPAL(regno));
521  schedule_palette_update(fbi, regno, val);
522  }
523 
524  break;
525 
526  default:
527  return 1; /* unknown type */
528  }
529 
530  return 0;
531 }
532 
533 /* s3c2410fb_lcd_enable
534  *
535  * shutdown the lcd controller
536  */
537 static void s3c2410fb_lcd_enable(struct s3c2410fb_info *fbi, int enable)
538 {
539  unsigned long flags;
540 
541  local_irq_save(flags);
542 
543  if (enable)
544  fbi->regs.lcdcon1 |= S3C2410_LCDCON1_ENVID;
545  else
546  fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
547 
548  writel(fbi->regs.lcdcon1, fbi->io + S3C2410_LCDCON1);
549 
550  local_irq_restore(flags);
551 }
552 
553 
554 /*
555  * s3c2410fb_blank
556  * @blank_mode: the blank mode we want.
557  * @info: frame buffer structure that represents a single frame buffer
558  *
559  * Blank the screen if blank_mode != 0, else unblank. Return 0 if
560  * blanking succeeded, != 0 if un-/blanking failed due to e.g. a
561  * video mode which doesn't support it. Implements VESA suspend
562  * and powerdown modes on hardware that supports disabling hsync/vsync:
563  *
564  * Returns negative errno on error, or zero on success.
565  *
566  */
567 static int s3c2410fb_blank(int blank_mode, struct fb_info *info)
568 {
569  struct s3c2410fb_info *fbi = info->par;
570  void __iomem *tpal_reg = fbi->io;
571 
572  dprintk("blank(mode=%d, info=%p)\n", blank_mode, info);
573 
574  tpal_reg += is_s3c2412(fbi) ? S3C2412_TPAL : S3C2410_TPAL;
575 
576  if (blank_mode == FB_BLANK_POWERDOWN)
577  s3c2410fb_lcd_enable(fbi, 0);
578  else
579  s3c2410fb_lcd_enable(fbi, 1);
580 
581  if (blank_mode == FB_BLANK_UNBLANK)
582  writel(0x0, tpal_reg);
583  else {
584  dprintk("setting TPAL to output 0x000000\n");
585  writel(S3C2410_TPAL_EN, tpal_reg);
586  }
587 
588  return 0;
589 }
590 
591 static int s3c2410fb_debug_show(struct device *dev,
592  struct device_attribute *attr, char *buf)
593 {
594  return snprintf(buf, PAGE_SIZE, "%s\n", debug ? "on" : "off");
595 }
596 
597 static int s3c2410fb_debug_store(struct device *dev,
598  struct device_attribute *attr,
599  const char *buf, size_t len)
600 {
601  if (len < 1)
602  return -EINVAL;
603 
604  if (strnicmp(buf, "on", 2) == 0 ||
605  strnicmp(buf, "1", 1) == 0) {
606  debug = 1;
607  dev_dbg(dev, "s3c2410fb: Debug On");
608  } else if (strnicmp(buf, "off", 3) == 0 ||
609  strnicmp(buf, "0", 1) == 0) {
610  debug = 0;
611  dev_dbg(dev, "s3c2410fb: Debug Off");
612  } else {
613  return -EINVAL;
614  }
615 
616  return len;
617 }
618 
619 static DEVICE_ATTR(debug, 0666, s3c2410fb_debug_show, s3c2410fb_debug_store);
620 
621 static struct fb_ops s3c2410fb_ops = {
622  .owner = THIS_MODULE,
623  .fb_check_var = s3c2410fb_check_var,
624  .fb_set_par = s3c2410fb_set_par,
625  .fb_blank = s3c2410fb_blank,
626  .fb_setcolreg = s3c2410fb_setcolreg,
627  .fb_fillrect = cfb_fillrect,
628  .fb_copyarea = cfb_copyarea,
629  .fb_imageblit = cfb_imageblit,
630 };
631 
632 /*
633  * s3c2410fb_map_video_memory():
634  * Allocates the DRAM memory for the frame buffer. This buffer is
635  * remapped into a non-cached, non-buffered, memory region to
636  * allow palette and pixel writes to occur without flushing the
637  * cache. Once this area is remapped, all virtual memory
638  * access to the video memory should occur at the new region.
639  */
640 static int __devinit s3c2410fb_map_video_memory(struct fb_info *info)
641 {
642  struct s3c2410fb_info *fbi = info->par;
643  dma_addr_t map_dma;
644  unsigned map_size = PAGE_ALIGN(info->fix.smem_len);
645 
646  dprintk("map_video_memory(fbi=%p) map_size %u\n", fbi, map_size);
647 
648  info->screen_base = dma_alloc_writecombine(fbi->dev, map_size,
649  &map_dma, GFP_KERNEL);
650 
651  if (info->screen_base) {
652  /* prevent initial garbage on screen */
653  dprintk("map_video_memory: clear %p:%08x\n",
654  info->screen_base, map_size);
655  memset(info->screen_base, 0x00, map_size);
656 
657  info->fix.smem_start = map_dma;
658 
659  dprintk("map_video_memory: dma=%08lx cpu=%p size=%08x\n",
660  info->fix.smem_start, info->screen_base, map_size);
661  }
662 
663  return info->screen_base ? 0 : -ENOMEM;
664 }
665 
666 static inline void s3c2410fb_unmap_video_memory(struct fb_info *info)
667 {
668  struct s3c2410fb_info *fbi = info->par;
669 
670  dma_free_writecombine(fbi->dev, PAGE_ALIGN(info->fix.smem_len),
671  info->screen_base, info->fix.smem_start);
672 }
673 
674 static inline void modify_gpio(void __iomem *reg,
675  unsigned long set, unsigned long mask)
676 {
677  unsigned long tmp;
678 
679  tmp = readl(reg) & ~mask;
680  writel(tmp | set, reg);
681 }
682 
683 /*
684  * s3c2410fb_init_registers - Initialise all LCD-related registers
685  */
686 static int s3c2410fb_init_registers(struct fb_info *info)
687 {
688  struct s3c2410fb_info *fbi = info->par;
689  struct s3c2410fb_mach_info *mach_info = fbi->dev->platform_data;
690  unsigned long flags;
691  void __iomem *regs = fbi->io;
692  void __iomem *tpal;
693  void __iomem *lpcsel;
694 
695  if (is_s3c2412(fbi)) {
696  tpal = regs + S3C2412_TPAL;
697  lpcsel = regs + S3C2412_TCONSEL;
698  } else {
699  tpal = regs + S3C2410_TPAL;
700  lpcsel = regs + S3C2410_LPCSEL;
701  }
702 
703  /* Initialise LCD with values from haret */
704 
705  local_irq_save(flags);
706 
707  /* modify the gpio(s) with interrupts set (bjd) */
708 
709  modify_gpio(S3C2410_GPCUP, mach_info->gpcup, mach_info->gpcup_mask);
710  modify_gpio(S3C2410_GPCCON, mach_info->gpccon, mach_info->gpccon_mask);
711  modify_gpio(S3C2410_GPDUP, mach_info->gpdup, mach_info->gpdup_mask);
712  modify_gpio(S3C2410_GPDCON, mach_info->gpdcon, mach_info->gpdcon_mask);
713 
714  local_irq_restore(flags);
715 
716  dprintk("LPCSEL = 0x%08lx\n", mach_info->lpcsel);
717  writel(mach_info->lpcsel, lpcsel);
718 
719  dprintk("replacing TPAL %08x\n", readl(tpal));
720 
721  /* ensure temporary palette disabled */
722  writel(0x00, tpal);
723 
724  return 0;
725 }
726 
727 static void s3c2410fb_write_palette(struct s3c2410fb_info *fbi)
728 {
729  unsigned int i;
730  void __iomem *regs = fbi->io;
731 
732  fbi->palette_ready = 0;
733 
734  for (i = 0; i < 256; i++) {
735  unsigned long ent = fbi->palette_buffer[i];
736  if (ent == PALETTE_BUFF_CLEAR)
737  continue;
738 
739  writel(ent, regs + S3C2410_TFTPAL(i));
740 
741  /* it seems the only way to know exactly
742  * if the palette wrote ok, is to check
743  * to see if the value verifies ok
744  */
745 
746  if (readw(regs + S3C2410_TFTPAL(i)) == ent)
748  else
749  fbi->palette_ready = 1; /* retry */
750  }
751 }
752 
753 static irqreturn_t s3c2410fb_irq(int irq, void *dev_id)
754 {
755  struct s3c2410fb_info *fbi = dev_id;
756  void __iomem *irq_base = fbi->irq_base;
757  unsigned long lcdirq = readl(irq_base + S3C24XX_LCDINTPND);
758 
759  if (lcdirq & S3C2410_LCDINT_FRSYNC) {
760  if (fbi->palette_ready)
761  s3c2410fb_write_palette(fbi);
762 
763  writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDINTPND);
764  writel(S3C2410_LCDINT_FRSYNC, irq_base + S3C24XX_LCDSRCPND);
765  }
766 
767  return IRQ_HANDLED;
768 }
769 
770 #ifdef CONFIG_CPU_FREQ
771 
772 static int s3c2410fb_cpufreq_transition(struct notifier_block *nb,
773  unsigned long val, void *data)
774 {
775  struct s3c2410fb_info *info;
776  struct fb_info *fbinfo;
777  long delta_f;
778 
779  info = container_of(nb, struct s3c2410fb_info, freq_transition);
780  fbinfo = platform_get_drvdata(to_platform_device(info->dev));
781 
782  /* work out change, <0 for speed-up */
783  delta_f = info->clk_rate - clk_get_rate(info->clk);
784 
785  if ((val == CPUFREQ_POSTCHANGE && delta_f > 0) ||
786  (val == CPUFREQ_PRECHANGE && delta_f < 0)) {
787  info->clk_rate = clk_get_rate(info->clk);
788  s3c2410fb_activate_var(fbinfo);
789  }
790 
791  return 0;
792 }
793 
794 static inline int s3c2410fb_cpufreq_register(struct s3c2410fb_info *info)
795 {
796  info->freq_transition.notifier_call = s3c2410fb_cpufreq_transition;
797 
798  return cpufreq_register_notifier(&info->freq_transition,
800 }
801 
802 static inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info)
803 {
804  cpufreq_unregister_notifier(&info->freq_transition,
806 }
807 
808 #else
809 static inline int s3c2410fb_cpufreq_register(struct s3c2410fb_info *info)
810 {
811  return 0;
812 }
813 
814 static inline void s3c2410fb_cpufreq_deregister(struct s3c2410fb_info *info)
815 {
816 }
817 #endif
818 
819 
820 static const char driver_name[] = "s3c2410fb";
821 
822 static int __devinit s3c24xxfb_probe(struct platform_device *pdev,
823  enum s3c_drv_type drv_type)
824 {
825  struct s3c2410fb_info *info;
826  struct s3c2410fb_display *display;
827  struct fb_info *fbinfo;
828  struct s3c2410fb_mach_info *mach_info;
829  struct resource *res;
830  int ret;
831  int irq;
832  int i;
833  int size;
834  u32 lcdcon1;
835 
836  mach_info = pdev->dev.platform_data;
837  if (mach_info == NULL) {
838  dev_err(&pdev->dev,
839  "no platform data for lcd, cannot attach\n");
840  return -EINVAL;
841  }
842 
843  if (mach_info->default_display >= mach_info->num_displays) {
844  dev_err(&pdev->dev, "default is %d but only %d displays\n",
845  mach_info->default_display, mach_info->num_displays);
846  return -EINVAL;
847  }
848 
849  display = mach_info->displays + mach_info->default_display;
850 
851  irq = platform_get_irq(pdev, 0);
852  if (irq < 0) {
853  dev_err(&pdev->dev, "no irq for device\n");
854  return -ENOENT;
855  }
856 
857  fbinfo = framebuffer_alloc(sizeof(struct s3c2410fb_info), &pdev->dev);
858  if (!fbinfo)
859  return -ENOMEM;
860 
861  platform_set_drvdata(pdev, fbinfo);
862 
863  info = fbinfo->par;
864  info->dev = &pdev->dev;
865  info->drv_type = drv_type;
866 
867  res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
868  if (res == NULL) {
869  dev_err(&pdev->dev, "failed to get memory registers\n");
870  ret = -ENXIO;
871  goto dealloc_fb;
872  }
873 
874  size = resource_size(res);
875  info->mem = request_mem_region(res->start, size, pdev->name);
876  if (info->mem == NULL) {
877  dev_err(&pdev->dev, "failed to get memory region\n");
878  ret = -ENOENT;
879  goto dealloc_fb;
880  }
881 
882  info->io = ioremap(res->start, size);
883  if (info->io == NULL) {
884  dev_err(&pdev->dev, "ioremap() of registers failed\n");
885  ret = -ENXIO;
886  goto release_mem;
887  }
888 
889  if (drv_type == DRV_S3C2412)
890  info->irq_base = info->io + S3C2412_LCDINTBASE;
891  else
892  info->irq_base = info->io + S3C2410_LCDINTBASE;
893 
894  dprintk("devinit\n");
895 
896  strcpy(fbinfo->fix.id, driver_name);
897 
898  /* Stop the video */
899  lcdcon1 = readl(info->io + S3C2410_LCDCON1);
900  writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, info->io + S3C2410_LCDCON1);
901 
902  fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
903  fbinfo->fix.type_aux = 0;
904  fbinfo->fix.xpanstep = 0;
905  fbinfo->fix.ypanstep = 0;
906  fbinfo->fix.ywrapstep = 0;
907  fbinfo->fix.accel = FB_ACCEL_NONE;
908 
909  fbinfo->var.nonstd = 0;
910  fbinfo->var.activate = FB_ACTIVATE_NOW;
911  fbinfo->var.accel_flags = 0;
912  fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
913 
914  fbinfo->fbops = &s3c2410fb_ops;
915  fbinfo->flags = FBINFO_FLAG_DEFAULT;
916  fbinfo->pseudo_palette = &info->pseudo_pal;
917 
918  for (i = 0; i < 256; i++)
920 
921  ret = request_irq(irq, s3c2410fb_irq, 0, pdev->name, info);
922  if (ret) {
923  dev_err(&pdev->dev, "cannot get irq %d - err %d\n", irq, ret);
924  ret = -EBUSY;
925  goto release_regs;
926  }
927 
928  info->clk = clk_get(NULL, "lcd");
929  if (IS_ERR(info->clk)) {
930  dev_err(&pdev->dev, "failed to get lcd clock source\n");
931  ret = PTR_ERR(info->clk);
932  goto release_irq;
933  }
934 
935  clk_enable(info->clk);
936  dprintk("got and enabled clock\n");
937 
938  usleep_range(1000, 1100);
939 
940  info->clk_rate = clk_get_rate(info->clk);
941 
942  /* find maximum required memory size for display */
943  for (i = 0; i < mach_info->num_displays; i++) {
944  unsigned long smem_len = mach_info->displays[i].xres;
945 
946  smem_len *= mach_info->displays[i].yres;
947  smem_len *= mach_info->displays[i].bpp;
948  smem_len >>= 3;
949  if (fbinfo->fix.smem_len < smem_len)
950  fbinfo->fix.smem_len = smem_len;
951  }
952 
953  /* Initialize video memory */
954  ret = s3c2410fb_map_video_memory(fbinfo);
955  if (ret) {
956  dev_err(&pdev->dev, "Failed to allocate video RAM: %d\n", ret);
957  ret = -ENOMEM;
958  goto release_clock;
959  }
960 
961  dprintk("got video memory\n");
962 
963  fbinfo->var.xres = display->xres;
964  fbinfo->var.yres = display->yres;
965  fbinfo->var.bits_per_pixel = display->bpp;
966 
967  s3c2410fb_init_registers(fbinfo);
968 
969  s3c2410fb_check_var(&fbinfo->var, fbinfo);
970 
971  ret = s3c2410fb_cpufreq_register(info);
972  if (ret < 0) {
973  dev_err(&pdev->dev, "Failed to register cpufreq\n");
974  goto free_video_memory;
975  }
976 
977  ret = register_framebuffer(fbinfo);
978  if (ret < 0) {
979  dev_err(&pdev->dev, "Failed to register framebuffer device: %d\n",
980  ret);
981  goto free_cpufreq;
982  }
983 
984  /* create device files */
985  ret = device_create_file(&pdev->dev, &dev_attr_debug);
986  if (ret)
987  dev_err(&pdev->dev, "failed to add debug attribute\n");
988 
989  dev_info(&pdev->dev, "fb%d: %s frame buffer device\n",
990  fbinfo->node, fbinfo->fix.id);
991 
992  return 0;
993 
994  free_cpufreq:
995  s3c2410fb_cpufreq_deregister(info);
996 free_video_memory:
997  s3c2410fb_unmap_video_memory(fbinfo);
998 release_clock:
999  clk_disable(info->clk);
1000  clk_put(info->clk);
1001 release_irq:
1002  free_irq(irq, info);
1003 release_regs:
1004  iounmap(info->io);
1005 release_mem:
1006  release_mem_region(res->start, size);
1007 dealloc_fb:
1008  platform_set_drvdata(pdev, NULL);
1009  framebuffer_release(fbinfo);
1010  return ret;
1011 }
1012 
1013 static int __devinit s3c2410fb_probe(struct platform_device *pdev)
1014 {
1015  return s3c24xxfb_probe(pdev, DRV_S3C2410);
1016 }
1017 
1018 static int __devinit s3c2412fb_probe(struct platform_device *pdev)
1019 {
1020  return s3c24xxfb_probe(pdev, DRV_S3C2412);
1021 }
1022 
1023 
1024 /*
1025  * Cleanup
1026  */
1027 static int __devexit s3c2410fb_remove(struct platform_device *pdev)
1028 {
1029  struct fb_info *fbinfo = platform_get_drvdata(pdev);
1030  struct s3c2410fb_info *info = fbinfo->par;
1031  int irq;
1032 
1033  unregister_framebuffer(fbinfo);
1034  s3c2410fb_cpufreq_deregister(info);
1035 
1036  s3c2410fb_lcd_enable(info, 0);
1037  usleep_range(1000, 1100);
1038 
1039  s3c2410fb_unmap_video_memory(fbinfo);
1040 
1041  if (info->clk) {
1042  clk_disable(info->clk);
1043  clk_put(info->clk);
1044  info->clk = NULL;
1045  }
1046 
1047  irq = platform_get_irq(pdev, 0);
1048  free_irq(irq, info);
1049 
1050  iounmap(info->io);
1051 
1052  release_mem_region(info->mem->start, resource_size(info->mem));
1053 
1054  platform_set_drvdata(pdev, NULL);
1055  framebuffer_release(fbinfo);
1056 
1057  return 0;
1058 }
1059 
1060 #ifdef CONFIG_PM
1061 
1062 /* suspend and resume support for the lcd controller */
1063 static int s3c2410fb_suspend(struct platform_device *dev, pm_message_t state)
1064 {
1065  struct fb_info *fbinfo = platform_get_drvdata(dev);
1066  struct s3c2410fb_info *info = fbinfo->par;
1067 
1068  s3c2410fb_lcd_enable(info, 0);
1069 
1070  /* sleep before disabling the clock, we need to ensure
1071  * the LCD DMA engine is not going to get back on the bus
1072  * before the clock goes off again (bjd) */
1073 
1074  usleep_range(1000, 1100);
1075  clk_disable(info->clk);
1076 
1077  return 0;
1078 }
1079 
1080 static int s3c2410fb_resume(struct platform_device *dev)
1081 {
1082  struct fb_info *fbinfo = platform_get_drvdata(dev);
1083  struct s3c2410fb_info *info = fbinfo->par;
1084 
1085  clk_enable(info->clk);
1086  usleep_range(1000, 1100);
1087 
1088  s3c2410fb_init_registers(fbinfo);
1089 
1090  /* re-activate our display after resume */
1091  s3c2410fb_activate_var(fbinfo);
1092  s3c2410fb_blank(FB_BLANK_UNBLANK, fbinfo);
1093 
1094  return 0;
1095 }
1096 
1097 #else
1098 #define s3c2410fb_suspend NULL
1099 #define s3c2410fb_resume NULL
1100 #endif
1101 
1102 static struct platform_driver s3c2410fb_driver = {
1103  .probe = s3c2410fb_probe,
1104  .remove = __devexit_p(s3c2410fb_remove),
1105  .suspend = s3c2410fb_suspend,
1106  .resume = s3c2410fb_resume,
1107  .driver = {
1108  .name = "s3c2410-lcd",
1109  .owner = THIS_MODULE,
1110  },
1111 };
1112 
1113 static struct platform_driver s3c2412fb_driver = {
1114  .probe = s3c2412fb_probe,
1115  .remove = __devexit_p(s3c2410fb_remove),
1116  .suspend = s3c2410fb_suspend,
1117  .resume = s3c2410fb_resume,
1118  .driver = {
1119  .name = "s3c2412-lcd",
1120  .owner = THIS_MODULE,
1121  },
1122 };
1123 
1125 {
1126  int ret = platform_driver_register(&s3c2410fb_driver);
1127 
1128  if (ret == 0)
1129  ret = platform_driver_register(&s3c2412fb_driver);
1130 
1131  return ret;
1132 }
1133 
1134 static void __exit s3c2410fb_cleanup(void)
1135 {
1136  platform_driver_unregister(&s3c2410fb_driver);
1137  platform_driver_unregister(&s3c2412fb_driver);
1138 }
1139 
1141 module_exit(s3c2410fb_cleanup);
1142 
1143 MODULE_AUTHOR("Arnaud Patard <[email protected]>");
1144 MODULE_AUTHOR("Ben Dooks <[email protected]>");
1145 MODULE_DESCRIPTION("Framebuffer driver for the s3c2410");
1146 MODULE_LICENSE("GPL");
1147 MODULE_ALIAS("platform:s3c2410-lcd");
1148 MODULE_ALIAS("platform:s3c2412-lcd");