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>
46 static char *fb_mode
= "640x480-16@60";
47 static unsigned long default_bpp
= 16;
49 static struct fb_videomode sm501_default_mode
= {
60 .sync
= FB_SYNC_HOR_HIGH_ACT
| FB_SYNC_VERT_HIGH_ACT
,
61 .vmode
= FB_VMODE_NONINTERLACED
64 #define NR_PALETTE 256
66 enum sm501_controller
{
71 /* SM501 memory address.
73 * This structure is used to track memory usage within the SM501 framebuffer
74 * allocation. The sm_addr field is stored as an offset as it is often used
75 * against both the physical and mapped addresses.
79 unsigned long sm_addr
; /* offset from base of sm501 fb. */
83 /* private data that is shared between all frambuffers* */
86 struct fb_info
*fb
[2]; /* fb info for both heads */
87 struct resource
*fbmem_res
; /* framebuffer resource */
88 struct resource
*regs_res
; /* registers resource */
89 struct resource
*regs2d_res
; /* 2d registers resource */
90 struct sm501_platdata_fb
*pdata
; /* our platform data */
92 unsigned long pm_crt_ctrl
; /* pm: crt ctrl save */
95 int swap_endian
; /* set to swap rgb=>bgr */
96 void __iomem
*regs
; /* remapped registers */
97 void __iomem
*regs2d
; /* 2d remapped registers */
98 void __iomem
*fbmem
; /* remapped framebuffer */
99 size_t fbmem_len
; /* length of remapped region */
103 /* per-framebuffer private data */
105 u32 pseudo_palette
[16];
107 enum sm501_controller head
;
108 struct sm501_mem cursor
;
109 struct sm501_mem screen
;
114 void __iomem
*cursor_regs
;
115 struct sm501fb_info
*info
;
118 /* Helper functions */
120 static inline int h_total(struct fb_var_screeninfo
*var
)
122 return var
->xres
+ var
->left_margin
+
123 var
->right_margin
+ var
->hsync_len
;
126 static inline int v_total(struct fb_var_screeninfo
*var
)
128 return var
->yres
+ var
->upper_margin
+
129 var
->lower_margin
+ var
->vsync_len
;
132 /* sm501fb_sync_regs()
134 * This call is mainly for PCI bus systems where we need to
135 * ensure that any writes to the bus are completed before the
136 * next phase, or after completing a function.
139 static inline void sm501fb_sync_regs(struct sm501fb_info
*info
)
141 smc501_readl(info
->regs
);
146 * This is an attempt to lay out memory for the two framebuffers and
149 * |fbmem_res->start fbmem_res->end|
151 * |fb[0].fix.smem_start | |fb[1].fix.smem_start | 2K |
152 * |-> fb[0].fix.smem_len <-| spare |-> fb[1].fix.smem_len <-|-> cursors <-|
154 * The "spare" space is for the 2d engine data
155 * the fixed is space for the cursors (2x1Kbyte)
157 * we need to allocate memory for the 2D acceleration engine
158 * command list and the data for the engine to deal with.
160 * - all allocations must be 128bit aligned
161 * - cursors are 64x64x2 bits (1Kbyte)
165 #define SM501_MEMF_CURSOR (1)
166 #define SM501_MEMF_PANEL (2)
167 #define SM501_MEMF_CRT (4)
168 #define SM501_MEMF_ACCEL (8)
170 static int sm501_alloc_mem(struct sm501fb_info
*inf
, struct sm501_mem
*mem
,
171 unsigned int why
, size_t size
, u32 smem_len
)
173 struct sm501fb_par
*par
;
179 case SM501_MEMF_CURSOR
:
180 ptr
= inf
->fbmem_len
- size
;
181 inf
->fbmem_len
= ptr
; /* adjust available memory. */
184 case SM501_MEMF_PANEL
:
185 if (size
> inf
->fbmem_len
)
188 ptr
= inf
->fbmem_len
- size
;
189 fbi
= inf
->fb
[HEAD_CRT
];
191 /* round down, some programs such as directfb do not draw
192 * 0,0 correctly unless the start is aligned to a page start.
196 ptr
&= ~(PAGE_SIZE
- 1);
198 if (fbi
&& ptr
< smem_len
)
206 /* check to see if we have panel memory allocated
207 * which would put an limit on available memory. */
209 fbi
= inf
->fb
[HEAD_PANEL
];
212 end
= par
->screen
.k_addr
? par
->screen
.sm_addr
: inf
->fbmem_len
;
214 end
= inf
->fbmem_len
;
216 if ((ptr
+ size
) > end
)
221 case SM501_MEMF_ACCEL
:
222 fbi
= inf
->fb
[HEAD_CRT
];
223 ptr
= fbi
? smem_len
: 0;
225 fbi
= inf
->fb
[HEAD_PANEL
];
228 end
= par
->screen
.sm_addr
;
230 end
= inf
->fbmem_len
;
232 if ((ptr
+ size
) > end
)
243 mem
->k_addr
= inf
->fbmem
+ ptr
;
245 dev_dbg(inf
->dev
, "%s: result %08lx, %p - %u, %zd\n",
246 __func__
, mem
->sm_addr
, mem
->k_addr
, why
, size
);
253 * Converts a period in picoseconds to Hz.
255 * Note, we try to keep this in Hz to minimise rounding with
256 * the limited PLL settings on the SM501.
259 static unsigned long sm501fb_ps_to_hz(unsigned long psvalue
)
261 unsigned long long numerator
=1000000000000ULL;
263 /* 10^12 / picosecond period gives frequency in Hz */
264 do_div(numerator
, psvalue
);
265 return (unsigned long)numerator
;
268 /* sm501fb_hz_to_ps is identical to the opposite transform */
270 #define sm501fb_hz_to_ps(x) sm501fb_ps_to_hz(x)
272 /* sm501fb_setup_gamma
274 * Programs a linear 1.0 gamma ramp in case the gamma
275 * correction is enabled without programming anything else.
278 static void sm501fb_setup_gamma(struct sm501fb_info
*fbi
,
279 unsigned long palette
)
281 unsigned long value
= 0;
284 /* set gamma values */
285 for (offset
= 0; offset
< 256 * 4; offset
+= 4) {
286 smc501_writel(value
, fbi
->regs
+ palette
+ offset
);
287 value
+= 0x010101; /* Advance RGB by 1,1,1.*/
293 * check common variables for both panel and crt
296 static int sm501fb_check_var(struct fb_var_screeninfo
*var
,
297 struct fb_info
*info
)
299 struct sm501fb_par
*par
= info
->par
;
300 struct sm501fb_info
*sm
= par
->info
;
303 /* check we can fit these values into the registers */
305 if (var
->hsync_len
> 255 || var
->vsync_len
> 63)
308 /* hdisplay end and hsync start */
309 if ((var
->xres
+ var
->right_margin
) > 4096)
312 /* vdisplay end and vsync start */
313 if ((var
->yres
+ var
->lower_margin
) > 2048)
316 /* hard limits of device */
318 if (h_total(var
) > 4096 || v_total(var
) > 2048)
321 /* check our line length is going to be 128 bit aligned */
323 tmp
= (var
->xres
* var
->bits_per_pixel
) / 8;
327 /* check the virtual size */
329 if (var
->xres_virtual
> 4096 || var
->yres_virtual
> 2048)
332 /* can cope with 8,16 or 32bpp */
334 if (var
->bits_per_pixel
<= 8)
335 var
->bits_per_pixel
= 8;
336 else if (var
->bits_per_pixel
<= 16)
337 var
->bits_per_pixel
= 16;
338 else if (var
->bits_per_pixel
== 24)
339 var
->bits_per_pixel
= 32;
341 /* set r/g/b positions and validate bpp */
342 switch(var
->bits_per_pixel
) {
344 var
->red
.length
= var
->bits_per_pixel
;
346 var
->green
.length
= var
->bits_per_pixel
;
347 var
->green
.offset
= 0;
348 var
->blue
.length
= var
->bits_per_pixel
;
349 var
->blue
.offset
= 0;
350 var
->transp
.length
= 0;
351 var
->transp
.offset
= 0;
356 if (sm
->pdata
->flags
& SM501_FBPD_SWAP_FB_ENDIAN
) {
357 var
->blue
.offset
= 11;
358 var
->green
.offset
= 5;
361 var
->red
.offset
= 11;
362 var
->green
.offset
= 5;
363 var
->blue
.offset
= 0;
365 var
->transp
.offset
= 0;
368 var
->green
.length
= 6;
369 var
->blue
.length
= 5;
370 var
->transp
.length
= 0;
374 if (sm
->pdata
->flags
& SM501_FBPD_SWAP_FB_ENDIAN
) {
375 var
->transp
.offset
= 0;
377 var
->green
.offset
= 16;
378 var
->blue
.offset
= 24;
380 var
->transp
.offset
= 24;
381 var
->red
.offset
= 16;
382 var
->green
.offset
= 8;
383 var
->blue
.offset
= 0;
387 var
->green
.length
= 8;
388 var
->blue
.length
= 8;
389 var
->transp
.length
= 0;
400 * sm501fb_check_var_crt():
402 * check the parameters for the CRT head, and either bring them
403 * back into range, or return -EINVAL.
406 static int sm501fb_check_var_crt(struct fb_var_screeninfo
*var
,
407 struct fb_info
*info
)
409 return sm501fb_check_var(var
, info
);
412 /* sm501fb_check_var_pnl():
414 * check the parameters for the CRT head, and either bring them
415 * back into range, or return -EINVAL.
418 static int sm501fb_check_var_pnl(struct fb_var_screeninfo
*var
,
419 struct fb_info
*info
)
421 return sm501fb_check_var(var
, info
);
424 /* sm501fb_set_par_common
426 * set common registers for framebuffers
429 static int sm501fb_set_par_common(struct fb_info
*info
,
430 struct fb_var_screeninfo
*var
)
432 struct sm501fb_par
*par
= info
->par
;
433 struct sm501fb_info
*fbi
= par
->info
;
434 unsigned long pixclock
; /* pixelclock in Hz */
435 unsigned long sm501pixclock
; /* pixelclock the 501 can achieve in Hz */
436 unsigned int mem_type
;
437 unsigned int clock_type
;
438 unsigned int head_addr
;
439 unsigned int smem_len
;
441 dev_dbg(fbi
->dev
, "%s: %dx%d, bpp = %d, virtual %dx%d\n",
442 __func__
, var
->xres
, var
->yres
, var
->bits_per_pixel
,
443 var
->xres_virtual
, var
->yres_virtual
);
447 mem_type
= SM501_MEMF_CRT
;
448 clock_type
= SM501_CLOCK_V2XCLK
;
449 head_addr
= SM501_DC_CRT_FB_ADDR
;
453 mem_type
= SM501_MEMF_PANEL
;
454 clock_type
= SM501_CLOCK_P2XCLK
;
455 head_addr
= SM501_DC_PANEL_FB_ADDR
;
459 mem_type
= 0; /* stop compiler warnings */
464 switch (var
->bits_per_pixel
) {
466 info
->fix
.visual
= FB_VISUAL_PSEUDOCOLOR
;
470 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
474 info
->fix
.visual
= FB_VISUAL_TRUECOLOR
;
478 /* allocate fb memory within 501 */
479 info
->fix
.line_length
= (var
->xres_virtual
* var
->bits_per_pixel
)/8;
480 smem_len
= info
->fix
.line_length
* var
->yres_virtual
;
482 dev_dbg(fbi
->dev
, "%s: line length = %u\n", __func__
,
483 info
->fix
.line_length
);
485 if (sm501_alloc_mem(fbi
, &par
->screen
, mem_type
, smem_len
, smem_len
)) {
486 dev_err(fbi
->dev
, "no memory available\n");
490 mutex_lock(&info
->mm_lock
);
491 info
->fix
.smem_start
= fbi
->fbmem_res
->start
+ par
->screen
.sm_addr
;
492 info
->fix
.smem_len
= smem_len
;
493 mutex_unlock(&info
->mm_lock
);
495 info
->screen_base
= fbi
->fbmem
+ par
->screen
.sm_addr
;
496 info
->screen_size
= info
->fix
.smem_len
;
498 /* set start of framebuffer to the screen */
500 smc501_writel(par
->screen
.sm_addr
| SM501_ADDR_FLIP
,
501 fbi
->regs
+ head_addr
);
503 /* program CRT clock */
505 pixclock
= sm501fb_ps_to_hz(var
->pixclock
);
507 sm501pixclock
= sm501_set_clock(fbi
->dev
->parent
, clock_type
,
510 /* update fb layer with actual clock used */
511 var
->pixclock
= sm501fb_hz_to_ps(sm501pixclock
);
513 dev_dbg(fbi
->dev
, "%s: pixclock(ps) = %u, pixclock(Hz) = %lu, "
514 "sm501pixclock = %lu, error = %ld%%\n",
515 __func__
, var
->pixclock
, pixclock
, sm501pixclock
,
516 ((pixclock
- sm501pixclock
)*100)/pixclock
);
521 /* sm501fb_set_par_geometry
523 * set the geometry registers for specified framebuffer.
526 static void sm501fb_set_par_geometry(struct fb_info
*info
,
527 struct fb_var_screeninfo
*var
)
529 struct sm501fb_par
*par
= info
->par
;
530 struct sm501fb_info
*fbi
= par
->info
;
531 void __iomem
*base
= fbi
->regs
;
534 if (par
->head
== HEAD_CRT
)
535 base
+= SM501_DC_CRT_H_TOT
;
537 base
+= SM501_DC_PANEL_H_TOT
;
539 /* set framebuffer width and display width */
541 reg
= info
->fix
.line_length
;
542 reg
|= ((var
->xres
* var
->bits_per_pixel
)/8) << 16;
544 smc501_writel(reg
, fbi
->regs
+ (par
->head
== HEAD_CRT
?
545 SM501_DC_CRT_FB_OFFSET
: SM501_DC_PANEL_FB_OFFSET
));
547 /* program horizontal total */
549 reg
= (h_total(var
) - 1) << 16;
550 reg
|= (var
->xres
- 1);
552 smc501_writel(reg
, base
+ SM501_OFF_DC_H_TOT
);
554 /* program horizontal sync */
556 reg
= var
->hsync_len
<< 16;
557 reg
|= var
->xres
+ var
->right_margin
- 1;
559 smc501_writel(reg
, base
+ SM501_OFF_DC_H_SYNC
);
561 /* program vertical total */
563 reg
= (v_total(var
) - 1) << 16;
564 reg
|= (var
->yres
- 1);
566 smc501_writel(reg
, base
+ SM501_OFF_DC_V_TOT
);
568 /* program vertical sync */
569 reg
= var
->vsync_len
<< 16;
570 reg
|= var
->yres
+ var
->lower_margin
- 1;
572 smc501_writel(reg
, base
+ SM501_OFF_DC_V_SYNC
);
577 * pan the CRT display output within an virtual framebuffer
580 static int sm501fb_pan_crt(struct fb_var_screeninfo
*var
,
581 struct fb_info
*info
)
583 struct sm501fb_par
*par
= info
->par
;
584 struct sm501fb_info
*fbi
= par
->info
;
585 unsigned int bytes_pixel
= info
->var
.bits_per_pixel
/ 8;
589 xoffs
= var
->xoffset
* bytes_pixel
;
591 reg
= smc501_readl(fbi
->regs
+ SM501_DC_CRT_CONTROL
);
593 reg
&= ~SM501_DC_CRT_CONTROL_PIXEL_MASK
;
594 reg
|= ((xoffs
& 15) / bytes_pixel
) << 4;
595 smc501_writel(reg
, fbi
->regs
+ SM501_DC_CRT_CONTROL
);
597 reg
= (par
->screen
.sm_addr
+ xoffs
+
598 var
->yoffset
* info
->fix
.line_length
);
599 smc501_writel(reg
| SM501_ADDR_FLIP
, fbi
->regs
+ SM501_DC_CRT_FB_ADDR
);
601 sm501fb_sync_regs(fbi
);
607 * pan the panel display output within an virtual framebuffer
610 static int sm501fb_pan_pnl(struct fb_var_screeninfo
*var
,
611 struct fb_info
*info
)
613 struct sm501fb_par
*par
= info
->par
;
614 struct sm501fb_info
*fbi
= par
->info
;
617 reg
= var
->xoffset
| (info
->var
.xres_virtual
<< 16);
618 smc501_writel(reg
, fbi
->regs
+ SM501_DC_PANEL_FB_WIDTH
);
620 reg
= var
->yoffset
| (info
->var
.yres_virtual
<< 16);
621 smc501_writel(reg
, fbi
->regs
+ SM501_DC_PANEL_FB_HEIGHT
);
623 sm501fb_sync_regs(fbi
);
627 /* sm501fb_set_par_crt
629 * Set the CRT video mode from the fb_info structure
632 static int sm501fb_set_par_crt(struct fb_info
*info
)
634 struct sm501fb_par
*par
= info
->par
;
635 struct sm501fb_info
*fbi
= par
->info
;
636 struct fb_var_screeninfo
*var
= &info
->var
;
637 unsigned long control
; /* control register */
640 /* activate new configuration */
642 dev_dbg(fbi
->dev
, "%s(%p)\n", __func__
, info
);
644 /* enable CRT DAC - note 0 is on!*/
645 sm501_misc_control(fbi
->dev
->parent
, 0, SM501_MISC_DAC_POWER
);
647 control
= smc501_readl(fbi
->regs
+ SM501_DC_CRT_CONTROL
);
649 control
&= (SM501_DC_CRT_CONTROL_PIXEL_MASK
|
650 SM501_DC_CRT_CONTROL_GAMMA
|
651 SM501_DC_CRT_CONTROL_BLANK
|
652 SM501_DC_CRT_CONTROL_SEL
|
653 SM501_DC_CRT_CONTROL_CP
|
654 SM501_DC_CRT_CONTROL_TVP
);
656 /* set the sync polarities before we check data source */
658 if ((var
->sync
& FB_SYNC_HOR_HIGH_ACT
) == 0)
659 control
|= SM501_DC_CRT_CONTROL_HSP
;
661 if ((var
->sync
& FB_SYNC_VERT_HIGH_ACT
) == 0)
662 control
|= SM501_DC_CRT_CONTROL_VSP
;
664 if ((control
& SM501_DC_CRT_CONTROL_SEL
) == 0) {
665 /* the head is displaying panel data... */
667 sm501_alloc_mem(fbi
, &par
->screen
, SM501_MEMF_CRT
, 0,
672 ret
= sm501fb_set_par_common(info
, var
);
674 dev_err(fbi
->dev
, "failed to set common parameters\n");
678 sm501fb_pan_crt(var
, info
);
679 sm501fb_set_par_geometry(info
, var
);
681 control
|= SM501_FIFO_3
; /* fill if >3 free slots */
683 switch(var
->bits_per_pixel
) {
685 control
|= SM501_DC_CRT_CONTROL_8BPP
;
689 control
|= SM501_DC_CRT_CONTROL_16BPP
;
690 sm501fb_setup_gamma(fbi
, SM501_DC_CRT_PALETTE
);
694 control
|= SM501_DC_CRT_CONTROL_32BPP
;
695 sm501fb_setup_gamma(fbi
, SM501_DC_CRT_PALETTE
);
702 control
|= SM501_DC_CRT_CONTROL_SEL
; /* CRT displays CRT data */
703 control
|= SM501_DC_CRT_CONTROL_TE
; /* enable CRT timing */
704 control
|= SM501_DC_CRT_CONTROL_ENABLE
; /* enable CRT plane */
707 dev_dbg(fbi
->dev
, "new control is %08lx\n", control
);
709 smc501_writel(control
, fbi
->regs
+ SM501_DC_CRT_CONTROL
);
710 sm501fb_sync_regs(fbi
);
715 static void sm501fb_panel_power(struct sm501fb_info
*fbi
, int to
)
717 unsigned long control
;
718 void __iomem
*ctrl_reg
= fbi
->regs
+ SM501_DC_PANEL_CONTROL
;
719 struct sm501_platdata_fbsub
*pd
= fbi
->pdata
->fb_pnl
;
721 control
= smc501_readl(ctrl_reg
);
723 if (to
&& (control
& SM501_DC_PANEL_CONTROL_VDD
) == 0) {
724 /* enable panel power */
726 control
|= SM501_DC_PANEL_CONTROL_VDD
; /* FPVDDEN */
727 smc501_writel(control
, ctrl_reg
);
728 sm501fb_sync_regs(fbi
);
731 control
|= SM501_DC_PANEL_CONTROL_DATA
; /* DATA */
732 smc501_writel(control
, ctrl_reg
);
733 sm501fb_sync_regs(fbi
);
738 if (!(pd
->flags
& SM501FB_FLAG_PANEL_NO_VBIASEN
)) {
739 if (pd
->flags
& SM501FB_FLAG_PANEL_INV_VBIASEN
)
740 control
&= ~SM501_DC_PANEL_CONTROL_BIAS
;
742 control
|= SM501_DC_PANEL_CONTROL_BIAS
;
744 smc501_writel(control
, ctrl_reg
);
745 sm501fb_sync_regs(fbi
);
749 if (!(pd
->flags
& SM501FB_FLAG_PANEL_NO_FPEN
)) {
750 if (pd
->flags
& SM501FB_FLAG_PANEL_INV_FPEN
)
751 control
&= ~SM501_DC_PANEL_CONTROL_FPEN
;
753 control
|= SM501_DC_PANEL_CONTROL_FPEN
;
755 smc501_writel(control
, ctrl_reg
);
756 sm501fb_sync_regs(fbi
);
759 } else if (!to
&& (control
& SM501_DC_PANEL_CONTROL_VDD
) != 0) {
760 /* disable panel power */
761 if (!(pd
->flags
& SM501FB_FLAG_PANEL_NO_FPEN
)) {
762 if (pd
->flags
& SM501FB_FLAG_PANEL_INV_FPEN
)
763 control
|= SM501_DC_PANEL_CONTROL_FPEN
;
765 control
&= ~SM501_DC_PANEL_CONTROL_FPEN
;
767 smc501_writel(control
, ctrl_reg
);
768 sm501fb_sync_regs(fbi
);
772 if (!(pd
->flags
& SM501FB_FLAG_PANEL_NO_VBIASEN
)) {
773 if (pd
->flags
& SM501FB_FLAG_PANEL_INV_VBIASEN
)
774 control
|= SM501_DC_PANEL_CONTROL_BIAS
;
776 control
&= ~SM501_DC_PANEL_CONTROL_BIAS
;
778 smc501_writel(control
, ctrl_reg
);
779 sm501fb_sync_regs(fbi
);
783 control
&= ~SM501_DC_PANEL_CONTROL_DATA
;
784 smc501_writel(control
, ctrl_reg
);
785 sm501fb_sync_regs(fbi
);
788 control
&= ~SM501_DC_PANEL_CONTROL_VDD
;
789 smc501_writel(control
, ctrl_reg
);
790 sm501fb_sync_regs(fbi
);
794 sm501fb_sync_regs(fbi
);
797 /* sm501fb_set_par_pnl
799 * Set the panel video mode from the fb_info structure
802 static int sm501fb_set_par_pnl(struct fb_info
*info
)
804 struct sm501fb_par
*par
= info
->par
;
805 struct sm501fb_info
*fbi
= par
->info
;
806 struct fb_var_screeninfo
*var
= &info
->var
;
807 unsigned long control
;
811 dev_dbg(fbi
->dev
, "%s(%p)\n", __func__
, info
);
813 /* activate this new configuration */
815 ret
= sm501fb_set_par_common(info
, var
);
819 sm501fb_pan_pnl(var
, info
);
820 sm501fb_set_par_geometry(info
, var
);
822 /* update control register */
824 control
= smc501_readl(fbi
->regs
+ SM501_DC_PANEL_CONTROL
);
825 control
&= (SM501_DC_PANEL_CONTROL_GAMMA
|
826 SM501_DC_PANEL_CONTROL_VDD
|
827 SM501_DC_PANEL_CONTROL_DATA
|
828 SM501_DC_PANEL_CONTROL_BIAS
|
829 SM501_DC_PANEL_CONTROL_FPEN
|
830 SM501_DC_PANEL_CONTROL_CP
|
831 SM501_DC_PANEL_CONTROL_CK
|
832 SM501_DC_PANEL_CONTROL_HP
|
833 SM501_DC_PANEL_CONTROL_VP
|
834 SM501_DC_PANEL_CONTROL_HPD
|
835 SM501_DC_PANEL_CONTROL_VPD
);
837 control
|= SM501_FIFO_3
; /* fill if >3 free slots */
839 switch(var
->bits_per_pixel
) {
841 control
|= SM501_DC_PANEL_CONTROL_8BPP
;
845 control
|= SM501_DC_PANEL_CONTROL_16BPP
;
846 sm501fb_setup_gamma(fbi
, SM501_DC_PANEL_PALETTE
);
850 control
|= SM501_DC_PANEL_CONTROL_32BPP
;
851 sm501fb_setup_gamma(fbi
, SM501_DC_PANEL_PALETTE
);
858 smc501_writel(0x0, fbi
->regs
+ SM501_DC_PANEL_PANNING_CONTROL
);
860 /* panel plane top left and bottom right location */
862 smc501_writel(0x00, fbi
->regs
+ SM501_DC_PANEL_TL_LOC
);
865 reg
|= (var
->yres
- 1) << 16;
867 smc501_writel(reg
, fbi
->regs
+ SM501_DC_PANEL_BR_LOC
);
869 /* program panel control register */
871 control
|= SM501_DC_PANEL_CONTROL_TE
; /* enable PANEL timing */
872 control
|= SM501_DC_PANEL_CONTROL_EN
; /* enable PANEL gfx plane */
874 if ((var
->sync
& FB_SYNC_HOR_HIGH_ACT
) == 0)
875 control
|= SM501_DC_PANEL_CONTROL_HSP
;
877 if ((var
->sync
& FB_SYNC_VERT_HIGH_ACT
) == 0)
878 control
|= SM501_DC_PANEL_CONTROL_VSP
;
880 smc501_writel(control
, fbi
->regs
+ SM501_DC_PANEL_CONTROL
);
881 sm501fb_sync_regs(fbi
);
883 /* ensure the panel interface is not tristated at this point */
885 sm501_modify_reg(fbi
->dev
->parent
, SM501_SYSTEM_CONTROL
,
886 0, SM501_SYSCTRL_PANEL_TRISTATE
);
888 /* power the panel up */
889 sm501fb_panel_power(fbi
, 1);
896 * convert a colour value into a field position
901 static inline unsigned int chan_to_field(unsigned int chan
,
902 struct fb_bitfield
*bf
)
905 chan
>>= 16 - bf
->length
;
906 return chan
<< bf
->offset
;
911 * set the colour mapping for modes that support palettised data
914 static int sm501fb_setcolreg(unsigned regno
,
915 unsigned red
, unsigned green
, unsigned blue
,
916 unsigned transp
, struct fb_info
*info
)
918 struct sm501fb_par
*par
= info
->par
;
919 struct sm501fb_info
*fbi
= par
->info
;
920 void __iomem
*base
= fbi
->regs
;
923 if (par
->head
== HEAD_CRT
)
924 base
+= SM501_DC_CRT_PALETTE
;
926 base
+= SM501_DC_PANEL_PALETTE
;
928 switch (info
->fix
.visual
) {
929 case FB_VISUAL_TRUECOLOR
:
930 /* true-colour, use pseuo-palette */
933 u32
*pal
= par
->pseudo_palette
;
935 val
= chan_to_field(red
, &info
->var
.red
);
936 val
|= chan_to_field(green
, &info
->var
.green
);
937 val
|= chan_to_field(blue
, &info
->var
.blue
);
943 case FB_VISUAL_PSEUDOCOLOR
:
945 val
= (red
>> 8) << 16;
946 val
|= (green
>> 8) << 8;
949 smc501_writel(val
, base
+ (regno
* 4));
955 return 1; /* unknown type */
963 * Blank or un-blank the panel interface
966 static int sm501fb_blank_pnl(int blank_mode
, struct fb_info
*info
)
968 struct sm501fb_par
*par
= info
->par
;
969 struct sm501fb_info
*fbi
= par
->info
;
971 dev_dbg(fbi
->dev
, "%s(mode=%d, %p)\n", __func__
, blank_mode
, info
);
973 switch (blank_mode
) {
974 case FB_BLANK_POWERDOWN
:
975 sm501fb_panel_power(fbi
, 0);
978 case FB_BLANK_UNBLANK
:
979 sm501fb_panel_power(fbi
, 1);
982 case FB_BLANK_NORMAL
:
983 case FB_BLANK_VSYNC_SUSPEND
:
984 case FB_BLANK_HSYNC_SUSPEND
:
994 * Blank or un-blank the crt interface
997 static int sm501fb_blank_crt(int blank_mode
, struct fb_info
*info
)
999 struct sm501fb_par
*par
= info
->par
;
1000 struct sm501fb_info
*fbi
= par
->info
;
1003 dev_dbg(fbi
->dev
, "%s(mode=%d, %p)\n", __func__
, blank_mode
, info
);
1005 ctrl
= smc501_readl(fbi
->regs
+ SM501_DC_CRT_CONTROL
);
1007 switch (blank_mode
) {
1008 case FB_BLANK_POWERDOWN
:
1009 ctrl
&= ~SM501_DC_CRT_CONTROL_ENABLE
;
1010 sm501_misc_control(fbi
->dev
->parent
, SM501_MISC_DAC_POWER
, 0);
1012 case FB_BLANK_NORMAL
:
1013 ctrl
|= SM501_DC_CRT_CONTROL_BLANK
;
1016 case FB_BLANK_UNBLANK
:
1017 ctrl
&= ~SM501_DC_CRT_CONTROL_BLANK
;
1018 ctrl
|= SM501_DC_CRT_CONTROL_ENABLE
;
1019 sm501_misc_control(fbi
->dev
->parent
, 0, SM501_MISC_DAC_POWER
);
1022 case FB_BLANK_VSYNC_SUSPEND
:
1023 case FB_BLANK_HSYNC_SUSPEND
:
1029 smc501_writel(ctrl
, fbi
->regs
+ SM501_DC_CRT_CONTROL
);
1030 sm501fb_sync_regs(fbi
);
1037 * set or change the hardware cursor parameters
1040 static int sm501fb_cursor(struct fb_info
*info
, struct fb_cursor
*cursor
)
1042 struct sm501fb_par
*par
= info
->par
;
1043 struct sm501fb_info
*fbi
= par
->info
;
1044 void __iomem
*base
= fbi
->regs
;
1045 unsigned long hwc_addr
;
1046 unsigned long fg
, bg
;
1048 dev_dbg(fbi
->dev
, "%s(%p,%p)\n", __func__
, info
, cursor
);
1050 if (par
->head
== HEAD_CRT
)
1051 base
+= SM501_DC_CRT_HWC_BASE
;
1053 base
+= SM501_DC_PANEL_HWC_BASE
;
1055 /* check not being asked to exceed capabilities */
1057 if (cursor
->image
.width
> 64)
1060 if (cursor
->image
.height
> 64)
1063 if (cursor
->image
.depth
> 1)
1066 hwc_addr
= smc501_readl(base
+ SM501_OFF_HWC_ADDR
);
1069 smc501_writel(hwc_addr
| SM501_HWC_EN
,
1070 base
+ SM501_OFF_HWC_ADDR
);
1072 smc501_writel(hwc_addr
& ~SM501_HWC_EN
,
1073 base
+ SM501_OFF_HWC_ADDR
);
1076 if (cursor
->set
& FB_CUR_SETPOS
) {
1077 unsigned int x
= cursor
->image
.dx
;
1078 unsigned int y
= cursor
->image
.dy
;
1080 if (x
>= 2048 || y
>= 2048 )
1083 dev_dbg(fbi
->dev
, "set position %d,%d\n", x
, y
);
1085 //y += cursor->image.height;
1087 smc501_writel(x
| (y
<< 16), base
+ SM501_OFF_HWC_LOC
);
1090 if (cursor
->set
& FB_CUR_SETCMAP
) {
1091 unsigned int bg_col
= cursor
->image
.bg_color
;
1092 unsigned int fg_col
= cursor
->image
.fg_color
;
1094 dev_dbg(fbi
->dev
, "%s: update cmap (%08x,%08x)\n",
1095 __func__
, bg_col
, fg_col
);
1097 bg
= ((info
->cmap
.red
[bg_col
] & 0xF8) << 8) |
1098 ((info
->cmap
.green
[bg_col
] & 0xFC) << 3) |
1099 ((info
->cmap
.blue
[bg_col
] & 0xF8) >> 3);
1101 fg
= ((info
->cmap
.red
[fg_col
] & 0xF8) << 8) |
1102 ((info
->cmap
.green
[fg_col
] & 0xFC) << 3) |
1103 ((info
->cmap
.blue
[fg_col
] & 0xF8) >> 3);
1105 dev_dbg(fbi
->dev
, "fgcol %08lx, bgcol %08lx\n", fg
, bg
);
1107 smc501_writel(bg
, base
+ SM501_OFF_HWC_COLOR_1_2
);
1108 smc501_writel(fg
, base
+ SM501_OFF_HWC_COLOR_3
);
1111 if (cursor
->set
& FB_CUR_SETSIZE
||
1112 cursor
->set
& (FB_CUR_SETIMAGE
| FB_CUR_SETSHAPE
)) {
1113 /* SM501 cursor is a two bpp 64x64 bitmap this routine
1114 * clears it to transparent then combines the cursor
1115 * shape plane with the colour plane to set the
1118 const unsigned char *pcol
= cursor
->image
.data
;
1119 const unsigned char *pmsk
= cursor
->mask
;
1120 void __iomem
*dst
= par
->cursor
.k_addr
;
1121 unsigned char dcol
= 0;
1122 unsigned char dmsk
= 0;
1125 dev_dbg(fbi
->dev
, "%s: setting shape (%d,%d)\n",
1126 __func__
, cursor
->image
.width
, cursor
->image
.height
);
1128 for (op
= 0; op
< (64*64*2)/8; op
+=4)
1129 smc501_writel(0x0, dst
+ op
);
1131 for (y
= 0; y
< cursor
->image
.height
; y
++) {
1132 for (x
= 0; x
< cursor
->image
.width
; x
++) {
1142 op
= (dcol
& 1) ? 1 : 3;
1143 op
<<= ((x
% 4) * 2);
1145 op
|= readb(dst
+ (x
/ 4));
1146 writeb(op
, dst
+ (x
/ 4));
1153 sm501fb_sync_regs(fbi
); /* ensure cursor data flushed */
1157 /* sm501fb_crtsrc_show
1159 * device attribute code to show where the crt output is sourced from
1162 static ssize_t
sm501fb_crtsrc_show(struct device
*dev
,
1163 struct device_attribute
*attr
, char *buf
)
1165 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1168 ctrl
= smc501_readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1169 ctrl
&= SM501_DC_CRT_CONTROL_SEL
;
1171 return snprintf(buf
, PAGE_SIZE
, "%s\n", ctrl
? "crt" : "panel");
1174 /* sm501fb_crtsrc_show
1176 * device attribute code to set where the crt output is sourced from
1179 static ssize_t
sm501fb_crtsrc_store(struct device
*dev
,
1180 struct device_attribute
*attr
,
1181 const char *buf
, size_t len
)
1183 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1184 enum sm501_controller head
;
1190 if (strnicmp(buf
, "crt", 3) == 0)
1192 else if (strnicmp(buf
, "panel", 5) == 0)
1197 dev_info(dev
, "setting crt source to head %d\n", head
);
1199 ctrl
= smc501_readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1201 if (head
== HEAD_CRT
) {
1202 ctrl
|= SM501_DC_CRT_CONTROL_SEL
;
1203 ctrl
|= SM501_DC_CRT_CONTROL_ENABLE
;
1204 ctrl
|= SM501_DC_CRT_CONTROL_TE
;
1206 ctrl
&= ~SM501_DC_CRT_CONTROL_SEL
;
1207 ctrl
&= ~SM501_DC_CRT_CONTROL_ENABLE
;
1208 ctrl
&= ~SM501_DC_CRT_CONTROL_TE
;
1211 smc501_writel(ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1212 sm501fb_sync_regs(info
);
1217 /* Prepare the device_attr for registration with sysfs later */
1218 static DEVICE_ATTR(crt_src
, 0666, sm501fb_crtsrc_show
, sm501fb_crtsrc_store
);
1220 /* sm501fb_show_regs
1222 * show the primary sm501 registers
1224 static int sm501fb_show_regs(struct sm501fb_info
*info
, char *ptr
,
1225 unsigned int start
, unsigned int len
)
1227 void __iomem
*mem
= info
->regs
;
1231 for (reg
= start
; reg
< (len
+ start
); reg
+= 4)
1232 ptr
+= sprintf(ptr
, "%08x = %08x\n", reg
,
1233 smc501_readl(mem
+ reg
));
1238 /* sm501fb_debug_show_crt
1240 * show the crt control and cursor registers
1243 static ssize_t
sm501fb_debug_show_crt(struct device
*dev
,
1244 struct device_attribute
*attr
, char *buf
)
1246 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1249 ptr
+= sm501fb_show_regs(info
, ptr
, SM501_DC_CRT_CONTROL
, 0x40);
1250 ptr
+= sm501fb_show_regs(info
, ptr
, SM501_DC_CRT_HWC_BASE
, 0x10);
1255 static DEVICE_ATTR(fbregs_crt
, 0444, sm501fb_debug_show_crt
, NULL
);
1257 /* sm501fb_debug_show_pnl
1259 * show the panel control and cursor registers
1262 static ssize_t
sm501fb_debug_show_pnl(struct device
*dev
,
1263 struct device_attribute
*attr
, char *buf
)
1265 struct sm501fb_info
*info
= dev_get_drvdata(dev
);
1268 ptr
+= sm501fb_show_regs(info
, ptr
, 0x0, 0x40);
1269 ptr
+= sm501fb_show_regs(info
, ptr
, SM501_DC_PANEL_HWC_BASE
, 0x10);
1274 static DEVICE_ATTR(fbregs_pnl
, 0444, sm501fb_debug_show_pnl
, NULL
);
1276 /* acceleration operations */
1277 static int sm501fb_sync(struct fb_info
*info
)
1279 int count
= 1000000;
1280 struct sm501fb_par
*par
= info
->par
;
1281 struct sm501fb_info
*fbi
= par
->info
;
1283 /* wait for the 2d engine to be ready */
1284 while ((count
> 0) &&
1285 (smc501_readl(fbi
->regs
+ SM501_SYSTEM_CONTROL
) &
1286 SM501_SYSCTRL_2D_ENGINE_STATUS
) != 0)
1290 dev_err(info
->dev
, "Timeout waiting for 2d engine sync\n");
1296 static void sm501fb_copyarea(struct fb_info
*info
, const struct fb_copyarea
*area
)
1298 struct sm501fb_par
*par
= info
->par
;
1299 struct sm501fb_info
*fbi
= par
->info
;
1300 int width
= area
->width
;
1301 int height
= area
->height
;
1306 unsigned long rtl
= 0;
1309 if ((sx
>= info
->var
.xres_virtual
) ||
1310 (sy
>= info
->var
.yres_virtual
))
1311 /* source Area not within virtual screen, skipping */
1313 if ((sx
+ width
) >= info
->var
.xres_virtual
)
1314 width
= info
->var
.xres_virtual
- sx
- 1;
1315 if ((sy
+ height
) >= info
->var
.yres_virtual
)
1316 height
= info
->var
.yres_virtual
- sy
- 1;
1319 if ((dx
>= info
->var
.xres_virtual
) ||
1320 (dy
>= info
->var
.yres_virtual
))
1321 /* Destination Area not within virtual screen, skipping */
1323 if ((dx
+ width
) >= info
->var
.xres_virtual
)
1324 width
= info
->var
.xres_virtual
- dx
- 1;
1325 if ((dy
+ height
) >= info
->var
.yres_virtual
)
1326 height
= info
->var
.yres_virtual
- dy
- 1;
1328 if ((sx
< dx
) || (sy
< dy
)) {
1336 if (sm501fb_sync(info
))
1339 /* set the base addresses */
1340 smc501_writel(par
->screen
.sm_addr
, fbi
->regs2d
+ SM501_2D_SOURCE_BASE
);
1341 smc501_writel(par
->screen
.sm_addr
,
1342 fbi
->regs2d
+ SM501_2D_DESTINATION_BASE
);
1344 /* set the window width */
1345 smc501_writel((info
->var
.xres
<< 16) | info
->var
.xres
,
1346 fbi
->regs2d
+ SM501_2D_WINDOW_WIDTH
);
1348 /* set window stride */
1349 smc501_writel((info
->var
.xres_virtual
<< 16) | info
->var
.xres_virtual
,
1350 fbi
->regs2d
+ SM501_2D_PITCH
);
1352 /* set data format */
1353 switch (info
->var
.bits_per_pixel
) {
1355 smc501_writel(0, fbi
->regs2d
+ SM501_2D_STRETCH
);
1358 smc501_writel(0x00100000, fbi
->regs2d
+ SM501_2D_STRETCH
);
1361 smc501_writel(0x00200000, fbi
->regs2d
+ SM501_2D_STRETCH
);
1365 /* 2d compare mask */
1366 smc501_writel(0xffffffff, fbi
->regs2d
+ SM501_2D_COLOR_COMPARE_MASK
);
1369 smc501_writel(0xffffffff, fbi
->regs2d
+ SM501_2D_MASK
);
1371 /* source and destination x y */
1372 smc501_writel((sx
<< 16) | sy
, fbi
->regs2d
+ SM501_2D_SOURCE
);
1373 smc501_writel((dx
<< 16) | dy
, fbi
->regs2d
+ SM501_2D_DESTINATION
);
1376 smc501_writel((width
<< 16) | height
, fbi
->regs2d
+ SM501_2D_DIMENSION
);
1379 smc501_writel(0x800000cc | rtl
, fbi
->regs2d
+ SM501_2D_CONTROL
);
1382 static void sm501fb_fillrect(struct fb_info
*info
, const struct fb_fillrect
*rect
)
1384 struct sm501fb_par
*par
= info
->par
;
1385 struct sm501fb_info
*fbi
= par
->info
;
1386 int width
= rect
->width
, height
= rect
->height
;
1388 if ((rect
->dx
>= info
->var
.xres_virtual
) ||
1389 (rect
->dy
>= info
->var
.yres_virtual
))
1390 /* Rectangle not within virtual screen, skipping */
1392 if ((rect
->dx
+ width
) >= info
->var
.xres_virtual
)
1393 width
= info
->var
.xres_virtual
- rect
->dx
- 1;
1394 if ((rect
->dy
+ height
) >= info
->var
.yres_virtual
)
1395 height
= info
->var
.yres_virtual
- rect
->dy
- 1;
1397 if (sm501fb_sync(info
))
1400 /* set the base addresses */
1401 smc501_writel(par
->screen
.sm_addr
, fbi
->regs2d
+ SM501_2D_SOURCE_BASE
);
1402 smc501_writel(par
->screen
.sm_addr
,
1403 fbi
->regs2d
+ SM501_2D_DESTINATION_BASE
);
1405 /* set the window width */
1406 smc501_writel((info
->var
.xres
<< 16) | info
->var
.xres
,
1407 fbi
->regs2d
+ SM501_2D_WINDOW_WIDTH
);
1409 /* set window stride */
1410 smc501_writel((info
->var
.xres_virtual
<< 16) | info
->var
.xres_virtual
,
1411 fbi
->regs2d
+ SM501_2D_PITCH
);
1413 /* set data format */
1414 switch (info
->var
.bits_per_pixel
) {
1416 smc501_writel(0, fbi
->regs2d
+ SM501_2D_STRETCH
);
1419 smc501_writel(0x00100000, fbi
->regs2d
+ SM501_2D_STRETCH
);
1422 smc501_writel(0x00200000, fbi
->regs2d
+ SM501_2D_STRETCH
);
1426 /* 2d compare mask */
1427 smc501_writel(0xffffffff, fbi
->regs2d
+ SM501_2D_COLOR_COMPARE_MASK
);
1430 smc501_writel(0xffffffff, fbi
->regs2d
+ SM501_2D_MASK
);
1433 smc501_writel(rect
->color
, fbi
->regs2d
+ SM501_2D_FOREGROUND
);
1436 smc501_writel((rect
->dx
<< 16) | rect
->dy
,
1437 fbi
->regs2d
+ SM501_2D_DESTINATION
);
1440 smc501_writel((width
<< 16) | height
, fbi
->regs2d
+ SM501_2D_DIMENSION
);
1442 /* do rectangle fill */
1443 smc501_writel(0x800100cc, fbi
->regs2d
+ SM501_2D_CONTROL
);
1447 static struct fb_ops sm501fb_ops_crt
= {
1448 .owner
= THIS_MODULE
,
1449 .fb_check_var
= sm501fb_check_var_crt
,
1450 .fb_set_par
= sm501fb_set_par_crt
,
1451 .fb_blank
= sm501fb_blank_crt
,
1452 .fb_setcolreg
= sm501fb_setcolreg
,
1453 .fb_pan_display
= sm501fb_pan_crt
,
1454 .fb_cursor
= sm501fb_cursor
,
1455 .fb_fillrect
= sm501fb_fillrect
,
1456 .fb_copyarea
= sm501fb_copyarea
,
1457 .fb_imageblit
= cfb_imageblit
,
1458 .fb_sync
= sm501fb_sync
,
1461 static struct fb_ops sm501fb_ops_pnl
= {
1462 .owner
= THIS_MODULE
,
1463 .fb_check_var
= sm501fb_check_var_pnl
,
1464 .fb_set_par
= sm501fb_set_par_pnl
,
1465 .fb_pan_display
= sm501fb_pan_pnl
,
1466 .fb_blank
= sm501fb_blank_pnl
,
1467 .fb_setcolreg
= sm501fb_setcolreg
,
1468 .fb_cursor
= sm501fb_cursor
,
1469 .fb_fillrect
= sm501fb_fillrect
,
1470 .fb_copyarea
= sm501fb_copyarea
,
1471 .fb_imageblit
= cfb_imageblit
,
1472 .fb_sync
= sm501fb_sync
,
1475 /* sm501_init_cursor
1477 * initialise hw cursor parameters
1480 static int sm501_init_cursor(struct fb_info
*fbi
, unsigned int reg_base
)
1482 struct sm501fb_par
*par
;
1483 struct sm501fb_info
*info
;
1492 par
->cursor_regs
= info
->regs
+ reg_base
;
1494 ret
= sm501_alloc_mem(info
, &par
->cursor
, SM501_MEMF_CURSOR
, 1024,
1499 /* initialise the colour registers */
1501 smc501_writel(par
->cursor
.sm_addr
,
1502 par
->cursor_regs
+ SM501_OFF_HWC_ADDR
);
1504 smc501_writel(0x00, par
->cursor_regs
+ SM501_OFF_HWC_LOC
);
1505 smc501_writel(0x00, par
->cursor_regs
+ SM501_OFF_HWC_COLOR_1_2
);
1506 smc501_writel(0x00, par
->cursor_regs
+ SM501_OFF_HWC_COLOR_3
);
1507 sm501fb_sync_regs(info
);
1512 /* sm501fb_info_start
1514 * fills the par structure claiming resources and remapping etc.
1517 static int sm501fb_start(struct sm501fb_info
*info
,
1518 struct platform_device
*pdev
)
1520 struct resource
*res
;
1521 struct device
*dev
= &pdev
->dev
;
1525 info
->irq
= ret
= platform_get_irq(pdev
, 0);
1527 /* we currently do not use the IRQ */
1528 dev_warn(dev
, "no irq for device\n");
1531 /* allocate, reserve and remap resources for display
1532 * controller registers */
1533 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
1535 dev_err(dev
, "no resource definition for registers\n");
1540 info
->regs_res
= request_mem_region(res
->start
,
1544 if (info
->regs_res
== NULL
) {
1545 dev_err(dev
, "cannot claim registers\n");
1550 info
->regs
= ioremap(res
->start
, resource_size(res
));
1551 if (info
->regs
== NULL
) {
1552 dev_err(dev
, "cannot remap registers\n");
1557 /* allocate, reserve and remap resources for 2d
1558 * controller registers */
1559 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 1);
1561 dev_err(dev
, "no resource definition for 2d registers\n");
1566 info
->regs2d_res
= request_mem_region(res
->start
,
1570 if (info
->regs2d_res
== NULL
) {
1571 dev_err(dev
, "cannot claim registers\n");
1576 info
->regs2d
= ioremap(res
->start
, resource_size(res
));
1577 if (info
->regs2d
== NULL
) {
1578 dev_err(dev
, "cannot remap registers\n");
1580 goto err_regs2d_res
;
1583 /* allocate, reserve resources for framebuffer */
1584 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 2);
1586 dev_err(dev
, "no memory resource defined\n");
1588 goto err_regs2d_map
;
1591 info
->fbmem_res
= request_mem_region(res
->start
,
1594 if (info
->fbmem_res
== NULL
) {
1595 dev_err(dev
, "cannot claim framebuffer\n");
1597 goto err_regs2d_map
;
1600 info
->fbmem
= ioremap(res
->start
, resource_size(res
));
1601 if (info
->fbmem
== NULL
) {
1602 dev_err(dev
, "cannot remap framebuffer\n");
1606 info
->fbmem_len
= resource_size(res
);
1608 /* clear framebuffer memory - avoids garbage data on unused fb */
1609 memset(info
->fbmem
, 0, info
->fbmem_len
);
1611 /* clear palette ram - undefined at power on */
1612 for (k
= 0; k
< (256 * 3); k
++)
1613 smc501_writel(0, info
->regs
+ SM501_DC_PANEL_PALETTE
+ (k
* 4));
1615 /* enable display controller */
1616 sm501_unit_power(dev
->parent
, SM501_GATE_DISPLAY
, 1);
1618 /* enable 2d controller */
1619 sm501_unit_power(dev
->parent
, SM501_GATE_2D_ENGINE
, 1);
1622 sm501_init_cursor(info
->fb
[HEAD_CRT
], SM501_DC_CRT_HWC_ADDR
);
1623 sm501_init_cursor(info
->fb
[HEAD_PANEL
], SM501_DC_PANEL_HWC_ADDR
);
1625 return 0; /* everything is setup */
1628 release_mem_region(info
->fbmem_res
->start
,
1629 resource_size(info
->fbmem_res
));
1632 iounmap(info
->regs2d
);
1635 release_mem_region(info
->regs2d_res
->start
,
1636 resource_size(info
->regs2d_res
));
1639 iounmap(info
->regs
);
1642 release_mem_region(info
->regs_res
->start
,
1643 resource_size(info
->regs_res
));
1649 static void sm501fb_stop(struct sm501fb_info
*info
)
1651 /* disable display controller */
1652 sm501_unit_power(info
->dev
->parent
, SM501_GATE_DISPLAY
, 0);
1654 iounmap(info
->fbmem
);
1655 release_mem_region(info
->fbmem_res
->start
,
1656 resource_size(info
->fbmem_res
));
1658 iounmap(info
->regs2d
);
1659 release_mem_region(info
->regs2d_res
->start
,
1660 resource_size(info
->regs2d_res
));
1662 iounmap(info
->regs
);
1663 release_mem_region(info
->regs_res
->start
,
1664 resource_size(info
->regs_res
));
1667 static int sm501fb_init_fb(struct fb_info
*fb
, enum sm501_controller head
,
1670 struct sm501_platdata_fbsub
*pd
;
1671 struct sm501fb_par
*par
= fb
->par
;
1672 struct sm501fb_info
*info
= par
->info
;
1674 unsigned int enable
;
1679 pd
= info
->pdata
->fb_crt
;
1680 ctrl
= smc501_readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
1681 enable
= (ctrl
& SM501_DC_CRT_CONTROL_ENABLE
) ? 1 : 0;
1683 /* ensure we set the correct source register */
1684 if (info
->pdata
->fb_route
!= SM501_FB_CRT_PANEL
) {
1685 ctrl
|= SM501_DC_CRT_CONTROL_SEL
;
1686 smc501_writel(ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1692 pd
= info
->pdata
->fb_pnl
;
1693 ctrl
= smc501_readl(info
->regs
+ SM501_DC_PANEL_CONTROL
);
1694 enable
= (ctrl
& SM501_DC_PANEL_CONTROL_EN
) ? 1 : 0;
1698 pd
= NULL
; /* stop compiler warnings */
1704 dev_info(info
->dev
, "fb %s %sabled at start\n",
1705 fbname
, enable
? "en" : "dis");
1707 /* check to see if our routing allows this */
1709 if (head
== HEAD_CRT
&& info
->pdata
->fb_route
== SM501_FB_CRT_PANEL
) {
1710 ctrl
&= ~SM501_DC_CRT_CONTROL_SEL
;
1711 smc501_writel(ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
1715 strlcpy(fb
->fix
.id
, fbname
, sizeof(fb
->fix
.id
));
1718 (head
== HEAD_CRT
) ? &sm501fb_ops_crt
: &sm501fb_ops_pnl
,
1719 sizeof(struct fb_ops
));
1721 /* update ops dependent on what we've been passed */
1723 if ((pd
->flags
& SM501FB_FLAG_USE_HWCURSOR
) == 0)
1724 par
->ops
.fb_cursor
= NULL
;
1726 fb
->fbops
= &par
->ops
;
1727 fb
->flags
= FBINFO_FLAG_DEFAULT
| FBINFO_READS_FAST
|
1728 FBINFO_HWACCEL_COPYAREA
| FBINFO_HWACCEL_FILLRECT
|
1729 FBINFO_HWACCEL_XPAN
| FBINFO_HWACCEL_YPAN
;
1731 #if defined(CONFIG_OF)
1733 if (of_get_property(info
->dev
->parent
->of_node
, "little-endian", NULL
))
1734 fb
->flags
|= FBINFO_FOREIGN_ENDIAN
;
1736 if (of_get_property(info
->dev
->parent
->of_node
, "big-endian", NULL
))
1737 fb
->flags
|= FBINFO_FOREIGN_ENDIAN
;
1742 fb
->fix
.type
= FB_TYPE_PACKED_PIXELS
;
1743 fb
->fix
.type_aux
= 0;
1744 fb
->fix
.xpanstep
= 1;
1745 fb
->fix
.ypanstep
= 1;
1746 fb
->fix
.ywrapstep
= 0;
1747 fb
->fix
.accel
= FB_ACCEL_NONE
;
1752 fb
->var
.activate
= FB_ACTIVATE_NOW
;
1753 fb
->var
.accel_flags
= 0;
1754 fb
->var
.vmode
= FB_VMODE_NONINTERLACED
;
1755 fb
->var
.bits_per_pixel
= 16;
1757 if (info
->edid_data
) {
1758 /* Now build modedb from EDID */
1759 fb_edid_to_monspecs(info
->edid_data
, &fb
->monspecs
);
1760 fb_videomode_to_modelist(fb
->monspecs
.modedb
,
1761 fb
->monspecs
.modedb_len
,
1765 if (enable
&& (pd
->flags
& SM501FB_FLAG_USE_INIT_MODE
) && 0) {
1766 /* TODO read the mode from the current display */
1769 dev_info(info
->dev
, "using supplied mode\n");
1770 fb_videomode_to_var(&fb
->var
, pd
->def_mode
);
1772 fb
->var
.bits_per_pixel
= pd
->def_bpp
? pd
->def_bpp
: 8;
1773 fb
->var
.xres_virtual
= fb
->var
.xres
;
1774 fb
->var
.yres_virtual
= fb
->var
.yres
;
1776 if (info
->edid_data
) {
1777 ret
= fb_find_mode(&fb
->var
, fb
, fb_mode
,
1778 fb
->monspecs
.modedb
,
1779 fb
->monspecs
.modedb_len
,
1780 &sm501_default_mode
, default_bpp
);
1781 /* edid_data is no longer needed, free it */
1782 kfree(info
->edid_data
);
1784 ret
= fb_find_mode(&fb
->var
, fb
,
1785 NULL
, NULL
, 0, NULL
, 8);
1790 dev_info(info
->dev
, "using mode specified in "
1794 dev_info(info
->dev
, "using mode specified in "
1795 "@mode with ignored refresh rate\n");
1798 dev_info(info
->dev
, "using mode default "
1802 dev_info(info
->dev
, "using mode from list\n");
1805 dev_info(info
->dev
, "ret = %d\n", ret
);
1806 dev_info(info
->dev
, "failed to find mode\n");
1812 /* initialise and set the palette */
1813 if (fb_alloc_cmap(&fb
->cmap
, NR_PALETTE
, 0)) {
1814 dev_err(info
->dev
, "failed to allocate cmap memory\n");
1817 fb_set_cmap(&fb
->cmap
, fb
);
1819 ret
= (fb
->fbops
->fb_check_var
)(&fb
->var
, fb
);
1821 dev_err(info
->dev
, "check_var() failed on initial setup?\n");
1826 /* default platform data if none is supplied (ie, PCI device) */
1828 static struct sm501_platdata_fbsub sm501fb_pdata_crt
= {
1829 .flags
= (SM501FB_FLAG_USE_INIT_MODE
|
1830 SM501FB_FLAG_USE_HWCURSOR
|
1831 SM501FB_FLAG_USE_HWACCEL
|
1832 SM501FB_FLAG_DISABLE_AT_EXIT
),
1836 static struct sm501_platdata_fbsub sm501fb_pdata_pnl
= {
1837 .flags
= (SM501FB_FLAG_USE_INIT_MODE
|
1838 SM501FB_FLAG_USE_HWCURSOR
|
1839 SM501FB_FLAG_USE_HWACCEL
|
1840 SM501FB_FLAG_DISABLE_AT_EXIT
),
1843 static struct sm501_platdata_fb sm501fb_def_pdata
= {
1844 .fb_route
= SM501_FB_OWN
,
1845 .fb_crt
= &sm501fb_pdata_crt
,
1846 .fb_pnl
= &sm501fb_pdata_pnl
,
1849 static char driver_name_crt
[] = "sm501fb-crt";
1850 static char driver_name_pnl
[] = "sm501fb-panel";
1852 static int sm501fb_probe_one(struct sm501fb_info
*info
,
1853 enum sm501_controller head
)
1855 unsigned char *name
= (head
== HEAD_CRT
) ? "crt" : "panel";
1856 struct sm501_platdata_fbsub
*pd
;
1857 struct sm501fb_par
*par
;
1858 struct fb_info
*fbi
;
1860 pd
= (head
== HEAD_CRT
) ? info
->pdata
->fb_crt
: info
->pdata
->fb_pnl
;
1862 /* Do not initialise if we've not been given any platform data */
1864 dev_info(info
->dev
, "no data for fb %s (disabled)\n", name
);
1868 fbi
= framebuffer_alloc(sizeof(struct sm501fb_par
), info
->dev
);
1870 dev_err(info
->dev
, "cannot allocate %s framebuffer\n", name
);
1877 fbi
->pseudo_palette
= &par
->pseudo_palette
;
1879 info
->fb
[head
] = fbi
;
1884 /* Free up anything allocated by sm501fb_init_fb */
1886 static void sm501_free_init_fb(struct sm501fb_info
*info
,
1887 enum sm501_controller head
)
1889 struct fb_info
*fbi
= info
->fb
[head
];
1891 fb_dealloc_cmap(&fbi
->cmap
);
1894 static int sm501fb_start_one(struct sm501fb_info
*info
,
1895 enum sm501_controller head
, const char *drvname
)
1897 struct fb_info
*fbi
= info
->fb
[head
];
1903 mutex_init(&info
->fb
[head
]->mm_lock
);
1905 ret
= sm501fb_init_fb(info
->fb
[head
], head
, drvname
);
1907 dev_err(info
->dev
, "cannot initialise fb %s\n", drvname
);
1911 ret
= register_framebuffer(info
->fb
[head
]);
1913 dev_err(info
->dev
, "failed to register fb %s\n", drvname
);
1914 sm501_free_init_fb(info
, head
);
1918 dev_info(info
->dev
, "fb%d: %s frame buffer\n", fbi
->node
, fbi
->fix
.id
);
1923 static int sm501fb_probe(struct platform_device
*pdev
)
1925 struct sm501fb_info
*info
;
1926 struct device
*dev
= &pdev
->dev
;
1929 /* allocate our framebuffers */
1931 info
= kzalloc(sizeof(struct sm501fb_info
), GFP_KERNEL
);
1933 dev_err(dev
, "failed to allocate state\n");
1937 info
->dev
= dev
= &pdev
->dev
;
1938 platform_set_drvdata(pdev
, info
);
1940 if (dev
->parent
->platform_data
) {
1941 struct sm501_platdata
*pd
= dev
->parent
->platform_data
;
1942 info
->pdata
= pd
->fb
;
1945 if (info
->pdata
== NULL
) {
1947 #if defined(CONFIG_OF)
1948 struct device_node
*np
= pdev
->dev
.parent
->of_node
;
1953 info
->pdata
= &sm501fb_def_pdata
;
1956 cp
= of_get_property(np
, "mode", &len
);
1958 strcpy(fb_mode
, cp
);
1959 prop
= of_get_property(np
, "edid", &len
);
1960 if (prop
&& len
== EDID_LENGTH
) {
1961 info
->edid_data
= kmemdup(prop
, EDID_LENGTH
,
1963 if (info
->edid_data
)
1969 dev_info(dev
, "using default configuration data\n");
1970 info
->pdata
= &sm501fb_def_pdata
;
1974 /* probe for the presence of each panel */
1976 ret
= sm501fb_probe_one(info
, HEAD_CRT
);
1978 dev_err(dev
, "failed to probe CRT\n");
1982 ret
= sm501fb_probe_one(info
, HEAD_PANEL
);
1984 dev_err(dev
, "failed to probe PANEL\n");
1985 goto err_probed_crt
;
1988 if (info
->fb
[HEAD_PANEL
] == NULL
&&
1989 info
->fb
[HEAD_CRT
] == NULL
) {
1990 dev_err(dev
, "no framebuffers found\n");
1994 /* get the resources for both of the framebuffers */
1996 ret
= sm501fb_start(info
, pdev
);
1998 dev_err(dev
, "cannot initialise SM501\n");
1999 goto err_probed_panel
;
2002 ret
= sm501fb_start_one(info
, HEAD_CRT
, driver_name_crt
);
2004 dev_err(dev
, "failed to start CRT\n");
2008 ret
= sm501fb_start_one(info
, HEAD_PANEL
, driver_name_pnl
);
2010 dev_err(dev
, "failed to start Panel\n");
2011 goto err_started_crt
;
2014 /* create device files */
2016 ret
= device_create_file(dev
, &dev_attr_crt_src
);
2018 goto err_started_panel
;
2020 ret
= device_create_file(dev
, &dev_attr_fbregs_pnl
);
2022 goto err_attached_crtsrc_file
;
2024 ret
= device_create_file(dev
, &dev_attr_fbregs_crt
);
2026 goto err_attached_pnlregs_file
;
2028 /* we registered, return ok */
2031 err_attached_pnlregs_file
:
2032 device_remove_file(dev
, &dev_attr_fbregs_pnl
);
2034 err_attached_crtsrc_file
:
2035 device_remove_file(dev
, &dev_attr_crt_src
);
2038 unregister_framebuffer(info
->fb
[HEAD_PANEL
]);
2039 sm501_free_init_fb(info
, HEAD_PANEL
);
2042 unregister_framebuffer(info
->fb
[HEAD_CRT
]);
2043 sm501_free_init_fb(info
, HEAD_CRT
);
2049 framebuffer_release(info
->fb
[HEAD_PANEL
]);
2052 framebuffer_release(info
->fb
[HEAD_CRT
]);
2064 static int sm501fb_remove(struct platform_device
*pdev
)
2066 struct sm501fb_info
*info
= platform_get_drvdata(pdev
);
2067 struct fb_info
*fbinfo_crt
= info
->fb
[0];
2068 struct fb_info
*fbinfo_pnl
= info
->fb
[1];
2070 device_remove_file(&pdev
->dev
, &dev_attr_fbregs_crt
);
2071 device_remove_file(&pdev
->dev
, &dev_attr_fbregs_pnl
);
2072 device_remove_file(&pdev
->dev
, &dev_attr_crt_src
);
2074 sm501_free_init_fb(info
, HEAD_CRT
);
2075 sm501_free_init_fb(info
, HEAD_PANEL
);
2077 unregister_framebuffer(fbinfo_crt
);
2078 unregister_framebuffer(fbinfo_pnl
);
2083 framebuffer_release(fbinfo_pnl
);
2084 framebuffer_release(fbinfo_crt
);
2091 static int sm501fb_suspend_fb(struct sm501fb_info
*info
,
2092 enum sm501_controller head
)
2094 struct fb_info
*fbi
= info
->fb
[head
];
2095 struct sm501fb_par
*par
= fbi
->par
;
2097 if (par
->screen
.size
== 0)
2100 /* blank the relevant interface to ensure unit power minimised */
2101 (par
->ops
.fb_blank
)(FB_BLANK_POWERDOWN
, fbi
);
2103 /* tell console/fb driver we are suspending */
2106 fb_set_suspend(fbi
, 1);
2109 /* backup copies in case chip is powered down over suspend */
2111 par
->store_fb
= vmalloc(par
->screen
.size
);
2112 if (par
->store_fb
== NULL
) {
2113 dev_err(info
->dev
, "no memory to store screen\n");
2117 par
->store_cursor
= vmalloc(par
->cursor
.size
);
2118 if (par
->store_cursor
== NULL
) {
2119 dev_err(info
->dev
, "no memory to store cursor\n");
2123 dev_dbg(info
->dev
, "suspending screen to %p\n", par
->store_fb
);
2124 dev_dbg(info
->dev
, "suspending cursor to %p\n", par
->store_cursor
);
2126 memcpy_fromio(par
->store_fb
, par
->screen
.k_addr
, par
->screen
.size
);
2127 memcpy_fromio(par
->store_cursor
, par
->cursor
.k_addr
, par
->cursor
.size
);
2132 vfree(par
->store_fb
);
2133 par
->store_fb
= NULL
;
2138 static void sm501fb_resume_fb(struct sm501fb_info
*info
,
2139 enum sm501_controller head
)
2141 struct fb_info
*fbi
= info
->fb
[head
];
2142 struct sm501fb_par
*par
= fbi
->par
;
2144 if (par
->screen
.size
== 0)
2147 /* re-activate the configuration */
2149 (par
->ops
.fb_set_par
)(fbi
);
2151 /* restore the data */
2153 dev_dbg(info
->dev
, "restoring screen from %p\n", par
->store_fb
);
2154 dev_dbg(info
->dev
, "restoring cursor from %p\n", par
->store_cursor
);
2157 memcpy_toio(par
->screen
.k_addr
, par
->store_fb
,
2160 if (par
->store_cursor
)
2161 memcpy_toio(par
->cursor
.k_addr
, par
->store_cursor
,
2165 fb_set_suspend(fbi
, 0);
2168 vfree(par
->store_fb
);
2169 vfree(par
->store_cursor
);
2173 /* suspend and resume support */
2175 static int sm501fb_suspend(struct platform_device
*pdev
, pm_message_t state
)
2177 struct sm501fb_info
*info
= platform_get_drvdata(pdev
);
2179 /* store crt control to resume with */
2180 info
->pm_crt_ctrl
= smc501_readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
2182 sm501fb_suspend_fb(info
, HEAD_CRT
);
2183 sm501fb_suspend_fb(info
, HEAD_PANEL
);
2185 /* turn off the clocks, in case the device is not powered down */
2186 sm501_unit_power(info
->dev
->parent
, SM501_GATE_DISPLAY
, 0);
2191 #define SM501_CRT_CTRL_SAVE (SM501_DC_CRT_CONTROL_TVP | \
2192 SM501_DC_CRT_CONTROL_SEL)
2195 static int sm501fb_resume(struct platform_device
*pdev
)
2197 struct sm501fb_info
*info
= platform_get_drvdata(pdev
);
2198 unsigned long crt_ctrl
;
2200 sm501_unit_power(info
->dev
->parent
, SM501_GATE_DISPLAY
, 1);
2202 /* restore the items we want to be saved for crt control */
2204 crt_ctrl
= smc501_readl(info
->regs
+ SM501_DC_CRT_CONTROL
);
2205 crt_ctrl
&= ~SM501_CRT_CTRL_SAVE
;
2206 crt_ctrl
|= info
->pm_crt_ctrl
& SM501_CRT_CTRL_SAVE
;
2207 smc501_writel(crt_ctrl
, info
->regs
+ SM501_DC_CRT_CONTROL
);
2209 sm501fb_resume_fb(info
, HEAD_CRT
);
2210 sm501fb_resume_fb(info
, HEAD_PANEL
);
2216 #define sm501fb_suspend NULL
2217 #define sm501fb_resume NULL
2220 static struct platform_driver sm501fb_driver
= {
2221 .probe
= sm501fb_probe
,
2222 .remove
= sm501fb_remove
,
2223 .suspend
= sm501fb_suspend
,
2224 .resume
= sm501fb_resume
,
2227 .owner
= THIS_MODULE
,
2231 module_platform_driver(sm501fb_driver
);
2233 module_param_named(mode
, fb_mode
, charp
, 0);
2234 MODULE_PARM_DESC(mode
,
2235 "Specify resolution as \"<xres>x<yres>[-<bpp>][@<refresh>]\" ");
2236 module_param_named(bpp
, default_bpp
, ulong
, 0);
2237 MODULE_PARM_DESC(bpp
, "Specify bit-per-pixel if not specified mode");
2238 MODULE_AUTHOR("Ben Dooks, Vincent Sanders");
2239 MODULE_DESCRIPTION("SM501 Framebuffer driver");
2240 MODULE_LICENSE("GPL v2");