/**
 * @file lcd_panel_mgmt.c
 * @author June6 (https://github.com/Junedoubling6)
 * @brief
 * @version 0.1
 * @date 2023-08-16
 *
 * @copyright Copyright (c) 2022 - , All rights reserved.
 *
 */
#include "lcd_panel_mgmt.h"

#include "driver/gpio.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_rgb.h"
#include "esp_system.h"
#include "esp_timer.h"
#include "hal/gpio_hal.h"
#include "lcd_display_drv.h"
#include "log.h"
#include "rtos_drv.h"
#include "soc/lcd_periph.h"
#include "ui.h"
#if CONFIG_LCD_TOUCH_ENABLED
#include "iic_drv.h"
#if CONFIG_LCD_TOUCH_CONTROLLER_GT911
#include "esp_lcd_touch_gt911.h"
#elif CONFIG_LCD_TOUCH_CONTROLLER_TT21100
#include "esp_lcd_touch_tt21100.h"
#elif CONFIG_LCD_TOUCH_CONTROLLER_FT5X06
#include "esp_lcd_touch_ft5x06.h"
#endif
#endif

#include "i2c_mcu.h"
#include "system_control.h"
#include "ui_app.h"

/* FIXME: June6 CONFIG_LCD_MIRROR_SOFTWARE 中的所有处理均需要修复,
 * 实际运行效果会反复跳变 */
#if CONFIG_LCD_MIRROR_SOFTWARE
/* NOTE: June6 目前只对180度进行调整 */
#define LCD_ROTATION_DEGREE 180
#else
#define LCD_ROTATION_DEGREE 0
#endif

/* NOTE: June6 水平和垂直方向的像素数 */
#define LCD_H_RES 480
#define LCD_V_RES 480
/* NOTE: June6 根据LCD屏幕的规格更新以下配置 */
/* NOTE: June6 在无法根据提高 Flash 及 PSRAM 速率, 增大 bounce buffer size,
 * 解决漂移问题时, 可适当降低 PCLK 频率 */
#define LCD_PIXEL_CLOCK_HZ (11 * 1000 * 1000)
#define HSYNC_BACK_PORCH 30
#define HSYNC_FRONT_PORCH 30
#define HSYNC_PULSE_WIDTH 8
#define VSYNC_BACK_PORCH 30
#define VSYNC_FRONT_PORCH 30
#define VSYNC_PULSE_WIDTH 8
#define LCD_PANEL_60HZ_RGB_TIMING()             \
  {                                             \
      .pclk_hz = LCD_PIXEL_CLOCK_HZ,            \
      .h_res = LCD_H_RES,                       \
      .v_res = LCD_V_RES,                       \
      .hsync_pulse_width = HSYNC_PULSE_WIDTH,   \
      .hsync_back_porch = HSYNC_BACK_PORCH,     \
      .hsync_front_porch = HSYNC_FRONT_PORCH,   \
      .vsync_pulse_width = VSYNC_PULSE_WIDTH,   \
      .vsync_back_porch = VSYNC_BACK_PORCH,     \
      .vsync_front_porch = VSYNC_FRONT_PORCH,   \
      .flags.pclk_active_neg = (uint32_t)false, \
  }
/* bounce buffer 分配的行数必须要能被总高度整除, 例如高度是480, 60,
 * 80行是可以的， 70行是不行的, 再加大不成功, 那就是ram不够了,
 * 可以打印下剩余内存确认下 */
#define LCD_RGB_BOUNCE_BUFFER_SIZE \
  (LCD_H_RES * CONFIG_LCD_RGB_BOUNCE_BUFFER_HEIGHT)
/* LVGL缓冲区相关参数, 可由用户调整:
 * (如果启用了避免撕裂功能, 这些参数将无效)
 *
 * -缓冲区分配的内存类型:
 * -MALLOC_CAP_SPIRAM: 在PSRAM中分配LVGL缓冲区
 * -MALLOC_CAP_INTERNAL: 在SRAM中分配LVGL缓冲区
 * (SRAM比PSRAM快, 但PSRAM的容量更大)
 */
#if CONFIG_LCD_BUF_PSRAM
#define LCD_BUFFER_MALLOC_CAPS (MALLOC_CAP_SPIRAM)
#elif CONFIG_LCD_BUF_INTERNAL
#define LCD__BUFFER_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#endif
/* 以下配置是根据上述配置自动设置的, 用户无需修改 */
#if CONFIG_LCD_RGB_AVOID_TEAR_ENABLE
#if (CONFIG_LCD_RGB_AVOID_TEAR_MODE == 1)
#define LCD_RGB_BUFFER_NUMS 2
#define LCD_RGB_FULL_REFRESH 1
#define LCD_RGB_DIRECT_MODE 0
#elif (CONFIG_LCD_RGB_AVOID_TEAR_MODE == 2)
#define LCD_RGB_BUFFER_NUMS 3
#define LCD_RGB_FULL_REFRESH 1
#define LCD_RGB_DIRECT_MODE 0
#elif (CONFIG_LCD_RGB_AVOID_TEAR_MODE == 3)
#define LCD_RGB_BUFFER_NUMS 2
#define LCD_RGB_FULL_REFRESH 0
#define LCD_RGB_DIRECT_MODE 1
#endif
#else
#define LCD_RGB_BUFFER_NUMS 1
#define LCD_RGB_FULL_REFRESH 0
#define LCD_RGB_DIRECT_MODE 0
#endif

#if CONFIG_LCD_TOUCH_ENABLED
#define LCD_TOUCH_IIC_PORT 0
/* NOTE: June6 LCD触摸识别, IIC速率: 400Khz */
#define LCD_TOUCH_IIC_FREQ_HZ 400000
#define TOUCH_PIN_NUM_INT (GPIO_NUM_7)
#define TOUCH_PIN_NUM_RST (GPIO_NUM_4)
#define TOUCH_H_RES 480
#define TOUCH_V_RES 480
#define RESET_TOUCH_TIMEOUT_MS ((uint32_t)600000)
#endif

#define LVGL_TICK_PERIOD_MS 1
/* 支持的对齐方式: 16、32、64, 更高的对齐量可以实现更高的突发传输大小,
 * 从而获得更高的 i80/RGB 总线吞吐量 */
#define PSRAM_DATA_ALIGNMENT 64

static bool on_vsync_event(esp_lcd_panel_handle_t panel,
                           const esp_lcd_rgb_panel_event_data_t* event_data,
                           void* user_data);

#if (LCD_ROTATION_DEGREE != 0)
typedef enum { FLUSH_STATUS_PART, FLUSH_STATUS_FULL } lv_port_flush_status_t;

typedef struct {
  uint16_t inv_p;
  uint8_t inv_area_joined[LV_INV_BUF_SIZE];
  lv_area_t inv_areas[LV_INV_BUF_SIZE];
} lv_port_dirty_area_t;

typedef enum {
  FLUSH_PROBE_PART_COPY,
  FLUSH_PROBE_SKIP_COPY,
  FLUSH_PROBE_FULL_COPY,
} lv_port_flush_probe_t;
#endif

static const char* TAG = "lcd_panel_mgmt";
static esp_timer_handle_t lvgl_tick_timer = NULL;
static esp_timer_handle_t restart_touch_timer = NULL;
static lv_disp_t* disp;
/* 包含回调函数 */
static lv_disp_drv_t disp_drv;
static esp_lcd_rgb_panel_event_callbacks_t panel_callbacks = {
#if (LCD_RGB_BOUNCE_BUFFER_SIZE > 0)
    .on_bounce_frame_finish = on_vsync_event,
#else
    .on_vsync = on_vsync_event,
#endif
};
/* 包含称为绘图缓冲区的内部图形缓冲区 */
static lv_disp_draw_buf_t disp_buf;
/* 触摸IO句柄 */
static esp_lcd_panel_io_handle_t tp_io_handle = NULL;
/* LCD 面板句柄 */
static esp_lcd_panel_handle_t panel_handle = NULL;
/* NOTE: June6
 * 分配LVGL使用的绘制缓冲区建议将绘制缓冲区的大小选择为至少1/10屏幕大小 */
static lv_color_t* lvgl_buf1 = NULL;
static lv_color_t* lvgl_buf2 = NULL;
#if (LCD_ROTATION_DEGREE != 0)
static lv_port_dirty_area_t dirty_area;
#endif

#if ((LCD_RGB_FULL_REFRESH) && (LCD_RGB_BUFFER_NUMS == 3) && \
     (LCD_ROTATION_DEGREE != 0))
static void* rgb_last_buf = NULL;
static void* rgb_next_buf = NULL;
static void* flush_next_buf = NULL;
#endif

#if CONFIG_LCD_TOUCH_ENABLED
static lv_indev_t* touch = NULL;
/* 输入设备驱动(触摸) */
static lv_indev_drv_t indev_drv;
static esp_lcd_touch_handle_t touch_handle = NULL;
#endif

#if (LCD_ROTATION_DEGREE != 0)
/**
 * @brief Get the next frame buffer object
 *
 * @param handle
 * @return void*
 */
static void* get_next_frame_buffer(esp_lcd_panel_handle_t handle) {
  static void* fb[2] = {NULL};
  static void* next_fb = NULL;
  if (next_fb == NULL) {
    ESP_ERROR_CHECK(
        esp_lcd_rgb_panel_get_frame_buffer(handle, 2, &fb[0], &fb[1]));
    next_fb = fb[1];
  } else {
    next_fb = (next_fb == fb[0]) ? fb[1] : fb[0];
  }

  return next_fb;
}

/**
 * @brief Rotate copy pixel
 *
 * @param from
 * @param to
 * @param x_start
 * @param y_start
 * @param x_end
 * @param y_end
 * @param w
 * @param h
 * @param rotation
 * @return IRAM_ATTR
 */
IRAM_ATTR static void rotate_copy_pixel(const uint16_t* from, uint16_t* to,
                                        uint16_t x_start, uint16_t y_start,
                                        uint16_t x_end, uint16_t y_end,
                                        uint16_t w, uint16_t h,
                                        uint16_t rotation) {
  int from_index = 0;
  int to_index = 0;
  int to_index_const = 0;
  switch (rotation) {
    case 90: {
      to_index_const = (w - x_start - 1) * h;
      for (int from_y = y_start; from_y < y_end + 1; from_y++) {
        from_index = from_y * w + x_start;
        to_index = to_index_const + from_y;
        for (int from_x = x_start; from_x < x_end + 1; from_x++) {
          *(to + to_index) = *(from + from_index);
          from_index += 1;
          to_index -= h;
        }
      }
    } break;

    case 180: {
      to_index_const = h * w - x_start - 1;
      for (int from_y = y_start; from_y < y_end + 1; from_y++) {
        from_index = from_y * w + x_start;
        to_index = to_index_const - from_y * w;
        for (int from_x = x_start; from_x < x_end + 1; from_x++) {
          *(to + to_index) = *(from + from_index);
          from_index += 1;
          to_index -= 1;
        }
      }
    } break;

    case 270: {
      to_index_const = (x_start + 1) * h - 1;
      for (int from_y = y_start; from_y < y_end + 1; from_y++) {
        from_index = from_y * w + x_start;
        to_index = to_index_const - from_y;
        for (int from_x = x_start; from_x < x_end + 1; from_x++) {
          *(to + to_index) = *(from + from_index);
          from_index += 1;
          to_index += h;
        }
      }
    } break;

    default: {
      USER_LOGE(TAG, "rotate_copy_pixel rotation error");
    } break;
  }
}
#endif

#if CONFIG_LCD_RGB_AVOID_TEAR_ENABLE
#if LCD_RGB_DIRECT_MODE
#if ((LCD_ROTATION_DEGREE != 0))
/**
 * @brief Save dirty area
 *
 * @param area
 */
static void flush_dirty_save(lv_port_dirty_area_t* area) {
  lv_disp_t* disp_refr = _lv_refr_get_disp_refreshing();
  area->inv_p = disp_refr->inv_p;
  for (int i = 0; i < disp_refr->inv_p; i++) {
    area->inv_area_joined[i] = disp_refr->inv_area_joined[i];
    area->inv_areas[i] = disp_refr->inv_areas[i];
  }
}

/**
 * @brief Probe dirty area to copy
 *
 * @param drv
 * @return lv_port_flush_probe_t
 * @details This function is used to avoid tearing effect, and only work with
 *          LVGL direct-mode.
 */
static lv_port_flush_probe_t flush_copy_probe(lv_disp_drv_t* drv) {
  static lv_port_flush_status_t prev_status = FLUSH_STATUS_PART;
  lv_port_flush_status_t cur_status;
  lv_port_flush_probe_t probe_result;
  lv_disp_t* disp_refr = _lv_refr_get_disp_refreshing();
  uint32_t flush_ver = 0;
  uint32_t flush_hor = 0;
  for (int i = 0; i < disp_refr->inv_p; i++) {
    if (disp_refr->inv_area_joined[i] == 0) {
      flush_ver = (disp_refr->inv_areas[i].y2 + 1 - disp_refr->inv_areas[i].y1);
      flush_hor = (disp_refr->inv_areas[i].x2 + 1 - disp_refr->inv_areas[i].x1);
      break;
    }
  }
  /* 检查当前全屏是否刷新 */
  cur_status = ((flush_ver == drv->ver_res) && (flush_hor == drv->hor_res))
                   ? (FLUSH_STATUS_FULL)
                   : (FLUSH_STATUS_PART);
  if (prev_status == FLUSH_STATUS_FULL) {
    if ((cur_status == FLUSH_STATUS_PART))
      probe_result = FLUSH_PROBE_FULL_COPY;
    else
      probe_result = FLUSH_PROBE_SKIP_COPY;
  } else {
    probe_result = FLUSH_PROBE_PART_COPY;
  }
  prev_status = cur_status;

  return probe_result;
}

/**
 * @brief Get Next buffer
 *
 * @param panel_handle
 * @return void*
 */
static inline void* flush_get_next_buf(void* panel_handle) {
  return get_next_frame_buffer(panel_handle);
}

/**
 * @brief Copy dirty area
 *
 * @param dst
 * @param src
 * @param area
 * @details This function is used to avoid tearing effect, and only work with
 *          LVGL direct-mode.
 */
static void flush_dirty_copy(void* dst, void* src, lv_port_dirty_area_t* area) {
  lv_coord_t x_start, x_end, y_start, y_end;
  for (int i = 0; i < area->inv_p; i++) {
    /* 刷新未连接的区域 */
    if (area->inv_area_joined[i] == 0) {
      x_start = area->inv_areas[i].x1;
      x_end = area->inv_areas[i].x2;
      y_start = area->inv_areas[i].y1;
      y_end = area->inv_areas[i].y2;
      rotate_copy_pixel(src, dst, x_start, y_start, x_end, y_end, LCD_H_RES,
                        LCD_V_RES, LCD_ROTATION_DEGREE);
    }
  }
}

/**
 * @brief lvgl flush callback.
 *
 * @param drv
 * @param area
 * @param color_map
 */
static void lvgl_flush_callback(lv_disp_drv_t* drv, const lv_area_t* area,
                                lv_color_t* color_map) {
  esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data;
  const int offsetx1 = area->x1;
  const int offsetx2 = area->x2;
  const int offsety1 = area->y1;
  const int offsety2 = area->y2;
  void* next_fb = NULL;
  lv_port_flush_probe_t probe_result = FLUSH_PROBE_PART_COPY;
  lv_disp_t* disp_default = lv_disp_get_default();
  /* 上次区域刷新后的操作 */
  if (lv_disp_flush_is_last(drv)) {
    /* 检查 full_refresh 标志是否已触发 */
    if (drv->full_refresh) {
      /* 重置 flag */
      drv->full_refresh = 0;
      /* 将数据从整个屏幕LVGL的缓冲区漫游并复制到下一帧缓冲区 */
      next_fb = flush_get_next_buf(panel_handle);
      rotate_copy_pixel((uint16_t*)color_map, next_fb, offsetx1, offsety1,
                        offsetx2, offsety2, LV_HOR_RES, LV_VER_RES,
                        LCD_ROTATION_DEGREE);
      /* 将当前RGB帧缓冲区切换到 next_fb */
      esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1, offsetx2 + 1,
                                offsety2 + 1, next_fb);
      /* 正在等待当前帧缓冲区完成传输 */
      ulTaskNotifyValueClear(NULL, ULONG_MAX);
      ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
      /* 同步更新另一个帧缓冲区的脏区域 */
      flush_dirty_copy(flush_get_next_buf(panel_handle), color_map,
                       &dirty_area);
      flush_get_next_buf(panel_handle);
    } else {
      /* 探测当前脏区域的复制方法 */
      probe_result = flush_copy_probe(drv);
      if (probe_result == FLUSH_PROBE_FULL_COPY) {
        /* 为下一帧缓冲区保存当前脏区域 */
        flush_dirty_save(&dirty_area);
        /* 设置LVGL完全刷新标志并提前设置刷新准备 */
        drv->full_refresh = true;
        disp_default->rendering_in_progress = false;
        lv_disp_flush_ready(drv);
        /* 强制刷新整个屏幕, 并将递归调用 flush_callback */
        lv_refr_now(_lv_refr_get_disp_refreshing());
      } else {
        /* 更新下一帧缓冲区的当前脏区域 */
        next_fb = flush_get_next_buf(panel_handle);
        flush_dirty_save(&dirty_area);
        flush_dirty_copy(next_fb, color_map, &dirty_area);
        /* 将当前RGB帧缓冲区切换到 next_fb */
        esp_lcd_panel_draw_bitmap(panel_handle, offsetx1, offsety1,
                                  offsetx2 + 1, offsety2 + 1, next_fb);
        /* 正在等待当前帧缓冲区完成传输  */
        ulTaskNotifyValueClear(NULL, ULONG_MAX);
        ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
        if (probe_result == FLUSH_PROBE_PART_COPY) {
          /* 同步更新另一个帧缓冲区的脏区域 */
          flush_dirty_save(&dirty_area);
          flush_dirty_copy(flush_get_next_buf(panel_handle), color_map,
                           &dirty_area);
          flush_get_next_buf(panel_handle);
        }
      }
    }
  }
  lv_disp_flush_ready(drv);
}
#else
/**
 * @brief lvgl flush callback.
 *
 * @param drv
 * @param area
 * @param color_map
 */
static void lvgl_flush_callback(lv_disp_drv_t* drv, const lv_area_t* area,
                                lv_color_t* color_map) {
  esp_lcd_panel_handle_t handle = (esp_lcd_panel_handle_t)drv->user_data;
  int offsetx1 = area->x1;
  int offsetx2 = area->x2;
  int offsety1 = area->y1;
  int offsety2 = area->y2;
  /* 上次区域刷新后的操作 */
  if (lv_disp_flush_is_last(drv)) {
    /* 将当前RGB帧缓冲区切换为 color_map */
    esp_lcd_panel_draw_bitmap(handle, offsetx1, offsety1, offsetx2 + 1,
                              offsety2 + 1, color_map);
    /* 正在等待最后一个帧缓冲区完成传输 */
    ulTaskNotifyValueClear(NULL, ULONG_MAX);
    ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  }
  lv_disp_flush_ready(drv);
}
#endif
#elif ((LCD_RGB_FULL_REFRESH) && (LCD_RGB_BUFFER_NUMS == 2))
/**
 * @brief lvgl flush callback.
 *
 * @param drv
 * @param area
 * @param color_map
 */
static void lvgl_flush_callback(lv_disp_drv_t* drv, const lv_area_t* area,
                                lv_color_t* color_map) {
  esp_lcd_panel_handle_t handle = (esp_lcd_panel_handle_t)drv->user_data;
  const int offsetx1 = area->x1;
  const int offsetx2 = area->x2;
  const int offsety1 = area->y1;
  const int offsety2 = area->y2;
  /* 将当前RGB帧缓冲区切换为 color_map */
  esp_lcd_panel_draw_bitmap(handle, offsetx1, offsety1, offsetx2 + 1,
                            offsety2 + 1, color_map);

  /* 正在等待最后一个帧缓冲区完成传输 */
  ulTaskNotifyValueClear(NULL, ULONG_MAX);
  ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
  lv_disp_flush_ready(drv);
}
#elif ((LCD_RGB_FULL_REFRESH) && (LCD_RGB_BUFFER_NUMS == 3))
void lvgl_flush_callback(lv_disp_drv_t* drv, const lv_area_t* area,
                         lv_color_t* color_map) {
  esp_lcd_panel_handle_t handle = (esp_lcd_panel_handle_t)drv->user_data;
  const int offsetx1 = area->x1;
  const int offsetx2 = area->x2;
  const int offsety1 = area->y1;
  const int offsety2 = area->y2;
#if (LCD_ROTATION_DEGREE != 0)
  void* next_fb = get_next_frame_buffer(handle);
  /* 将脏区域从当前LVGL的缓冲区旋转并复制到下一个RGB帧缓冲区 */
  rotate_copy_pixel((uint16_t*)color_map, next_fb, offsetx1, offsety1, offsetx2,
                    offsety2, LV_HOR_RES, LV_VER_RES, LCD_ROTATION_DEGREE);
  /* 将当前RGB帧缓冲区切换到 next_fb */
  esp_lcd_panel_draw_bitmap(handle, offsetx1, offsety1, offsetx2 + 1,
                            offsety2 + 1, next_fb);
#else
  drv->draw_buf->buf1 = color_map;
  drv->draw_buf->buf2 = flush_next_buf;
  flush_next_buf = color_map;
  /* 将当前RGB帧缓冲区切换为 color_map */
  esp_lcd_panel_draw_bitmap(handle, offsetx1, offsety1, offsetx2 + 1,
                            offsety2 + 1, color_map);
  rgb_next_buf = color_map;
#endif
  lv_disp_flush_ready(drv);
}
#endif
#else
void lvgl_flush_callback(lv_disp_drv_t* drv, const lv_area_t* area,
                         lv_color_t* color_map) {
  esp_lcd_panel_handle_t handle = (esp_lcd_panel_handle_t)drv->user_data;
  const int offsetx1 = area->x1;
  const int offsetx2 = area->x2;
  const int offsety1 = area->y1;
  const int offsety2 = area->y2;
  /* 只需将数据从颜色映射复制到RGB帧缓冲区 */
  esp_lcd_panel_draw_bitmap(handle, offsetx1, offsety1, offsetx2 + 1,
                            offsety2 + 1, color_map);
  lv_disp_flush_ready(drv);
}
#endif

/**
 * @brief lvgl update callback.
 *
 * @param drv
 */
static void lvgl_update_callback(lv_disp_drv_t* drv) {
  /* 在LVGL中旋转屏幕时, 旋转显示器和触摸 在更新驱动程序参数时调用 */
  /* 动态调用 lv_disp_set_rotation(lv_disp_t * disp, lv_disp_rot_t rotation) */
  esp_lcd_panel_handle_t handle = (esp_lcd_panel_handle_t)drv->user_data;
  switch (drv->rotated) {
    case LV_DISP_ROT_NONE: {
      /* 显示旋转 */
      esp_lcd_panel_swap_xy(handle, false);
      esp_lcd_panel_mirror(handle, true, false);
#if CONFIG_LCD_TOUCH_ENABLED
      /* 显示触摸 */
      esp_lcd_touch_set_mirror_y(touch_handle, false);
      esp_lcd_touch_set_mirror_x(touch_handle, false);
#endif
    } break;

    case LV_DISP_ROT_90: {
      /* 显示旋转 */
      esp_lcd_panel_swap_xy(handle, true);
      esp_lcd_panel_mirror(handle, true, true);
#if CONFIG_LCD_TOUCH_ENABLED
      /* 显示触摸 */
      esp_lcd_touch_set_mirror_y(touch_handle, false);
      esp_lcd_touch_set_mirror_x(touch_handle, false);
#endif
    } break;

    case LV_DISP_ROT_180: {
      /* 显示旋转 */
      esp_lcd_panel_swap_xy(handle, false);
      esp_lcd_panel_mirror(handle, false, true);
#if CONFIG_LCD_TOUCH_ENABLED
      /* 显示触摸 */
      esp_lcd_touch_set_mirror_y(touch_handle, false);
      esp_lcd_touch_set_mirror_x(touch_handle, false);
#endif
    } break;

    case LV_DISP_ROT_270: {
      /* 显示旋转 */
      esp_lcd_panel_swap_xy(handle, true);
      esp_lcd_panel_mirror(handle, false, false);
#if CONFIG_LCD_TOUCH_ENABLED
      /* 显示触摸 */
      esp_lcd_touch_set_mirror_y(touch_handle, false);
      esp_lcd_touch_set_mirror_x(touch_handle, false);
#endif
    } break;
  }
}

/**
 * @brief On vsync event.
 *
 * @param panel
 * @param event_data
 * @param user_data
 * @return true
 * @return false
 */
static bool on_vsync_event(esp_lcd_panel_handle_t panel,
                           const esp_lcd_rgb_panel_event_data_t* event_data,
                           void* user_data) {
  BaseType_t need_yield = pdFALSE;
#if ((LCD_RGB_FULL_REFRESH) && (LCD_RGB_BUFFER_NUMS == 3) && \
     (LCD_ROTATION_DEGREE == 0))
  if (rgb_next_buf != rgb_last_buf) {
    flush_next_buf = rgb_last_buf;
    rgb_last_buf = rgb_next_buf;
  }
#elif CONFIG_LCD_RGB_AVOID_TEAR_ENABLE
  /* 通知当前RGB帧缓冲区已传输 */
  if (lcd_panel_ui_task_handler == NULL) {
    USER_LOGE(TAG, "lcd_panel_ui_task_handler is NULL");
    goto End;
  }
  xTaskNotifyFromISR(lcd_panel_ui_task_handler, ULONG_MAX, eNoAction,
                     &need_yield);
#endif

End:
  return (need_yield == pdTRUE);
}

#if CONFIG_LCD_TOUCH_ENABLED
/**
 * @brief Read input device data.
 *
 * @param drv
 * @param data
 */
static void lvgl_touch_read_cb(lv_indev_drv_t* drv, lv_indev_data_t* data) {
  esp_lcd_touch_handle_t handle = (esp_lcd_touch_handle_t)drv->user_data;
  uint16_t touchpad_x[1] = {0};
  uint16_t touchpad_y[1] = {0};
  uint8_t touchpad_cnt = 0;
  /* 读取触摸控制器数据 */
  esp_lcd_touch_read_data(handle);
  /* NOTE: June6 获取坐标 */
  bool touchpad_pressed = esp_lcd_touch_get_coordinates(
      handle, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
  if (touchpad_pressed && touchpad_cnt > 0) {
    data->point.x = touchpad_x[0];
    data->point.y = touchpad_y[0];
    data->state = LV_INDEV_STATE_PRESSED;
    USER_LOGD(TAG, "Touch position: %d,%d", touchpad_x, touchpad_y);
  } else {
    data->state = LV_INDEV_STATE_RELEASED;
  }
}

/**
 * @brief Input device feedback.
 *
 * @param drv
 * @param data
 */
static void lvgl_touch_feedback_cb(lv_indev_drv_t* drv, uint8_t event) {
  if ((event == LV_EVENT_CLICKED) || (event == LV_EVENT_GESTURE) ||
      (event == LV_EVENT_READY) || (event == LV_EVENT_PRESSING)) {
    if (event != LV_EVENT_PRESSING) {
      system_request_beep();
    }

#if CONFIG_LCD_CONTROLLER_SLEEP_WAKEUP
    if ((lcd_panel_restart_task_handler != NULL) &&
        (!get_lcd_display_work_state())) {
      xTaskNotifyGive(lcd_panel_restart_task_handler);
    }

#endif

    if (!breathing_plate_flag_state_get()) {
#if CONFIG_LCD_TOUCH_CONTROLLER_GT911
      touch_gt911_reset(touch_handle);
#elif CONFIG_LCD_TOUCH_CONTROLLER_TT21100
      touch_tt21100_reset(touch_handle);
#elif CONFIG_LCD_TOUCH_CONTROLLER_FT5X06
      touch_ft5x06_reset(touch_handle);
#endif
      breathing_plate_flag_state_set(1);
      system_backlight_onoff(1);
    }
    breathing_plate_counter_set(0);
  }
}
#endif

/**
 * @brief Increase lvgl tick.
 *
 * @param arg
 */
static void increase_lvgl_tick(void* arg) {
  /* 告诉LVGL经过了多少毫秒 */
  lv_tick_inc(LVGL_TICK_PERIOD_MS);
}

/**
 * @brief Reset touch callback.
 *
 * @param arg
 */
static void reset_touch_callback(void* arg) {
  if (touch_handle == NULL) goto End;
#if CONFIG_LCD_TOUCH_CONTROLLER_GT911
  touch_gt911_reset(touch_handle);
#elif CONFIG_LCD_TOUCH_CONTROLLER_TT21100
  touch_tt21100_reset(touch_handle);
#elif CONFIG_LCD_TOUCH_CONTROLLER_FT5X06
  touch_ft5x06_reset(touch_handle);
#endif
  if (!get_lcd_display_work_state()) esp_timer_stop(restart_touch_timer);

End:
  return;
}

/**
 * @brief Config lcd interface bus.
 *
 * @details Turn off LCD backlight after config.
 */
static void lcd_interface_bus_config(void) {
  esp_lcd_rgb_panel_config_t panel_config = {
      .clk_src = LCD_CLK_SRC_DEFAULT,
      .psram_trans_align = PSRAM_DATA_ALIGNMENT,
      .data_width = CONFIG_LCD_RGB_BUS_WIDTH,
      .bits_per_pixel = CONFIG_LV_COLOR_DEPTH,
      .disp_gpio_num = LCD_DISPLAY_DISP_EN,
      .pclk_gpio_num = LCD_DISPLAY_PCLK,
      .vsync_gpio_num = LCD_DISPLAY_VSYNC,
      .hsync_gpio_num = LCD_DISPLAY_HSYNC,
      .de_gpio_num = LCD_DISPLAY_DE,
      .data_gpio_nums =
          {
              LCD_DISPLAY_DATA0,
              LCD_DISPLAY_DATA1,
              LCD_DISPLAY_DATA2,
              LCD_DISPLAY_DATA3,
              LCD_DISPLAY_DATA4,
              LCD_DISPLAY_DATA5,
              LCD_DISPLAY_DATA6,
              LCD_DISPLAY_DATA7,
              LCD_DISPLAY_DATA8,
              LCD_DISPLAY_DATA9,
              LCD_DISPLAY_DATA10,
              LCD_DISPLAY_DATA11,
              LCD_DISPLAY_DATA12,
              LCD_DISPLAY_DATA13,
              LCD_DISPLAY_DATA14,
              LCD_DISPLAY_DATA15,
          },
      .timings = LCD_PANEL_60HZ_RGB_TIMING(),
      /* 是否在PSRAM中分配帧缓冲区 */
      .flags.fb_in_psram = (uint32_t)true,
      .num_fbs = LCD_RGB_BUFFER_NUMS,
      /* bounce buffer 分配的行数必须要能被总高度整除, 例如高度是480, 60,
         80行是可以的， 70行是不行的, 再加大不成功, 那就是ram不够了,
         可以打印下剩余内存确认下 */
      .bounce_buffer_size_px = LCD_RGB_BOUNCE_BUFFER_SIZE,
  };
  USER_LOGD(
      TAG, "Before new heap_size = %ld, %ld, %d",
      esp_get_free_internal_heap_size(), esp_get_free_heap_size(),
      heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
  ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
  USER_LOGD(
      TAG, "After new heap_size = %ld, %ld, %d",
      esp_get_free_internal_heap_size(), esp_get_free_heap_size(),
      heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
  USER_LOGD(TAG, "Install RGB LCD panel driver");
}

/**
 * @brief Config lcd panel display.
 *
 */
static void lcd_panel_display_config(void) {
  lcd_display_rgb_driver_init();
  lcd_interface_bus_config();
  /* NOTE: June6 软件处理180度旋转屏幕(X, Y轴均镜像) */
  ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
  ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
  size_t buffer_size = 0;
#if CONFIG_LCD_RGB_AVOID_TEAR_ENABLE
  /* 为了避免撕裂效应, 我们应该至少使用两个帧缓冲区: 一个用于LVGL渲染,
   * 另一个用于RGB输出 */
  buffer_size = LCD_H_RES * LCD_V_RES;
#if ((LCD_RGB_FULL_REFRESH) && (LCD_RGB_BUFFER_NUMS == 3) && \
     (LCD_ROTATION_DEGREE == 0))
  /* 通过使用三个缓冲区和完全刷新, 我们总是有一个缓冲区可用于渲染,
   * 无需等待RGB的同步信号 */
  ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(
      panel_handle, LCD_RGB_BUFFER_NUMS, &rgb_last_buf, &lvgl_buf1,
      &lvgl_buf2));
  rgb_next_buf = rgb_last_buf;
  flush_next_buf = lvgl_buf2;
#elif ((LCD_RGB_BUFFER_NUMS == 3) && (LCD_ROTATION_DEGREE != 0))
  /* 这里我们使用三个帧缓冲区, 一个用于LVGL渲染, 另两个用于RGB驱动程序
   * (其中一个用于旋转) */
  void* fbs[3];
  ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(panel_handle, 3, &fbs[0],
                                                     &fbs[1], &fbs[2]));
  lvgl_buf1 = fbs[2];
#else
  ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(
      panel_handle, LCD_RGB_BUFFER_NUMS, &lvgl_buf1, &lvgl_buf2));
#endif
#else
  /* 正常情况下, 对于RGB LCD, 我们只使用一个缓冲区进行LVGL渲染 */
  buffer_size = LCD_H_RES * CONFIG_LCD_RGB_BUF_HEIGHT;
  if (lvgl_buf1 == NULL)
    lvgl_buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t),
                                 LCD_BUFFER_MALLOC_CAPS);
  USER_LOGD(TAG, "LVGL buffer size: %dKB",
            buffer_size * sizeof(lv_color_t) / 1024);
#endif
  if ((lvgl_buf1 == NULL) ||
      ((LCD_RGB_BUFFER_NUMS == 2) && (lvgl_buf2 == NULL))) {
    USER_LOGE(TAG, "lvgl_buf1 or lvgl_buf2 is NULL");
    goto End;
  }
  USER_LOGD(TAG, "lvgl_buf1@%p, lvgl_buf2@%p", lvgl_buf1, lvgl_buf2);
#if CONFIG_LCD_SWAP_XY
  /* FIXME: June6 若想使其有效, 则需要使用 esp_lcd_new_panel_st7789
   * 等其他驱动创建LCD面板 */
  /* NOTE: June6 本质上与 SPI 直接处理 X轴与Y轴交换是同一方法 */
  /* NOTE: June6 X轴与Y轴交换 */
  ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, true));
#else
  ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_handle, false));
#endif

#if CONFIG_LCD_COLOR_INVERSION
  /* FIXME: June6 若想使其有效, 则需要使用 esp_lcd_new_panel_st7789
   * 等其他驱动创建LCD面板 */
  /* NOTE: June6 本质上与 SPI 直接处理颜色反转是同一方法 */
  /* NOTE: June6 颜色反转 */
  // ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
#endif

#if CONFIG_LCD_MIRROR_HARDWARE
  /* FIXME: June6 若想使其有效, 则需要使用 esp_lcd_new_panel_st7789
   * 等其他驱动创建LCD面板 */
  /* NOTE: June6 本质上与 SPI 直接处理反扫是同一方法 */
  /* NOTE: June6 180度旋转屏幕(X, Y轴均镜像) */
  // ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_handle, true, true));
#endif
  /* NOTE: June6 本质上与 SPI 直接处理 offset 是同一方法 */
  esp_lcd_panel_set_gap(panel_handle, 0, 0);
  lcd_panel_display_on_off(true);
  USER_LOGD(TAG, "Initialize RGB LCD panel");

End:
  return;
}

/**
 * @brief Deconfig lcd panel display.
 *
 */
static void lcd_panel_display_deconfig(void) {
  lcd_panel_display_on_off(false);
  USER_LOGD(
      TAG, "Before del heap_size = %ld, %ld, %d",
      esp_get_free_internal_heap_size(), esp_get_free_heap_size(),
      heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
  ESP_ERROR_CHECK(esp_lcd_panel_del(panel_handle));
  USER_LOGD(
      TAG, "After del heap_size = %ld, %ld, %d",
      esp_get_free_internal_heap_size(), esp_get_free_heap_size(),
      heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
  USER_LOGD(TAG, "Uninitialize RGB LCD panel");
}

/**
 * @brief Config lcd panel touch and lvgl.
 *
 */
static void lcd_panel_touch_lvgl_config(void) {
#if CONFIG_LCD_TOUCH_ENABLED
  ESP_ERROR_CHECK(iic_master_config(LCD_TOUCH_IIC_PORT, IIC_PIN_NUM_SCL,
                                    IIC_PIN_NUM_SDA, LCD_TOUCH_IIC_FREQ_HZ));
#if CONFIG_LCD_TOUCH_CONTROLLER_GT911
  esp_lcd_panel_io_i2c_config_t tp_io_config =
      ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
#elif CONFIG_LCD_TOUCH_CONTROLLER_TT21100
  esp_lcd_panel_io_i2c_config_t tp_io_config =
      ESP_LCD_TOUCH_IO_I2C_TT21100_CONFIG();
#elif CONFIG_LCD_TOUCH_CONTROLLER_FT5X06
  esp_lcd_panel_io_i2c_config_t tp_io_config =
      ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG();
#endif
  ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(
      (esp_lcd_i2c_bus_handle_t)IIC_MASTER_NUM, &tp_io_config, &tp_io_handle));
  USER_LOGD(TAG, "Initialize touch IO (I2C)");
  /* NOTE: June6 垂直屏幕 */
  esp_lcd_touch_config_t tp_cfg = {
      .x_max = TOUCH_H_RES,
      .y_max = TOUCH_V_RES,
      .rst_gpio_num = TOUCH_PIN_NUM_RST,
      .int_gpio_num = TOUCH_PIN_NUM_INT,
      .flags =
          {
#if CONFIG_LCD_SWAP_XY
              /* NOTE: June6 X轴与Y轴交换 */
              .swap_xy = 1,
#else
              .swap_xy = 0,
#endif

#if CONFIG_LCD_MIRROR
              .mirror_x = 1,
              .mirror_y = 1,
#else
              .mirror_x = 0,
              .mirror_y = 0,
#endif
          },
  };
  /* NOTE: June6 初始化触摸 */
#if CONFIG_LCD_TOUCH_CONTROLLER_GT911
  ESP_ERROR_CHECK(
      esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &touch_handle));
  USER_LOGD(TAG, "Initialize touch controller GT911");
#elif CONFIG_LCD_TOUCH_CONTROLLER_TT21100
  ESP_ERROR_CHECK(
      esp_lcd_touch_new_i2c_tt21100(tp_io_handle, &tp_cfg, &touch_handle));
  USER_LOGD(TAG, "Initialize touch controller TT21100");
#elif CONFIG_LCD_TOUCH_CONTROLLER_FT5X06
  ESP_ERROR_CHECK(
      esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, &touch_handle));
  USER_LOGD(TAG, "Initialize touch controller FT5X06");
#endif
#endif
}

/**
 * @brief Config lcd panel and lvgl.
 *
 * @return lv_disp_t*
 */
static lv_disp_t* lcd_panel_lvgl_config(void) {
  lv_init();
  USER_LOGD(TAG, "Initialize LVGL library");
  if (disp == NULL) {
    /* 初始化LVGL绘制缓冲区 */
    lv_disp_draw_buf_init(&disp_buf, lvgl_buf1, lvgl_buf2,
                          LCD_H_RES * LCD_V_RES);
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = LCD_H_RES;
    disp_drv.ver_res = LCD_V_RES;
    disp_drv.flush_cb = lvgl_flush_callback;
    /* NOTE: June6 可结合 lv_disp_set_rotation(lv_disp_t * disp, lv_disp_rot_t
     * rotation) 动态调用屏幕旋转 */
    // disp_drv.drv_update_cb = lvgl_update_callback;
    // disp_drv.rotated = LV_DISP_ROT_NONE;
    disp_drv.draw_buf = &disp_buf;
#if CONFIG_LCD_MIRROR_SOFTWARE
    // disp_drv.sw_rotate = (bool)true;
    // lv_disp_set_rotation(disp, LV_DISP_ROT_180);
#endif

#if LCD_RGB_FULL_REFRESH
    disp_drv.full_refresh = true;
#elif LCD_RGB_DIRECT_MODE
    disp_drv.direct_mode = true;
#endif
    disp_drv.user_data = panel_handle;
    disp = lv_disp_drv_register(&disp_drv);
    USER_LOGD(TAG, "Register display driver to LVGL");
    ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(
        panel_handle, &panel_callbacks, &disp_drv));
    USER_LOGD(TAG, "Register event callbacks");
  }

#if CONFIG_LCD_TOUCH_ENABLED
  if (touch == NULL) {
    lv_indev_drv_init(&indev_drv);
    /* NOTE: June6 可设置滚动像素, 滚动速度, 长按时间, 长按重复时间,
     * 识别手势像素, 手势识别速度 */
    // indev_drv.scroll_limit =
    // indev_drv.scroll_throw =
    // indev_drv.gesture_limit =
    // indev_drv.gesture_min_velocity =
    indev_drv.long_press_time = LV_INDEV_DEF_LONG_PRESS_TIME;
    indev_drv.long_press_repeat_time = LV_INDEV_DEF_LONG_PRESS_REP_TIME;
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.disp = disp;
    indev_drv.read_cb = lvgl_touch_read_cb;
    indev_drv.feedback_cb = lvgl_touch_feedback_cb;
    indev_drv.user_data = touch_handle;
    touch = lv_indev_drv_register(&indev_drv);
  }
#endif
  /* LVGL的Tick接口(使用esp_timer生成1ms周期性事件) */
  if (lvgl_tick_timer == NULL) {
    const esp_timer_create_args_t lvgl_tick_timer_args = {
        .callback = &increase_lvgl_tick, .name = "lvgl_tick"};
    ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));
  }

  if (!esp_timer_is_active(lvgl_tick_timer))
    ESP_ERROR_CHECK(
        esp_timer_start_periodic(lvgl_tick_timer, LVGL_TICK_PERIOD_MS * 1000));
  USER_LOGD(TAG, "Install LVGL tick timer");

  return disp;
}

/**
 * @brief Update lcd panel and lvgl.
 *
 */
static void lcd_panel_lvgl_update(void) {
  lv_disp_draw_buf_init(&disp_buf, lvgl_buf1, lvgl_buf2, LCD_H_RES * LCD_V_RES);
  disp_drv.draw_buf = &disp_buf;
  disp_drv.user_data = panel_handle;
  ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(
      panel_handle, &panel_callbacks, &disp_drv));
  lv_disp_drv_update(disp, &disp_drv);
  indev_drv.disp = disp;
  lv_indev_drv_update(touch, &indev_drv);
}

/**
 * @brief Initialize LCD panel display.
 *
 */
void lcd_panel_display_init(void) {
  lcd_display_gpio_config();
  lcd_panel_display_config();
  USER_LOGD(TAG, "Init lcd panel display");
}

/**
 * @brief On off lcd panel display.
 *
 * @param cmd
 */
void lcd_panel_display_on_off(bool cmd) {
  /* NOTE: June6 可以在打开屏幕或背光之前将预定义的图案刷新到屏幕上 */
  ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, cmd));
}

/**
 * @brief Retime Reset Touch
 *
 * @param cmd
 * @details 亮屏后一段时间, 固定重置一次触摸, 避免硬件有问题,
 *          导致一直处于误触状态
 */
void retime_reset_touch(bool cmd) {
#if CONFIG_LCD_TOUCH_ENABLED
  if (cmd) {
    if (restart_touch_timer == NULL) {
      esp_timer_create_args_t reset_touch_timer_args = {
          .callback = &reset_touch_callback,
          .name = "restart_touch_timer",
      };
      ESP_ERROR_CHECK(
          esp_timer_create(&reset_touch_timer_args, &restart_touch_timer));
    }

    if (esp_timer_is_active(restart_touch_timer))
      ESP_ERROR_CHECK(esp_timer_restart(restart_touch_timer,
                                        RESET_TOUCH_TIMEOUT_MS * 1000));
    else
      ESP_ERROR_CHECK(esp_timer_start_periodic(restart_touch_timer,
                                               RESET_TOUCH_TIMEOUT_MS * 1000));
  } else {
    if (restart_touch_timer == NULL) goto End;

    if (esp_timer_is_active(restart_touch_timer))
      ESP_ERROR_CHECK(esp_timer_stop(restart_touch_timer));
  }

End:
  return;
#endif
}

/**
 * @brief Initialize LCD panel and ui server.
 *
 * @param core_num
 * @return lv_disp_t*
 */
lv_disp_t* lcd_panel_ui_server_init(uint8_t core_num) {
  lv_disp_t* disp = NULL;
  lcd_panel_display_init();
  lcd_panel_touch_lvgl_config();
  disp = lcd_panel_lvgl_config();
  /* NOTE: June6 !!!!!! 注意创建顺序 !!!!!! */
  /* NOTE: June6 如果UI事件执行过程中出现崩溃, 请考虑添加任务堆栈空间大小 */
  if (lvgl_mutex_semaphore_handler == NULL) {
    lvgl_mutex_semaphore_handler = xSemaphoreCreateMutex();
    if (lvgl_mutex_semaphore_handler == NULL) {
      USER_LOGE(TAG, "lvgl_mutex_semaphore_handler create failed");
      goto End;
    }
  }
#if CONFIG_CHIP_DUAL_CORE
  if (lcd_panel_ui_task_handler == NULL) {
    lcd_panel_ui_task_handler = xTaskCreateStaticPinnedToCore(
        (TaskFunction_t)lcd_panel_ui_task, LCD_PANEL_UI_TASK,
        LCD_PANEL_UI_STK_SIZE, (void*)NULL, LCD_PANEL_UI_TASK_PRIO,
        lcd_panel_ui_task_stack_buffer, &lcd_panel_ui_task_buffer, core_num);
    if (lcd_panel_ui_task_handler == NULL) {
      USER_LOGE(TAG, "lcd_panel_ui_task create failed");
      goto End;
    }
  }
#if CONFIG_LCD_CONTROLLER_SLEEP_WAKEUP
  if (lcd_panel_restart_task_handler == NULL) {
    lcd_panel_restart_task_handler = xTaskCreateStaticPinnedToCore(
        (TaskFunction_t)lcd_panel_restart_task, LCD_PANEL_RESTART_TASK,
        LCD_PANEL_RESTART_STK_SIZE, (void*)NULL, LCD_PANEL_RESTART_TASK_PRIO,
        lcd_panel_restart_task_stack_buffer, &lcd_panel_restart_task_buffer,
        core_num);
    if (lcd_panel_restart_task_handler == NULL) {
      USER_LOGE(TAG, "lcd_panel_restart_task create failed");
      goto End;
    }
  }
#endif
#else
  if (lcd_panel_ui_task_handler == NULL) {
    lcd_panel_ui_task_handler = xTaskCreateStatic(
        (TaskFunction_t)lcd_panel_ui_task, LCD_PANEL_UI_TASK,
        LCD_PANEL_UI_STK_SIZE, (void*)NULL, LCD_PANEL_UI_TASK_PRIO,
        lcd_panel_ui_task_stack_buffer, &lcd_panel_ui_task_buffer);
    if (lcd_panel_ui_task_handler == NULL) {
      USER_LOGE(TAG, "lcd_panel_ui_task create failed");
      goto End;
    }
  }
#if CONFIG_LCD_CONTROLLER_SLEEP_WAKEUP
  if (lcd_panel_restart_task_handler == NULL) {
    lcd_panel_restart_task_handler = xTaskCreateStatic(
        (TaskFunction_t)lcd_panel_restart_task, LCD_PANEL_RESTART_TASK,
        LCD_PANEL_RESTART_STK_SIZE, (void*)NULL, LCD_PANEL_RESTART_TASK_PRIO,
        lcd_panel_restart_task_stack_buffer, &lcd_panel_restart_task_buffer);
    if (lcd_panel_restart_task_handler == NULL) {
      USER_LOGE(TAG, "lcd_panel_restart_task create failed");
      goto End;
    }
  }
#endif
#endif
  ui_init();
  /* NOTE: June6 上电亮屏 */
  lcd_panel_backlight_enable(true);
  retime_reset_touch(true);
  USER_LOGD(TAG, "Init lcd panel with lvgl");

End:
  return disp;
}

/**
 * @brief LCD panel ui task.
 *
 * @param pvParameters
 */
void lcd_panel_ui_task(void* pvParameters) {
  for (;;) {
    if (lvgl_mutex_semaphore_handler != NULL)
      /* 获取互斥锁, 其他任务将阻塞直到互斥锁被释放 */
      xSemaphoreTake(lvgl_mutex_semaphore_handler, portMAX_DELAY);
    /* 运行lv_timer_handler的任务优先级应低于运行lv_tick_inc的任务优先级 */
    /* 执行LVGL相关的更新和渲染操作 */
    lv_timer_handler();
    if (lvgl_mutex_semaphore_handler != NULL)
      /* 释放互斥锁, 以允许其他任务访问LVGL资源 */
      xSemaphoreGive(lvgl_mutex_semaphore_handler);
    /* 提高LVGL的任务优先级和/或缩短处理程序周期可以提高性能 */
    vTaskDelay(pdMS_TO_TICKS(LCD_PANEL_UI_TASK_PERIOD_MS));
  }
}

#if CONFIG_LCD_CONTROLLER_SLEEP_WAKEUP
/**
 * @brief LCD panel restart task.
 *
 * @param pvParameters
 */
void lcd_panel_restart_task(void* pvParameters) {
  for (;;) {
    if (ulTaskNotifyTake(pdTRUE, portMAX_DELAY)) {
      vTaskSuspend(lcd_panel_ui_task_handler);
      if (lvgl_tick_timer != NULL)
        if (esp_timer_is_active(lvgl_tick_timer))
          ESP_ERROR_CHECK(esp_timer_stop(lvgl_tick_timer));
      /* GPIO复用会有冲突 */
      if ((LCD_DISPLAY_SCLK == LCD_DISPLAY_DATA0) ||
          (LCD_DISPLAY_MOSI == LCD_DISPLAY_DATA1)) {
        /* NOTE: June6 保持RGB时序不断, 直接失能 RGB IO 口功能, 又恢复的话,
         * 会使屏幕闪一下; 所以亮屏第一时间必须保证是纯黑 */
        /* 失能 RGB IO */
        assert(GPIO_IS_VALID_GPIO(LCD_DISPLAY_DATA0));
        gpio_reset_pin(LCD_DISPLAY_DATA0);
        assert(GPIO_IS_VALID_GPIO(LCD_DISPLAY_DATA1));
        gpio_reset_pin(LCD_DISPLAY_DATA1);
      }
      lcd_display_rgb_driver_init();
      /* 使能 RGB IO */
      if ((LCD_DISPLAY_SCLK == LCD_DISPLAY_DATA0) ||
          (LCD_DISPLAY_MOSI == LCD_DISPLAY_DATA1)) {
        gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[LCD_DISPLAY_DATA0],
                                PIN_FUNC_GPIO);
        gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[LCD_DISPLAY_DATA1],
                                PIN_FUNC_GPIO);
        gpio_set_direction(LCD_DISPLAY_DATA0, GPIO_MODE_OUTPUT);
        gpio_set_direction(LCD_DISPLAY_DATA1, GPIO_MODE_OUTPUT);
        /* FIXME: June6 从 ESP-IDF 底层中获取 rgb_panel->panel_id */
        esp_rom_gpio_connect_out_signal(
            LCD_DISPLAY_DATA0, lcd_periph_signals.panels[0].data_sigs[0], false,
            false);
        esp_rom_gpio_connect_out_signal(
            LCD_DISPLAY_DATA1, lcd_periph_signals.panels[0].data_sigs[1], false,
            false);
      }

      if (lvgl_tick_timer != NULL)
        if (!esp_timer_is_active(lvgl_tick_timer))
          ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer,
                                                   LVGL_TICK_PERIOD_MS * 1000));
      vTaskResume(lcd_panel_ui_task_handler);
    }
  }
}
#endif
