aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/s3c-fb.c
diff options
context:
space:
mode:
authorBen Dooks <ben-linux@fluff.org>2009-03-31 18:25:39 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-04-01 11:59:33 -0400
commitec549a0fdc32171b26677f1ef0b5309faa743362 (patch)
tree5b560bf154069e7d6c36a1ab1cb5430081e5bc15 /drivers/video/s3c-fb.c
parent4c8714310afbaabd94ac30db1e499a90e4a69c4e (diff)
fb: add s3c-fb driver for newer Samsung SoC framebuffer devices
Add support for the newer Samsung devices, such as found in the S3C2443, S3C6400 or S3C6410 series SoC. It currently does not support all the alpha- or chroma-key options but it will support more exporting more than one framebuffer ready for adding overlay and blending functions. Signed-off-by: Ben Dooks <ben-linux@fluff.org> Cc: Krzysztof Helt <krzysztof.h1@poczta.fm> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/s3c-fb.c')
-rw-r--r--drivers/video/s3c-fb.c1036
1 files changed, 1036 insertions, 0 deletions
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c
new file mode 100644
index 000000000000..5e9c6302433b
--- /dev/null
+++ b/drivers/video/s3c-fb.c
@@ -0,0 +1,1036 @@
1/* linux/drivers/video/s3c-fb.c
2 *
3 * Copyright 2008 Openmoko Inc.
4 * Copyright 2008 Simtec Electronics
5 * Ben Dooks <ben@simtec.co.uk>
6 * http://armlinux.simtec.co.uk/
7 *
8 * Samsung SoC Framebuffer driver
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
13*/
14
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/platform_device.h>
18#include <linux/dma-mapping.h>
19#include <linux/init.h>
20#include <linux/gfp.h>
21#include <linux/clk.h>
22#include <linux/fb.h>
23#include <linux/io.h>
24
25#include <mach/map.h>
26#include <mach/regs-fb.h>
27#include <plat/fb.h>
28
29/* This driver will export a number of framebuffer interfaces depending
30 * on the configuration passed in via the platform data. Each fb instance
31 * maps to a hardware window. Currently there is no support for runtime
32 * setting of the alpha-blending functions that each window has, so only
33 * window 0 is actually useful.
34 *
35 * Window 0 is treated specially, it is used for the basis of the LCD
36 * output timings and as the control for the output power-down state.
37*/
38
39/* note, some of the functions that get called are derived from including
40 * <mach/regs-fb.h> as they are specific to the architecture that the code
41 * is being built for.
42*/
43
44#ifdef CONFIG_FB_S3C_DEBUG_REGWRITE
45#undef writel
46#define writel(v, r) do { \
47 printk(KERN_DEBUG "%s: %08x => %p\n", __func__, (unsigned int)v, r); \
48 __raw_writel(v, r); } while(0)
49#endif /* FB_S3C_DEBUG_REGWRITE */
50
51struct s3c_fb;
52
53/**
54 * struct s3c_fb_win - per window private data for each framebuffer.
55 * @windata: The platform data supplied for the window configuration.
56 * @parent: The hardware that this window is part of.
57 * @fbinfo: Pointer pack to the framebuffer info for this window.
58 * @palette_buffer: Buffer/cache to hold palette entries.
59 * @pseudo_palette: For use in TRUECOLOUR modes for entries 0..15/
60 * @index: The window number of this window.
61 * @palette: The bitfields for changing r/g/b into a hardware palette entry.
62 */
63struct s3c_fb_win {
64 struct s3c_fb_pd_win *windata;
65 struct s3c_fb *parent;
66 struct fb_info *fbinfo;
67 struct s3c_fb_palette palette;
68
69 u32 *palette_buffer;
70 u32 pseudo_palette[16];
71 unsigned int index;
72};
73
74/**
75 * struct s3c_fb - overall hardware state of the hardware
76 * @dev: The device that we bound to, for printing, etc.
77 * @regs_res: The resource we claimed for the IO registers.
78 * @bus_clk: The clk (hclk) feeding our interface and possibly pixclk.
79 * @regs: The mapped hardware registers.
80 * @enabled: A bitmask of enabled hardware windows.
81 * @pdata: The platform configuration data passed with the device.
82 * @windows: The hardware windows that have been claimed.
83 */
84struct s3c_fb {
85 struct device *dev;
86 struct resource *regs_res;
87 struct clk *bus_clk;
88 void __iomem *regs;
89
90 unsigned char enabled;
91
92 struct s3c_fb_platdata *pdata;
93 struct s3c_fb_win *windows[S3C_FB_MAX_WIN];
94};
95
96/**
97 * s3c_fb_win_has_palette() - determine if a mode has a palette
98 * @win: The window number being queried.
99 * @bpp: The number of bits per pixel to test.
100 *
101 * Work out if the given window supports palletised data at the specified bpp.
102 */
103static int s3c_fb_win_has_palette(unsigned int win, unsigned int bpp)
104{
105 return s3c_fb_win_pal_size(win) <= (1 << bpp);
106}
107
108/**
109 * s3c_fb_check_var() - framebuffer layer request to verify a given mode.
110 * @var: The screen information to verify.
111 * @info: The framebuffer device.
112 *
113 * Framebuffer layer call to verify the given information and allow us to
114 * update various information depending on the hardware capabilities.
115 */
116static int s3c_fb_check_var(struct fb_var_screeninfo *var,
117 struct fb_info *info)
118{
119 struct s3c_fb_win *win = info->par;
120 struct s3c_fb_pd_win *windata = win->windata;
121 struct s3c_fb *sfb = win->parent;
122
123 dev_dbg(sfb->dev, "checking parameters\n");
124
125 var->xres_virtual = max((unsigned int)windata->virtual_x, var->xres);
126 var->yres_virtual = max((unsigned int)windata->virtual_y, var->yres);
127
128 if (!s3c_fb_validate_win_bpp(win->index, var->bits_per_pixel)) {
129 dev_dbg(sfb->dev, "win %d: unsupported bpp %d\n",
130 win->index, var->bits_per_pixel);
131 return -EINVAL;
132 }
133
134 /* always ensure these are zero, for drop through cases below */
135 var->transp.offset = 0;
136 var->transp.length = 0;
137
138 switch (var->bits_per_pixel) {
139 case 1:
140 case 2:
141 case 4:
142 case 8:
143 if (!s3c_fb_win_has_palette(win->index, var->bits_per_pixel)) {
144 /* non palletised, A:1,R:2,G:3,B:2 mode */
145 var->red.offset = 4;
146 var->green.offset = 2;
147 var->blue.offset = 0;
148 var->red.length = 5;
149 var->green.length = 3;
150 var->blue.length = 2;
151 var->transp.offset = 7;
152 var->transp.length = 1;
153 } else {
154 var->red.offset = 0;
155 var->red.length = var->bits_per_pixel;
156 var->green = var->red;
157 var->blue = var->red;
158 }
159 break;
160
161 case 19:
162 /* 666 with one bit alpha/transparency */
163 var->transp.offset = 18;
164 var->transp.length = 1;
165 case 18:
166 var->bits_per_pixel = 32;
167
168 /* 666 format */
169 var->red.offset = 12;
170 var->green.offset = 6;
171 var->blue.offset = 0;
172 var->red.length = 6;
173 var->green.length = 6;
174 var->blue.length = 6;
175 break;
176
177 case 16:
178 /* 16 bpp, 565 format */
179 var->red.offset = 11;
180 var->green.offset = 5;
181 var->blue.offset = 0;
182 var->red.length = 5;
183 var->green.length = 6;
184 var->blue.length = 5;
185 break;
186
187 case 28:
188 case 25:
189 var->transp.length = var->bits_per_pixel - 24;
190 var->transp.offset = 24;
191 /* drop through */
192 case 24:
193 /* our 24bpp is unpacked, so 32bpp */
194 var->bits_per_pixel = 32;
195 case 32:
196 var->red.offset = 16;
197 var->red.length = 8;
198 var->green.offset = 8;
199 var->green.length = 8;
200 var->blue.offset = 0;
201 var->blue.length = 8;
202 break;
203
204 default:
205 dev_err(sfb->dev, "invalid bpp\n");
206 }
207
208 dev_dbg(sfb->dev, "%s: verified parameters\n", __func__);
209 return 0;
210}
211
212/**
213 * s3c_fb_calc_pixclk() - calculate the divider to create the pixel clock.
214 * @sfb: The hardware state.
215 * @pixclock: The pixel clock wanted, in picoseconds.
216 *
217 * Given the specified pixel clock, work out the necessary divider to get
218 * close to the output frequency.
219 */
220static int s3c_fb_calc_pixclk(struct s3c_fb *sfb, unsigned int pixclk)
221{
222 unsigned long clk = clk_get_rate(sfb->bus_clk);
223 unsigned long long tmp;
224 unsigned int result;
225
226 tmp = (unsigned long long)clk;
227 tmp *= pixclk;
228
229 do_div(tmp, 1000000000UL);
230 result = (unsigned int)tmp / 1000;
231
232 dev_dbg(sfb->dev, "pixclk=%u, clk=%lu, div=%d (%lu)\n",
233 pixclk, clk, result, clk / result);
234
235 return result;
236}
237
238/**
239 * s3c_fb_align_word() - align pixel count to word boundary
240 * @bpp: The number of bits per pixel
241 * @pix: The value to be aligned.
242 *
243 * Align the given pixel count so that it will start on an 32bit word
244 * boundary.
245 */
246static int s3c_fb_align_word(unsigned int bpp, unsigned int pix)
247{
248 int pix_per_word;
249
250 if (bpp > 16)
251 return pix;
252
253 pix_per_word = (8 * 32) / bpp;
254 return ALIGN(pix, pix_per_word);
255}
256
257/**
258 * s3c_fb_set_par() - framebuffer request to set new framebuffer state.
259 * @info: The framebuffer to change.
260 *
261 * Framebuffer layer request to set a new mode for the specified framebuffer
262 */
263static int s3c_fb_set_par(struct fb_info *info)
264{
265 struct fb_var_screeninfo *var = &info->var;
266 struct s3c_fb_win *win = info->par;
267 struct s3c_fb *sfb = win->parent;
268 void __iomem *regs = sfb->regs;
269 int win_no = win->index;
270 u32 data;
271 u32 pagewidth;
272 int clkdiv;
273
274 dev_dbg(sfb->dev, "setting framebuffer parameters\n");
275
276 switch (var->bits_per_pixel) {
277 case 32:
278 case 24:
279 case 16:
280 case 12:
281 info->fix.visual = FB_VISUAL_TRUECOLOR;
282 break;
283 case 8:
284 if (s3c_fb_win_has_palette(win_no, 8))
285 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
286 else
287 info->fix.visual = FB_VISUAL_TRUECOLOR;
288 break;
289 case 1:
290 info->fix.visual = FB_VISUAL_MONO01;
291 break;
292 default:
293 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
294 break;
295 }
296
297 info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
298
299 /* disable the window whilst we update it */
300 writel(0, regs + WINCON(win_no));
301
302 /* use window 0 as the basis for the lcd output timings */
303
304 if (win_no == 0) {
305 clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock);
306
307 data = sfb->pdata->vidcon0;
308 data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
309
310 if (clkdiv > 1)
311 data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR;
312 else
313 data &= ~VIDCON0_CLKDIR; /* 1:1 clock */
314
315 /* write the timing data to the panel */
316
317 data |= VIDCON0_ENVID | VIDCON0_ENVID_F;
318 writel(data, regs + VIDCON0);
319
320 data = VIDTCON0_VBPD(var->upper_margin - 1) |
321 VIDTCON0_VFPD(var->lower_margin - 1) |
322 VIDTCON0_VSPW(var->vsync_len - 1);
323
324 writel(data, regs + VIDTCON0);
325
326 data = VIDTCON1_HBPD(var->left_margin - 1) |
327 VIDTCON1_HFPD(var->right_margin - 1) |
328 VIDTCON1_HSPW(var->hsync_len - 1);
329
330 writel(data, regs + VIDTCON1);
331
332 data = VIDTCON2_LINEVAL(var->yres - 1) |
333 VIDTCON2_HOZVAL(var->xres - 1);
334 writel(data, regs + VIDTCON2);
335 }
336
337 /* write the buffer address */
338
339 writel(info->fix.smem_start, regs + VIDW_BUF_START(win_no));
340
341 data = info->fix.smem_start + info->fix.line_length * var->yres;
342 writel(data, regs + VIDW_BUF_END(win_no));
343
344 pagewidth = (var->xres * var->bits_per_pixel) >> 3;
345 data = VIDW_BUF_SIZE_OFFSET(info->fix.line_length - pagewidth) |
346 VIDW_BUF_SIZE_PAGEWIDTH(pagewidth);
347 writel(data, regs + VIDW_BUF_SIZE(win_no));
348
349 /* write 'OSD' registers to control position of framebuffer */
350
351 data = VIDOSDxA_TOPLEFT_X(0) | VIDOSDxA_TOPLEFT_Y(0);
352 writel(data, regs + VIDOSD_A(win_no));
353
354 data = VIDOSDxB_BOTRIGHT_X(s3c_fb_align_word(var->bits_per_pixel,
355 var->xres - 1)) |
356 VIDOSDxB_BOTRIGHT_Y(var->yres - 1);
357
358 writel(data, regs + VIDOSD_B(win_no));
359
360 data = var->xres * var->yres;
361 if (s3c_fb_has_osd_d(win_no)) {
362 writel(data, regs + VIDOSD_D(win_no));
363 writel(0, regs + VIDOSD_C(win_no));
364 } else
365 writel(data, regs + VIDOSD_C(win_no));
366
367 data = WINCONx_ENWIN;
368
369 /* note, since we have to round up the bits-per-pixel, we end up
370 * relying on the bitfield information for r/g/b/a to work out
371 * exactly which mode of operation is intended. */
372
373 switch (var->bits_per_pixel) {
374 case 1:
375 data |= WINCON0_BPPMODE_1BPP;
376 data |= WINCONx_BITSWP;
377 data |= WINCONx_BURSTLEN_4WORD;
378 break;
379 case 2:
380 data |= WINCON0_BPPMODE_2BPP;
381 data |= WINCONx_BITSWP;
382 data |= WINCONx_BURSTLEN_8WORD;
383 break;
384 case 4:
385 data |= WINCON0_BPPMODE_4BPP;
386 data |= WINCONx_BITSWP;
387 data |= WINCONx_BURSTLEN_8WORD;
388 break;
389 case 8:
390 if (var->transp.length != 0)
391 data |= WINCON1_BPPMODE_8BPP_1232;
392 else
393 data |= WINCON0_BPPMODE_8BPP_PALETTE;
394 data |= WINCONx_BURSTLEN_8WORD;
395 data |= WINCONx_BYTSWP;
396 break;
397 case 16:
398 if (var->transp.length != 0)
399 data |= WINCON1_BPPMODE_16BPP_A1555;
400 else
401 data |= WINCON0_BPPMODE_16BPP_565;
402 data |= WINCONx_HAWSWP;
403 data |= WINCONx_BURSTLEN_16WORD;
404 break;
405 case 24:
406 case 32:
407 if (var->red.length == 6) {
408 if (var->transp.length != 0)
409 data |= WINCON1_BPPMODE_19BPP_A1666;
410 else
411 data |= WINCON1_BPPMODE_18BPP_666;
412 } else if (var->transp.length != 0)
413 data |= WINCON1_BPPMODE_25BPP_A1888;
414 else
415 data |= WINCON0_BPPMODE_24BPP_888;
416
417 data |= WINCONx_BURSTLEN_16WORD;
418 break;
419 }
420
421 writel(data, regs + WINCON(win_no));
422 writel(0x0, regs + WINxMAP(win_no));
423
424 return 0;
425}
426
427/**
428 * s3c_fb_update_palette() - set or schedule a palette update.
429 * @sfb: The hardware information.
430 * @win: The window being updated.
431 * @reg: The palette index being changed.
432 * @value: The computed palette value.
433 *
434 * Change the value of a palette register, either by directly writing to
435 * the palette (this requires the palette RAM to be disconnected from the
436 * hardware whilst this is in progress) or schedule the update for later.
437 *
438 * At the moment, since we have no VSYNC interrupt support, we simply set
439 * the palette entry directly.
440 */
441static void s3c_fb_update_palette(struct s3c_fb *sfb,
442 struct s3c_fb_win *win,
443 unsigned int reg,
444 u32 value)
445{
446 void __iomem *palreg;
447 u32 palcon;
448
449 palreg = sfb->regs + s3c_fb_pal_reg(win->index, reg);
450
451 dev_dbg(sfb->dev, "%s: win %d, reg %d (%p): %08x\n",
452 __func__, win->index, reg, palreg, value);
453
454 win->palette_buffer[reg] = value;
455
456 palcon = readl(sfb->regs + WPALCON);
457 writel(palcon | WPALCON_PAL_UPDATE, sfb->regs + WPALCON);
458
459 if (s3c_fb_pal_is16(win->index))
460 writew(value, palreg);
461 else
462 writel(value, palreg);
463
464 writel(palcon, sfb->regs + WPALCON);
465}
466
467static inline unsigned int chan_to_field(unsigned int chan,
468 struct fb_bitfield *bf)
469{
470 chan &= 0xffff;
471 chan >>= 16 - bf->length;
472 return chan << bf->offset;
473}
474
475/**
476 * s3c_fb_setcolreg() - framebuffer layer request to change palette.
477 * @regno: The palette index to change.
478 * @red: The red field for the palette data.
479 * @green: The green field for the palette data.
480 * @blue: The blue field for the palette data.
481 * @trans: The transparency (alpha) field for the palette data.
482 * @info: The framebuffer being changed.
483 */
484static int s3c_fb_setcolreg(unsigned regno,
485 unsigned red, unsigned green, unsigned blue,
486 unsigned transp, struct fb_info *info)
487{
488 struct s3c_fb_win *win = info->par;
489 struct s3c_fb *sfb = win->parent;
490 unsigned int val;
491
492 dev_dbg(sfb->dev, "%s: win %d: %d => rgb=%d/%d/%d\n",
493 __func__, win->index, regno, red, green, blue);
494
495 switch (info->fix.visual) {
496 case FB_VISUAL_TRUECOLOR:
497 /* true-colour, use pseudo-palette */
498
499 if (regno < 16) {
500 u32 *pal = info->pseudo_palette;
501
502 val = chan_to_field(red, &info->var.red);
503 val |= chan_to_field(green, &info->var.green);
504 val |= chan_to_field(blue, &info->var.blue);
505
506 pal[regno] = val;
507 }
508 break;
509
510 case FB_VISUAL_PSEUDOCOLOR:
511 if (regno < s3c_fb_win_pal_size(win->index)) {
512 val = chan_to_field(red, &win->palette.r);
513 val |= chan_to_field(green, &win->palette.g);
514 val |= chan_to_field(blue, &win->palette.b);
515
516 s3c_fb_update_palette(sfb, win, regno, val);
517 }
518
519 break;
520
521 default:
522 return 1; /* unknown type */
523 }
524
525 return 0;
526}
527
528/**
529 * s3c_fb_enable() - Set the state of the main LCD output
530 * @sfb: The main framebuffer state.
531 * @enable: The state to set.
532 */
533static void s3c_fb_enable(struct s3c_fb *sfb, int enable)
534{
535 u32 vidcon0 = readl(sfb->regs + VIDCON0);
536
537 if (enable)
538 vidcon0 |= VIDCON0_ENVID | VIDCON0_ENVID_F;
539 else {
540 /* see the note in the framebuffer datasheet about
541 * why you cannot take both of these bits down at the
542 * same time. */
543
544 if (!(vidcon0 & VIDCON0_ENVID))
545 return;
546
547 vidcon0 |= VIDCON0_ENVID;
548 vidcon0 &= ~VIDCON0_ENVID_F;
549 }
550
551 writel(vidcon0, sfb->regs + VIDCON0);
552}
553
554/**
555 * s3c_fb_blank() - blank or unblank the given window
556 * @blank_mode: The blank state from FB_BLANK_*
557 * @info: The framebuffer to blank.
558 *
559 * Framebuffer layer request to change the power state.
560 */
561static int s3c_fb_blank(int blank_mode, struct fb_info *info)
562{
563 struct s3c_fb_win *win = info->par;
564 struct s3c_fb *sfb = win->parent;
565 unsigned int index = win->index;
566 u32 wincon;
567
568 dev_dbg(sfb->dev, "blank mode %d\n", blank_mode);
569
570 wincon = readl(sfb->regs + WINCON(index));
571
572 switch (blank_mode) {
573 case FB_BLANK_POWERDOWN:
574 wincon &= ~WINCONx_ENWIN;
575 sfb->enabled &= ~(1 << index);
576 /* fall through to FB_BLANK_NORMAL */
577
578 case FB_BLANK_NORMAL:
579 /* disable the DMA and display 0x0 (black) */
580 writel(WINxMAP_MAP | WINxMAP_MAP_COLOUR(0x0),
581 sfb->regs + WINxMAP(index));
582 break;
583
584 case FB_BLANK_UNBLANK:
585 writel(0x0, sfb->regs + WINxMAP(index));
586 wincon |= WINCONx_ENWIN;
587 sfb->enabled |= (1 << index);
588 break;
589
590 case FB_BLANK_VSYNC_SUSPEND:
591 case FB_BLANK_HSYNC_SUSPEND:
592 default:
593 return 1;
594 }
595
596 writel(wincon, sfb->regs + WINCON(index));
597
598 /* Check the enabled state to see if we need to be running the
599 * main LCD interface, as if there are no active windows then
600 * it is highly likely that we also do not need to output
601 * anything.
602 */
603
604 /* We could do something like the following code, but the current
605 * system of using framebuffer events means that we cannot make
606 * the distinction between just window 0 being inactive and all
607 * the windows being down.
608 *
609 * s3c_fb_enable(sfb, sfb->enabled ? 1 : 0);
610 */
611
612 /* we're stuck with this until we can do something about overriding
613 * the power control using the blanking event for a single fb.
614 */
615 if (index == 0)
616 s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0);
617
618 return 0;
619}
620
621static struct fb_ops s3c_fb_ops = {
622 .owner = THIS_MODULE,
623 .fb_check_var = s3c_fb_check_var,
624 .fb_set_par = s3c_fb_set_par,
625 .fb_blank = s3c_fb_blank,
626 .fb_setcolreg = s3c_fb_setcolreg,
627 .fb_fillrect = cfb_fillrect,
628 .fb_copyarea = cfb_copyarea,
629 .fb_imageblit = cfb_imageblit,
630};
631
632/**
633 * s3c_fb_alloc_memory() - allocate display memory for framebuffer window
634 * @sfb: The base resources for the hardware.
635 * @win: The window to initialise memory for.
636 *
637 * Allocate memory for the given framebuffer.
638 */
639static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb,
640 struct s3c_fb_win *win)
641{
642 struct s3c_fb_pd_win *windata = win->windata;
643 unsigned int real_size, virt_size, size;
644 struct fb_info *fbi = win->fbinfo;
645 dma_addr_t map_dma;
646
647 dev_dbg(sfb->dev, "allocating memory for display\n");
648
649 real_size = windata->win_mode.xres * windata->win_mode.yres;
650 virt_size = windata->virtual_x * windata->virtual_y;
651
652 dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n",
653 real_size, windata->win_mode.xres, windata->win_mode.yres,
654 virt_size, windata->virtual_x, windata->virtual_y);
655
656 size = (real_size > virt_size) ? real_size : virt_size;
657 size *= (windata->max_bpp > 16) ? 32 : windata->max_bpp;
658 size /= 8;
659
660 fbi->fix.smem_len = size;
661 size = PAGE_ALIGN(size);
662
663 dev_dbg(sfb->dev, "want %u bytes for window\n", size);
664
665 fbi->screen_base = dma_alloc_writecombine(sfb->dev, size,
666 &map_dma, GFP_KERNEL);
667 if (!fbi->screen_base)
668 return -ENOMEM;
669
670 dev_dbg(sfb->dev, "mapped %x to %p\n",
671 (unsigned int)map_dma, fbi->screen_base);
672
673 memset(fbi->screen_base, 0x0, size);
674 fbi->fix.smem_start = map_dma;
675
676 return 0;
677}
678
679/**
680 * s3c_fb_free_memory() - free the display memory for the given window
681 * @sfb: The base resources for the hardware.
682 * @win: The window to free the display memory for.
683 *
684 * Free the display memory allocated by s3c_fb_alloc_memory().
685 */
686static void s3c_fb_free_memory(struct s3c_fb *sfb, struct s3c_fb_win *win)
687{
688 struct fb_info *fbi = win->fbinfo;
689
690 dma_free_writecombine(sfb->dev, PAGE_ALIGN(fbi->fix.smem_len),
691 fbi->screen_base, fbi->fix.smem_start);
692}
693
694/**
695 * s3c_fb_release_win() - release resources for a framebuffer window.
696 * @win: The window to cleanup the resources for.
697 *
698 * Release the resources that where claimed for the hardware window,
699 * such as the framebuffer instance and any memory claimed for it.
700 */
701static void s3c_fb_release_win(struct s3c_fb *sfb, struct s3c_fb_win *win)
702{
703 fb_dealloc_cmap(&win->fbinfo->cmap);
704 unregister_framebuffer(win->fbinfo);
705 s3c_fb_free_memory(sfb, win);
706}
707
708/**
709 * s3c_fb_probe_win() - register an hardware window
710 * @sfb: The base resources for the hardware
711 * @res: Pointer to where to place the resultant window.
712 *
713 * Allocate and do the basic initialisation for one of the hardware's graphics
714 * windows.
715 */
716static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no,
717 struct s3c_fb_win **res)
718{
719 struct fb_var_screeninfo *var;
720 struct fb_videomode *initmode;
721 struct s3c_fb_pd_win *windata;
722 struct s3c_fb_win *win;
723 struct fb_info *fbinfo;
724 int palette_size;
725 int ret;
726
727 dev_dbg(sfb->dev, "probing window %d\n", win_no);
728
729 palette_size = s3c_fb_win_pal_size(win_no);
730
731 fbinfo = framebuffer_alloc(sizeof(struct s3c_fb_win) +
732 palette_size * sizeof(u32), sfb->dev);
733 if (!fbinfo) {
734 dev_err(sfb->dev, "failed to allocate framebuffer\n");
735 return -ENOENT;
736 }
737
738 windata = sfb->pdata->win[win_no];
739 initmode = &windata->win_mode;
740
741 WARN_ON(windata->max_bpp == 0);
742 WARN_ON(windata->win_mode.xres == 0);
743 WARN_ON(windata->win_mode.yres == 0);
744
745 win = fbinfo->par;
746 var = &fbinfo->var;
747 win->fbinfo = fbinfo;
748 win->parent = sfb;
749 win->windata = windata;
750 win->index = win_no;
751 win->palette_buffer = (u32 *)(win + 1);
752
753 ret = s3c_fb_alloc_memory(sfb, win);
754 if (ret) {
755 dev_err(sfb->dev, "failed to allocate display memory\n");
756 goto err_framebuffer;
757 }
758
759 /* setup the r/b/g positions for the window's palette */
760 s3c_fb_init_palette(win_no, &win->palette);
761
762 /* setup the initial video mode from the window */
763 fb_videomode_to_var(&fbinfo->var, initmode);
764
765 fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
766 fbinfo->fix.accel = FB_ACCEL_NONE;
767 fbinfo->var.activate = FB_ACTIVATE_NOW;
768 fbinfo->var.vmode = FB_VMODE_NONINTERLACED;
769 fbinfo->var.bits_per_pixel = windata->default_bpp;
770 fbinfo->fbops = &s3c_fb_ops;
771 fbinfo->flags = FBINFO_FLAG_DEFAULT;
772 fbinfo->pseudo_palette = &win->pseudo_palette;
773
774 /* prepare to actually start the framebuffer */
775
776 ret = s3c_fb_check_var(&fbinfo->var, fbinfo);
777 if (ret < 0) {
778 dev_err(sfb->dev, "check_var failed on initial video params\n");
779 goto err_alloc_mem;
780 }
781
782 /* create initial colour map */
783
784 ret = fb_alloc_cmap(&fbinfo->cmap, s3c_fb_win_pal_size(win_no), 1);
785 if (ret == 0)
786 fb_set_cmap(&fbinfo->cmap, fbinfo);
787 else
788 dev_err(sfb->dev, "failed to allocate fb cmap\n");
789
790 s3c_fb_set_par(fbinfo);
791
792 dev_dbg(sfb->dev, "about to register framebuffer\n");
793
794 /* run the check_var and set_par on our configuration. */
795
796 ret = register_framebuffer(fbinfo);
797 if (ret < 0) {
798 dev_err(sfb->dev, "failed to register framebuffer\n");
799 goto err_alloc_mem;
800 }
801
802 *res = win;
803 dev_info(sfb->dev, "window %d: fb %s\n", win_no, fbinfo->fix.id);
804
805 return 0;
806
807err_alloc_mem:
808 s3c_fb_free_memory(sfb, win);
809
810err_framebuffer:
811 unregister_framebuffer(fbinfo);
812 return ret;
813}
814
815/**
816 * s3c_fb_clear_win() - clear hardware window registers.
817 * @sfb: The base resources for the hardware.
818 * @win: The window to process.
819 *
820 * Reset the specific window registers to a known state.
821 */
822static void s3c_fb_clear_win(struct s3c_fb *sfb, int win)
823{
824 void __iomem *regs = sfb->regs;
825
826 writel(0, regs + WINCON(win));
827 writel(0xffffff, regs + WxKEYCONy(win, 0));
828 writel(0xffffff, regs + WxKEYCONy(win, 1));
829
830 writel(0, regs + VIDOSD_A(win));
831 writel(0, regs + VIDOSD_B(win));
832 writel(0, regs + VIDOSD_C(win));
833}
834
835static int __devinit s3c_fb_probe(struct platform_device *pdev)
836{
837 struct device *dev = &pdev->dev;
838 struct s3c_fb_platdata *pd;
839 struct s3c_fb *sfb;
840 struct resource *res;
841 int win;
842 int ret = 0;
843
844 pd = pdev->dev.platform_data;
845 if (!pd) {
846 dev_err(dev, "no platform data specified\n");
847 return -EINVAL;
848 }
849
850 sfb = kzalloc(sizeof(struct s3c_fb), GFP_KERNEL);
851 if (!sfb) {
852 dev_err(dev, "no memory for framebuffers\n");
853 return -ENOMEM;
854 }
855
856 sfb->dev = dev;
857 sfb->pdata = pd;
858
859 sfb->bus_clk = clk_get(dev, "lcd");
860 if (IS_ERR(sfb->bus_clk)) {
861 dev_err(dev, "failed to get bus clock\n");
862 goto err_sfb;
863 }
864
865 clk_enable(sfb->bus_clk);
866
867 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
868 if (!res) {
869 dev_err(dev, "failed to find registers\n");
870 ret = -ENOENT;
871 goto err_clk;
872 }
873
874 sfb->regs_res = request_mem_region(res->start, resource_size(res),
875 dev_name(dev));
876 if (!sfb->regs_res) {
877 dev_err(dev, "failed to claim register region\n");
878 ret = -ENOENT;
879 goto err_clk;
880 }
881
882 sfb->regs = ioremap(res->start, resource_size(res));
883 if (!sfb->regs) {
884 dev_err(dev, "failed to map registers\n");
885 ret = -ENXIO;
886 goto err_req_region;
887 }
888
889 dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs);
890
891 /* setup gpio and output polarity controls */
892
893 pd->setup_gpio();
894
895 writel(pd->vidcon1, sfb->regs + VIDCON1);
896
897 /* zero all windows before we do anything */
898
899 for (win = 0; win < S3C_FB_MAX_WIN; win++)
900 s3c_fb_clear_win(sfb, win);
901
902 /* we have the register setup, start allocating framebuffers */
903
904 for (win = 0; win < S3C_FB_MAX_WIN; win++) {
905 if (!pd->win[win])
906 continue;
907
908 ret = s3c_fb_probe_win(sfb, win, &sfb->windows[win]);
909 if (ret < 0) {
910 dev_err(dev, "failed to create window %d\n", win);
911 for (; win >= 0; win--)
912 s3c_fb_release_win(sfb, sfb->windows[win]);
913 goto err_ioremap;
914 }
915 }
916
917 platform_set_drvdata(pdev, sfb);
918
919 return 0;
920
921err_ioremap:
922 iounmap(sfb->regs);
923
924err_req_region:
925 release_resource(sfb->regs_res);
926 kfree(sfb->regs_res);
927
928err_clk:
929 clk_disable(sfb->bus_clk);
930 clk_put(sfb->bus_clk);
931
932err_sfb:
933 kfree(sfb);
934 return ret;
935}
936
937/**
938 * s3c_fb_remove() - Cleanup on module finalisation
939 * @pdev: The platform device we are bound to.
940 *
941 * Shutdown and then release all the resources that the driver allocated
942 * on initialisation.
943 */
944static int __devexit s3c_fb_remove(struct platform_device *pdev)
945{
946 struct s3c_fb *sfb = platform_get_drvdata(pdev);
947 int win;
948
949 for (win = 0; win <= S3C_FB_MAX_WIN; win++)
950 s3c_fb_release_win(sfb, sfb->windows[win]);
951
952 iounmap(sfb->regs);
953
954 clk_disable(sfb->bus_clk);
955 clk_put(sfb->bus_clk);
956
957 release_resource(sfb->regs_res);
958 kfree(sfb->regs_res);
959
960 kfree(sfb);
961
962 return 0;
963}
964
965#ifdef CONFIG_PM
966static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state)
967{
968 struct s3c_fb *sfb = platform_get_drvdata(pdev);
969 struct s3c_fb_win *win;
970 int win_no;
971
972 for (win_no = S3C_FB_MAX_WIN; win_no >= 0; win_no--) {
973 win = sfb->windows[win_no];
974 if (!win)
975 continue;
976
977 /* use the blank function to push into power-down */
978 s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo);
979 }
980
981 clk_disable(sfb->bus_clk);
982 return 0;
983}
984
985static int s3c_fb_resume(struct platform_device *pdev)
986{
987 struct s3c_fb *sfb = platform_get_drvdata(pdev);
988 struct s3c_fb_win *win;
989 int win_no;
990
991 clk_enable(sfb->bus_clk);
992
993 for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) {
994 win = sfb->windows[win_no];
995 if (!win)
996 continue;
997
998 dev_dbg(&pdev->dev, "resuming window %d\n", win_no);
999 s3c_fb_set_par(win->fbinfo);
1000 }
1001
1002 return 0;
1003}
1004#else
1005#define s3c_fb_suspend NULL
1006#define s3c_fb_resume NULL
1007#endif
1008
1009static struct platform_driver s3c_fb_driver = {
1010 .probe = s3c_fb_probe,
1011 .remove = s3c_fb_remove,
1012 .suspend = s3c_fb_suspend,
1013 .resume = s3c_fb_resume,
1014 .driver = {
1015 .name = "s3c-fb",
1016 .owner = THIS_MODULE,
1017 },
1018};
1019
1020static int __init s3c_fb_init(void)
1021{
1022 return platform_driver_register(&s3c_fb_driver);
1023}
1024
1025static void __exit s3c_fb_cleanup(void)
1026{
1027 platform_driver_unregister(&s3c_fb_driver);
1028}
1029
1030module_init(s3c_fb_init);
1031module_exit(s3c_fb_cleanup);
1032
1033MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
1034MODULE_DESCRIPTION("Samsung S3C SoC Framebuffer driver");
1035MODULE_LICENSE("GPL");
1036MODULE_ALIAS("platform:s3c-fb");