产品设计出来之后啊,大家使用的时候觉得反过来使用更加便捷。但是屏幕显示是反的。那怎么办那?????
修改硬件费时费工,那能否软件实现那?????
如果纯软件使用那就太费系统资源了。于是就想到了使用全志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博客
手机扫一扫
移动阅读更方便
你可能感兴趣的文章