全志G2D實現螢幕旋轉,開機logo實現手動旋轉。

2023-06-21 18:01:20

產品設計出來之後啊,大家使用的時候覺得反過來使用更加便捷。但是螢幕顯示是反的。那怎麼辦那?????

修改硬體費時費工,那能否軟體實現那?????

 如果純軟體使用那就太費系統資源了。於是就想到了使用全志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)