/*Framebuffer底层硬件操
作各接口函数*/
static
struct
fb_ops my2440fb_ops =
{
.
owner =
THIS_MODULE,
.
fb_check_var =
my2440fb_check_var,
/*第②步中已实现*/
.
fb_set_par =
my2440fb_set_par,
/*设置fb_info中的参数,主要是LCD的显示模式*/
.
fb_blank =
my2440fb_blank,
/*显示空白(即:LCD开关控制)*/
.
fb_setcolreg =
my2440fb_setcolreg,
/*设置颜色表*/
/*以
下三个函数是可选的,主要是提供fb_console的支持,在内核中已经实现,这里直接调用即可*/
.
fb_fillrect =
cfb_fillrect,
/*定义在
drivers/video/cfbfillrect.c中*/
.
fb_copyarea =
cfb_copyarea,
/*定义在drivers/video/cfbcopyarea.c
中*/
.
fb_imageblit =
cfb_imageblit,
/*定义在
drivers/video/cfbimgblt.c中*/
};
/*设置
fb_info中的参数,这里根据用户设置的可变参数var调整固定参数fix*/
static
int
my2440fb_set_par(
struct
fb_info *
fbinfo)
{
/*获得fb_info中的可变参数*/
struct
fb_var_screeninfo *
var =
&
fbinfo-
>
var;
/*判断可变参数中的色位模式,根据色位模式来设置色彩模式*
/
switch
(
var-
>
bits_per_pixel)
{
case
32:
case
16:
case
12:
/*12BPP时,设置为真彩色(分成红、绿、蓝三基色)*/
fbinfo-
>
fix.
visual
=
FB_VISUAL_TRUECOLOR;
break
;
case
1:
/*1BPP时,设置为黑白色(分黑、白两种
色,FB_VISUAL_MONO01代表
黑,FB_VISUAL_MONO10代表白)*/
fbinfo-
>
fix.
visual =
FB_VISUAL_MONO01;
break
;
default
:
/*默认设置为伪彩色,采用索引颜色显示*/
fbinfo-
>
fix.
visual
=
FB_VISUAL_PSEUDOCOLOR;
break
;
}
/*设置fb_info中固定参数中一行的字节数,公式:1行
字节数=(1行像素
个数*每像素位数BPP)/8 */
fbinfo-
>
fix.
line_length =
(
var-
>
xres_virtual *
var-
>
bits_per_pixel)
/
8;
/*修改以上参数后,重新激活fb_info中的参数配置
(即:使修改后的参数在硬件上生效)*/
my2440fb_activate_var(
fbinfo)
;
return
0;
}
/*重新激活fb_info中的参数配置*/
static
void
my2440fb_activate_var(
struct
fb_info *
fbinfo)
{
/*获得结构体变
量*/
struct
my2440fb_var *
fbvar =
fbinfo-
>
par;
void
__iomem *
regs =
fbvar-
>
lcd_base;
/*获得fb_info可变参数*/
struct
fb_var_screeninfo *
var =
&
fbinfo-
>
var;
/*计算LCD控制寄存器1中的CLKVAL值,
根据数据手册中该寄存器的描述,计算公式如下:
* STN屏:VCLK = HCLK / (CLKVAL * 2),
CLKVAL要求>= 2
* TFT屏:VCLK = HCLK / [(CLKVAL + 1) * 2],
CLKVAL要求>= 0*/
int
clkdiv =
my2440fb_calc_pixclk(
fbvar,
var-
>
pixclock)
/
2;
/*获得屏幕的类型*/
int
type =
fbvar-
>
regs.
lcdcon1 &
S3C2410_LCDCON1_TFT;
if
(
type =
=
S3C2410_LCDCON1_TFT)
{
/*根据数据手册按照TFT屏的要求配置LCD控制寄
存器1-5*/
my2440fb_config_tft_lcd_regs(
fbinfo,
&
fbvar-
>
regs)
;
-
-
clkdiv;
if
(
clkdiv <
0)
{
clkdiv =
0;
}
}
else
{
/*根据数据手册按照STN屏的要求配置LCD控制寄
存器1-5*/
my2440fb_config_stn_lcd_regs(
fbinfo,
&
fbvar-
>
regs)
;
if
(
clkdiv <
2)
{
clkdiv =
2;
}
}
/*设置计算的LCD控制寄存器1中的CLKVAL值*/
fbvar-
>
regs.
lcdcon1 |
=
S3C2410_LCDCON1_CLKVAL(
clkdiv)
;
/*将各参数
值写入LCD控制寄存器1-5中*/
writel(
fbvar-
>
regs.
lcdcon1 &
~
S3C2410_LCDCON1_ENVID,
regs +
S3C2410_LCDCON1)
;
writel(
fbvar-
>
regs.
lcdcon2,
regs +
S3C2410_LCDCON2)
;
writel(
fbvar-
>
regs.
lcdcon3,
regs +
S3C2410_LCDCON3)
;
writel(
fbvar-
>
regs.
lcdcon4,
regs +
S3C2410_LCDCON4)
;
writel(
fbvar-
>
regs.
lcdcon5,
regs +
S3C2410_LCDCON5)
;
/*配置帧缓冲起始地址寄存器1-3*/
my2440fb_set_lcdaddr(
fbinfo)
;
fbvar-
>
regs.
lcdcon1
|
=
S3C2410_LCDCON1_ENVID,
writel(
fbvar-
>
regs.
lcdcon1,
regs +
S3C2410_LCDCON1)
;
}
/*计算LCD
控制寄存器1中的CLKVAL值*/
static
unsigned
int
my2440fb_calc_pixclk(
struct
my2440fb_var *
fbvar,
unsigned
long
pixclk)
{
/*获得LCD的时钟*/
unsigned
long
clk =
clk_get_rate(
fbvar-
>
lcd_clock)
;
/*
像素时钟单位是皮秒,而时钟的单位是赫兹,所以计算公式为:
* Hz -> picoseconds is / 10^-12
*/
unsigned
long
long
div
=
(
unsigned
long
long
)
clk *
pixclk;
div
>
>
=
12;
/* div / 2^12 */
do_div(
div
,
625 *
625UL *
625)
;
/* div / 5^12,
do_div宏定义在asm/div64.h中*/
return
div
;
}
/*根据数据手册按照TFT屏的要求配置LCD控制寄存器1-5*/
static
void
my2440fb_config_tft_lcd_regs(
const
struct
fb_info *
fbinfo,
struct
s3c2410fb_hw *
regs)
{
const
struct
my2440fb_var *
fbvar =
fbinfo-
>
par;
const
struct
fb_var_screeninfo *
var =
&
fbinfo-
>
var;
/*根据色位模式设置LCD控制寄存器1和5,参考数据手册*
/
switch
(
var-
>
bits_per_pixel)
{
case
1:
/*1BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_TFT1BPP;
break
;
case
2:
/*2BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_TFT2BPP;
break
;
case
4:
/*4BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_TFT4BPP;
break
;
case
8:
/*8BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_TFT8BPP;
regs-
>
lcdcon5 |
=
S3C2410_LCDCON5_BSWP |
S3C2410_LCDCON5_FRM565;
regs-
>
lcdcon5 &
=
~
S3C2410_LCDCON5_HWSWP;
break
;
case
16:
/*16BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_TFT16BPP;
regs-
>
lcdcon5 &
=
~
S3C2410_LCDCON5_BSWP;
regs-
>
lcdcon5 |
=
S3C2410_LCDCON5_HWSWP;
break
;
case
32:
/*32BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_TFT24BPP;
regs-
>
lcdcon5 &
=
~
(
S3C2410_LCDCON5_BSWP
|
S3C2410_LCDCON5_HWSWP |
S3C2410_LCDCON5_BPP24BL)
;
break
;
default
:
/*无效的BPP*/
dev_err(
fbvar-
>
dev,
"invalid bpp %d\n"
,
var-
>
bits_per_pixel)
;
}
/*设置LCD配置寄存器2、3、4*/
regs-
>
lcdcon2 =
S3C2410_LCDCON2_LINEVAL(
var-
>
yres -
1)
|
S3C2410_LCDCON2_VBPD(
var-
>
upper_margin -
1)
|
S3C2410_LCDCON2_VFPD(
var-
>
lower_margin
-
1)
|
S3C2410_LCDCON2_VSPW(
var-
>
vsync_len
-
1)
;
regs-
>
lcdcon3 =
S3C2410_LCDCON3_HBPD(
var-
>
right_margin -
1)
|
S3C2410_LCDCON3_HFPD(
var-
>
left_margin
-
1)
|
S3C2410_LCDCON3_HOZVAL(
var-
>
xres -
1)
;
regs-
>
lcdcon4 =
S3C2410_LCDCON4_HSPW(
var-
>
hsync_len -
1)
;
}
/*根据数据手册按照STN屏的要求配置LCD控制寄存器1-5*/
static
void
my2440fb_config_stn_lcd_regs(
const
struct
fb_info *
fbinfo,
struct
s3c2410fb_hw *
regs)
{
const
struct
my2440fb_var *
fbvar =
fbinfo-
>
par;
const
struct
fb_var_screeninfo *
var =
&
fbinfo-
>
var;
int
type =
regs-
>
lcdcon1 &
~
S3C2410_LCDCON1_TFT;
int
hs =
var-
>
xres >
>
2;
unsigned
wdly =
(
var-
>
left_margin >
>
4)
-
1;
unsigned
wlh =
(
var-
>
hsync_len >
>
4)
-
1;
if
(
type !
=
S3C2410_LCDCON1_STN4)
{
hs >
>
=
1;
}
/*根据色位模式设置LCD控制寄存器1,参考数据手册*/
switch
(
var-
>
bits_per_pixel)
{
case
1:
/*1BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_STN1BPP;
break
;
case
2:
/*2BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_STN2GREY;
break
;
case
4:
/*4BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_STN4GREY;
break
;
case
8:
/*8BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_STN8BPP;
hs *
=
3;
break
;
case
12:
/*12BPP*/
regs-
>
lcdcon1 |
=
S3C2410_LCDCON1_STN12BPP;
hs *
=
3;
break
;
default
:
/*无效的BPP*/
dev_err(
fbvar-
>
dev,
"invalid bpp
%d\n"
,
var-
>
bits_per_pixel)
;
}
/*设置LCD配置寄存器2、3、4, 参考数据手册*/
if
(
wdly
>
3)
wdly =
3;
if
(
wlh >
3)
wlh =
3;
regs-
>
lcdcon2 =
S3C2410_LCDCON2_LINEVAL(
var-
>
yres -
1)
;
regs-
>
lcdcon3 =
S3C2410_LCDCON3_WDLY(
wdly)
|
S3C2410_LCDCON3_LINEBLANK(
var-
>
right_margin
/
8)
|
S3C2410_LCDCON3_HOZVAL(
hs -
1)
;
regs-
>
lcdcon4 =
S3C2410_LCDCON4_WLH(
wlh)
;
}
/*配置帧缓冲起始地址寄存器1-3,参考数据手册*/
static
void
my2440fb_set_lcdaddr(
struct
fb_info *
fbinfo)
{
unsigned
long
saddr1,
saddr2,
saddr3;
struct
my2440fb_var *
fbvar =
fbinfo-
>
par;
void
__iomem *
regs =
fbvar-
>
lcd_base;
saddr1 =
fbinfo-
>
fix.
smem_start >
>
1;
saddr2 =
fbinfo-
>
fix.
smem_start;
saddr2 +
=
fbinfo-
>
fix.
line_length
*
fbinfo-
>
var.
yres;
saddr2 >
>
=
1;
saddr3
=
S3C2410_OFFSIZE(
0)
|
S3C2410_PAGEWIDTH(
(
fbinfo-
>
fix.
line_length /
2)
&
0x3ff)
;
writel(
saddr1,
regs +
S3C2410_LCDSADDR1)
;
writel(
saddr2,
regs +
S3C2410_LCDSADDR2)
;
writel(
saddr3,
regs +
S3C2410_LCDSADDR3)
;
}
/*显示空白,blank
mode有5种模式,定义在fb.h中,是一个枚举*/
static
int
my2440fb_blank(
int
blank_mode,
struct
fb_info *
fbinfo)
{
struct
my2440fb_var *
fbvar =
fbinfo-
>
par;
void
__iomem *
regs =
fbvar-
>
lcd_base;
/*根据显示空白的模式来设置LCD是开启还是停止*/
if
(
blank_mode =
=
FB_BLANK_POWERDOWN)
{
my2440fb_lcd_enable(
fbvar,
0)
;
/*在第②步中定义*/
}
else
{
my2440fb_lcd_enable(
fbvar,
1)
;
/*在第②步中定义*/
}
/*根据显示空白的模式来控制临时调色板寄存器*/
if
(
blank_mode =
=
FB_BLANK_UNBLANK)
{
/*临时调色板寄存器无效*/
writel(
0x0,
regs +
S3C2410_TPAL)
;
}
else
{
/*临时调色
板寄存器有效*/
writel(
S3C2410_TPAL_EN,
regs +
S3C2410_TPAL)
;
}
return
0;
}
/*设置颜色表*/
static
int
my2440fb_setcolreg(
unsigned
regno,
unsigned
red,
unsigned
green,
unsigned
blue,
unsigned
transp,
struct
fb_info *
fbinfo)
{
unsigned
int
val;
struct
my2440fb_var *
fbvar =
fbinfo-
>
par;
void
__iomem *
regs =
fbvar-
>
lcd_base;
switch
(
fbinfo-
>
fix.
visual)
{
case
FB_VISUAL_TRUECOLOR:
/*
真彩色*/
if
(
regno <
16)
{
u32
*
pal =
fbinfo-
>
pseudo_palette;
val =
chan_to_field(
red,
&
fbinfo-
>
var.
red)
;
val |
=
chan_to_field(
green,
&
fbinfo-
>
var.
green)
;
val |
=
chan_to_field(
blue,
&
fbinfo-
>
var.
blue)
;
pal[
regno]
=
val;
}
break
;
case
FB_VISUAL_PSEUDOCOLOR:
/*伪彩色*/
if
(
regno <
256)
{
val =
(
red >
>
0)
&
0xf800;
val
|
=
(
green >
>
5)
&
0x07e0;
val |
=
(
blue >
>
11)
&
0x001f;
writel(
val,
regs +
S3C2410_TFTPAL(
regno)
)
;
/*修改调色板*/
schedule_palette_update(
fbvar,
regno,
val)
;
}
break
;
default
:
return
1;
}
return
0;
}
static
inline
unsigned
int
chan_to_field(
unsigned
int
chan,
struct
fb_bitfield *
bf)
{
chan &
=
0xffff;
chan >
>
=
16 -
bf-
>
length;
return
chan <
<
bf-
>
offset;
}
/*修改调色
板*/
static
void
schedule_palette_update(
struct
my2440fb_var *
fbvar,
unsigned
int
regno,
unsigned
int
val)
{
unsigned
long
flags;
unsigned
long
irqen;
/*LCD中断挂起寄存器基地址*/
void
__iomem *
lcd_irq_base =
fbvar-
>
lcd_base +
S3C2410_LCDINTBASE;
/*在修改中断寄存器值之前先屏蔽中断,将中断状态保存到
flags中*/
local_irq_save(
flags)
;
fbvar-
>
palette_buffer[
regno]
=
val;
/*判断调色板是否准备就像*/
if
(
!
fbvar-
>
palette_ready)
{
fbvar-
>
palette_ready =
1;
/*使能中断屏蔽寄存器*/
irqen =
readl(
lcd_irq_base +
S3C24XX_LCDINTMSK)
;
irqen &
=
~
S3C2410_LCDINT_FRSYNC;
writel(
irqen,
lcd_irq_base +
S3C24XX_LCDINTMSK)
;
}
/*恢复被屏蔽的中断*/
local_irq_restore(
flags)
;
}
|