1 /* linux/drivers/video/sm501fb.c
3 * Copyright (c) 2006 Simtec Electronics
4 * Vincent Sanders <vince@simtec.co.uk>
5 * Ben Dooks <ben@simtec.co.uk>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * Framebuffer driver for the Silicon Motion SM501
14 #include <linux/module.h>
15 #include <linux/kernel.h>
16 #include <linux/errno.h>
17 #include <linux/string.h>
19 #include <linux/tty.h>
20 #include <linux/slab.h>
21 #include <linux/delay.h>
23 #include <linux/init.h>
24 #include <linux/vmalloc.h>
25 #include <linux/dma-mapping.h>
26 #include <linux/interrupt.h>
27 #include <linux/workqueue.h>
28 #include <linux/wait.h>
29 #include <linux/platform_device.h>
30 #include <linux/clk.h>
31 #include <linux/console.h>
34 #include <asm/uaccess.h>
35 #include <asm/div64.h>
41 #include <linux/sm501.h>
42 #include <linux/sm501-regs.h>
44 #define NR_PALETTE 256
46 enum sm501_controller
{
51 /* SM501 memory address */
54 unsigned long sm_addr
;
58 /* private data that is shared between all frambuffers* */
61 struct fb_info
*fb
[2]; /* fb info for both heads */
62 struct resource
*fbmem_res
; /* framebuffer resource */
63 struct resource
*regs_res
; /* registers resource */
64 struct sm501_platdata_fb
*pdata
; /* our platform data */
66 unsigned long pm_crt_ctrl
; /* pm: crt ctrl save */
69 int swap_endian
; /* set to swap rgb=>bgr */
70 void __iomem
*regs
; /* remapped registers */
71 void __iomem
*fbmem
; /* remapped framebuffer */
72 size_t fbmem_len
; /* length of remapped region */
75 /* per-framebuffer private data */
77 u32 pseudo_palette
[16];
79 enum sm501_controller head
;
80 struct sm501_mem cursor
;
81 struct sm501_mem screen
;
86 void __iomem
*cursor_regs
;
87 struct sm501fb_info
*info
;
90 /* Helper functions */
92 static inline int h_total(struct fb_var_screeninfo
*var
)
94 return var
->xres
+ var
->left_margin
+
95 var
->right_margin
+ var
->hsync_len
;
98 static inline int v_total(struct fb_var_screeninfo
*var
)
100 return var
->yres
+ var
->upper_margin
+
101 var
->lower_margin
+ var
->vsync_len
;
104 /* sm501fb_sync_regs()
106 * This call is mainly for PCI bus systems where we need to
107 * ensure that any writes to the bus are completed before the
108 * next phase, or after completing a function.
111 static inline void sm501fb_sync_regs(struct sm501fb_info
*info
)
118 * This is an attempt to lay out memory for the two framebuffers and
121 * |fbmem_res->start fbmem_res->end|
123 * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K |
124 * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-|
126 * The "spare" space is for the 2d engine data
127 * the fixed is space for the cursors (2x1Kbyte)
129 * we need to allocate memory for the 2D acceleration engine
130 * command list and the data for the engine to deal with.
132 * - all allocations must be 128bit aligned
133 * - cursors are 64x64x2 bits (1Kbyte)
137 #define SM501_MEMF_CURSOR (1)
138 #define SM501_MEMF_PANEL (2)
139 #define SM501_MEMF_CRT (4)
140 #define SM501_MEMF_ACCEL (8)
142 static int sm501_alloc_mem(struct sm501fb_info
*inf
, struct sm501_mem
*mem
,
143 unsigned int why
, size_t size
)
145 unsigned int ptr
= 0;
148 case SM501_MEMF_CURSOR
:
149 ptr
= inf
->fbmem_len
- size
;
150 inf
->fbmem_len
= ptr
;
153 case SM501_MEMF_PANEL
:
154 ptr
= inf
->fbmem_len
- size
;
155 if (ptr
< inf
->fb
[0]->fix
.smem_len
)
164 case SM501_MEMF_ACCEL
:
165 ptr
= inf
->fb
[0]->fix
.smem_len
;
168 (inf
->fb
[1]->fix
.smem_start
- inf
->fbmem_res
->start
))
178 mem
->k_addr
= inf
->fbmem
+ ptr
;
180 dev_dbg(inf
->dev
, "%s: result %08lx, %p - %u, %zd\n",
181 __func__
, mem
->sm_addr
, mem
->k_addr
, why
, size
);
188 * Converts a period in picoseconds to Hz.
190 * Note, we try to keep this in Hz to minimise rounding with
191 * the limited PLL settings on the SM501.
194 static unsigned long sm501fb_ps_to_hz(unsigned long psvalue
)
196 unsigned long long numerator
=1000000000000ULL;
198 /* 10^12 / picosecond period gives frequency in Hz */
199 do_div(numerator
, psvalue
);
200 return (unsigned long)numerator
;
203 /* sm501fb_hz_to_ps is identical to the oposite transform */
205 #define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x)
207 /* sm501fb_setup_gamma
209 * Programs a linear 1.0 gamma ramp in case the gamma
210 * correction is enabled without programming anything else.
213 static void sm501fb_setup_gamma(struct sm501fb_info
*fbi
,
214 unsigned long palette
)
216 unsigned long value
= 0;
219 /* set gamma values */
220 for (offset
= 0; offset
< 256 * 4; offset
+= 4) {
221 writel(value
, fbi
->regs
+ palette
+ offset
);
222 value
+= 0x010101; /* Advance RGB by 1,1,1.*/
228 * check common variables for both panel and crt
231 static int sm501fb_check_var(struct fb_var_screeninfo
*var
,
232 struct fb_info
*info
)
234 struct sm501fb_par
*par
= info
->par
;
235 struct sm501fb_info
*sm
= par
->info
;
238 /* check we can fit these values into the registers */
240 <<<<<<< HEAD
:drivers
/video
/sm501fb
.c
241 if (var
->hsync_len
> 255 || var
->vsync_len
> 255)
243 if (var
->hsync_len
> 255 || var
->vsync_len
> 63)
244 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a
:drivers
/video
/sm501fb
.c
247 <<<<<<< HEAD
:drivers
/video
/sm501fb
.c
248 if ((var
->xres
+ var
->right_margin
) >= 4096)
250 /* hdisplay end and hsync start */
251 if ((var
->xres
+ var
->right_margin
) > 4096)
252 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a
:drivers
/video
/sm501fb
.c
255 <<<<<<< HEAD
:drivers
/video
/sm501fb
.c
257 /* vdisplay end and vsync start */
258 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a
:drivers
/video
/sm501fb
.c
259 if ((var
->yres
+ var
->lower_margin
) > 2048)
262 /* hard limits of device */
264 if (h_total(var
) > 4096 || v_total(var
) > 2048)
267 /* check our line length is going to be 128 bit aligned */
269 tmp
= (var
->xres
* var
->bits_per_pixel
) / 8;
273 /* check the virtual size */
275 if (var
->xres_virtual
> 4096 || var
->yres_virtual
> 2048)
278 /* can cope with 8,16 or 32bpp */
280 if (var
->bits_per_pixel
<= 8)
281 var
->bits_per_pixel
= 8;
282 else if (var
->bits_per_pixel
<= 16)
283 var
->bits_per_pixel
= 16;
284 else if (var
->bits_per_pixel
== 24)
285 var
->bits_per_pixel
= 32;
287 /* set r/g/b positions and validate bpp */
288 switch(var
->bits_per_pixel
) {
290 var
->red
.length
= var
->bits_per_pixel
;
292 var
->green
.length
= var
->bits_per_pixel
;
293 var
->green
.offset
= 0;
294 var
->blue
.length
= var
->bits_per_pixel
;
295 var
->blue
.offset
= 0;
296 var
->transp
.length
= 0;
297 <<<<<<< HEAD
:drivers
/video
/sm501fb
.c
299 var
->transp
.offset
= 0;
300 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a
:drivers
/video
/sm501fb
.c
305 if (sm
->pdata
->flags
& SM501_FBPD_SWAP_FB_ENDIAN
) {
306 <<<<<<< HEAD
:drivers
/video
/sm501fb
.c
307 var
->red
.offset
= 11;
308 var
->green
.offset
= 5;
309 var
->blue
.offset
= 0;
312 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a
:drivers
/video
/sm501fb
.c
313 var
->blue
.offset
= 11;
314 var
->green
.offset
= 5;
316 <<<<<<< HEAD
:drivers
/video
/sm501fb
.c
319 var
->red
.offset
= 11;
320 var
->green
.offset
= 5;
321 var
->blue
.offset
= 0;
322 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a
:drivers
/video
/sm501fb
.c
324 <<<<<<< HEAD
:drivers
/video
/sm501fb
.c
326 var
->transp
.offset
= 0;
327 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a
:drivers
/video
/sm501fb
.c
330 var
->green
.length
= 6;
331 var
->blue
.length
= 5;
332 var
->transp
.length
= 0;
336 if (sm
->pdata
->flags
& SM501_FBPD_SWAP_FB_ENDIAN
) {
337 var
->transp
.offset
= 0;
339 var
->green
.offset
= 16;
340 var
->blue
.offset
= 24;
342 var
->transp
.offset
= 24;
343 var
->red
.offset
= 16;
344 var
->green
.offset
= 8;
345 var
->blue
.offset
= 0;
349 var
->green
.length
= 8;
350 var
->blue
.length
= 8;
351 var
->transp
.length
= 0;
362 * sm501fb_check_var_crt():
364 * check the parameters for the CRT head, and either bring them
365 * back into range, or return -EINVAL.
368 static int sm501fb_check_var_crt(struct fb_var_screeninfo
*var
,
369 struct fb_info
*info
)
371 return sm501fb_check_var(var
, info
);
374 /* sm501fb_check_var_pnl():
376 * check the parameters for the CRT head, and either bring them
377 * back into range, or return -EINVAL.
380 static int sm501fb_check_var_pnl(struct fb_var_screeninfo
*var
,
381 struct fb_info
*info
)
383 return sm501fb_check_var(var
, info
);
386 /* sm501fb_set_par_common
388 * set common registers for framebuffers
391 static int sm501fb_set_par_common(struct fb_info
*info
,
392 struct fb_var_screeninfo
*var
)
394 struct sm501fb_par
*par
= info
->par
;
395 struct sm501fb_info
*fbi
= par
->info
;
396 unsigned long pixclock
; /* pixelclock in Hz */
397 unsigned long sm501pixclock
; /* pixelclock the 501 can achive in Hz */
398 unsigned int mem_type
;
399 unsigned int clock_type
;
400 unsigned int head_addr
;
402 dev_dbg(fbi
->dev
, "%s: %dx%d, bpp = %d, virtual %dx%d\n",
403 __func__
, var
->xres
, var
->yres
, var
->bits_per_pixel
,
404 var
->xres_virtual
, var
->yres_virtual
);
408 mem_type
= SM501_MEMF_CRT
;
409 clock_type
= SM501_CLOCK_V2XCLK
;
410 head_addr
= SM501_DC_CRT_FB_ADDR
;
414 mem_type
= SM501_MEMF_PANEL
;
415 clock_type
= SM501_CLOCK_P2XCLK
;
416 head_addr
= SM501_DC_PANEL_FB_ADDR
;
420 mem_type
= 0; /* stop compiler warnings */
425 switch (var
->bits_per_pixel
) {
427 info
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
431 <<<<<<< HEAD
:drivers
/video
/sm501fb
.c
432 info
->fix
.visual
= FB_VISUAL_DIRECTCOLOR
;
434 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
435 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a
:drivers
/video
/sm501fb
.c
439 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
443 /* allocate fb memory within 501 */
444 info
->fix
.line_length
= (var
->xres_virtual
* var
->bits_per_pixel
)/8;
445 info
->fix
.smem_len
= info
->fix
.line_length
* var
->yres_virtual
;
447 dev_dbg(fbi
->dev
, "%s: line length = %u\n", __func__
,
448 info
->fix
.line_length
);
450 if (sm501_alloc_mem(fbi
, &par
->screen
, mem_type
,
451 info
->fix
.smem_len
)) {
452 dev_err(fbi
->dev
, "no memory available\n");
456 info
->fix
.smem_start
= fbi
->fbmem_res
->start
+ par
->screen
.sm_addr
;
458 info
->screen_base
= fbi
->fbmem
+ par
->screen
.sm_addr
;
459 info
->screen_size
= info
->fix
.smem_len
;
461 /* set start of framebuffer to the screen */
463 writel(par
->screen
.sm_addr
| SM501_ADDR_FLIP
, fbi
->regs
+ head_addr
);
465 /* program CRT clock */
467 pixclock
= sm501fb_ps_to_hz(var
->pixclock
);
469 sm501pixclock
= sm501_set_clock(fbi
->dev
->parent
, clock_type
,
472 /* update fb layer with actual clock used */
473 var
->pixclock
= sm501fb_hz_to_ps(sm501pixclock
);
475 dev_dbg(fbi
->dev
, "%s: pixclock(ps) = %u, pixclock(Hz) = %lu, "
476 "sm501pixclock = %lu, error = %ld%%\n",
477 __func__
, var
->pixclock
, pixclock
, sm501pixclock
,
478 ((pixclock
- sm501pixclock
)*100)/pixclock
);
483 /* sm501fb_set_par_geometry
485 * set the geometry registers for specified framebuffer.
488 static void sm501fb_set_par_geometry(struct fb_info
*info
,
489 struct fb_var_screeninfo
*var
)
491 struct sm501fb_par
*par
= info
->par
;
492 struct sm501fb_info
*fbi
= par
->info
;
493 void __iomem
*base
= fbi
->regs
;
496 if (par
->head
== HEAD_CRT
)
497 base
+= SM501_DC_CRT_H_TOT
;
499 base
+= SM501_DC_PANEL_H_TOT
;
501 /* set framebuffer width and display width */
503 reg
= info
->fix
.line_length
;
504 reg
|= ((var
->xres
* var
->bits_per_pixel
)/8) << 16;
506 writel(reg
, fbi
->regs
+ (par
->head
== HEAD_CRT
?
507 SM501_DC_CRT_FB_OFFSET
: SM501_DC_PANEL_FB_OFFSET
));
509 /* program horizontal total */
511 reg
= (h_total(var
) - 1) << 16;
512 reg
|= (var
->xres
- 1);
514 writel(reg
, base
+ SM501_OFF_DC_H_TOT
);
516 /* program horizontal sync */
518 reg
= var
->hsync_len
<< 16;
519 reg
|= var
->xres
+ var
->right_margin
- 1;
521 writel(reg
, base
+ SM501_OFF_DC_H_SYNC
);
523 /* program vertical total */
525 reg
= (v_total(var
) - 1) << 16;
526 reg
|= (var
->yres
- 1);
528 writel(reg
, base
+ SM501_OFF_DC_V_TOT
);
530 /* program vertical sync */
531 reg
= var
->vsync_len
<< 16;
532 reg
|= var
->yres
+ var
->lower_margin
- 1;
534 writel(reg
, base
+ SM501_OFF_DC_V_SYNC
);
539 * pan the CRT display output within an virtual framebuffer
542 static int sm501fb_pan_crt(struct fb_var_screeninfo
*var
,
543 struct fb_info
*info
)
545 struct sm501fb_par
*par
= info
->par
;
546 struct sm501fb_info
*fbi
= par
->info
;
547 unsigned int bytes_pixel
= var
->bits_per_pixel
/ 8;
551 xoffs
= var
->xoffset
* bytes_pixel
;
553 reg
= readl(fbi
->regs
+ SM501_DC_CRT_CONTROL
);
555 reg
&= ~SM501_DC_CRT_CONTROL_PIXEL_MASK
;
556 reg
|= ((xoffs
& 15) / bytes_pixel
) << 4;
557 writel(reg
, fbi
->regs
+ SM501_DC_CRT_CONTROL
);
559 reg
= (par
->screen
.sm_addr
+ xoffs
+
560 var
->yoffset
* info
->fix
.line_length
);
561 writel(reg
| SM501_ADDR_FLIP
, fbi
->regs
+ SM501_DC_CRT_FB_ADDR
);
563 sm501fb_sync_regs(fbi
);
569 * pan the panel display output within an virtual framebuffer
572 static int sm501fb_pan_pnl(struct fb_var_screeninfo
*var
,
573 struct fb_info
*info
)
575 struct sm501fb_par
*par
= info
->par
;
576 struct sm501fb_info
*fbi
= par
->info
;
579 reg
= var
->xoffset
| (var
->xres_virtual
<< 16);
580 writel(reg
, fbi
->regs
+ SM501_DC_PANEL_FB_WIDTH
);
582 reg
= var
->yoffset
| (var
->yres_virtual
<< 16);
583 writel(reg
, fbi
->regs
+ SM501_DC_PANEL_FB_HEIGHT
);
585 sm501fb_sync_regs(fbi
);
589 /* sm501fb_set_par_crt
591 * Set the CRT video mode from the fb_info structure
594 static int sm501fb_set_par_crt(struct fb_info
*info
)
596 struct sm501fb_par
*par
= info
->par
;
597 struct sm501fb_info
*fbi
= par
->info
;
598 struct fb_var_screeninfo
*var
= &info
->var
;
599 unsigned long control
; /* control register */
602 /* activate new configuration */
604 dev_dbg(fbi
->dev
, "%s(%p)\n", __func__
, info
);
606 /* enable CRT DAC - note 0 is on!*/
607 sm501_misc_control(fbi
->dev
->parent
, 0, SM501_MISC_DAC_POWER
);
609 control
= readl(fbi
->regs
+ SM501_DC_CRT_CONTROL
);
611 control
&= (SM501_DC_CRT_CONTROL_PIXEL_MASK
|
612 SM501_DC_CRT_CONTROL_GAMMA
|
613 SM501_DC_CRT_CONTROL_BLANK
|
614 SM501_DC_CRT_CONTROL_SEL
|
615 SM501_DC_CRT_CONTROL_CP
|
616 SM501_DC_CRT_CONTROL_TVP
);
618 /* set the sync polarities before we check data source */
620 if ((var
->sync
& FB_SYNC_HOR_HIGH_ACT
) == 0)
621 control
|= SM501_DC_CRT_CONTROL_HSP
;
623 if ((var
->sync
& FB_SYNC_VERT_HIGH_ACT
) == 0)
624 control
|= SM501_DC_CRT_CONTROL_VSP
;
626 if ((control
& SM501_DC_CRT_CONTROL_SEL
) == 0) {
627 /* the head is displaying panel data... */
629 sm501_alloc_mem(fbi
, &par
->screen
, SM501_MEMF_CRT
, 0);
633 ret
= sm501fb_set_par_common(info
, var
);
635 dev_err(fbi
->dev
, "failed to set common parameters\n");
639 sm501fb_pan_crt(var
, info
);
640 sm501fb_set_par_geometry(info
, var
);
642 control
|= SM501_FIFO_3
; /* fill if >3 free slots */
644 switch(var
->bits_per_pixel
) {
646 control
|= SM501_DC_CRT_CONTROL_8BPP
;
650 control
|= SM501_DC_CRT_CONTROL_16BPP
;
651 <<<<<<< HEAD
:drivers
/video
/sm501fb
.c
653 sm501fb_setup_gamma(fbi
, SM501_DC_CRT_PALETTE
);
654 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a
:drivers
/video
/sm501fb
.c
658 control
|= SM501_DC_CRT_CONTROL_32BPP
;
659 sm501fb_setup_gamma(fbi
, SM501_DC_CRT_PALETTE
);
666 control
|= SM501_DC_CRT_CONTROL_SEL
; /* CRT displays CRT data */
667 control
|= SM501_DC_CRT_CONTROL_TE
; /* enable CRT timing */
668 control
|= SM501_DC_CRT_CONTROL_ENABLE
; /* enable CRT plane */
671 dev_dbg(fbi
->dev
, "new control is %08lx\n", control
);
673 writel(control
, fbi
->regs
+ SM501_DC_CRT_CONTROL
);
674 sm501fb_sync_regs(fbi
);
679 static void sm501fb_panel_power(struct sm501fb_info
*fbi
, int to
)
681 unsigned long control
;
682 void __iomem
*ctrl_reg
= fbi
->regs
+ SM501_DC_PANEL_CONTROL
;
683 struct sm501_platdata_fbsub
*pd
= fbi
->pdata
->fb_pnl
;
685 control
= readl(ctrl_reg
);
687 if (to
&& (control
& SM501_DC_PANEL_CONTROL_VDD
) == 0) {
688 /* enable panel power */
690 control
|= SM501_DC_PANEL_CONTROL_VDD
; /* FPVDDEN */
691 writel(control
, ctrl_reg
);
692 sm501fb_sync_regs(fbi
);
695 control
|= SM501_DC_PANEL_CONTROL_DATA
; /* DATA */
696 writel(control
, ctrl_reg
);
697 sm501fb_sync_regs(fbi
);
700 if (pd
->flags
& SM501FB_FLAG_PANEL_USE_VBIASEN
) {
701 control
|= SM501_DC_PANEL_CONTROL_BIAS
; /* VBIASEN */
702 writel(control
, ctrl_reg
);
703 sm501fb_sync_regs(fbi
);
707 if (pd
->flags
& SM501FB_FLAG_PANEL_USE_FPEN
) {
708 control
|= SM501_DC_PANEL_CONTROL_FPEN
;
709 writel(control
, ctrl_reg
);
710 sm501fb_sync_regs(fbi
);
713 } else if (!to
&& (control
& SM501_DC_PANEL_CONTROL_VDD
) != 0) {
714 /* disable panel power */
715 if (pd
->flags
& SM501FB_FLAG_PANEL_USE_FPEN
) {
716 control
&= ~SM501_DC_PANEL_CONTROL_FPEN
;
717 writel(control
, ctrl_reg
);
718 sm501fb_sync_regs(fbi
);
722 if (pd
->flags
& SM501FB_FLAG_PANEL_USE_VBIASEN
) {
723 control
&= ~SM501_DC_PANEL_CONTROL_BIAS
;
724 writel(control
, ctrl_reg
);
725 sm501fb_sync_regs(fbi
);
729 control
&= ~SM501_DC_PANEL_CONTROL_DATA
;
730 writel(control
, ctrl_reg
);
731 sm501fb_sync_regs(fbi
);
734 control
&= ~SM501_DC_PANEL_CONTROL_VDD
;
735 writel(control
, ctrl_reg
);
736 sm501fb_sync_regs(fbi
);
740 sm501fb_sync_regs(fbi
);
743 /* sm501fb_set_par_pnl
745 * Set the panel video mode from the fb_info structure
748 static int sm501fb_set_par_pnl(struct fb_info
*info
)
750 struct sm501fb_par
*par
= info
->par
;
751 struct sm501fb_info
*fbi
= par
->info
;
752 struct fb_var_screeninfo
*var
= &info
->var
;
753 unsigned long control
;
757 dev_dbg(fbi
->dev
, "%s(%p)\n", __func__
, info
);
759 /* activate this new configuration */
761 ret
= sm501fb_set_par_common(info
, var
);
765 sm501fb_pan_pnl(var
, info
);
766 sm501fb_set_par_geometry(info
, var
);
768 /* update control register */
770 control
= readl(fbi
->regs
+ SM501_DC_PANEL_CONTROL
);
771 control
&= (SM501_DC_PANEL_CONTROL_GAMMA
|
772 SM501_DC_PANEL_CONTROL_VDD
|
773 SM501_DC_PANEL_CONTROL_DATA
|
774 SM501_DC_PANEL_CONTROL_BIAS
|
775 SM501_DC_PANEL_CONTROL_FPEN
|
776 SM501_DC_PANEL_CONTROL_CP
|
777 SM501_DC_PANEL_CONTROL_CK
|
778 SM501_DC_PANEL_CONTROL_HP
|
779 SM501_DC_PANEL_CONTROL_VP
|
780 SM501_DC_PANEL_CONTROL_HPD
|
781 SM501_DC_PANEL_CONTROL_VPD
);
783 control
|= SM501_FIFO_3
; /* fill if >3 free slots */
785 switch(var
->bits_per_pixel
) {
787 control
|= SM501_DC_PANEL_CONTROL_8BPP
;
791 control
|= SM501_DC_PANEL_CONTROL_16BPP
;
792 <<<<<<< HEAD
:drivers
/video
/sm501fb
.c
794 sm501fb_setup_gamma(fbi
, SM501_DC_PANEL_PALETTE
);
795 >>>>>>> 264e3e889d86e552b4191d69bb60f4f3b383135a
:drivers
/video
/sm501fb
.c
799 control
|= SM501_DC_PANEL_CONTROL_32BPP
;
800 sm501fb_setup_gamma(fbi
, SM501_DC_PANEL_PALETTE
);
807 writel(0x0, fbi
->regs
+ SM501_DC_PANEL_PANNING_CONTROL
);
809 /* panel plane top left and bottom right location */
811 writel(0x00, fbi
->regs
+ SM501_DC_PANEL_TL_LOC
);
814 reg
|= (var
->yres
- 1) << 16;
816 writel(reg
, fbi
->regs
+ SM501_DC_PANEL_BR_LOC
);
818 /* program panel control register */
820 control
|= SM501_DC_PANEL_CONTROL_TE
; /* enable PANEL timing */
821 control
|= SM501_DC_PANEL_CONTROL_EN
; /* enable PANEL gfx plane */
823 if ((var
->sync
& FB_SYNC_HOR_HIGH_ACT
) == 0)
824 control
|= SM501_DC_PANEL_CONTROL_HSP
;
826 if ((var
->sync
& FB_SYNC_VERT_HIGH_ACT
) == 0)
827 control
|= SM501_DC_PANEL_CONTROL_VSP
;
829 writel(control
, fbi
->regs
+ SM501_DC_PANEL_CONTROL
);
830 sm501fb_sync_regs(fbi
);
832 /* ensure the panel interface is not tristated at this point */
834 sm501_modify_reg(fbi
->dev
->parent
, SM501_SYSTEM_CONTROL
,
835 0, SM501_SYSCTRL_PANEL_TRISTATE
);
837 /* power the panel up */
838 sm501fb_panel_power(fbi
, 1);
845 * convert a colour value into a field position
850 static inline unsigned int chan_to_field(unsigned int chan
,
851 struct fb_bitfield
*bf
)
854 chan
>>= 16 - bf
->length
;
855 return chan
<< bf
->offset
;
860 * set the colour mapping for modes that support palettised data
863 static int sm501fb_setcolreg(unsigned regno
,
864 unsigned red
, unsigned green
, unsigned blue
,
865 unsigned transp
, struct fb_info
*info
)
867 struct sm501fb_par
*par
= info
->par
;
868 struct sm501fb_info
*fbi
= par
->info
;
869 void __iomem
*base
= fbi
->regs
;
872 if (par
->head
== HEAD_CRT
)
873 base
+= SM501_DC_CRT_PALETTE
;
875 base
+= SM501_DC_PANEL_PALETTE
;
877 switch (info
->fix
.visual
) {
878 case FB_VISUAL_TRUECOLOR
:
879 /* true-colour, use pseuo-palette */
882 u32
*pal
= par
->pseudo_palette
;
884 val
= chan_to_field(red
, &info
->var
.red
);
885 val
|= chan_to_field(green
, &info
->var
.green
);
886 val
|= chan_to_field(blue
, &info
->var
.blue
);
892 case FB_VISUAL_PSEUDOCOLOR
:
894 val
= (red
>> 8) << 16;
895 val
|= (green
>> 8) << 8;
898 writel(val
, base
+ (regno
* 4));
904 return 1; /* unknown type */
912 * Blank or un-blank the panel interface
915 static int sm501fb_blank_pnl(int blank_mode
, struct fb_info
*info
)
917 struct sm501fb_par
*par
= info
->par
;
918 struct sm501fb_info
*fbi
= par
->info
;
920 dev_dbg(fbi
->dev
, "%s(mode=%d, %p)\n", __func__
, blank_mode
, info
);
922 switch (blank_mode
) {
923 case FB_BLANK_POWERDOWN
:
924 sm501fb_panel_power(fbi
, 0);
927 case FB_BLANK_UNBLANK
:
928 sm501fb_panel_power(fbi
, 1);
931 case FB_BLANK_NORMAL
:
932 case FB_BLANK_VSYNC_SUSPEND
:
933 case FB_BLANK_HSYNC_SUSPEND
:
943 * Blank or un-blank the crt interface
946 static int sm501fb_blank_crt(int blank_mode
, struct fb_info
*info
)
948 struct sm501fb_par
*par
= info
->par
;
949 struct sm501fb_info
*fbi
= par
->info
;
952 dev_dbg(fbi
->dev
, "%s(mode=%d, %p)\n", __func__
, blank_mode
, info
);
954 ctrl
= readl(fbi
->regs
+ SM501_DC_CRT_CONTROL
);
956 switch (blank_mode
) {
957 case FB_BLANK_POWERDOWN
:
958 ctrl
&= ~SM501_DC_CRT_CONTROL_ENABLE
;
959 sm501_misc_control(fbi
->dev
->parent
, SM501_MISC_DAC_POWER
, 0);
961 case FB_BLANK_NORMAL
:
962 ctrl
|= SM501_DC_CRT_CONTROL_BLANK
;
965 case FB_BLANK_UNBLANK
:
966 ctrl
&= ~SM501_DC_CRT_CONTROL_BLANK
;
967 ctrl
|= SM501_DC_CRT_CONTROL_ENABLE
;
968 sm501_misc_control(fbi
->dev
->parent
, 0, SM501_MISC_DAC_POWER
);
971 case FB_BLANK_VSYNC_SUSPEND
:
972 case FB_BLANK_HSYNC_SUSPEND
:
978 writel(ctrl
, fbi
->regs
+ SM501_DC_CRT_CONTROL
);
979 sm501fb_sync_regs(fbi
);
986 * set or change the hardware cursor parameters
989 static int sm501fb_cursor(struct fb_info
*info
, struct fb_cursor
*cursor
)
991 struct sm501fb_par
*par
= info
->par
;
992 struct sm501fb_info
*fbi
= par
->info
;
993 void __iomem
*base
= fbi
->regs
;
994 unsigned long hwc_addr
;
995 unsigned long fg
, bg
;
997 dev_dbg(fbi
->dev
, "%s(%p,%p)\n", __func__
, info
, cursor
);
999 if (par
->head
== HEAD_CRT
)
1000 base
+= SM501_DC_CRT_HWC_BASE
;
1002 base
+= SM501_DC_PANEL_HWC_BASE
;
1004 /* check not being asked to exceed capabilities */
1006 if (cursor
->image
.width
> 64)
1009 if (cursor
->image
.height
> 64)
1012 if (cursor
->image
.depth
> 1)
1015 hwc_addr
= readl(base
+ SM501_OFF_HWC_ADDR
);
1018 writel(hwc_addr
| SM501_HWC_EN
, base
+ SM501_OFF_HWC_ADDR
);
1020 writel(hwc_addr
& ~SM501_HWC_EN
, base
+ SM501_OFF_HWC_ADDR
);
1023 if (cursor
->set
& FB_CUR_SETPOS
) {
1024 unsigned int x
= cursor
->image
.dx
;
1025 unsigned int y
= cursor
->image
.dy
;
1027 if (x
>= 2048 || y
>= 2048 )
1030 dev_dbg(fbi
->dev
, "set position %d,%d\n", x
, y
);
1032 //y += cursor->image.height;
1034 writel(x
| (y
<< 16), base
+ SM501_OFF_HWC_LOC
);
1037 if (cursor
->set
& FB_CUR_SETCMAP
) {
1038 unsigned int bg_col
= cursor
->image
.bg_color
;
1039 unsigned int fg_col
= cursor
->image
.fg_color
;
1041 dev_dbg(fbi
->dev
, "%s: update cmap (%08x,%08x)\n",
1042 __func__
, bg_col
, fg_col
);
1044 bg
= ((info
->cmap
.red
[bg_col
] & 0xF8) << 8) |
1045 ((info
->cmap
.green
[bg_col
] & 0xFC) << 3) |
1046 ((info
->cmap
.blue
[bg_col
] & 0xF8) >> 3);
1048 fg
= ((info
->cmap
.red
[fg_col
] & 0xF8) << 8) |
1049 ((info
->cmap
.green
[fg_col
] & 0xFC) << 3) |
1050 ((info
->cmap
.blue
[fg_col
] & 0xF8) >> 3);
1052 dev_dbg(fbi
->dev
, "fgcol %08lx, bgcol %08lx\n", fg
, bg
);
1054 writel(bg
, base
+ SM501_OFF_HWC_COLOR_1_2
);
1055 writel(fg
, base
+ SM501_OFF_HWC_COLOR_3
);
1058 if (cursor
->set
& FB_CUR_SETSIZE
||
1059 cursor
->set
& (FB_CUR_SETIMAGE
| FB_CUR_SETSHAPE
)) {
1060 /* SM501 cursor is a two bpp 64x64 bitmap this routine
1061 * clears it to transparent then combines the cursor
1062 * shape plane with the colour plane to set the
1065 const unsigned char *pcol
= cursor
->image
.data
;
1066 const unsigned char *pmsk
= cursor
->mask
;
1067 void __iomem
*dst
= par
->cursor
.k_addr
;
1068 unsigned char dcol
= 0;
1069 unsigned char dmsk
= 0;
1072 dev_dbg(fbi
->dev
, "%s: setting shape (%d,%d)\n",
1073 __func__
, cursor
->image
.width
, cursor
->image
.height
);
1075 for (op
= 0; op
< (64*64*2)/8; op
+=4)
1076 writel(0x0, dst
+ op
);
1078 for (y
= 0; y
< cursor
->image
.height
; y
++) {
1079 for (x
= 0; x
< cursor
->image
.width
; x
++) {
1089 op
= (dcol
& 1) ? 1 : 3;
1090 op
<<= ((x
% 4) * 2);
1092 op
|= readb(dst
+ (x
/ 4));
1093 writeb(op
, dst
+ (x
/ 4));
1100 sm501fb_sync_regs(fbi
); /* ensure cursor data flushed */
1104 /* sm501fb_crtsrc_show
1106 * device attribute code to show where the crt output is sourced from
1109 static ssize_t
sm501fb_crtsrc_show(struct device
*dev
,
1110 struct device_attribute
*attr
, char *buf
)
1112 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1115 ctrl
= readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1116 ctrl
&= SM501_DC_CRT_CONTROL_SEL
;
1118 return snprintf(buf
, PAGE_SIZE
, "%s\n", ctrl
? "crt" : "panel");
1121 /* sm501fb_crtsrc_show
1123 * device attribute code to set where the crt output is sourced from
1126 static ssize_t
sm501fb_crtsrc_store(struct device
*dev
,
1127 struct device_attribute
*attr
,
1128 const char *buf
, size_t len
)
1130 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1131 enum sm501_controller head
;
1137 if (strnicmp(buf
, "crt", 3) == 0)
1139 else if (strnicmp(buf
, "panel", 5) == 0)
1144 dev_info(dev
, "setting crt source to head %d\n", head
);
1146 ctrl
= readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1148 if (head
== HEAD_CRT
) {
1149 ctrl
|= SM501_DC_CRT_CONTROL_SEL
;
1150 ctrl
|= SM501_DC_CRT_CONTROL_ENABLE
;
1151 ctrl
|= SM501_DC_CRT_CONTROL_TE
;
1153 ctrl
&= ~SM501_DC_CRT_CONTROL_SEL
;
1154 ctrl
&= ~SM501_DC_CRT_CONTROL_ENABLE
;
1155 ctrl
&= ~SM501_DC_CRT_CONTROL_TE
;
1158 writel(ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1159 sm501fb_sync_regs(info
);
1164 /* Prepare the device_attr for registration with sysfs later */
1165 static DEVICE_ATTR(crt_src
, 0666, sm501fb_crtsrc_show
, sm501fb_crtsrc_store
);
1167 /* sm501fb_show_regs
1169 * show the primary sm501 registers
1171 static int sm501fb_show_regs(struct sm501fb_info
*info
, char *ptr
,
1172 unsigned int start
, unsigned int len
)
1174 void __iomem
*mem
= info
->regs
;
1178 for (reg
= start
; reg
< (len
+ start
); reg
+= 4)
1179 ptr
+= sprintf(ptr
, "%08x = %08x\n", reg
, readl(mem
+ reg
));
1184 /* sm501fb_debug_show_crt
1186 * show the crt control and cursor registers
1189 static ssize_t
sm501fb_debug_show_crt(struct device
*dev
,
1190 struct device_attribute
*attr
, char *buf
)
1192 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1195 ptr
+= sm501fb_show_regs(info
, ptr
, SM501_DC_CRT_CONTROL
, 0x40);
1196 ptr
+= sm501fb_show_regs(info
, ptr
, SM501_DC_CRT_HWC_BASE
, 0x10);
1201 static DEVICE_ATTR(fbregs_crt
, 0444, sm501fb_debug_show_crt
, NULL
);
1203 /* sm501fb_debug_show_pnl
1205 * show the panel control and cursor registers
1208 static ssize_t
sm501fb_debug_show_pnl(struct device
*dev
,
1209 struct device_attribute
*attr
, char *buf
)
1211 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1214 ptr
+= sm501fb_show_regs(info
, ptr
, 0x0, 0x40);
1215 ptr
+= sm501fb_show_regs(info
, ptr
, SM501_DC_PANEL_HWC_BASE
, 0x10);
1220 static DEVICE_ATTR(fbregs_pnl
, 0444, sm501fb_debug_show_pnl
, NULL
);
1222 /* framebuffer ops */
1224 static struct fb_ops sm501fb_ops_crt
= {
1225 .owner
= THIS_MODULE
,
1226 .fb_check_var
= sm501fb_check_var_crt
,
1227 .fb_set_par
= sm501fb_set_par_crt
,
1228 .fb_blank
= sm501fb_blank_crt
,
1229 .fb_setcolreg
= sm501fb_setcolreg
,
1230 .fb_pan_display
= sm501fb_pan_crt
,
1231 .fb_cursor
= sm501fb_cursor
,
1232 .fb_fillrect
= cfb_fillrect
,
1233 .fb_copyarea
= cfb_copyarea
,
1234 .fb_imageblit
= cfb_imageblit
,
1237 static struct fb_ops sm501fb_ops_pnl
= {
1238 .owner
= THIS_MODULE
,
1239 .fb_check_var
= sm501fb_check_var_pnl
,
1240 .fb_set_par
= sm501fb_set_par_pnl
,
1241 .fb_pan_display
= sm501fb_pan_pnl
,
1242 .fb_blank
= sm501fb_blank_pnl
,
1243 .fb_setcolreg
= sm501fb_setcolreg
,
1244 .fb_cursor
= sm501fb_cursor
,
1245 .fb_fillrect
= cfb_fillrect
,
1246 .fb_copyarea
= cfb_copyarea
,
1247 .fb_imageblit
= cfb_imageblit
,
1250 /* sm501fb_info_alloc
1252 * creates and initialises an sm501fb_info structure
1255 static struct sm501fb_info
*sm501fb_info_alloc(struct fb_info
*fbinfo_crt
,
1256 struct fb_info
*fbinfo_pnl
)
1258 struct sm501fb_info
*info
;
1259 struct sm501fb_par
*par
;
1261 info
= kzalloc(sizeof(struct sm501fb_info
), GFP_KERNEL
);
1263 /* set the references back */
1265 par
= fbinfo_crt
->par
;
1267 par
->head
= HEAD_CRT
;
1268 fbinfo_crt
->pseudo_palette
= &par
->pseudo_palette
;
1270 par
= fbinfo_pnl
->par
;
1272 par
->head
= HEAD_PANEL
;
1273 fbinfo_pnl
->pseudo_palette
= &par
->pseudo_palette
;
1275 /* store the two fbs into our info */
1276 info
->fb
[HEAD_CRT
] = fbinfo_crt
;
1277 info
->fb
[HEAD_PANEL
] = fbinfo_pnl
;
1283 /* sm501_init_cursor
1285 * initialise hw cursor parameters
1288 static int sm501_init_cursor(struct fb_info
*fbi
, unsigned int reg_base
)
1290 struct sm501fb_par
*par
= fbi
->par
;
1291 struct sm501fb_info
*info
= par
->info
;
1294 par
->cursor_regs
= info
->regs
+ reg_base
;
1296 ret
= sm501_alloc_mem(info
, &par
->cursor
, SM501_MEMF_CURSOR
, 1024);
1300 /* initialise the colour registers */
1302 writel(par
->cursor
.sm_addr
, par
->cursor_regs
+ SM501_OFF_HWC_ADDR
);
1304 writel(0x00, par
->cursor_regs
+ SM501_OFF_HWC_LOC
);
1305 writel(0x00, par
->cursor_regs
+ SM501_OFF_HWC_COLOR_1_2
);
1306 writel(0x00, par
->cursor_regs
+ SM501_OFF_HWC_COLOR_3
);
1307 sm501fb_sync_regs(info
);
1312 /* sm501fb_info_start
1314 * fills the par structure claiming resources and remapping etc.
1317 static int sm501fb_start(struct sm501fb_info
*info
,
1318 struct platform_device
*pdev
)
1320 struct resource
*res
;
1325 info
->dev
= dev
= &pdev
->dev
;
1326 platform_set_drvdata(pdev
, info
);
1328 info
->irq
= ret
= platform_get_irq(pdev
, 0);
1330 /* we currently do not use the IRQ */
1331 dev_warn(dev
, "no irq for device\n");
1334 /* allocate, reserve and remap resources for registers */
1335 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
1337 dev_err(dev
, "no resource definition for registers\n");
1342 info
->regs_res
= request_mem_region(res
->start
,
1343 res
->end
- res
->start
,
1346 if (info
->regs_res
== NULL
) {
1347 dev_err(dev
, "cannot claim registers\n");
1352 info
->regs
= ioremap(res
->start
, (res
->end
- res
->start
)+1);
1353 if (info
->regs
== NULL
) {
1354 dev_err(dev
, "cannot remap registers\n");
1359 /* allocate, reserve resources for framebuffer */
1360 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 2);
1362 dev_err(dev
, "no memory resource defined\n");
1367 info
->fbmem_res
= request_mem_region(res
->start
,
1368 (res
->end
- res
->start
)+1,
1370 if (info
->fbmem_res
== NULL
) {
1371 dev_err(dev
, "cannot claim framebuffer\n");
1376 info
->fbmem
= ioremap(res
->start
, (res
->end
- res
->start
)+1);
1377 if (info
->fbmem
== NULL
) {
1378 dev_err(dev
, "cannot remap framebuffer\n");
1382 info
->fbmem_len
= (res
->end
- res
->start
)+1;
1384 /* clear framebuffer memory - avoids garbage data on unused fb */
1385 memset(info
->fbmem
, 0, info
->fbmem_len
);
1387 /* clear palette ram - undefined at power on */
1388 for (k
= 0; k
< (256 * 3); k
++)
1389 writel(0, info
->regs
+ SM501_DC_PANEL_PALETTE
+ (k
* 4));
1391 /* enable display controller */
1392 sm501_unit_power(dev
->parent
, SM501_GATE_DISPLAY
, 1);
1396 sm501_init_cursor(info
->fb
[HEAD_CRT
], SM501_DC_CRT_HWC_ADDR
);
1397 sm501_init_cursor(info
->fb
[HEAD_PANEL
], SM501_DC_PANEL_HWC_ADDR
);
1399 return 0; /* everything is setup */
1402 release_resource(info
->fbmem_res
);
1403 kfree(info
->fbmem_res
);
1406 iounmap(info
->regs
);
1409 release_resource(info
->regs_res
);
1410 kfree(info
->regs_res
);
1416 static void sm501fb_stop(struct sm501fb_info
*info
)
1418 /* disable display controller */
1419 sm501_unit_power(info
->dev
->parent
, SM501_GATE_DISPLAY
, 0);
1421 iounmap(info
->fbmem
);
1422 release_resource(info
->fbmem_res
);
1423 kfree(info
->fbmem_res
);
1425 iounmap(info
->regs
);
1426 release_resource(info
->regs_res
);
1427 kfree(info
->regs_res
);
1430 static void sm501fb_info_release(struct sm501fb_info
*info
)
1435 static int sm501fb_init_fb(struct fb_info
*fb
,
1436 enum sm501_controller head
,
1439 struct sm501_platdata_fbsub
*pd
;
1440 struct sm501fb_par
*par
= fb
->par
;
1441 struct sm501fb_info
*info
= par
->info
;
1443 unsigned int enable
;
1448 pd
= info
->pdata
->fb_crt
;
1449 ctrl
= readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1450 enable
= (ctrl
& SM501_DC_CRT_CONTROL_ENABLE
) ? 1 : 0;
1452 /* ensure we set the correct source register */
1453 if (info
->pdata
->fb_route
!= SM501_FB_CRT_PANEL
) {
1454 ctrl
|= SM501_DC_CRT_CONTROL_SEL
;
1455 writel(ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1461 pd
= info
->pdata
->fb_pnl
;
1462 ctrl
= readl(info
->regs
+ SM501_DC_PANEL_CONTROL
);
1463 enable
= (ctrl
& SM501_DC_PANEL_CONTROL_EN
) ? 1 : 0;
1467 pd
= NULL
; /* stop compiler warnings */
1473 dev_info(info
->dev
, "fb %s %sabled at start\n",
1474 fbname
, enable
? "en" : "dis");
1476 /* check to see if our routing allows this */
1478 if (head
== HEAD_CRT
&& info
->pdata
->fb_route
== SM501_FB_CRT_PANEL
) {
1479 ctrl
&= ~SM501_DC_CRT_CONTROL_SEL
;
1480 writel(ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1484 strlcpy(fb
->fix
.id
, fbname
, sizeof(fb
->fix
.id
));
1487 (head
== HEAD_CRT
) ? &sm501fb_ops_crt
: &sm501fb_ops_pnl
,
1488 sizeof(struct fb_ops
));
1490 /* update ops dependant on what we've been passed */
1492 if ((pd
->flags
& SM501FB_FLAG_USE_HWCURSOR
) == 0)
1493 par
->ops
.fb_cursor
= NULL
;
1495 fb
->fbops
= &par
->ops
;
1496 fb
->flags
= FBINFO_FLAG_DEFAULT
|
1497 FBINFO_HWACCEL_XPAN
| FBINFO_HWACCEL_YPAN
;
1501 fb
->fix
.type
= FB_TYPE_PACKED_PIXELS
;
1502 fb
->fix
.type_aux
= 0;
1503 fb
->fix
.xpanstep
= 1;
1504 fb
->fix
.ypanstep
= 1;
1505 fb
->fix
.ywrapstep
= 0;
1506 fb
->fix
.accel
= FB_ACCEL_NONE
;
1511 fb
->var
.activate
= FB_ACTIVATE_NOW
;
1512 fb
->var
.accel_flags
= 0;
1513 fb
->var
.vmode
= FB_VMODE_NONINTERLACED
;
1514 fb
->var
.bits_per_pixel
= 16;
1516 if (enable
&& (pd
->flags
& SM501FB_FLAG_USE_INIT_MODE
) && 0) {
1517 /* TODO read the mode from the current display */
1521 dev_info(info
->dev
, "using supplied mode\n");
1522 fb_videomode_to_var(&fb
->var
, pd
->def_mode
);
1524 fb
->var
.bits_per_pixel
= pd
->def_bpp
? pd
->def_bpp
: 8;
1525 fb
->var
.xres_virtual
= fb
->var
.xres
;
1526 fb
->var
.yres_virtual
= fb
->var
.yres
;
1528 ret
= fb_find_mode(&fb
->var
, fb
,
1529 NULL
, NULL
, 0, NULL
, 8);
1531 if (ret
== 0 || ret
== 4) {
1533 "failed to get initial mode\n");
1539 /* initialise and set the palette */
1540 fb_alloc_cmap(&fb
->cmap
, NR_PALETTE
, 0);
1541 fb_set_cmap(&fb
->cmap
, fb
);
1543 ret
= (fb
->fbops
->fb_check_var
)(&fb
->var
, fb
);
1545 dev_err(info
->dev
, "check_var() failed on initial setup?\n");
1547 /* ensure we've activated our new configuration */
1548 (fb
->fbops
->fb_set_par
)(fb
);
1553 /* default platform data if none is supplied (ie, PCI device) */
1555 static struct sm501_platdata_fbsub sm501fb_pdata_crt
= {
1556 .flags
= (SM501FB_FLAG_USE_INIT_MODE
|
1557 SM501FB_FLAG_USE_HWCURSOR
|
1558 SM501FB_FLAG_USE_HWACCEL
|
1559 SM501FB_FLAG_DISABLE_AT_EXIT
),
1563 static struct sm501_platdata_fbsub sm501fb_pdata_pnl
= {
1564 .flags
= (SM501FB_FLAG_USE_INIT_MODE
|
1565 SM501FB_FLAG_USE_HWCURSOR
|
1566 SM501FB_FLAG_USE_HWACCEL
|
1567 SM501FB_FLAG_DISABLE_AT_EXIT
),
1570 static struct sm501_platdata_fb sm501fb_def_pdata
= {
1571 .fb_route
= SM501_FB_OWN
,
1572 .fb_crt
= &sm501fb_pdata_crt
,
1573 .fb_pnl
= &sm501fb_pdata_pnl
,
1576 static char driver_name_crt
[] = "sm501fb-crt";
1577 static char driver_name_pnl
[] = "sm501fb-panel";
1579 static int __init
sm501fb_probe(struct platform_device
*pdev
)
1581 struct sm501fb_info
*info
;
1582 struct device
*dev
= &pdev
->dev
;
1583 struct fb_info
*fbinfo_crt
;
1584 struct fb_info
*fbinfo_pnl
;
1587 /* allocate our framebuffers */
1589 fbinfo_crt
= framebuffer_alloc(sizeof(struct sm501fb_par
), dev
);
1590 if (fbinfo_crt
== NULL
) {
1591 dev_err(dev
, "cannot allocate crt framebuffer\n");
1595 fbinfo_pnl
= framebuffer_alloc(sizeof(struct sm501fb_par
), dev
);
1596 if (fbinfo_pnl
== NULL
) {
1597 dev_err(dev
, "cannot allocate panel framebuffer\n");
1599 goto fbinfo_crt_alloc_fail
;
1602 info
= sm501fb_info_alloc(fbinfo_crt
, fbinfo_pnl
);
1604 dev_err(dev
, "cannot allocate par\n");
1606 goto sm501fb_alloc_fail
;
1609 if (dev
->parent
->platform_data
) {
1610 struct sm501_platdata
*pd
= dev
->parent
->platform_data
;
1611 info
->pdata
= pd
->fb
;
1614 if (info
->pdata
== NULL
) {
1615 dev_info(dev
, "using default configuration data\n");
1616 info
->pdata
= &sm501fb_def_pdata
;
1619 /* start the framebuffers */
1621 ret
= sm501fb_start(info
, pdev
);
1623 dev_err(dev
, "cannot initialise SM501\n");
1624 goto sm501fb_start_fail
;
1627 /* CRT framebuffer setup */
1629 ret
= sm501fb_init_fb(fbinfo_crt
, HEAD_CRT
, driver_name_crt
);
1631 dev_err(dev
, "cannot initialise CRT fb\n");
1632 goto sm501fb_start_fail
;
1635 /* Panel framebuffer setup */
1637 ret
= sm501fb_init_fb(fbinfo_pnl
, HEAD_PANEL
, driver_name_pnl
);
1639 dev_err(dev
, "cannot initialise Panel fb\n");
1640 goto sm501fb_start_fail
;
1643 /* register framebuffers */
1645 ret
= register_framebuffer(fbinfo_crt
);
1647 dev_err(dev
, "failed to register CRT fb (%d)\n", ret
);
1648 goto register_crt_fail
;
1651 ret
= register_framebuffer(fbinfo_pnl
);
1653 dev_err(dev
, "failed to register panel fb (%d)\n", ret
);
1654 goto register_pnl_fail
;
1657 dev_info(dev
, "fb%d: %s frame buffer device\n",
1658 fbinfo_crt
->node
, fbinfo_crt
->fix
.id
);
1660 dev_info(dev
, "fb%d: %s frame buffer device\n",
1661 fbinfo_pnl
->node
, fbinfo_pnl
->fix
.id
);
1663 /* create device files */
1665 ret
= device_create_file(dev
, &dev_attr_crt_src
);
1669 ret
= device_create_file(dev
, &dev_attr_fbregs_pnl
);
1671 goto fbregs_pnl_fail
;
1673 ret
= device_create_file(dev
, &dev_attr_fbregs_crt
);
1675 goto fbregs_crt_fail
;
1677 /* we registered, return ok */
1681 device_remove_file(dev
, &dev_attr_fbregs_pnl
);
1684 device_remove_file(dev
, &dev_attr_crt_src
);
1687 unregister_framebuffer(fbinfo_pnl
);
1690 unregister_framebuffer(fbinfo_crt
);
1696 sm501fb_info_release(info
);
1699 framebuffer_release(fbinfo_pnl
);
1701 fbinfo_crt_alloc_fail
:
1702 framebuffer_release(fbinfo_crt
);
1711 static int sm501fb_remove(struct platform_device
*pdev
)
1713 struct sm501fb_info
*info
= platform_get_drvdata(pdev
);
1714 struct fb_info
*fbinfo_crt
= info
->fb
[0];
1715 struct fb_info
*fbinfo_pnl
= info
->fb
[1];
1717 device_remove_file(&pdev
->dev
, &dev_attr_fbregs_crt
);
1718 device_remove_file(&pdev
->dev
, &dev_attr_fbregs_pnl
);
1719 device_remove_file(&pdev
->dev
, &dev_attr_crt_src
);
1721 unregister_framebuffer(fbinfo_crt
);
1722 unregister_framebuffer(fbinfo_pnl
);
1725 sm501fb_info_release(info
);
1727 framebuffer_release(fbinfo_pnl
);
1728 framebuffer_release(fbinfo_crt
);
1735 static int sm501fb_suspend_fb(struct sm501fb_info
*info
,
1736 enum sm501_controller head
)
1738 struct fb_info
*fbi
= info
->fb
[head
];
1739 struct sm501fb_par
*par
= fbi
->par
;
1741 if (par
->screen
.size
== 0)
1744 /* blank the relevant interface to ensure unit power minimised */
1745 (par
->ops
.fb_blank
)(FB_BLANK_POWERDOWN
, fbi
);
1747 /* tell console/fb driver we are suspending */
1749 acquire_console_sem();
1750 fb_set_suspend(fbi
, 1);
1751 release_console_sem();
1753 /* backup copies in case chip is powered down over suspend */
1755 par
->store_fb
= vmalloc(par
->screen
.size
);
1756 if (par
->store_fb
== NULL
) {
1757 dev_err(info
->dev
, "no memory to store screen\n");
1761 par
->store_cursor
= vmalloc(par
->cursor
.size
);
1762 if (par
->store_cursor
== NULL
) {
1763 dev_err(info
->dev
, "no memory to store cursor\n");
1767 dev_dbg(info
->dev
, "suspending screen to %p\n", par
->store_fb
);
1768 dev_dbg(info
->dev
, "suspending cursor to %p\n", par
->store_cursor
);
1770 memcpy_fromio(par
->store_fb
, par
->screen
.k_addr
, par
->screen
.size
);
1771 memcpy_fromio(par
->store_cursor
, par
->cursor
.k_addr
, par
->cursor
.size
);
1776 vfree(par
->store_fb
);
1777 par
->store_fb
= NULL
;
1782 static void sm501fb_resume_fb(struct sm501fb_info
*info
,
1783 enum sm501_controller head
)
1785 struct fb_info
*fbi
= info
->fb
[head
];
1786 struct sm501fb_par
*par
= fbi
->par
;
1788 if (par
->screen
.size
== 0)
1791 /* re-activate the configuration */
1793 (par
->ops
.fb_set_par
)(fbi
);
1795 /* restore the data */
1797 dev_dbg(info
->dev
, "restoring screen from %p\n", par
->store_fb
);
1798 dev_dbg(info
->dev
, "restoring cursor from %p\n", par
->store_cursor
);
1801 memcpy_toio(par
->screen
.k_addr
, par
->store_fb
,
1804 if (par
->store_cursor
)
1805 memcpy_toio(par
->cursor
.k_addr
, par
->store_cursor
,
1808 acquire_console_sem();
1809 fb_set_suspend(fbi
, 0);
1810 release_console_sem();
1812 vfree(par
->store_fb
);
1813 vfree(par
->store_cursor
);
1817 /* suspend and resume support */
1819 static int sm501fb_suspend(struct platform_device
*pdev
, pm_message_t state
)
1821 struct sm501fb_info
*info
= platform_get_drvdata(pdev
);
1823 /* store crt control to resume with */
1824 info
->pm_crt_ctrl
= readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1826 sm501fb_suspend_fb(info
, HEAD_CRT
);
1827 sm501fb_suspend_fb(info
, HEAD_PANEL
);
1829 /* turn off the clocks, in case the device is not powered down */
1830 sm501_unit_power(info
->dev
->parent
, SM501_GATE_DISPLAY
, 0);
1835 #define SM501_CRT_CTRL_SAVE (SM501_DC_CRT_CONTROL_TVP | \
1836 SM501_DC_CRT_CONTROL_SEL)
1839 static int sm501fb_resume(struct platform_device
*pdev
)
1841 struct sm501fb_info
*info
= platform_get_drvdata(pdev
);
1842 unsigned long crt_ctrl
;
1844 sm501_unit_power(info
->dev
->parent
, SM501_GATE_DISPLAY
, 1);
1846 /* restore the items we want to be saved for crt control */
1848 crt_ctrl
= readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1849 crt_ctrl
&= ~SM501_CRT_CTRL_SAVE
;
1850 crt_ctrl
|= info
->pm_crt_ctrl
& SM501_CRT_CTRL_SAVE
;
1851 writel(crt_ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1853 sm501fb_resume_fb(info
, HEAD_CRT
);
1854 sm501fb_resume_fb(info
, HEAD_PANEL
);
1860 #define sm501fb_suspend NULL
1861 #define sm501fb_resume NULL
1864 static struct platform_driver sm501fb_driver
= {
1865 .probe
= sm501fb_probe
,
1866 .remove
= sm501fb_remove
,
1867 .suspend
= sm501fb_suspend
,
1868 .resume
= sm501fb_resume
,
1871 .owner
= THIS_MODULE
,
1875 static int __devinit
sm501fb_init(void)
1877 return platform_driver_register(&sm501fb_driver
);
1880 static void __exit
sm501fb_cleanup(void)
1882 platform_driver_unregister(&sm501fb_driver
);
1885 module_init(sm501fb_init
);
1886 module_exit(sm501fb_cleanup
);
1888 MODULE_AUTHOR("Ben Dooks, Vincent Sanders");
1889 MODULE_DESCRIPTION("SM501 Framebuffer driver");
1890 MODULE_LICENSE("GPL v2");