背景
该章节主要讲述在fbtft驱动框架中加入开机logo
实现
原理:在tina系统的基础上,在bootlogo分区里面去获取开机logo图片,在内核部分进行显示
修改:
在fbtft-core.c中加入从分区显示的代码,该种显示方式需要用来tina系统uboot源码中去读取logo分区数据到物理内存中,方便内核部分去显示
tina\lichee\linux-3.10\drivers\video\fbtft\fbtft-core.c
#define P_LOGO 1
#if P_LOGO
phys_addr_t bootlogo_addr;
int bootlogo_sz;
struct bmp_color_table_entry {
__u8 blue;
__u8 green;
__u8 red;
__u8 reserved;
} __packed;
struct bmp_header {
/* Header */
char signature[2];
__u32 file_size;
__u32 reserved;
__u32 data_offset;
/* InfoHeader */
__u32 size;
__u32 width;
__u32 height;
__u16 planes;
__u16 bit_count;
__u32 compression;
__u32 image_size;
__u32 x_pixels_per_m;
__u32 y_pixels_per_m;
__u32 colors_used;
__u32 colors_important;
/* ColorTable */
} __packed;
struct bmp_image {
struct bmp_header header;
/* We use a zero sized array just as a placeholder for variable
sized array */
struct bmp_color_table_entry color_table[0];
};
struct sunxi_bmp_store_t {
int x;
int y;
int bit;
void *buffer;
};
static int __init bootlogo_parse(char *str)
{
char *endp;
printk("fb_base = %p\n",str);
bootlogo_sz = SZ_2M;
bootlogo_addr = memparse(str, &endp);
if (*endp == '@') {
bootlogo_sz = bootlogo_addr;
bootlogo_addr = memparse(endp + 1, NULL);
}
return 0;
}
__setup("fb_base=", bootlogo_parse);
void *Fb_map_kernel(unsigned long phys_addr, unsigned long size)
{
int npages = PAGE_ALIGN(size) / PAGE_SIZE;
struct page **pages = vmalloc(sizeof(struct page *) * npages);
struct page **tmp = pages;
struct page *cur_page = phys_to_page(phys_addr);
pgprot_t pgprot;
void *vaddr;
int i;
if (!pages)
return 0;
for (i = 0; i < npages; i++)
*(tmp++) = cur_page++;
pgprot = pgprot_noncached(PAGE_KERNEL);
vaddr = vmap(pages, npages, VM_MAP, pgprot);
vfree(pages);
return vaddr;
}
#if defined(CONFIG_ARCH_SUN3IW1P1)
static void Fb_unmap_kernel(void *vaddr)
{
vunmap(vaddr);
}
static int Fb_map_kernel_logo(struct fb_info *info)
{
void *vaddr = NULL;
unsigned int paddr = 0;
void *screen_offset = NULL, *image_offset = NULL;
char *tmp_buffer = NULL;
char *bmp_data = NULL;
struct sunxi_bmp_store_t s_bmp_info;
struct sunxi_bmp_store_t *bmp_info = &s_bmp_info;
struct bmp_image *bmp = NULL;
int zero_num = 0;
unsigned long x, y, bmp_bpix, fb_width, fb_height;
unsigned int effective_width, effective_height;
unsigned int offset;
int i = 0;
paddr = bootlogo_addr;
if (0 == paddr) {
printk("Fb_map_kernel_logo failed!");
return -1;
}
/* parser bmp header */
offset = paddr & ~PAGE_MASK;
vaddr = (void *)Fb_map_kernel(paddr, sizeof(struct bmp_header));
if (0 == vaddr) {
printk("fb_map_kernel failed, paddr=0x%x,size=0x%x\n", paddr,
sizeof(struct bmp_header));
return -1;
}
bmp = (struct bmp_image *)vaddr + offset;
if ((bmp->header.signature[0] != 'B') ||
(bmp->header.signature[1] != 'M')) {
printk("this is not a bmp picture\n");
return -1;
}
bmp_bpix = bmp->header.bit_count / 8;
if ((bmp_bpix != 2) && (bmp_bpix != 3) && (bmp_bpix != 4))
return -1;
if (bmp_bpix == 3)
zero_num = (4 - ((3*bmp->header.width) % 4))&3;
x = bmp->header.width;
y = (bmp->header.height & 0x80000000) ? (-bmp->header.height)
: (bmp->header.height);
fb_width = info->var.xres;
fb_height = info->var.yres;
if ((paddr <= 0) || x <= 1 || y <= 1) {
printk("kernel logo para error!\n");
return -EINVAL;
}
bmp_info->x = x;
bmp_info->y = y;
bmp_info->bit = bmp->header.bit_count;
bmp_info->buffer = (void *)(info->screen_base);
if (bmp_bpix == 3)
info->var.bits_per_pixel = 24;
else if (bmp_bpix == 4)
info->var.bits_per_pixel = 32;
else if (bmp_bpix == 2)
info->var.bits_per_pixel = 16;
else
info->var.bits_per_pixel = 32;
Fb_unmap_kernel(vaddr);
/* map the total bmp buffer */
vaddr = (void *)Fb_map_kernel(paddr,
x * y * bmp_bpix + sizeof(struct bmp_header));
if (0 == vaddr) {
printk("fb_map_kernel failed, paddr=0x%x,size=0x%x\n", paddr,
(unsigned int)(x * y
* bmp_bpix + sizeof(struct bmp_header)));
return -1;
}
bmp = (struct bmp_image *)vaddr + offset;
tmp_buffer = (char *)bmp_info->buffer;
screen_offset = (void *)bmp_info->buffer;
bmp_data = (char *)(vaddr + bmp->header.data_offset);
image_offset = (void *)bmp_data;
effective_width = (fb_width < x) ? fb_width : x;
effective_height = (fb_height < y) ? fb_height : y;
if (bmp->header.height & 0x80000000) {
if (fb_width > x) {
screen_offset = (void *)((u32)info->screen_base
+ (fb_width * (abs(fb_height - y) / 2)
+ abs(fb_width - x) / 2)
* (info->var.bits_per_pixel >> 3));
} else if (fb_width < x) {
image_offset = (void *)((u32)bmp_data
+ (x * ((y - fb_height) / 2)
+ (x - fb_width) / 2)
* (info->var.bits_per_pixel >> 3));
}
for (i = 0; i < effective_height; i++) {
memcpy((void *)screen_offset, image_offset,
effective_width
*(info->var.bits_per_pixel >> 3));
screen_offset = (void *)((u32)screen_offset
+ fb_width*(info->var.bits_per_pixel >> 3));
image_offset = (void *)image_offset
+ x * (info->var.bits_per_pixel >> 3);
}
} else {
screen_offset = (void *)((u32)info->screen_base
+(fb_width * (abs(fb_height - y) / 2)
+ abs(fb_width - x) / 2)
* (info->var.bits_per_pixel >> 3));
image_offset = (void *)((u32)image_offset
+ (x * (abs(y - fb_height) / 2)
+ abs(x - fb_width) / 2)
* (info->var.bits_per_pixel >> 3));
image_offset = (void *)bmp_data
+ (effective_height-1)
* x * (info->var.bits_per_pixel >> 3);
for (i = effective_height-1; i >= 0; i--) {
memcpy((void *)screen_offset, image_offset,
effective_width
*(info->var.bits_per_pixel >> 3));
screen_offset = (void *)((u32)screen_offset
+ fb_width*(info->var.bits_per_pixel >> 3));
image_offset = (void *)bmp_data
+ i * x * (info->var.bits_per_pixel >> 3);
}
}
Fb_unmap_kernel(vaddr);
return 0;
}
#endif
#endif
logo显示位置(在下面函数位置添加):
int fbtft_register_framebuffer(struct fb_info *fb_info)
{
。。。。。
ret = par->fbtftops.init_display(par);
if (ret < 0)
goto reg_fail;
if (par->fbtftops.set_var) {
ret = par->fbtftops.set_var(par);
if (ret < 0)
goto reg_fail;
}
/*logo 显示*/
Fb_map_kernel_logo(fb_info); /*加在此处*/
/* update the entire display */
par->fbtftops.update_display(par, 0, par->info->var.yres - 1);
。。。。
}
到这里功能已经实现了,可能单独看这篇文章有不懂很突兀,可以看一下我前面的博客,logo显示方式,另外也附上完整c文件:
linux(tina)系统内核驱动显示开机logo
全志f1c100s(tina) linux-3.10 支持spi接口屏移植(1)