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.
53 * This structure is used to track memory usage within the SM501 framebuffer
54 * allocation. The sm_addr field is stored as an offset as it is often used
55 * against both the physical and mapped addresses.
59 unsigned long sm_addr
; /* offset from base of sm501 fb. */
63 /* private data that is shared between all frambuffers* */
66 struct fb_info
*fb
[2]; /* fb info for both heads */
67 struct resource
*fbmem_res
; /* framebuffer resource */
68 struct resource
*regs_res
; /* registers resource */
69 struct sm501_platdata_fb
*pdata
; /* our platform data */
71 unsigned long pm_crt_ctrl
; /* pm: crt ctrl save */
74 int swap_endian
; /* set to swap rgb=>bgr */
75 void __iomem
*regs
; /* remapped registers */
76 void __iomem
*fbmem
; /* remapped framebuffer */
77 size_t fbmem_len
; /* length of remapped region */
80 /* per-framebuffer private data */
82 u32 pseudo_palette
[16];
84 enum sm501_controller head
;
85 struct sm501_mem cursor
;
86 struct sm501_mem screen
;
91 void __iomem
*cursor_regs
;
92 struct sm501fb_info
*info
;
95 /* Helper functions */
97 static inline int h_total(struct fb_var_screeninfo
*var
)
99 return var
->xres
+ var
->left_margin
+
100 var
->right_margin
+ var
->hsync_len
;
103 static inline int v_total(struct fb_var_screeninfo
*var
)
105 return var
->yres
+ var
->upper_margin
+
106 var
->lower_margin
+ var
->vsync_len
;
109 /* sm501fb_sync_regs()
111 * This call is mainly for PCI bus systems where we need to
112 * ensure that any writes to the bus are completed before the
113 * next phase, or after completing a function.
116 static inline void sm501fb_sync_regs(struct sm501fb_info
*info
)
123 * This is an attempt to lay out memory for the two framebuffers and
126 * |fbmem_res->start fbmem_res->end|
128 * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K |
129 * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-|
131 * The "spare" space is for the 2d engine data
132 * the fixed is space for the cursors (2x1Kbyte)
134 * we need to allocate memory for the 2D acceleration engine
135 * command list and the data for the engine to deal with.
137 * - all allocations must be 128bit aligned
138 * - cursors are 64x64x2 bits (1Kbyte)
142 #define SM501_MEMF_CURSOR (1)
143 #define SM501_MEMF_PANEL (2)
144 #define SM501_MEMF_CRT (4)
145 #define SM501_MEMF_ACCEL (8)
147 static int sm501_alloc_mem(struct sm501fb_info
*inf
, struct sm501_mem
*mem
,
148 unsigned int why
, size_t size
)
150 struct sm501fb_par
*par
;
156 case SM501_MEMF_CURSOR
:
157 ptr
= inf
->fbmem_len
- size
;
158 inf
->fbmem_len
= ptr
; /* adjust available memory. */
161 case SM501_MEMF_PANEL
:
162 if (size
> inf
->fbmem_len
)
165 ptr
= inf
->fbmem_len
- size
;
166 fbi
= inf
->fb
[HEAD_CRT
];
168 /* round down, some programs such as directfb do not draw
169 * 0,0 correctly unless the start is aligned to a page start.
173 ptr
&= ~(PAGE_SIZE
- 1);
175 if (fbi
&& ptr
< fbi
->fix
.smem_len
)
183 /* check to see if we have panel memory allocated
184 * which would put an limit on available memory. */
186 fbi
= inf
->fb
[HEAD_PANEL
];
189 end
= par
->screen
.k_addr
? par
->screen
.sm_addr
: inf
->fbmem_len
;
191 end
= inf
->fbmem_len
;
193 if ((ptr
+ size
) > end
)
198 case SM501_MEMF_ACCEL
:
199 fbi
= inf
->fb
[HEAD_CRT
];
200 ptr
= fbi
? fbi
->fix
.smem_len
: 0;
202 fbi
= inf
->fb
[HEAD_PANEL
];
205 end
= par
->screen
.sm_addr
;
207 end
= inf
->fbmem_len
;
209 if ((ptr
+ size
) > end
)
220 mem
->k_addr
= inf
->fbmem
+ ptr
;
222 dev_dbg(inf
->dev
, "%s: result %08lx, %p - %u, %zd\n",
223 __func__
, mem
->sm_addr
, mem
->k_addr
, why
, size
);
230 * Converts a period in picoseconds to Hz.
232 * Note, we try to keep this in Hz to minimise rounding with
233 * the limited PLL settings on the SM501.
236 static unsigned long sm501fb_ps_to_hz(unsigned long psvalue
)
238 unsigned long long numerator
=1000000000000ULL;
240 /* 10^12 / picosecond period gives frequency in Hz */
241 do_div(numerator
, psvalue
);
242 return (unsigned long)numerator
;
245 /* sm501fb_hz_to_ps is identical to the oposite transform */
247 #define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x)
249 /* sm501fb_setup_gamma
251 * Programs a linear 1.0 gamma ramp in case the gamma
252 * correction is enabled without programming anything else.
255 static void sm501fb_setup_gamma(struct sm501fb_info
*fbi
,
256 unsigned long palette
)
258 unsigned long value
= 0;
261 /* set gamma values */
262 for (offset
= 0; offset
< 256 * 4; offset
+= 4) {
263 writel(value
, fbi
->regs
+ palette
+ offset
);
264 value
+= 0x010101; /* Advance RGB by 1,1,1.*/
270 * check common variables for both panel and crt
273 static int sm501fb_check_var(struct fb_var_screeninfo
*var
,
274 struct fb_info
*info
)
276 struct sm501fb_par
*par
= info
->par
;
277 struct sm501fb_info
*sm
= par
->info
;
280 /* check we can fit these values into the registers */
282 if (var
->hsync_len
> 255 || var
->vsync_len
> 63)
285 /* hdisplay end and hsync start */
286 if ((var
->xres
+ var
->right_margin
) > 4096)
289 /* vdisplay end and vsync start */
290 if ((var
->yres
+ var
->lower_margin
) > 2048)
293 /* hard limits of device */
295 if (h_total(var
) > 4096 || v_total(var
) > 2048)
298 /* check our line length is going to be 128 bit aligned */
300 tmp
= (var
->xres
* var
->bits_per_pixel
) / 8;
304 /* check the virtual size */
306 if (var
->xres_virtual
> 4096 || var
->yres_virtual
> 2048)
309 /* can cope with 8,16 or 32bpp */
311 if (var
->bits_per_pixel
<= 8)
312 var
->bits_per_pixel
= 8;
313 else if (var
->bits_per_pixel
<= 16)
314 var
->bits_per_pixel
= 16;
315 else if (var
->bits_per_pixel
== 24)
316 var
->bits_per_pixel
= 32;
318 /* set r/g/b positions and validate bpp */
319 switch(var
->bits_per_pixel
) {
321 var
->red
.length
= var
->bits_per_pixel
;
323 var
->green
.length
= var
->bits_per_pixel
;
324 var
->green
.offset
= 0;
325 var
->blue
.length
= var
->bits_per_pixel
;
326 var
->blue
.offset
= 0;
327 var
->transp
.length
= 0;
328 var
->transp
.offset
= 0;
333 if (sm
->pdata
->flags
& SM501_FBPD_SWAP_FB_ENDIAN
) {
334 var
->blue
.offset
= 11;
335 var
->green
.offset
= 5;
338 var
->red
.offset
= 11;
339 var
->green
.offset
= 5;
340 var
->blue
.offset
= 0;
342 var
->transp
.offset
= 0;
345 var
->green
.length
= 6;
346 var
->blue
.length
= 5;
347 var
->transp
.length
= 0;
351 if (sm
->pdata
->flags
& SM501_FBPD_SWAP_FB_ENDIAN
) {
352 var
->transp
.offset
= 0;
354 var
->green
.offset
= 16;
355 var
->blue
.offset
= 24;
357 var
->transp
.offset
= 24;
358 var
->red
.offset
= 16;
359 var
->green
.offset
= 8;
360 var
->blue
.offset
= 0;
364 var
->green
.length
= 8;
365 var
->blue
.length
= 8;
366 var
->transp
.length
= 0;
377 * sm501fb_check_var_crt():
379 * check the parameters for the CRT head, and either bring them
380 * back into range, or return -EINVAL.
383 static int sm501fb_check_var_crt(struct fb_var_screeninfo
*var
,
384 struct fb_info
*info
)
386 return sm501fb_check_var(var
, info
);
389 /* sm501fb_check_var_pnl():
391 * check the parameters for the CRT head, and either bring them
392 * back into range, or return -EINVAL.
395 static int sm501fb_check_var_pnl(struct fb_var_screeninfo
*var
,
396 struct fb_info
*info
)
398 return sm501fb_check_var(var
, info
);
401 /* sm501fb_set_par_common
403 * set common registers for framebuffers
406 static int sm501fb_set_par_common(struct fb_info
*info
,
407 struct fb_var_screeninfo
*var
)
409 struct sm501fb_par
*par
= info
->par
;
410 struct sm501fb_info
*fbi
= par
->info
;
411 unsigned long pixclock
; /* pixelclock in Hz */
412 unsigned long sm501pixclock
; /* pixelclock the 501 can achive in Hz */
413 unsigned int mem_type
;
414 unsigned int clock_type
;
415 unsigned int head_addr
;
417 dev_dbg(fbi
->dev
, "%s: %dx%d, bpp = %d, virtual %dx%d\n",
418 __func__
, var
->xres
, var
->yres
, var
->bits_per_pixel
,
419 var
->xres_virtual
, var
->yres_virtual
);
423 mem_type
= SM501_MEMF_CRT
;
424 clock_type
= SM501_CLOCK_V2XCLK
;
425 head_addr
= SM501_DC_CRT_FB_ADDR
;
429 mem_type
= SM501_MEMF_PANEL
;
430 clock_type
= SM501_CLOCK_P2XCLK
;
431 head_addr
= SM501_DC_PANEL_FB_ADDR
;
435 mem_type
= 0; /* stop compiler warnings */
440 switch (var
->bits_per_pixel
) {
442 info
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
446 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
450 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
454 /* allocate fb memory within 501 */
455 info
->fix
.line_length
= (var
->xres_virtual
* var
->bits_per_pixel
)/8;
456 info
->fix
.smem_len
= info
->fix
.line_length
* var
->yres_virtual
;
458 dev_dbg(fbi
->dev
, "%s: line length = %u\n", __func__
,
459 info
->fix
.line_length
);
461 if (sm501_alloc_mem(fbi
, &par
->screen
, mem_type
,
462 info
->fix
.smem_len
)) {
463 dev_err(fbi
->dev
, "no memory available\n");
467 info
->fix
.smem_start
= fbi
->fbmem_res
->start
+ par
->screen
.sm_addr
;
469 info
->screen_base
= fbi
->fbmem
+ par
->screen
.sm_addr
;
470 info
->screen_size
= info
->fix
.smem_len
;
472 /* set start of framebuffer to the screen */
474 writel(par
->screen
.sm_addr
| SM501_ADDR_FLIP
, fbi
->regs
+ head_addr
);
476 /* program CRT clock */
478 pixclock
= sm501fb_ps_to_hz(var
->pixclock
);
480 sm501pixclock
= sm501_set_clock(fbi
->dev
->parent
, clock_type
,
483 /* update fb layer with actual clock used */
484 var
->pixclock
= sm501fb_hz_to_ps(sm501pixclock
);
486 dev_dbg(fbi
->dev
, "%s: pixclock(ps) = %u, pixclock(Hz) = %lu, "
487 "sm501pixclock = %lu, error = %ld%%\n",
488 __func__
, var
->pixclock
, pixclock
, sm501pixclock
,
489 ((pixclock
- sm501pixclock
)*100)/pixclock
);
494 /* sm501fb_set_par_geometry
496 * set the geometry registers for specified framebuffer.
499 static void sm501fb_set_par_geometry(struct fb_info
*info
,
500 struct fb_var_screeninfo
*var
)
502 struct sm501fb_par
*par
= info
->par
;
503 struct sm501fb_info
*fbi
= par
->info
;
504 void __iomem
*base
= fbi
->regs
;
507 if (par
->head
== HEAD_CRT
)
508 base
+= SM501_DC_CRT_H_TOT
;
510 base
+= SM501_DC_PANEL_H_TOT
;
512 /* set framebuffer width and display width */
514 reg
= info
->fix
.line_length
;
515 reg
|= ((var
->xres
* var
->bits_per_pixel
)/8) << 16;
517 writel(reg
, fbi
->regs
+ (par
->head
== HEAD_CRT
?
518 SM501_DC_CRT_FB_OFFSET
: SM501_DC_PANEL_FB_OFFSET
));
520 /* program horizontal total */
522 reg
= (h_total(var
) - 1) << 16;
523 reg
|= (var
->xres
- 1);
525 writel(reg
, base
+ SM501_OFF_DC_H_TOT
);
527 /* program horizontal sync */
529 reg
= var
->hsync_len
<< 16;
530 reg
|= var
->xres
+ var
->right_margin
- 1;
532 writel(reg
, base
+ SM501_OFF_DC_H_SYNC
);
534 /* program vertical total */
536 reg
= (v_total(var
) - 1) << 16;
537 reg
|= (var
->yres
- 1);
539 writel(reg
, base
+ SM501_OFF_DC_V_TOT
);
541 /* program vertical sync */
542 reg
= var
->vsync_len
<< 16;
543 reg
|= var
->yres
+ var
->lower_margin
- 1;
545 writel(reg
, base
+ SM501_OFF_DC_V_SYNC
);
550 * pan the CRT display output within an virtual framebuffer
553 static int sm501fb_pan_crt(struct fb_var_screeninfo
*var
,
554 struct fb_info
*info
)
556 struct sm501fb_par
*par
= info
->par
;
557 struct sm501fb_info
*fbi
= par
->info
;
558 unsigned int bytes_pixel
= var
->bits_per_pixel
/ 8;
562 xoffs
= var
->xoffset
* bytes_pixel
;
564 reg
= readl(fbi
->regs
+ SM501_DC_CRT_CONTROL
);
566 reg
&= ~SM501_DC_CRT_CONTROL_PIXEL_MASK
;
567 reg
|= ((xoffs
& 15) / bytes_pixel
) << 4;
568 writel(reg
, fbi
->regs
+ SM501_DC_CRT_CONTROL
);
570 reg
= (par
->screen
.sm_addr
+ xoffs
+
571 var
->yoffset
* info
->fix
.line_length
);
572 writel(reg
| SM501_ADDR_FLIP
, fbi
->regs
+ SM501_DC_CRT_FB_ADDR
);
574 sm501fb_sync_regs(fbi
);
580 * pan the panel display output within an virtual framebuffer
583 static int sm501fb_pan_pnl(struct fb_var_screeninfo
*var
,
584 struct fb_info
*info
)
586 struct sm501fb_par
*par
= info
->par
;
587 struct sm501fb_info
*fbi
= par
->info
;
590 reg
= var
->xoffset
| (var
->xres_virtual
<< 16);
591 writel(reg
, fbi
->regs
+ SM501_DC_PANEL_FB_WIDTH
);
593 reg
= var
->yoffset
| (var
->yres_virtual
<< 16);
594 writel(reg
, fbi
->regs
+ SM501_DC_PANEL_FB_HEIGHT
);
596 sm501fb_sync_regs(fbi
);
600 /* sm501fb_set_par_crt
602 * Set the CRT video mode from the fb_info structure
605 static int sm501fb_set_par_crt(struct fb_info
*info
)
607 struct sm501fb_par
*par
= info
->par
;
608 struct sm501fb_info
*fbi
= par
->info
;
609 struct fb_var_screeninfo
*var
= &info
->var
;
610 unsigned long control
; /* control register */
613 /* activate new configuration */
615 dev_dbg(fbi
->dev
, "%s(%p)\n", __func__
, info
);
617 /* enable CRT DAC - note 0 is on!*/
618 sm501_misc_control(fbi
->dev
->parent
, 0, SM501_MISC_DAC_POWER
);
620 control
= readl(fbi
->regs
+ SM501_DC_CRT_CONTROL
);
622 control
&= (SM501_DC_CRT_CONTROL_PIXEL_MASK
|
623 SM501_DC_CRT_CONTROL_GAMMA
|
624 SM501_DC_CRT_CONTROL_BLANK
|
625 SM501_DC_CRT_CONTROL_SEL
|
626 SM501_DC_CRT_CONTROL_CP
|
627 SM501_DC_CRT_CONTROL_TVP
);
629 /* set the sync polarities before we check data source */
631 if ((var
->sync
& FB_SYNC_HOR_HIGH_ACT
) == 0)
632 control
|= SM501_DC_CRT_CONTROL_HSP
;
634 if ((var
->sync
& FB_SYNC_VERT_HIGH_ACT
) == 0)
635 control
|= SM501_DC_CRT_CONTROL_VSP
;
637 if ((control
& SM501_DC_CRT_CONTROL_SEL
) == 0) {
638 /* the head is displaying panel data... */
640 sm501_alloc_mem(fbi
, &par
->screen
, SM501_MEMF_CRT
, 0);
644 ret
= sm501fb_set_par_common(info
, var
);
646 dev_err(fbi
->dev
, "failed to set common parameters\n");
650 sm501fb_pan_crt(var
, info
);
651 sm501fb_set_par_geometry(info
, var
);
653 control
|= SM501_FIFO_3
; /* fill if >3 free slots */
655 switch(var
->bits_per_pixel
) {
657 control
|= SM501_DC_CRT_CONTROL_8BPP
;
661 control
|= SM501_DC_CRT_CONTROL_16BPP
;
662 sm501fb_setup_gamma(fbi
, SM501_DC_CRT_PALETTE
);
666 control
|= SM501_DC_CRT_CONTROL_32BPP
;
667 sm501fb_setup_gamma(fbi
, SM501_DC_CRT_PALETTE
);
674 control
|= SM501_DC_CRT_CONTROL_SEL
; /* CRT displays CRT data */
675 control
|= SM501_DC_CRT_CONTROL_TE
; /* enable CRT timing */
676 control
|= SM501_DC_CRT_CONTROL_ENABLE
; /* enable CRT plane */
679 dev_dbg(fbi
->dev
, "new control is %08lx\n", control
);
681 writel(control
, fbi
->regs
+ SM501_DC_CRT_CONTROL
);
682 sm501fb_sync_regs(fbi
);
687 static void sm501fb_panel_power(struct sm501fb_info
*fbi
, int to
)
689 unsigned long control
;
690 void __iomem
*ctrl_reg
= fbi
->regs
+ SM501_DC_PANEL_CONTROL
;
691 struct sm501_platdata_fbsub
*pd
= fbi
->pdata
->fb_pnl
;
693 control
= readl(ctrl_reg
);
695 if (to
&& (control
& SM501_DC_PANEL_CONTROL_VDD
) == 0) {
696 /* enable panel power */
698 control
|= SM501_DC_PANEL_CONTROL_VDD
; /* FPVDDEN */
699 writel(control
, ctrl_reg
);
700 sm501fb_sync_regs(fbi
);
703 control
|= SM501_DC_PANEL_CONTROL_DATA
; /* DATA */
704 writel(control
, ctrl_reg
);
705 sm501fb_sync_regs(fbi
);
710 if (!(pd
->flags
& SM501FB_FLAG_PANEL_NO_VBIASEN
)) {
711 if (pd
->flags
& SM501FB_FLAG_PANEL_INV_VBIASEN
)
712 control
&= ~SM501_DC_PANEL_CONTROL_BIAS
;
714 control
|= SM501_DC_PANEL_CONTROL_BIAS
;
716 writel(control
, ctrl_reg
);
717 sm501fb_sync_regs(fbi
);
721 if (!(pd
->flags
& SM501FB_FLAG_PANEL_NO_FPEN
)) {
722 if (pd
->flags
& SM501FB_FLAG_PANEL_INV_FPEN
)
723 control
&= ~SM501_DC_PANEL_CONTROL_FPEN
;
725 control
|= SM501_DC_PANEL_CONTROL_FPEN
;
727 writel(control
, ctrl_reg
);
728 sm501fb_sync_regs(fbi
);
731 } else if (!to
&& (control
& SM501_DC_PANEL_CONTROL_VDD
) != 0) {
732 /* disable panel power */
733 if (!(pd
->flags
& SM501FB_FLAG_PANEL_NO_FPEN
)) {
734 if (pd
->flags
& SM501FB_FLAG_PANEL_INV_FPEN
)
735 control
|= SM501_DC_PANEL_CONTROL_FPEN
;
737 control
&= ~SM501_DC_PANEL_CONTROL_FPEN
;
739 writel(control
, ctrl_reg
);
740 sm501fb_sync_regs(fbi
);
744 if (!(pd
->flags
& SM501FB_FLAG_PANEL_NO_VBIASEN
)) {
745 if (pd
->flags
& SM501FB_FLAG_PANEL_INV_VBIASEN
)
746 control
|= SM501_DC_PANEL_CONTROL_BIAS
;
748 control
&= ~SM501_DC_PANEL_CONTROL_BIAS
;
750 writel(control
, ctrl_reg
);
751 sm501fb_sync_regs(fbi
);
755 control
&= ~SM501_DC_PANEL_CONTROL_DATA
;
756 writel(control
, ctrl_reg
);
757 sm501fb_sync_regs(fbi
);
760 control
&= ~SM501_DC_PANEL_CONTROL_VDD
;
761 writel(control
, ctrl_reg
);
762 sm501fb_sync_regs(fbi
);
766 sm501fb_sync_regs(fbi
);
769 /* sm501fb_set_par_pnl
771 * Set the panel video mode from the fb_info structure
774 static int sm501fb_set_par_pnl(struct fb_info
*info
)
776 struct sm501fb_par
*par
= info
->par
;
777 struct sm501fb_info
*fbi
= par
->info
;
778 struct fb_var_screeninfo
*var
= &info
->var
;
779 unsigned long control
;
783 dev_dbg(fbi
->dev
, "%s(%p)\n", __func__
, info
);
785 /* activate this new configuration */
787 ret
= sm501fb_set_par_common(info
, var
);
791 sm501fb_pan_pnl(var
, info
);
792 sm501fb_set_par_geometry(info
, var
);
794 /* update control register */
796 control
= readl(fbi
->regs
+ SM501_DC_PANEL_CONTROL
);
797 control
&= (SM501_DC_PANEL_CONTROL_GAMMA
|
798 SM501_DC_PANEL_CONTROL_VDD
|
799 SM501_DC_PANEL_CONTROL_DATA
|
800 SM501_DC_PANEL_CONTROL_BIAS
|
801 SM501_DC_PANEL_CONTROL_FPEN
|
802 SM501_DC_PANEL_CONTROL_CP
|
803 SM501_DC_PANEL_CONTROL_CK
|
804 SM501_DC_PANEL_CONTROL_HP
|
805 SM501_DC_PANEL_CONTROL_VP
|
806 SM501_DC_PANEL_CONTROL_HPD
|
807 SM501_DC_PANEL_CONTROL_VPD
);
809 control
|= SM501_FIFO_3
; /* fill if >3 free slots */
811 switch(var
->bits_per_pixel
) {
813 control
|= SM501_DC_PANEL_CONTROL_8BPP
;
817 control
|= SM501_DC_PANEL_CONTROL_16BPP
;
818 sm501fb_setup_gamma(fbi
, SM501_DC_PANEL_PALETTE
);
822 control
|= SM501_DC_PANEL_CONTROL_32BPP
;
823 sm501fb_setup_gamma(fbi
, SM501_DC_PANEL_PALETTE
);
830 writel(0x0, fbi
->regs
+ SM501_DC_PANEL_PANNING_CONTROL
);
832 /* panel plane top left and bottom right location */
834 writel(0x00, fbi
->regs
+ SM501_DC_PANEL_TL_LOC
);
837 reg
|= (var
->yres
- 1) << 16;
839 writel(reg
, fbi
->regs
+ SM501_DC_PANEL_BR_LOC
);
841 /* program panel control register */
843 control
|= SM501_DC_PANEL_CONTROL_TE
; /* enable PANEL timing */
844 control
|= SM501_DC_PANEL_CONTROL_EN
; /* enable PANEL gfx plane */
846 if ((var
->sync
& FB_SYNC_HOR_HIGH_ACT
) == 0)
847 control
|= SM501_DC_PANEL_CONTROL_HSP
;
849 if ((var
->sync
& FB_SYNC_VERT_HIGH_ACT
) == 0)
850 control
|= SM501_DC_PANEL_CONTROL_VSP
;
852 writel(control
, fbi
->regs
+ SM501_DC_PANEL_CONTROL
);
853 sm501fb_sync_regs(fbi
);
855 /* ensure the panel interface is not tristated at this point */
857 sm501_modify_reg(fbi
->dev
->parent
, SM501_SYSTEM_CONTROL
,
858 0, SM501_SYSCTRL_PANEL_TRISTATE
);
860 /* power the panel up */
861 sm501fb_panel_power(fbi
, 1);
868 * convert a colour value into a field position
873 static inline unsigned int chan_to_field(unsigned int chan
,
874 struct fb_bitfield
*bf
)
877 chan
>>= 16 - bf
->length
;
878 return chan
<< bf
->offset
;
883 * set the colour mapping for modes that support palettised data
886 static int sm501fb_setcolreg(unsigned regno
,
887 unsigned red
, unsigned green
, unsigned blue
,
888 unsigned transp
, struct fb_info
*info
)
890 struct sm501fb_par
*par
= info
->par
;
891 struct sm501fb_info
*fbi
= par
->info
;
892 void __iomem
*base
= fbi
->regs
;
895 if (par
->head
== HEAD_CRT
)
896 base
+= SM501_DC_CRT_PALETTE
;
898 base
+= SM501_DC_PANEL_PALETTE
;
900 switch (info
->fix
.visual
) {
901 case FB_VISUAL_TRUECOLOR
:
902 /* true-colour, use pseuo-palette */
905 u32
*pal
= par
->pseudo_palette
;
907 val
= chan_to_field(red
, &info
->var
.red
);
908 val
|= chan_to_field(green
, &info
->var
.green
);
909 val
|= chan_to_field(blue
, &info
->var
.blue
);
915 case FB_VISUAL_PSEUDOCOLOR
:
917 val
= (red
>> 8) << 16;
918 val
|= (green
>> 8) << 8;
921 writel(val
, base
+ (regno
* 4));
927 return 1; /* unknown type */
935 * Blank or un-blank the panel interface
938 static int sm501fb_blank_pnl(int blank_mode
, struct fb_info
*info
)
940 struct sm501fb_par
*par
= info
->par
;
941 struct sm501fb_info
*fbi
= par
->info
;
943 dev_dbg(fbi
->dev
, "%s(mode=%d, %p)\n", __func__
, blank_mode
, info
);
945 switch (blank_mode
) {
946 case FB_BLANK_POWERDOWN
:
947 sm501fb_panel_power(fbi
, 0);
950 case FB_BLANK_UNBLANK
:
951 sm501fb_panel_power(fbi
, 1);
954 case FB_BLANK_NORMAL
:
955 case FB_BLANK_VSYNC_SUSPEND
:
956 case FB_BLANK_HSYNC_SUSPEND
:
966 * Blank or un-blank the crt interface
969 static int sm501fb_blank_crt(int blank_mode
, struct fb_info
*info
)
971 struct sm501fb_par
*par
= info
->par
;
972 struct sm501fb_info
*fbi
= par
->info
;
975 dev_dbg(fbi
->dev
, "%s(mode=%d, %p)\n", __func__
, blank_mode
, info
);
977 ctrl
= readl(fbi
->regs
+ SM501_DC_CRT_CONTROL
);
979 switch (blank_mode
) {
980 case FB_BLANK_POWERDOWN
:
981 ctrl
&= ~SM501_DC_CRT_CONTROL_ENABLE
;
982 sm501_misc_control(fbi
->dev
->parent
, SM501_MISC_DAC_POWER
, 0);
984 case FB_BLANK_NORMAL
:
985 ctrl
|= SM501_DC_CRT_CONTROL_BLANK
;
988 case FB_BLANK_UNBLANK
:
989 ctrl
&= ~SM501_DC_CRT_CONTROL_BLANK
;
990 ctrl
|= SM501_DC_CRT_CONTROL_ENABLE
;
991 sm501_misc_control(fbi
->dev
->parent
, 0, SM501_MISC_DAC_POWER
);
994 case FB_BLANK_VSYNC_SUSPEND
:
995 case FB_BLANK_HSYNC_SUSPEND
:
1001 writel(ctrl
, fbi
->regs
+ SM501_DC_CRT_CONTROL
);
1002 sm501fb_sync_regs(fbi
);
1009 * set or change the hardware cursor parameters
1012 static int sm501fb_cursor(struct fb_info
*info
, struct fb_cursor
*cursor
)
1014 struct sm501fb_par
*par
= info
->par
;
1015 struct sm501fb_info
*fbi
= par
->info
;
1016 void __iomem
*base
= fbi
->regs
;
1017 unsigned long hwc_addr
;
1018 unsigned long fg
, bg
;
1020 dev_dbg(fbi
->dev
, "%s(%p,%p)\n", __func__
, info
, cursor
);
1022 if (par
->head
== HEAD_CRT
)
1023 base
+= SM501_DC_CRT_HWC_BASE
;
1025 base
+= SM501_DC_PANEL_HWC_BASE
;
1027 /* check not being asked to exceed capabilities */
1029 if (cursor
->image
.width
> 64)
1032 if (cursor
->image
.height
> 64)
1035 if (cursor
->image
.depth
> 1)
1038 hwc_addr
= readl(base
+ SM501_OFF_HWC_ADDR
);
1041 writel(hwc_addr
| SM501_HWC_EN
, base
+ SM501_OFF_HWC_ADDR
);
1043 writel(hwc_addr
& ~SM501_HWC_EN
, base
+ SM501_OFF_HWC_ADDR
);
1046 if (cursor
->set
& FB_CUR_SETPOS
) {
1047 unsigned int x
= cursor
->image
.dx
;
1048 unsigned int y
= cursor
->image
.dy
;
1050 if (x
>= 2048 || y
>= 2048 )
1053 dev_dbg(fbi
->dev
, "set position %d,%d\n", x
, y
);
1055 //y += cursor->image.height;
1057 writel(x
| (y
<< 16), base
+ SM501_OFF_HWC_LOC
);
1060 if (cursor
->set
& FB_CUR_SETCMAP
) {
1061 unsigned int bg_col
= cursor
->image
.bg_color
;
1062 unsigned int fg_col
= cursor
->image
.fg_color
;
1064 dev_dbg(fbi
->dev
, "%s: update cmap (%08x,%08x)\n",
1065 __func__
, bg_col
, fg_col
);
1067 bg
= ((info
->cmap
.red
[bg_col
] & 0xF8) << 8) |
1068 ((info
->cmap
.green
[bg_col
] & 0xFC) << 3) |
1069 ((info
->cmap
.blue
[bg_col
] & 0xF8) >> 3);
1071 fg
= ((info
->cmap
.red
[fg_col
] & 0xF8) << 8) |
1072 ((info
->cmap
.green
[fg_col
] & 0xFC) << 3) |
1073 ((info
->cmap
.blue
[fg_col
] & 0xF8) >> 3);
1075 dev_dbg(fbi
->dev
, "fgcol %08lx, bgcol %08lx\n", fg
, bg
);
1077 writel(bg
, base
+ SM501_OFF_HWC_COLOR_1_2
);
1078 writel(fg
, base
+ SM501_OFF_HWC_COLOR_3
);
1081 if (cursor
->set
& FB_CUR_SETSIZE
||
1082 cursor
->set
& (FB_CUR_SETIMAGE
| FB_CUR_SETSHAPE
)) {
1083 /* SM501 cursor is a two bpp 64x64 bitmap this routine
1084 * clears it to transparent then combines the cursor
1085 * shape plane with the colour plane to set the
1088 const unsigned char *pcol
= cursor
->image
.data
;
1089 const unsigned char *pmsk
= cursor
->mask
;
1090 void __iomem
*dst
= par
->cursor
.k_addr
;
1091 unsigned char dcol
= 0;
1092 unsigned char dmsk
= 0;
1095 dev_dbg(fbi
->dev
, "%s: setting shape (%d,%d)\n",
1096 __func__
, cursor
->image
.width
, cursor
->image
.height
);
1098 for (op
= 0; op
< (64*64*2)/8; op
+=4)
1099 writel(0x0, dst
+ op
);
1101 for (y
= 0; y
< cursor
->image
.height
; y
++) {
1102 for (x
= 0; x
< cursor
->image
.width
; x
++) {
1112 op
= (dcol
& 1) ? 1 : 3;
1113 op
<<= ((x
% 4) * 2);
1115 op
|= readb(dst
+ (x
/ 4));
1116 writeb(op
, dst
+ (x
/ 4));
1123 sm501fb_sync_regs(fbi
); /* ensure cursor data flushed */
1127 /* sm501fb_crtsrc_show
1129 * device attribute code to show where the crt output is sourced from
1132 static ssize_t
sm501fb_crtsrc_show(struct device
*dev
,
1133 struct device_attribute
*attr
, char *buf
)
1135 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1138 ctrl
= readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1139 ctrl
&= SM501_DC_CRT_CONTROL_SEL
;
1141 return snprintf(buf
, PAGE_SIZE
, "%s\n", ctrl
? "crt" : "panel");
1144 /* sm501fb_crtsrc_show
1146 * device attribute code to set where the crt output is sourced from
1149 static ssize_t
sm501fb_crtsrc_store(struct device
*dev
,
1150 struct device_attribute
*attr
,
1151 const char *buf
, size_t len
)
1153 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1154 enum sm501_controller head
;
1160 if (strnicmp(buf
, "crt", 3) == 0)
1162 else if (strnicmp(buf
, "panel", 5) == 0)
1167 dev_info(dev
, "setting crt source to head %d\n", head
);
1169 ctrl
= readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1171 if (head
== HEAD_CRT
) {
1172 ctrl
|= SM501_DC_CRT_CONTROL_SEL
;
1173 ctrl
|= SM501_DC_CRT_CONTROL_ENABLE
;
1174 ctrl
|= SM501_DC_CRT_CONTROL_TE
;
1176 ctrl
&= ~SM501_DC_CRT_CONTROL_SEL
;
1177 ctrl
&= ~SM501_DC_CRT_CONTROL_ENABLE
;
1178 ctrl
&= ~SM501_DC_CRT_CONTROL_TE
;
1181 writel(ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1182 sm501fb_sync_regs(info
);
1187 /* Prepare the device_attr for registration with sysfs later */
1188 static DEVICE_ATTR(crt_src
, 0666, sm501fb_crtsrc_show
, sm501fb_crtsrc_store
);
1190 /* sm501fb_show_regs
1192 * show the primary sm501 registers
1194 static int sm501fb_show_regs(struct sm501fb_info
*info
, char *ptr
,
1195 unsigned int start
, unsigned int len
)
1197 void __iomem
*mem
= info
->regs
;
1201 for (reg
= start
; reg
< (len
+ start
); reg
+= 4)
1202 ptr
+= sprintf(ptr
, "%08x = %08x\n", reg
, readl(mem
+ reg
));
1207 /* sm501fb_debug_show_crt
1209 * show the crt control and cursor registers
1212 static ssize_t
sm501fb_debug_show_crt(struct device
*dev
,
1213 struct device_attribute
*attr
, char *buf
)
1215 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1218 ptr
+= sm501fb_show_regs(info
, ptr
, SM501_DC_CRT_CONTROL
, 0x40);
1219 ptr
+= sm501fb_show_regs(info
, ptr
, SM501_DC_CRT_HWC_BASE
, 0x10);
1224 static DEVICE_ATTR(fbregs_crt
, 0444, sm501fb_debug_show_crt
, NULL
);
1226 /* sm501fb_debug_show_pnl
1228 * show the panel control and cursor registers
1231 static ssize_t
sm501fb_debug_show_pnl(struct device
*dev
,
1232 struct device_attribute
*attr
, char *buf
)
1234 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1237 ptr
+= sm501fb_show_regs(info
, ptr
, 0x0, 0x40);
1238 ptr
+= sm501fb_show_regs(info
, ptr
, SM501_DC_PANEL_HWC_BASE
, 0x10);
1243 static DEVICE_ATTR(fbregs_pnl
, 0444, sm501fb_debug_show_pnl
, NULL
);
1245 /* framebuffer ops */
1247 static struct fb_ops sm501fb_ops_crt
= {
1248 .owner
= THIS_MODULE
,
1249 .fb_check_var
= sm501fb_check_var_crt
,
1250 .fb_set_par
= sm501fb_set_par_crt
,
1251 .fb_blank
= sm501fb_blank_crt
,
1252 .fb_setcolreg
= sm501fb_setcolreg
,
1253 .fb_pan_display
= sm501fb_pan_crt
,
1254 .fb_cursor
= sm501fb_cursor
,
1255 .fb_fillrect
= cfb_fillrect
,
1256 .fb_copyarea
= cfb_copyarea
,
1257 .fb_imageblit
= cfb_imageblit
,
1260 static struct fb_ops sm501fb_ops_pnl
= {
1261 .owner
= THIS_MODULE
,
1262 .fb_check_var
= sm501fb_check_var_pnl
,
1263 .fb_set_par
= sm501fb_set_par_pnl
,
1264 .fb_pan_display
= sm501fb_pan_pnl
,
1265 .fb_blank
= sm501fb_blank_pnl
,
1266 .fb_setcolreg
= sm501fb_setcolreg
,
1267 .fb_cursor
= sm501fb_cursor
,
1268 .fb_fillrect
= cfb_fillrect
,
1269 .fb_copyarea
= cfb_copyarea
,
1270 .fb_imageblit
= cfb_imageblit
,
1273 /* sm501_init_cursor
1275 * initialise hw cursor parameters
1278 static int sm501_init_cursor(struct fb_info
*fbi
, unsigned int reg_base
)
1280 struct sm501fb_par
*par
;
1281 struct sm501fb_info
*info
;
1290 par
->cursor_regs
= info
->regs
+ reg_base
;
1292 ret
= sm501_alloc_mem(info
, &par
->cursor
, SM501_MEMF_CURSOR
, 1024);
1296 /* initialise the colour registers */
1298 writel(par
->cursor
.sm_addr
, par
->cursor_regs
+ SM501_OFF_HWC_ADDR
);
1300 writel(0x00, par
->cursor_regs
+ SM501_OFF_HWC_LOC
);
1301 writel(0x00, par
->cursor_regs
+ SM501_OFF_HWC_COLOR_1_2
);
1302 writel(0x00, par
->cursor_regs
+ SM501_OFF_HWC_COLOR_3
);
1303 sm501fb_sync_regs(info
);
1308 /* sm501fb_info_start
1310 * fills the par structure claiming resources and remapping etc.
1313 static int sm501fb_start(struct sm501fb_info
*info
,
1314 struct platform_device
*pdev
)
1316 struct resource
*res
;
1317 struct device
*dev
= &pdev
->dev
;
1321 info
->irq
= ret
= platform_get_irq(pdev
, 0);
1323 /* we currently do not use the IRQ */
1324 dev_warn(dev
, "no irq for device\n");
1327 /* allocate, reserve and remap resources for registers */
1328 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
1330 dev_err(dev
, "no resource definition for registers\n");
1335 info
->regs_res
= request_mem_region(res
->start
,
1336 res
->end
- res
->start
,
1339 if (info
->regs_res
== NULL
) {
1340 dev_err(dev
, "cannot claim registers\n");
1345 info
->regs
= ioremap(res
->start
, (res
->end
- res
->start
)+1);
1346 if (info
->regs
== NULL
) {
1347 dev_err(dev
, "cannot remap registers\n");
1352 /* allocate, reserve resources for framebuffer */
1353 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 2);
1355 dev_err(dev
, "no memory resource defined\n");
1360 info
->fbmem_res
= request_mem_region(res
->start
,
1361 (res
->end
- res
->start
)+1,
1363 if (info
->fbmem_res
== NULL
) {
1364 dev_err(dev
, "cannot claim framebuffer\n");
1369 info
->fbmem
= ioremap(res
->start
, (res
->end
- res
->start
)+1);
1370 if (info
->fbmem
== NULL
) {
1371 dev_err(dev
, "cannot remap framebuffer\n");
1375 info
->fbmem_len
= (res
->end
- res
->start
)+1;
1377 /* clear framebuffer memory - avoids garbage data on unused fb */
1378 memset(info
->fbmem
, 0, info
->fbmem_len
);
1380 /* clear palette ram - undefined at power on */
1381 for (k
= 0; k
< (256 * 3); k
++)
1382 writel(0, info
->regs
+ SM501_DC_PANEL_PALETTE
+ (k
* 4));
1384 /* enable display controller */
1385 sm501_unit_power(dev
->parent
, SM501_GATE_DISPLAY
, 1);
1389 sm501_init_cursor(info
->fb
[HEAD_CRT
], SM501_DC_CRT_HWC_ADDR
);
1390 sm501_init_cursor(info
->fb
[HEAD_PANEL
], SM501_DC_PANEL_HWC_ADDR
);
1392 return 0; /* everything is setup */
1395 release_resource(info
->fbmem_res
);
1396 kfree(info
->fbmem_res
);
1399 iounmap(info
->regs
);
1402 release_resource(info
->regs_res
);
1403 kfree(info
->regs_res
);
1409 static void sm501fb_stop(struct sm501fb_info
*info
)
1411 /* disable display controller */
1412 sm501_unit_power(info
->dev
->parent
, SM501_GATE_DISPLAY
, 0);
1414 iounmap(info
->fbmem
);
1415 release_resource(info
->fbmem_res
);
1416 kfree(info
->fbmem_res
);
1418 iounmap(info
->regs
);
1419 release_resource(info
->regs_res
);
1420 kfree(info
->regs_res
);
1423 static int sm501fb_init_fb(struct fb_info
*fb
,
1424 enum sm501_controller head
,
1427 struct sm501_platdata_fbsub
*pd
;
1428 struct sm501fb_par
*par
= fb
->par
;
1429 struct sm501fb_info
*info
= par
->info
;
1431 unsigned int enable
;
1436 pd
= info
->pdata
->fb_crt
;
1437 ctrl
= readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1438 enable
= (ctrl
& SM501_DC_CRT_CONTROL_ENABLE
) ? 1 : 0;
1440 /* ensure we set the correct source register */
1441 if (info
->pdata
->fb_route
!= SM501_FB_CRT_PANEL
) {
1442 ctrl
|= SM501_DC_CRT_CONTROL_SEL
;
1443 writel(ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1449 pd
= info
->pdata
->fb_pnl
;
1450 ctrl
= readl(info
->regs
+ SM501_DC_PANEL_CONTROL
);
1451 enable
= (ctrl
& SM501_DC_PANEL_CONTROL_EN
) ? 1 : 0;
1455 pd
= NULL
; /* stop compiler warnings */
1461 dev_info(info
->dev
, "fb %s %sabled at start\n",
1462 fbname
, enable
? "en" : "dis");
1464 /* check to see if our routing allows this */
1466 if (head
== HEAD_CRT
&& info
->pdata
->fb_route
== SM501_FB_CRT_PANEL
) {
1467 ctrl
&= ~SM501_DC_CRT_CONTROL_SEL
;
1468 writel(ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1472 strlcpy(fb
->fix
.id
, fbname
, sizeof(fb
->fix
.id
));
1475 (head
== HEAD_CRT
) ? &sm501fb_ops_crt
: &sm501fb_ops_pnl
,
1476 sizeof(struct fb_ops
));
1478 /* update ops dependant on what we've been passed */
1480 if ((pd
->flags
& SM501FB_FLAG_USE_HWCURSOR
) == 0)
1481 par
->ops
.fb_cursor
= NULL
;
1483 fb
->fbops
= &par
->ops
;
1484 fb
->flags
= FBINFO_FLAG_DEFAULT
|
1485 FBINFO_HWACCEL_XPAN
| FBINFO_HWACCEL_YPAN
;
1489 fb
->fix
.type
= FB_TYPE_PACKED_PIXELS
;
1490 fb
->fix
.type_aux
= 0;
1491 fb
->fix
.xpanstep
= 1;
1492 fb
->fix
.ypanstep
= 1;
1493 fb
->fix
.ywrapstep
= 0;
1494 fb
->fix
.accel
= FB_ACCEL_NONE
;
1499 fb
->var
.activate
= FB_ACTIVATE_NOW
;
1500 fb
->var
.accel_flags
= 0;
1501 fb
->var
.vmode
= FB_VMODE_NONINTERLACED
;
1502 fb
->var
.bits_per_pixel
= 16;
1504 if (enable
&& (pd
->flags
& SM501FB_FLAG_USE_INIT_MODE
) && 0) {
1505 /* TODO read the mode from the current display */
1509 dev_info(info
->dev
, "using supplied mode\n");
1510 fb_videomode_to_var(&fb
->var
, pd
->def_mode
);
1512 fb
->var
.bits_per_pixel
= pd
->def_bpp
? pd
->def_bpp
: 8;
1513 fb
->var
.xres_virtual
= fb
->var
.xres
;
1514 fb
->var
.yres_virtual
= fb
->var
.yres
;
1516 ret
= fb_find_mode(&fb
->var
, fb
,
1517 NULL
, NULL
, 0, NULL
, 8);
1519 if (ret
== 0 || ret
== 4) {
1521 "failed to get initial mode\n");
1527 /* initialise and set the palette */
1528 fb_alloc_cmap(&fb
->cmap
, NR_PALETTE
, 0);
1529 fb_set_cmap(&fb
->cmap
, fb
);
1531 ret
= (fb
->fbops
->fb_check_var
)(&fb
->var
, fb
);
1533 dev_err(info
->dev
, "check_var() failed on initial setup?\n");
1535 /* ensure we've activated our new configuration */
1536 (fb
->fbops
->fb_set_par
)(fb
);
1541 /* default platform data if none is supplied (ie, PCI device) */
1543 static struct sm501_platdata_fbsub sm501fb_pdata_crt
= {
1544 .flags
= (SM501FB_FLAG_USE_INIT_MODE
|
1545 SM501FB_FLAG_USE_HWCURSOR
|
1546 SM501FB_FLAG_USE_HWACCEL
|
1547 SM501FB_FLAG_DISABLE_AT_EXIT
),
1551 static struct sm501_platdata_fbsub sm501fb_pdata_pnl
= {
1552 .flags
= (SM501FB_FLAG_USE_INIT_MODE
|
1553 SM501FB_FLAG_USE_HWCURSOR
|
1554 SM501FB_FLAG_USE_HWACCEL
|
1555 SM501FB_FLAG_DISABLE_AT_EXIT
),
1558 static struct sm501_platdata_fb sm501fb_def_pdata
= {
1559 .fb_route
= SM501_FB_OWN
,
1560 .fb_crt
= &sm501fb_pdata_crt
,
1561 .fb_pnl
= &sm501fb_pdata_pnl
,
1564 static char driver_name_crt
[] = "sm501fb-crt";
1565 static char driver_name_pnl
[] = "sm501fb-panel";
1567 static int __devinit
sm501fb_probe_one(struct sm501fb_info
*info
,
1568 enum sm501_controller head
)
1570 unsigned char *name
= (head
== HEAD_CRT
) ? "crt" : "panel";
1571 struct sm501_platdata_fbsub
*pd
;
1572 struct sm501fb_par
*par
;
1573 struct fb_info
*fbi
;
1575 pd
= (head
== HEAD_CRT
) ? info
->pdata
->fb_crt
: info
->pdata
->fb_pnl
;
1577 /* Do not initialise if we've not been given any platform data */
1579 dev_info(info
->dev
, "no data for fb %s (disabled)\n", name
);
1583 fbi
= framebuffer_alloc(sizeof(struct sm501fb_par
), info
->dev
);
1585 dev_err(info
->dev
, "cannot allocate %s framebuffer\n", name
);
1592 fbi
->pseudo_palette
= &par
->pseudo_palette
;
1594 info
->fb
[head
] = fbi
;
1599 /* Free up anything allocated by sm501fb_init_fb */
1601 static void sm501_free_init_fb(struct sm501fb_info
*info
,
1602 enum sm501_controller head
)
1604 struct fb_info
*fbi
= info
->fb
[head
];
1606 fb_dealloc_cmap(&fbi
->cmap
);
1609 static int __devinit
sm501fb_start_one(struct sm501fb_info
*info
,
1610 enum sm501_controller head
,
1611 const char *drvname
)
1613 struct fb_info
*fbi
= info
->fb
[head
];
1619 ret
= sm501fb_init_fb(info
->fb
[head
], head
, drvname
);
1621 dev_err(info
->dev
, "cannot initialise fb %s\n", drvname
);
1625 ret
= register_framebuffer(info
->fb
[head
]);
1627 dev_err(info
->dev
, "failed to register fb %s\n", drvname
);
1628 sm501_free_init_fb(info
, head
);
1632 dev_info(info
->dev
, "fb%d: %s frame buffer\n", fbi
->node
, fbi
->fix
.id
);
1637 static int __devinit
sm501fb_probe(struct platform_device
*pdev
)
1639 struct sm501fb_info
*info
;
1640 struct device
*dev
= &pdev
->dev
;
1643 /* allocate our framebuffers */
1645 info
= kzalloc(sizeof(struct sm501fb_info
), GFP_KERNEL
);
1647 dev_err(dev
, "failed to allocate state\n");
1651 info
->dev
= dev
= &pdev
->dev
;
1652 platform_set_drvdata(pdev
, info
);
1654 if (dev
->parent
->platform_data
) {
1655 struct sm501_platdata
*pd
= dev
->parent
->platform_data
;
1656 info
->pdata
= pd
->fb
;
1659 if (info
->pdata
== NULL
) {
1660 dev_info(dev
, "using default configuration data\n");
1661 info
->pdata
= &sm501fb_def_pdata
;
1664 /* probe for the presence of each panel */
1666 ret
= sm501fb_probe_one(info
, HEAD_CRT
);
1668 dev_err(dev
, "failed to probe CRT\n");
1672 ret
= sm501fb_probe_one(info
, HEAD_PANEL
);
1674 dev_err(dev
, "failed to probe PANEL\n");
1675 goto err_probed_crt
;
1678 if (info
->fb
[HEAD_PANEL
] == NULL
&&
1679 info
->fb
[HEAD_CRT
] == NULL
) {
1680 dev_err(dev
, "no framebuffers found\n");
1684 /* get the resources for both of the framebuffers */
1686 ret
= sm501fb_start(info
, pdev
);
1688 dev_err(dev
, "cannot initialise SM501\n");
1689 goto err_probed_panel
;
1692 ret
= sm501fb_start_one(info
, HEAD_CRT
, driver_name_crt
);
1694 dev_err(dev
, "failed to start CRT\n");
1698 ret
= sm501fb_start_one(info
, HEAD_PANEL
, driver_name_pnl
);
1700 dev_err(dev
, "failed to start Panel\n");
1701 goto err_started_crt
;
1704 /* create device files */
1706 ret
= device_create_file(dev
, &dev_attr_crt_src
);
1708 goto err_started_panel
;
1710 ret
= device_create_file(dev
, &dev_attr_fbregs_pnl
);
1712 goto err_attached_crtsrc_file
;
1714 ret
= device_create_file(dev
, &dev_attr_fbregs_crt
);
1716 goto err_attached_pnlregs_file
;
1718 /* we registered, return ok */
1721 err_attached_pnlregs_file
:
1722 device_remove_file(dev
, &dev_attr_fbregs_pnl
);
1724 err_attached_crtsrc_file
:
1725 device_remove_file(dev
, &dev_attr_crt_src
);
1728 unregister_framebuffer(info
->fb
[HEAD_PANEL
]);
1729 sm501_free_init_fb(info
, HEAD_PANEL
);
1732 unregister_framebuffer(info
->fb
[HEAD_CRT
]);
1733 sm501_free_init_fb(info
, HEAD_CRT
);
1739 framebuffer_release(info
->fb
[HEAD_PANEL
]);
1742 framebuffer_release(info
->fb
[HEAD_CRT
]);
1754 static int sm501fb_remove(struct platform_device
*pdev
)
1756 struct sm501fb_info
*info
= platform_get_drvdata(pdev
);
1757 struct fb_info
*fbinfo_crt
= info
->fb
[0];
1758 struct fb_info
*fbinfo_pnl
= info
->fb
[1];
1760 device_remove_file(&pdev
->dev
, &dev_attr_fbregs_crt
);
1761 device_remove_file(&pdev
->dev
, &dev_attr_fbregs_pnl
);
1762 device_remove_file(&pdev
->dev
, &dev_attr_crt_src
);
1764 sm501_free_init_fb(info
, HEAD_CRT
);
1765 sm501_free_init_fb(info
, HEAD_PANEL
);
1767 unregister_framebuffer(fbinfo_crt
);
1768 unregister_framebuffer(fbinfo_pnl
);
1773 framebuffer_release(fbinfo_pnl
);
1774 framebuffer_release(fbinfo_crt
);
1781 static int sm501fb_suspend_fb(struct sm501fb_info
*info
,
1782 enum sm501_controller head
)
1784 struct fb_info
*fbi
= info
->fb
[head
];
1785 struct sm501fb_par
*par
= fbi
->par
;
1787 if (par
->screen
.size
== 0)
1790 /* blank the relevant interface to ensure unit power minimised */
1791 (par
->ops
.fb_blank
)(FB_BLANK_POWERDOWN
, fbi
);
1793 /* tell console/fb driver we are suspending */
1795 acquire_console_sem();
1796 fb_set_suspend(fbi
, 1);
1797 release_console_sem();
1799 /* backup copies in case chip is powered down over suspend */
1801 par
->store_fb
= vmalloc(par
->screen
.size
);
1802 if (par
->store_fb
== NULL
) {
1803 dev_err(info
->dev
, "no memory to store screen\n");
1807 par
->store_cursor
= vmalloc(par
->cursor
.size
);
1808 if (par
->store_cursor
== NULL
) {
1809 dev_err(info
->dev
, "no memory to store cursor\n");
1813 dev_dbg(info
->dev
, "suspending screen to %p\n", par
->store_fb
);
1814 dev_dbg(info
->dev
, "suspending cursor to %p\n", par
->store_cursor
);
1816 memcpy_fromio(par
->store_fb
, par
->screen
.k_addr
, par
->screen
.size
);
1817 memcpy_fromio(par
->store_cursor
, par
->cursor
.k_addr
, par
->cursor
.size
);
1822 vfree(par
->store_fb
);
1823 par
->store_fb
= NULL
;
1828 static void sm501fb_resume_fb(struct sm501fb_info
*info
,
1829 enum sm501_controller head
)
1831 struct fb_info
*fbi
= info
->fb
[head
];
1832 struct sm501fb_par
*par
= fbi
->par
;
1834 if (par
->screen
.size
== 0)
1837 /* re-activate the configuration */
1839 (par
->ops
.fb_set_par
)(fbi
);
1841 /* restore the data */
1843 dev_dbg(info
->dev
, "restoring screen from %p\n", par
->store_fb
);
1844 dev_dbg(info
->dev
, "restoring cursor from %p\n", par
->store_cursor
);
1847 memcpy_toio(par
->screen
.k_addr
, par
->store_fb
,
1850 if (par
->store_cursor
)
1851 memcpy_toio(par
->cursor
.k_addr
, par
->store_cursor
,
1854 acquire_console_sem();
1855 fb_set_suspend(fbi
, 0);
1856 release_console_sem();
1858 vfree(par
->store_fb
);
1859 vfree(par
->store_cursor
);
1863 /* suspend and resume support */
1865 static int sm501fb_suspend(struct platform_device
*pdev
, pm_message_t state
)
1867 struct sm501fb_info
*info
= platform_get_drvdata(pdev
);
1869 /* store crt control to resume with */
1870 info
->pm_crt_ctrl
= readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1872 sm501fb_suspend_fb(info
, HEAD_CRT
);
1873 sm501fb_suspend_fb(info
, HEAD_PANEL
);
1875 /* turn off the clocks, in case the device is not powered down */
1876 sm501_unit_power(info
->dev
->parent
, SM501_GATE_DISPLAY
, 0);
1881 #define SM501_CRT_CTRL_SAVE (SM501_DC_CRT_CONTROL_TVP | \
1882 SM501_DC_CRT_CONTROL_SEL)
1885 static int sm501fb_resume(struct platform_device
*pdev
)
1887 struct sm501fb_info
*info
= platform_get_drvdata(pdev
);
1888 unsigned long crt_ctrl
;
1890 sm501_unit_power(info
->dev
->parent
, SM501_GATE_DISPLAY
, 1);
1892 /* restore the items we want to be saved for crt control */
1894 crt_ctrl
= readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1895 crt_ctrl
&= ~SM501_CRT_CTRL_SAVE
;
1896 crt_ctrl
|= info
->pm_crt_ctrl
& SM501_CRT_CTRL_SAVE
;
1897 writel(crt_ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1899 sm501fb_resume_fb(info
, HEAD_CRT
);
1900 sm501fb_resume_fb(info
, HEAD_PANEL
);
1906 #define sm501fb_suspend NULL
1907 #define sm501fb_resume NULL
1910 static struct platform_driver sm501fb_driver
= {
1911 .probe
= sm501fb_probe
,
1912 .remove
= sm501fb_remove
,
1913 .suspend
= sm501fb_suspend
,
1914 .resume
= sm501fb_resume
,
1917 .owner
= THIS_MODULE
,
1921 static int __devinit
sm501fb_init(void)
1923 return platform_driver_register(&sm501fb_driver
);
1926 static void __exit
sm501fb_cleanup(void)
1928 platform_driver_unregister(&sm501fb_driver
);
1931 module_init(sm501fb_init
);
1932 module_exit(sm501fb_cleanup
);
1934 MODULE_AUTHOR("Ben Dooks, Vincent Sanders");
1935 MODULE_DESCRIPTION("SM501 Framebuffer driver");
1936 MODULE_LICENSE("GPL v2");