在全志T113平臺上,開發觸控式螢幕的QT產品。基於成本選擇了豎屏,但是產品是橫屏形式。
QT應用豎屏轉橫屏可以走純軟體的實現,但是T113平臺效能孱弱,使用純軟體實現UI操作不流暢,有卡頓現象。
全志T113有G2D模組,可以啟用G2D旋轉螢幕,減少CPU消耗,避免UI卡頓。
所以:
- 啟用G2D旋轉螢幕
- 旋轉觸控驅動
- 修改開機LOGO顯示,使uboot和linux中的logo顯示一致
啟用G2D旋轉螢幕
linux配置
- Device Drivers -> SUNXI G2D Driver -> sunxi g2d ratate module
- Device Drivers -> Graphics support -> Frame buffer Devices -> DISP2 Framebuffer rotation support -> Hardware(G2D) rotation support
裝置樹修改
/*----------------------------------------------------------------------------------
disp init configuration
disp_mode (0:screen0<screen0,fb0>)
screenx_output_type (0:none; 1:lcd; 2:tv; 3:hdmi;5:vdpo)
screenx_output_mode (used for hdmi output, 0:480i 1:576i 2:480p 3:576p 4:720p50)
(5:720p60 6:1080i50 7:1080i60 8:1080p24 9:1080p50 10:1080p60)
screenx_output_format (for hdmi, 0:RGB 1:yuv444 2:yuv422 3:yuv420)
screenx_output_bits (for hdmi, 0:8bit 1:10bit 2:12bit 2:16bit)
screenx_output_eotf (for hdmi, 0:reserve 4:SDR 16:HDR10 18:HLG)
screenx_output_cs (for hdmi, 0:undefined 257:BT709 260:BT601 263:BT2020)
screenx_output_dvi_hdmi (for hdmi, 0:undefined 1:dvi mode 2:hdmi mode)
screen0_output_range (for hdmi, 0:default 1:full 2:limited)
screen0_output_scan (for hdmi, 0:no data 1:overscan 2:underscan)
screen0_output_aspect_ratio (for hdmi, 8-same as original picture 9-4:3 10-16:9 11-14:9)
fbx format (4:RGB655 5:RGB565 6:RGB556 7:ARGB1555 8:RGBA5551 9:RGB888 10:ARGB8888 12:ARGB4444)
fbx pixel sequence (0:ARGB 1:BGRA 2:ABGR 3:RGBA)
fb0_scaler_mode_enable(scaler mode enable, used FE)
fbx_width,fbx_height (framebuffer horizontal/vertical pixels, fix to output resolution while equal 0)
lcdx_backlight (lcd init backlight,the range:[0,256],default:197
lcdx_yy (lcd init screen bright/contrast/saturation/hue, value:0~100, default:50/50/57/50)
lcd0_contrast (LCD contrast, 0~100)
lcd0_saturation (LCD saturation, 0~100)
lcd0_hue (LCD hue, 0~100)
framebuffer software rotation setting:
disp_rotation_used: (0:disable; 1:enable,you must set fbX_width to lcd_y,
set fbX_height to lcd_x)
degreeX: (X:screen index; 0:0 degree; 1:90 degree; 3:270 degree)
degreeX_Y: (X:screen index; Y:layer index 0~15; 0:0 degree; 1:90 degree; 3:270 degree)
devX_output_type : config output type in bootGUI framework in UBOOT-2018.
(0:none; 1:lcd; 2:tv; 4:hdmi;)
devX_output_mode : config output resolution(see include/video/sunxi_display2.h) of bootGUI framework in UBOOT-2018
devX_screen_id : config display index of bootGUI framework in UBOOT-2018
devX_do_hpd : whether do hpd detectation or not in UBOOT-2018
chn_cfg_mode : Hardware DE channel allocation config. 0:single display with 6
channel, 1:dual display with 4 channel in main display and 2 channel in second
display, 2:dual display with 3 channel in main display and 3 channel in second
in display.
----------------------------------------------------------------------------------*/
&disp {
disp_init_enable = <1>;
disp_mode = <0>;
screen0_output_type = <1>;
screen0_output_mode = <4>;
screen1_output_type = <1>;
screen1_output_mode = <4>;
screen1_output_format = <0>;
screen1_output_bits = <0>;
screen1_output_eotf = <4>;
screen1_output_cs = <257>;
screen1_output_dvi_hdmi = <2>;
screen1_output_range = <2>;
screen1_output_scan = <0>;
screen1_output_aspect_ratio = <8>;
dev0_output_type = <1>;
dev0_output_mode = <4>;
dev0_screen_id = <0>;
dev0_do_hpd = <0>;
dev1_output_type = <4>;
dev1_output_mode = <10>;
dev1_screen_id = <1>;
dev1_do_hpd = <1>;
def_output_dev = <0>;
hdmi_mode_check = <1>;
fb0_format = <0>;
fb0_width = <800>;
fb0_height = <480>;
disp_rotation_used = <1>;
degree0 = <3>;
fb1_format = <0>;
fb1_width = <0>;
fb1_height = <0>;
chn_cfg_mode = <1>;
disp_para_zone = <1>;
/*VCC-LCD*/
/* dc1sw-supply = <®_dc1sw>;*/
/*VCC-DSI*/
/* eldo3-supply = <®_eldo3>;*/
/*VCC-PD*/
/* dcdc1-supply = <®_dcdc1>;*/
};
需要新增和修改的地方就是
fb0_width = <800>;
fb0_height = <480>;
disp_rotation_used = <1>;
degree0 = <3>;
觸控驅動旋轉
觸控啟動修改只需要修改裝置樹
ctp@14 {
compatible = "allwinner,goodix";
device_type = "ctp";
reg = <0x14>;
status = "okay";
ctp_name = "gt9xxnew_ts";
ctp_twi_id = <0x2>;
ctp_twi_addr = <0x14>;
ctp_screen_max_x = <0x320>; /* 800 */
ctp_screen_max_y = <0x1e0>; /* 400 */
ctp_revert_x_flag = <0x1>;
ctp_revert_y_flag = <0x0>;
ctp_exchange_x_y_flag = <0x1>;
ctp_int_port = <&pio PE 9 GPIO_ACTIVE_HIGH>;
ctp_wakeup = <&pio PE 8 GPIO_ACTIVE_HIGH>;
/*ctp-supply = <®_aldo2>;*/
/*ctp_power_ldo = <®_aldo2>;*/
/*ctp_power_ldo_vol = <3300>;*/
};
其中 ctp_revert_x_flag
、ctp_exchange_x_y_flag
是重點修改的地方。
開機LOGO
如果你只修改了,以上兩個功能,產品已經是橫屏狀態。但是,在開機logo階段,由於uboot沒有啟用G2D功能,所有uboot階段和linux階段logo顯示角度不一樣。修改uboot和核心顯示logo原始碼可以使得logo顯示一致。事後,我發現,可以製作一個旋轉後的logo圖片,達到只修改uboot或者linux一處就可以了。
- uboot階段旋轉logo,
- linux反向旋轉logo,防止二次旋轉
U-Boot
uboot中修改的檔案路徑u-boot-2018/drivers/video/sunxi/logo_display/cmd_sunxi_bmp.c
。rotateBMP90
在螢幕上的效果是將bmp圖片逆時針旋轉了90度。
#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)
void rotateBMP90(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;
if (info_header->compression == 0) {
info_header->image_size = row_size * height;
}
// 分配記憶體來儲存旋轉後的影像資料
uint8_t* rotated_data = (uint8_t*)malloc(info_header->image_size);
// 旋轉影像資料90度
for (int32_t y = 0; y < height; y++) {
for (int32_t x = 0; x < width; x++) {
for (int32_t byte = 0; byte < row_size / width; byte++) {
rotated_data[(x * height + (height - y - 1)) * (row_size / width) + byte] =
bmp_head_add[file_header->offset + (y * width + x) * (row_size / width) + byte];
}
}
}
// 更新影像資訊頭中的寬度和高度
info_header->width = height;
info_header->height = width;
memcpy(bmp_head_add + file_header->offset, rotated_data, info_header->image_size);
// 將旋轉後的影像資料寫入新的BMP檔案
//FILE* out_file = fopen("rotated_logo_270.bmp", "wb");
// fwrite(file_header, sizeof(BMPFileHeader), 1, out_file);
// fwrite(info_header, sizeof(BMPInfoHeader), 1, out_file);
// fwrite(rotated_data, info_header->image_size, 1, out_file);
// 釋放記憶體並關閉檔案
free(rotated_data);
// fclose(out_file);
}
int sunxi_bmp_display(char *name)
{
int ret = -1;
char *argv[6];
char bmp_head[32];
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;
}
rotateBMP90(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;
}
Linux
void rotateImage90(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 = (char*)vmalloc(row_bytes * height);
int y = 0, x = 0;
// 複製旋轉後的影像資料到臨時緩衝區
for (y = 0; y < height; y++) {
char* src_row_start = src_addr + (y * stride);
for (x = 0; x < width; x++) {
char* src_pixel = src_row_start + (x * (bpp / 8));
char* dest_pixel = temp_data + (x * height + (height - 1 - y)) * (bpp / 8);
// 複製畫素值
memcpy(dest_pixel, src_pixel, (bpp / 8));
}
}
// 將旋轉後的影像資料寫回原始記憶體地址
for (y = 0; y < width; y++) {
char* dest_row_start = src_addr + (y * stride);
char* src_row_start = temp_data + (y * height * (bpp / 8));
memcpy(dest_row_start, src_row_start, height * (bpp / 8));
}
// 釋放臨時緩衝區
vfree(temp_data);
}
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;
int tmp;
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;
}
// rotateImage180(dst_addr, dst_width, dst_height, dst_bpp, dst_stride);
tmp = dst_width;
dst_width = dst_height;
dst_height = tmp;
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);
}
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;
#if defined(CONFIG_ION)
sunxi_ion_dma_buf_begin_cpu_access(g_fbi.mem[info->node]->p_item->dmabuf);
#endif
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);
rotateImage90(dst_addr, dst_width, dst_height, dst_bpp, dst_stride);
#if defined(CONFIG_ION)
sunxi_ion_dma_buf_end_cpu_access(g_fbi.mem[info->node]->p_item->dmabuf);
#endif
Fb_unmap_kernel(src_addr);
g_fbi.wait_for_free[sel] = true;
//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;
}