全志G2D实现屏幕旋转,开机logo实现手动旋转。
阅读原文时间:2023年08月17日阅读:6

产品设计出来之后啊,大家使用的时候觉得反过来使用更加便捷。但是屏幕显示是反的。那怎么办那?????

修改硬件费时费工,那能否软件实现那?????

如果纯软件使用那就太费系统资源了。于是就想到了使用全志R528 自带的G2D功能(硬件加速功能)。

使用它进行旋转,后又发现uboot阶段系统没有G2D导致开机logo不能自动旋转,内核启动后G2D 启动logo 又旋转了。

(好烦啊!!!!!!!!!!!!)

于是就需要把uboot 阶段手动把图片数据旋转过来。在G2D启动前把uboot 传递给内核的logo 图片数据也旋转过来。

下面具体步骤:

一、开启G2D功能。

1、由于此前公版默认在modules.mk屏蔽了屏旋转相关配置, 如果你的版本是禁用旋转的, 需要把相关配置去掉.
device/config/chips/r528/configs/evb1/linux-5.4/config-5.4

2759:# CONFIG_SUNXI_DISP2_FB_DISABLE_ROTATE is not set
以下3个相关选项

CONFIG_DISP2_SUNXI=m \
#CONFIG_SUNXI_DISP2_FB_DISABLE_ROTATE=y \
#CONFIG_SUNXI_DISP2_FB_ROTATION_SUPPORT=n \
#CONFIG_SUNXI_DISP2_FB_HW_ROTATION_SUPPORT=n \
修改为:

CONFIG_DISP2_SUNXI=m \
#CONFIG_SUNXI_DISP2_FB_DISABLE_ROTATE=n \
#CONFIG_SUNXI_DISP2_FB_ROTATION_SUPPORT=n \
#CONFIG_SUNXI_DISP2_FB_HW_ROTATION_SUPPORT=y \

2、硬件旋转需要确保G2D驱动已经使能

make kernel_menuconfig
Device Drivers --->
<*> SUNXI G2D Driver
[*] sunxi g2d mixer module
[*] sunxi g2d rotate module
[] sunxi sync fence implement for rotate jobs synchronous

3.打开显示驱动旋转支持

make kernel_menuconfig
Device Drivers --->
Graphics support --->
Frame buffer Devices --->
Video support for sunxi --->
DISP2 Framebuffer rotation support (Disable rotation) --->
( ) Disable rotation
( ) Software rotation support (不要选这个,方案未支持)
(X) Hardware(G2D) rotation support (选择G2D旋转)

4.dts配置

board.dts 和 uboot-board.dts同步修改.

&disp{
…..
disp_rotation_used = <1>;/* 使能旋转功能 */
degree0 = <2>; /* X:screen index; 0:0 degree; 1:90 degree; 3:270 degree */
fb0_width = <800>;/*fb 的长宽交换*/
fb0_height = <480>;
…..
};

5.旋转后framebuffer编程是需要注意,旋转后的buffer不会直接显示到屏幕上, 需要在应用刷屏的地方调用FBIOPAN_DISPLAY接口.同步旋转后的buffer到LCD上.

以修改公版旋转的GUI刷屏示例:

路径:package/gui/littlevgl-6/lv_drivers/display/fbdev.c

void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p){
….
lv_disp_flush_ready(drv);
ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo); /*函数最后,在刷屏函数后面,调用 FBIOPAN_DISPLAY 接口*/
}

我们系统时自己的UI系统。是在重绘回调函数中,增加FBIOPAN_DISPLAY

main()
{
int fp=0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;

fp = open("/dev/fb0", O_RDWR);

if(fp < 0) {
printf("Error : Can not open framebuffer device/n");
exit(1);
}
if(ioctl(fp, FBIOGET_VSCREENINFO, &vinfo)){
printf("Error reading variable information/n");
exit(3);
}

vinfo.xoffset = 0;
vinfo.yoffset = 0;
}

void sys_paint(void) // 每次重绘调用一次
{
  ioctl(fbfd, FBIOPAN_DISPLAY, &vinfo); /*函数最后,在刷屏函数后面,调用 FBIOPAN_DISPLAY 接口*/
}

二、uboot阶段手动修改旋转图片数据

追踪uboot  logo 执行过程

static int run_main_loop(void)
{
….
#ifdef CONFIG_ARCH_SUNXI
initr_sunxi_plat,
#endif
….
}

initr_sunxi_plat
{
….
#ifdef CONFIG_BOOT_GUI
void board_bootlogo_display(void);
board_bootlogo_display();
#else
….
}

void board_bootlogo_display(void)
{
….
#if defined(CONFIG_CMD_SUNXI_BMP)
sunxi_bmp_display("bootlogo.bmp"); //指定bootlogo的名字。通过修改这个地方可以修改指定logo的名称

#elif defined(CONFIG_CMD_SUNXI_JPEG)
sunxi_jpeg_display("bootlogo.jpg");
#endif
….
}

int sunxi_bmp_display(char *name)
{
int ret = -1;
char *argv[6];
char bmp_head[32];273
char bmp_name[32];
char part_info[16] = {0};
char size[32] = {0};
int partno = -1;
unsigned long file_size = 0;
char *bmp_head_addr;
struct bmp_image *bmp;
bmp = memalign(CONFIG_SYS_CACHELINE_SIZE, ALIGN(sizeof(struct bmp_header), CONFIG_SYS_CACHELINE_SIZE));
if (bmp) {
sprintf(bmp_head, "%lx", (ulong)bmp);
} else {
pr_error("sunxi bmp: alloc buffer for %s fail\n", name);
goto out;
}
partno = sunxi_partition_get_partno_byname("bootloader"); /*android*/
if (partno < 0) {
partno = sunxi_partition_get_partno_byname(
"boot-resource"); /*linux*/
if (partno < 0) {
pr_error("Get bootloader and boot-resource partition number fail!\n");
goto free1;
}
}
snprintf(part_info, 16, "0:%x", partno);
strncpy(bmp_name, name, sizeof(bmp_name));
snprintf(size, 16, "%lx", (ulong)sizeof(struct bmp_header));

argv\[0\] = "fatload";  
argv\[1\] = "sunxi\_flash";  
argv\[2\] = part\_info;  
argv\[3\] = bmp\_head;  
argv\[4\] = bmp\_name;  
argv\[5\] = size;

if (do\_fat\_fsload(0, 0, 6, argv)) {  
    pr\_error("sunxi bmp info error : unable to open logo file %s\\n",  
           argv\[4\]);  
    goto free1;  
}  
if ((bmp->header.signature\[0\] != 'B') ||  
    (bmp->header.signature\[1\] != 'M')) {  
    pr\_error("this is not a bmp picture\\n");  
    goto free1;  
}  
file\_size = bmp->header.file\_size;

bmp\_head\_addr = memalign(CONFIG\_SYS\_CACHELINE\_SIZE,  ALIGN(file\_size, CONFIG\_SYS\_CACHELINE\_SIZE));  
if (bmp\_head\_addr) {  
    sprintf(bmp\_head, "%lx", (ulong)bmp\_head\_addr);  
} else {  
    pr\_error("sunxi bmp: alloc buffer for %s fail\\n", name);  
    goto free1;  
}

snprintf(size, 16, "%lx", (ulong)file\_size);

tick\_printf("bmp\_name=%s size %ld\\n", bmp\_name, file\_size);

if (do\_fat\_fsload(0, 0, 6, argv)) {  
    pr\_error("sunxi bmp info error : unable to open logo file %s\\n",  
           argv\[4\]);  
    goto free2;  
}

//在调用show_bmp_on_fb 之前将bmp_head_addr中的图片数据进行旋转

  // 调用旋转函数
  rotateBMP180(bmp_head_addr);

ret = show\_bmp\_on\_fb(bmp\_head\_addr, FB\_ID\_0);  
if (ret != 0)  
    pr\_error("show bmp on fb failed !%d\\n", ret);

free2:
free(bmp_head_addr);
free1:
free(bmp);
out:
return ret;
}

旋转函数:

主要发现uboot 阶段malloc不能申请太大的内存空间,所以代码中申请的行的大小。

#pragma pack(push, 1)
typedef struct {
uint16_t type;
uint32_t size;
uint16_t reserved1;
uint16_t reserved2;
uint32_t offset;
} BMPFileHeader;
#pragma pack(pop)

// 定义BMP图像信息头结构体
#pragma pack(push, 1)
typedef struct {
uint32_t header_size;
int32_t width;
int32_t height;
uint16_t planes;
uint16_t bit_count;
uint32_t compression;
uint32_t image_size;
int32_t x_pixels_per_meter;
int32_t y_pixels_per_meter;
uint32_t colors_used;
uint32_t colors_important;
} BMPInfoHeader;
#pragma pack(pop)

// 旋转BMP图像180度
void rotateBMP180(char * bmp_head_add) {
// 读取文件头
BMPFileHeader* file_header = (BMPFileHeader*)bmp_head_add;

// 读取图像信息头  
BMPInfoHeader\* info\_header = (BMPInfoHeader\*)(bmp\_head\_add + sizeof(BMPFileHeader));

// 获取图像宽度、高度和每行像素所占字节数  
int32\_t width = info\_header->width;  
int32\_t height = info\_header->height;  
uint32\_t row\_size = (info\_header->bit\_count \* width + 31) / 32 \* 4;

// 创建临时缓冲区存储旋转后的图像数据  
uint8\_t\* temp\_data = (uint8\_t\*)malloc(row\_size);  
if (temp\_data == NULL) {  
    printf("Failed to allocate memory for temporary data.\\n");  
    return;  
}

// 旋转图像  
for (int32\_t row = 0; row < height / 2; ++row) {  
    for (int32\_t col = 0; col < width; ++col) {  
        // 计算当前像素位置和对应的对称像素位置  
        int32\_t original\_index = row \* row\_size + col \* 3;  
        int32\_t symmetric\_index = (height - 1 - row) \* row\_size + (width - 1 - col) \* 3;

        // 交换像素颜色值  
        uint8\_t temp\_red = bmp\_head\_add\[file\_header->offset + original\_index\];  
        uint8\_t temp\_green = bmp\_head\_add\[file\_header->offset + original\_index + 1\];  
        uint8\_t temp\_blue = bmp\_head\_add\[file\_header->offset + original\_index + 2\];

        bmp\_head\_add\[file\_header->offset + original\_index\] = bmp\_head\_add\[file\_header->offset + symmetric\_index\];  
        bmp\_head\_add\[file\_header->offset + original\_index + 1\] = bmp\_head\_add\[file\_header->offset + symmetric\_index + 1\];  
        bmp\_head\_add\[file\_header->offset + original\_index + 2\] = bmp\_head\_add\[file\_header->offset + symmetric\_index + 2\];

        bmp\_head\_add\[file\_header->offset + symmetric\_index\] = temp\_red;  
        bmp\_head\_add\[file\_header->offset + symmetric\_index + 1\] = temp\_green;  
        bmp\_head\_add\[file\_header->offset + symmetric\_index + 2\] = temp\_blue;  
    }  
}

// 释放临时缓冲区内存  
free(temp\_data);  

}

三、kernel阶段手动修改旋转logo图片数据

追踪内核执行过程

disp_module_init{
……..
platform_driver_unregister(&disp_driver);
#ifndef CONFIG_OF
platform_device_unregister(&disp_device);
#endif
……..

}

static int disp_probe(struct platform_device *pdev){
……..
bsp_disp_init(para);
……..
}

static s32 disp_init(struct platform_device *pdev)
{
……..
lcd_init();
bsp_disp_open();

fb\_init(pdev);  

……..

}

s32 fb_init(struct platform_device *pdev)
{
……..
ret = display_fb_request(i, &fb_para);
……..
}

static s32 display_fb_request(u32 fb_id, struct disp_fb_create_info *fb_para)
{
……..
Fb_map_kernel_logo(sel, info);
……..
}

static int Fb_map_kernel_logo(u32 sel, struct fb_info *info)
{
……..
paddr = bootlogo_addr;
if (paddr == 0) {
__inf("Fb_map_kernel_logo failed!");
return Fb_copy_boot_fb(sel, info);
}
……..
}

static int Fb_copy_boot_fb(u32 sel, struct fb_info *info)
{
enum {
BOOT_FB_ADDR = 0,
BOOT_FB_WIDTH,
BOOT_FB_HEIGHT,
BOOT_FB_BPP,
BOOT_FB_STRIDE,
BOOT_FB_CROP_L,
BOOT_FB_CROP_T,
BOOT_FB_CROP_R,
BOOT_FB_CROP_B,
};

char \*boot\_fb\_str = NULL;  
char \*src\_phy\_addr = NULL;  
char \*src\_addr = NULL;  
char \*src\_addr\_b = NULL;  
char \*src\_addr\_e = NULL;  
int src\_width = 0;  
int src\_height = 0;  
int fb\_height = 0;  
int src\_bpp = 0;  
int src\_stride = 0;  
int src\_cp\_btyes = 0;  
int src\_crop\_l = 0;  
int src\_crop\_t = 0;  
int src\_crop\_r = 0;  
int src\_crop\_b = 0;

char \*dst\_addr = NULL;  
int dst\_width = 0;  
int dst\_height = 0;  
int dst\_bpp = 0;  
int dst\_stride = 0;  
int ret;

unsigned long map\_offset;

if (info == NULL) {  
    \_\_wrn("%s,%d: null pointer\\n", \_\_func\_\_, \_\_LINE\_\_);  
    return -1;  
}

boot\_fb\_str = (char \*)disp\_boot\_para\_parse\_str("boot\_fb0");  
if (boot\_fb\_str != NULL) {  
    int i = 0;  
    char boot\_fb\[128\] = { 0 };  
    int len = strlen(boot\_fb\_str);

    if (sizeof(boot\_fb) - 1 < len) {  
        \_\_wrn("need bigger array size\[%d\] for boot\_fb\\n", len);  
        return -1;  
    }  
    memcpy((void \*)boot\_fb, (void \*)boot\_fb\_str, len);  
    boot\_fb\[len\] = '\\0';  
    boot\_fb\_str = boot\_fb;  
    for (i = 0;; ++i) {  
        char \*p = strstr(boot\_fb\_str, ",");

        if (p != NULL)  
            \*p = '\\0';  
        if (i == BOOT\_FB\_ADDR) {  
            ret = kstrtoul(boot\_fb\_str, 16,  
                (unsigned long \*)&src\_phy\_addr);  
            if (ret)  
                pr\_warn("parse src\_phy\_addr fail!\\n");  
        } else if (i == BOOT\_FB\_WIDTH) {  
            ret = kstrtou32(boot\_fb\_str, 16, &src\_width);  
            if (ret)  
                pr\_warn("parse src\_width fail!\\n");  
        } else if (i == BOOT\_FB\_HEIGHT) {  
            ret = kstrtou32(boot\_fb\_str, 16, &src\_height);  
            fb\_height = src\_height;  
            if (ret)  
                pr\_warn("parse src\_height fail!\\n");  
        } else if (i == BOOT\_FB\_BPP) {  
            ret = kstrtou32(boot\_fb\_str, 16, &src\_bpp);  
            if (ret)  
                pr\_warn("parse src\_bpp fail!\\n");  
        } else if (i == BOOT\_FB\_STRIDE) {  
            ret = kstrtou32(boot\_fb\_str, 16, &src\_stride);  
            if (ret)  
                pr\_warn("parse src\_stride fail!\\n");  
        } else if (i == BOOT\_FB\_CROP\_L) {  
            ret = kstrtou32(boot\_fb\_str, 16, &src\_crop\_l);  
            if (ret)  
                pr\_warn("parse src\_crop\_l fail!\\n");  
        } else if (i == BOOT\_FB\_CROP\_T) {  
            ret = kstrtou32(boot\_fb\_str, 16, &src\_crop\_t);  
            if (ret)  
                pr\_warn("parse src\_crop\_t fail!\\n");  
        } else if (i == BOOT\_FB\_CROP\_R) {  
            ret = kstrtou32(boot\_fb\_str, 16, &src\_crop\_r);  
            if (ret)  
                pr\_warn("parse src\_crop\_r fail!\\n");  
        } else if (i == BOOT\_FB\_CROP\_B) {  
            ret = kstrtou32(boot\_fb\_str, 16, &src\_crop\_b);  
            if (ret)  
                pr\_warn("parse src\_crop\_b fail!\\n");  
        } else {  
            break;  
        }

        if (p == NULL)  
            break;  
        boot\_fb\_str = p + 1;  
    }  
} else {  
    \_\_wrn("no boot\_fb0\\n");  
    return -1;  
}

dst\_addr = (char \*)(info->screen\_base);  
dst\_width = info->var.xres;  
dst\_height = info->var.yres;  
dst\_bpp = info->var.bits\_per\_pixel;  
dst\_stride = info->fix.line\_length;

if ((src\_phy\_addr == NULL)  
    || (src\_width <= 0)  
    || (src\_height <= 0)  
    || (src\_stride <= 0)  
    || (src\_bpp <= 0)  
    || (dst\_addr == NULL)  
    || (dst\_width <= 0)  
    || (dst\_height <= 0)  
    || (dst\_stride <= 0)  
    || (dst\_bpp <= 0)  
    || (src\_bpp != dst\_bpp)) {  
    \_\_wrn  
        ("wrong para: src\[phy\_addr=%p,w=%d,h=%d,bpp=%d,stride=%d\], dst\[addr=%p,w=%d,h=%d,bpp=%d,stride=%d\]\\n",  
         src\_phy\_addr,  
         src\_width, src\_height, src\_bpp, src\_stride, dst\_addr,  
         dst\_width, dst\_height, dst\_bpp, dst\_stride);  
    return -1;  
}

map\_offset = (unsigned long)src\_phy\_addr + PAGE\_SIZE  
    - PAGE\_ALIGN((unsigned long)src\_phy\_addr + 1);  
src\_addr = (char \*)Fb\_map\_kernel\_cache((unsigned long)src\_phy\_addr -  
                       map\_offset,  
                       src\_stride \* src\_height +  
                       map\_offset);  
if (src\_addr == NULL) {  
    \_\_wrn("Fb\_map\_kernel\_cache for src\_addr failed\\n");  
    return -1;  
}

src\_addr\_b = src\_addr + map\_offset;  
if ((src\_crop\_b > src\_crop\_t) &&  
    (src\_height > src\_crop\_b - src\_crop\_t) &&  
    (src\_crop\_t >= 0) &&  
    (src\_height >= src\_crop\_b)) {  
    src\_height = src\_crop\_b - src\_crop\_t;  
    src\_addr\_b += (src\_stride \* src\_crop\_t);  
}  
if ((src\_crop\_r > src\_crop\_l)  
    && (src\_width > src\_crop\_r - src\_crop\_l)  
    && (src\_crop\_l >= 0)  
    && (src\_width >= src\_crop\_r)) {  
    src\_width = src\_crop\_r - src\_crop\_l;  
    src\_addr\_b += (src\_crop\_l \* src\_bpp >> 3);  
}

// 旋转图片数据  

// rotateImage180(src_addr_b, src_width, src_height, src_bpp, src_stride);

if (src\_height < dst\_height) {  
    int dst\_crop\_t = (dst\_height - src\_height) >> 1;

    dst\_addr += (dst\_stride \* dst\_crop\_t);  
} else if (src\_height > dst\_height) {  
    \_\_wrn("src\_height(%d) > dst\_height(%d),please cut the height\\n",  
          src\_height,  
          dst\_height);  
    Fb\_unmap\_kernel(src\_addr);  
    return -1;  
}  
if (src\_width < dst\_width) {  
    int dst\_crop\_l = (dst\_width - src\_width) >> 1;

    dst\_addr += (dst\_crop\_l \* dst\_bpp >> 3);  
} else if (src\_width > dst\_width) {  
    \_\_wrn("src\_width(%d) > dst\_width(%d),please cut the width!\\n",  
          src\_width,  
          dst\_width);  
    Fb\_unmap\_kernel(src\_addr);  
    return -1;  
}

src\_cp\_btyes = src\_width \* src\_bpp >> 3;  
src\_addr\_e = src\_addr\_b + src\_stride \* src\_height;  
for (; src\_addr\_b != src\_addr\_e; src\_addr\_b += src\_stride) {  
    memcpy((void \*)dst\_addr, (void \*)src\_addr\_b, src\_cp\_btyes);  
    dst\_addr += dst\_stride;  
}  
   //再此地方旋转修改  
dst\_addr = (char \*)(info->screen\_base);  
rotateImage180(dst\_addr, dst\_width, dst\_height, dst\_bpp, dst\_stride);

Fb\_unmap\_kernel(src\_addr);  
memblock\_free((unsigned long)src\_phy\_addr, src\_stride \* fb\_height);  
free\_reserved\_area(\_\_va(src\_phy\_addr), \_\_va(src\_phy\_addr + PAGE\_ALIGN(src\_stride \* fb\_height)), 0x00, "logo buffer");  
return 0;  

}

旋转180函数:

放在 tina-r528\lichee\linux-5.4\drivers\video\fbdev\sunxi\disp2\disp\dev_fb.c

注意此处由于申请的内存空间比较大,所以用的是vmalloc

void rotateImage180(char* src_addr, int width, int height, int bpp, int stride) {
// 计算每行像素数据的字节数
int row_bytes = width * (bpp / 8);
char * src_addr_e = src_addr + stride* height; //最后的地址位置

// 创建临时缓冲区用于保存旋转后的图像数据  
char\* temp\_data = vmalloc(row\_bytes \* height);  
    int y=0,x=0;

// 复制旋转后的图像数据到临时缓冲区  
for (y = 0; y < height; y++) {  
    char\* src\_row\_start = src\_addr + (y \* stride);  
    char\* dest\_row\_start = temp\_data + ((height - 1 - y) \* row\_bytes);  
            // 复制像素数据并进行左右翻转  
                    for ( x = 0; x < width; x++) {  
                            char\* src\_pixel = src\_row\_start + (x \* (bpp / 8));  
                            char\* dest\_pixel = dest\_row\_start + ((width - 1 - x) \* (bpp / 8));  
                            // 复制像素值  
                            memcpy(dest\_pixel, src\_pixel, (bpp / 8));  
                    }

}       //printk("----%s--%d\\n",\_\_func\_\_,\_\_LINE\_\_);  
    // 将旋转后的图像数据写回原始内存地址  
    for (; src\_addr != src\_addr\_e; src\_addr += stride) {  //循环复制每一行  
            memcpy((void \*)src\_addr, (void \*)temp\_data, row\_bytes);  
            temp\_data += row\_bytes;  //地址是增加地址宽度  
    }  
    //printk("----%s--%d\\n",\_\_func\_\_,\_\_LINE\_\_);  
// 释放临时缓冲区  
vfree(temp\_data);  

}

参考资料:

(67条消息) uboot修改启动logo-sunxi_u-boot启动时改横屏_Chasing_Chasing的博客-CSDN博客

【FAQ】全志F133(D1s)芯片 如何在Tina下进行显示旋转? | 全志在线开发者论坛 (aw-ol.com)

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章