T113啟動G2D旋轉豎屏當作橫屏使用

liamyi發表於2024-11-11

在全志T113平臺上,開發觸控式螢幕的QT產品。基於成本選擇了豎屏,但是產品是橫屏形式。
QT應用豎屏轉橫屏可以走純軟體的實現,但是T113平臺效能孱弱,使用純軟體實現UI操作不流暢,有卡頓現象。
全志T113有G2D模組,可以啟用G2D旋轉螢幕,減少CPU消耗,避免UI卡頓。
所以:

  1. 啟用G2D旋轉螢幕
  2. 旋轉觸控驅動
  3. 修改開機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 = <&reg_dc1sw>;*/
	/*VCC-DSI*/
/*	eldo3-supply = <&reg_eldo3>;*/
	/*VCC-PD*/
/*	dcdc1-supply = <&reg_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 = <&reg_aldo2>;*/
	/*ctp_power_ldo = <&reg_aldo2>;*/
	/*ctp_power_ldo_vol = <3300>;*/
};

其中 ctp_revert_x_flagctp_exchange_x_y_flag是重點修改的地方。

如果你只修改了,以上兩個功能,產品已經是橫屏狀態。但是,在開機logo階段,由於uboot沒有啟用G2D功能,所有uboot階段和linux階段logo顯示角度不一樣。修改uboot和核心顯示logo原始碼可以使得logo顯示一致。事後,我發現,可以製作一個旋轉後的logo圖片,達到只修改uboot或者linux一處就可以了。

  1. uboot階段旋轉logo,
  2. linux反向旋轉logo,防止二次旋轉

U-Boot

uboot中修改的檔案路徑u-boot-2018/drivers/video/sunxi/logo_display/cmd_sunxi_bmp.crotateBMP90在螢幕上的效果是將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;
}

相關文章