/*
 * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

// #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG

#include <stdlib.h>
#include <sys/cdefs.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_lcd_panel_interface.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_commands.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_check.h"

//8080 · DATA
#define GPIO_OUTPUT_DATA_0    13
#define GPIO_OUTPUT_DATA_1    18
#define GPIO_OUTPUT_DATA_2    12
#define GPIO_OUTPUT_DATA_3    17
#define GPIO_OUTPUT_DATA_4    11
#define GPIO_OUTPUT_DATA_5    16
#define GPIO_OUTPUT_DATA_6    10
#define GPIO_OUTPUT_DATA_7    15
#define GPIO_OUTPUT_DATA_8    9
#define GPIO_OUTPUT_DATA_9    7
#define GPIO_OUTPUT_DATA_10   46
#define GPIO_OUTPUT_DATA_11   6
#define GPIO_OUTPUT_DATA_12   3
#define GPIO_OUTPUT_DATA_13   5
#define GPIO_OUTPUT_DATA_14   8
#define GPIO_OUTPUT_DATA_15   4
//signal
#define GPIO_OUTPUT_WR        42
#define GPIO_OUTPUT_CS        41
#define GPIO_OUTPUT_RS        1

#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_DATA_0) | (1ULL<<GPIO_OUTPUT_DATA_1) | (1ULL<<GPIO_OUTPUT_DATA_2) | (1ULL<<GPIO_OUTPUT_DATA_3) | (1ULL<<GPIO_OUTPUT_DATA_4) | (1ULL<<GPIO_OUTPUT_DATA_5)\
                            | (1ULL<<GPIO_OUTPUT_DATA_6) | (1ULL<<GPIO_OUTPUT_DATA_7) | (1ULL<<GPIO_OUTPUT_DATA_8) | (1ULL<<GPIO_OUTPUT_DATA_9) | (1ULL<<GPIO_OUTPUT_DATA_10) | (1ULL<<GPIO_OUTPUT_DATA_11)\
                            | (1ULL<<GPIO_OUTPUT_DATA_12) | (1ULL<<GPIO_OUTPUT_DATA_13) | (1ULL<<GPIO_OUTPUT_DATA_14) | (1ULL<<GPIO_OUTPUT_DATA_15) | (1ULL<<GPIO_OUTPUT_WR) | (1ULL<<GPIO_OUTPUT_CS)\
                            | (1ULL<<GPIO_OUTPUT_RS))
                
#define WR_HIGH   gpio_set_level(GPIO_OUTPUT_WR, 1)
#define WR_LOW    gpio_set_level(GPIO_OUTPUT_WR, 0)
#define CS_HIGH   gpio_set_level(GPIO_OUTPUT_CS, 1)
#define CS_LOW    gpio_set_level(GPIO_OUTPUT_CS, 0)
#define RS_HIGH   gpio_set_level(GPIO_OUTPUT_RS, 1)
#define RS_LOW    gpio_set_level(GPIO_OUTPUT_RS, 0)

static const char *TAG = "lcd_panel.hx8369a";

static esp_err_t panel_hx8369a_del(esp_lcd_panel_t *panel);
static esp_err_t panel_hx8369a_reset(esp_lcd_panel_t *panel);
static esp_err_t panel_hx8369a_init(esp_lcd_panel_t *panel);
static esp_err_t panel_hx8369a_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data);
static esp_err_t panel_hx8369a_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
static esp_err_t panel_hx8369a_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
static esp_err_t panel_hx8369a_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
static esp_err_t panel_hx8369a_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
static esp_err_t panel_hx8369a_disp_off(esp_lcd_panel_t *panel, bool off);

typedef struct {
    esp_lcd_panel_t base;
    esp_lcd_panel_io_handle_t io;
    int reset_gpio_num;
    bool reset_level;
    int x_gap;
    int y_gap;
    unsigned int bits_per_pixel;
    uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
    uint8_t colmod_cal; // save surrent value of LCD_CMD_COLMOD register
} hx8369a_panel_t;

esp_err_t esp_lcd_new_panel_hx8369a(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
{
    esp_err_t ret = ESP_OK;
    hx8369a_panel_t *hx8369a = NULL;
    ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
    hx8369a = calloc(1, sizeof(hx8369a_panel_t));
    ESP_GOTO_ON_FALSE(hx8369a, ESP_ERR_NO_MEM, err, TAG, "no mem for hx8369a panel");

    if (panel_dev_config->reset_gpio_num >= 0) {
        gpio_config_t io_conf = {
            .mode = GPIO_MODE_OUTPUT,
            .pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num,
        };
        ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
    }

    switch (panel_dev_config->color_space) {
    case ESP_LCD_COLOR_SPACE_RGB:
        hx8369a->madctl_val = 0;
        break;
    case ESP_LCD_COLOR_SPACE_BGR:
        hx8369a->madctl_val |= LCD_CMD_BGR_BIT;
        break;
    default:
        ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
        break;
    }

    switch (panel_dev_config->bits_per_pixel) {
    case 16:
        hx8369a->colmod_cal = 0x55;
        break;
    case 18:
        hx8369a->colmod_cal = 0x66;
        break;
    case 24:
        hx8369a->colmod_cal = 0x77;
        break;
    default:
        ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
        break;
    }

    hx8369a->io = io;
    hx8369a->bits_per_pixel = panel_dev_config->bits_per_pixel;
    hx8369a->reset_gpio_num = panel_dev_config->reset_gpio_num;
    hx8369a->reset_level = panel_dev_config->flags.reset_active_high;
    hx8369a->base.del = panel_hx8369a_del;
    hx8369a->base.reset = panel_hx8369a_reset;
    hx8369a->base.init = panel_hx8369a_init;
    hx8369a->base.draw_bitmap = panel_hx8369a_draw_bitmap;
    hx8369a->base.invert_color = panel_hx8369a_invert_color;
    hx8369a->base.set_gap = panel_hx8369a_set_gap;
    hx8369a->base.mirror = panel_hx8369a_mirror;
    hx8369a->base.swap_xy = panel_hx8369a_swap_xy;
    hx8369a->base.disp_off = panel_hx8369a_disp_off;
    *ret_panel = &(hx8369a->base);
    ESP_LOGD(TAG, "new hx8369a panel @%p", hx8369a);

    return ESP_OK;

err:
    if (hx8369a) {
        if (panel_dev_config->reset_gpio_num >= 0) {
            gpio_reset_pin(panel_dev_config->reset_gpio_num);
        }
        free(hx8369a);
    }
    return ret;
}

static esp_err_t panel_hx8369a_del(esp_lcd_panel_t *panel)
{
    hx8369a_panel_t *hx8369a = __containerof(panel, hx8369a_panel_t, base);

    if (hx8369a->reset_gpio_num >= 0) {
        gpio_reset_pin(hx8369a->reset_gpio_num);
    }
    ESP_LOGD(TAG, "del hx8369a panel @%p", hx8369a);
    free(hx8369a);
    return ESP_OK;
}

static esp_err_t panel_hx8369a_reset(esp_lcd_panel_t *panel)
{
    hx8369a_panel_t *hx8369a = __containerof(panel, hx8369a_panel_t, base);
    esp_lcd_panel_io_handle_t io = hx8369a->io;

    // perform hardware reset
    if (hx8369a->reset_gpio_num >= 0) {
        gpio_set_level(hx8369a->reset_gpio_num, hx8369a->reset_level);
        vTaskDelay(pdMS_TO_TICKS(10));
        gpio_set_level(hx8369a->reset_gpio_num, !hx8369a->reset_level);
        vTaskDelay(pdMS_TO_TICKS(10));
    } else { // perform software reset
        esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0);
        vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5m before sending new command
    }

    return ESP_OK;
}

void gpio_write(uint32_t cmd)
{
    gpio_set_level(GPIO_OUTPUT_DATA_0, cmd & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_1, (cmd >> 1) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_2, (cmd >> 2) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_3, (cmd >> 3) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_4, (cmd >> 4) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_5, (cmd >> 5) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_6, (cmd >> 6) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_7, (cmd >> 7) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_8, (cmd >> 8) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_9, (cmd >> 9) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_10, (cmd >> 10) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_11, (cmd >> 11) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_12, (cmd >> 12) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_13, (cmd >> 13) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_14, (cmd >> 14) & 0x01);
    gpio_set_level(GPIO_OUTPUT_DATA_15, (cmd >> 15) & 0x01);
}

void write_comm(uint32_t cmd)
{
    gpio_set_level(1, 0);
    gpio_write(cmd);
    gpio_set_level(42, 0);
    gpio_set_level(42, 1);
}

void write_data(uint32_t dat)
{
    gpio_set_level(1, 1);
    gpio_write(dat);
    gpio_set_level(42, 0);
    gpio_set_level(42, 1);
}

static esp_err_t panel_hx8369a_init(esp_lcd_panel_t *panel)
{
    int i;
    hx8369a_panel_t *hx8369a = __containerof(panel, hx8369a_panel_t, base);
    esp_lcd_panel_io_handle_t io = hx8369a->io;
/*
    gpio_config_t io_conf;
    io_conf.intr_type = GPIO_INTR_DISABLE;
    io_conf.mode = GPIO_MODE_OUTPUT;
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    io_conf.pull_down_en = 0;
    io_conf.pull_up_en = 0;
    gpio_config(&io_conf);
*/
    CS_LOW;
    gpio_set_level(41, 0);
    write_comm(0x2D); 
    for (i=0; i<=63; i++)
    {
    write_data(i*8);   
    }

    for (i=0; i<=63; i++)
    {
    write_data(i*4);   
    }

    for (i=0; i<=63; i++)
    {
    write_data(i*8);   
    }
/*
    esp_lcd_panel_io_tx_param(io, 0x2D, (uint8_t[]) {
        0&0xff, 8&0xff, 16&0xff, 24&0xff, 32&0xff, 40&0xff, 48&0xff, 56&0xff, 64&0xff, 72&0xff, 80&0xff, 88&0xff, 96&0xff, 104&0xff, 112&0xff, 120&0xff,
        128&0xff, 136&0xff, 144&0xff, 152&0xff, 160&0xff, 168&0xff, 176&0xff, 184&0xff, 192&0xff, 200&0xff, 208&0xff, 216&0xff, 224&0xff,
        232&0xff, 240&0xff, 248&0xff, 256&0xff, 264&0xff, 272&0xff, 280&0xff, 288&0xff, 296&0xff, 304&0xff, 312&0xff, 320&0xff, 328&0xff,
        336&0xff, 344&0xff, 352&0xff, 360&0xff, 368&0xff, 376&0xff, 384&0xff, 392&0xff, 400&0xff, 408&0xff, 416&0xff, 424&0xff, 432&0xff,
        440&0xff, 448&0xff, 456&0xff, 464&0xff, 472&0xff, 480&0xff, 488&0xff, 496&0xff, 504&0xff,

        0&0xFF, 4&0xFF, 8&0xFF, 12&0xFF, 14&0xFF, 16&0xFF, 20&0xFF, 24&0xFF, 28&0xFF, 32&0xFF, 36&0xFF, 40&0xFF, 44&0xFF, 48&0xFF, 
        52&0xFF, 56&0xFF, 60&0xFF, 64&0xFF, 68&0xFF, 72&0xFF, 76&0xFF, 80&0xFF, 84&0xFF, 88&0xFF, 92&0xFF, 96&0xFF, 100&0xFF, 
        104&0xFF, 108&0xFF, 112&0xFF, 116&0xFF, 120&0xFF, 124&0xFF, 128&0xFF, 132&0xFF, 136&0xFF, 140&0xFF, 144&0xFF, 
        148&0xFF, 152&0xFF, 156&0xFF, 160&0xFF, 164&0xFF, 168&0xFF, 172&0xFF, 176&0xFF, 180&0xFF, 184&0xFF, 188&0xFF, 
        192&0xFF, 196&0xFF, 200&0xFF, 204&0xFF, 208&0xFF, 212&0xFF, 216&0xFF, 220&0xFF, 224&0xFF, 228&0xFF, 232&0xFF, 
        236&0xFF, 240&0xFF, 244&0xFF, 248&0xFF, 252&0xFF,

        0&0xff, 8&0xff, 16&0xff, 24&0xff, 32&0xff, 40&0xff, 48&0xff, 56&0xff, 64&0xff, 72&0xff, 80&0xff, 88&0xff, 96&0xff, 104&0xff, 112&0xff, 120&0xff,
        128&0xff, 136&0xff, 144&0xff, 152&0xff, 160&0xff, 168&0xff, 176&0xff, 184&0xff, 192&0xff, 200&0xff, 208&0xff, 216&0xff, 224&0xff,
        232&0xff, 240&0xff, 248&0xff, 256&0xff, 264&0xff, 272&0xff, 280&0xff, 288&0xff, 296&0xff, 304&0xff, 312&0xff, 320&0xff, 328&0xff,
        336&0xff, 344&0xff, 352&0xff, 360&0xff, 368&0xff, 376&0xff, 384&0xff, 392&0xff, 400&0xff, 408&0xff, 416&0xff, 424&0xff, 432&0xff,
        440&0xff, 448&0xff, 456&0xff, 464&0xff, 472&0xff, 480&0xff, 488&0xff, 496&0xff, 504&0xff,
    }, 64);*/

    esp_lcd_panel_io_tx_param(io, 0xB9, (uint8_t[]) {
        0xFF, 0X83, 0X69,
    }, 3);

    esp_lcd_panel_io_tx_param(io, 0xB1, (uint8_t[]) {
        0x85, 0x00, 0x34, 0x0A, 0x00, 0x0F, 0x0F, 0x2A, 0x32, 0x3F, 0x3F, 0x01,
        0x23, 0x01, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6,
    }, 19);

    esp_lcd_panel_io_tx_param(io, 0xB2, (uint8_t[]) {
        0x00, 0x20, 0x0A, 0x0A, 0x70, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x03,
        0x03, 0x00, 0x01,
    }, 15);

    esp_lcd_panel_io_tx_param(io, 0xB4, (uint8_t[]) {
        0x00, 0x18, 0x80, 0x10, 0x01, 
    }, 5);

    esp_lcd_panel_io_tx_param(io, 0xB6, (uint8_t[]) {
        0x2C, 0x2C,
    }, 2);

    esp_lcd_panel_io_tx_param(io, 0xD5, (uint8_t[]) {
        0x00, 0x05, 0x03, 0x00, 0x01, 0x09, 0x10, 0x80, 0x37, 0x37, 0x20, 0x31, 
        0x46, 0x8A, 0x57, 0x9B, 0x20, 0x31, 0x46, 0x8A, 0x57, 0x9B, 0x07, 0x0F,
        0x02, 0x00,
    }, 26);

    esp_lcd_panel_io_tx_param(io, 0xE0, (uint8_t[]) {
        0x00, 0x08, 0x0D, 0x2D, 0x34, 0x3F, 0x19, 0x38, 0x09, 0x0E, 0x0E, 0x12, 
        0x14, 0x12, 0x14, 0x13, 0x19, 0x00, 0x08, 0x0D, 0x2D, 0x34, 0x3F, 0x19,
        0x38, 0x09, 0x0E, 0x0E, 0x12, 0x14, 0x12, 0x14, 0x13, 0x19,  
    }, 34);

    esp_lcd_panel_io_tx_param(io, 0xC1, (uint8_t[]) {
        0x01, 0x02, 0x08, 0x12, 0x1A, 0x22, 0x2A, 0x31, 0x36, 0x3F, 0x48, 0x51, 
        0x58, 0x60, 0x68, 0x70, 0x78, 0x80, 0x88, 0x90, 0x98, 0xA0, 0xA7, 0xAF,
        0xB6, 0xBE, 0xC7, 0xCE, 0xD6, 0xDE, 0xE6, 0xEF, 0xF5, 0xFB, 0xFC, 0xFE,
        0x8C, 0xA4, 0x19, 0xEC, 0x1B, 0x4C, 0x40, 0x02, 0x08, 0x12, 0x1A, 0x22, 
        0x2A, 0x31, 0x36, 0x3F, 0x48, 0x51, 0x58, 0x60, 0x68, 0x70, 0x78, 0x80, 
        0x88, 0x90, 0x98, 0xA0, 0xA7, 0xAF, 0xB6, 0xBE, 0xC7, 0xCE, 0xD6, 0xDE, 
        0xE6, 0xEF, 0xF5, 0xFB, 0xFC, 0xFE, 0x8C, 0xA4, 0x19, 0xEC, 0x1B, 0x4C, 
        0x40, 0x02, 0x08, 0x12, 0x1A, 0x22, 0x2A, 0x31, 0x36, 0x3F, 0x48, 0x51, 
        0x58, 0x60, 0x68, 0x70, 0x78, 0x80, 0x88, 0x90, 0x98, 0xA0, 0xA7, 0xAF, 
        0xB6, 0xBE, 0xC7, 0xCE, 0xD6, 0xDE, 0xE6, 0xEF, 0xF5, 0xFB, 0xFC, 0xFE, 
        0x8C, 0xA4, 0x19, 0xEC, 0x1B, 0x4C, 0x40,
    }, 127);

    esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) {
        0x55,
    }, 1);

    // LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first
    esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0);

    vTaskDelay(pdMS_TO_TICKS(120));
    // turn on display
    esp_lcd_panel_io_tx_param(io, LCD_CMD_DISPON, NULL, 0);

    vTaskDelay(pdMS_TO_TICKS(100));

    esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]) {
        0x55,
    }, 1);
    printf("\n%x\n", hx8369a->madctl_val);
    esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
        hx8369a->madctl_val,
    }, 1);

    return ESP_OK;
}

static esp_err_t panel_hx8369a_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
{
    hx8369a_panel_t *hx8369a = __containerof(panel, hx8369a_panel_t, base);
    assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
    esp_lcd_panel_io_handle_t io = hx8369a->io;

    x_start += hx8369a->x_gap;
    x_end += hx8369a->x_gap;
    y_start += hx8369a->y_gap;
    y_end += hx8369a->y_gap;

    // define an area of frame memory where MCU can access
    esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]) {
        (x_start >> 8) & 0xFF,
        x_start & 0xFF,
        ((x_end - 1) >> 8) & 0xFF,
        (x_end - 1) & 0xFF,
    }, 4);
    esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]) {
        (y_start >> 8) & 0xFF,
        y_start & 0xFF,
        ((y_end - 1) >> 8) & 0xFF,
        (y_end - 1) & 0xFF,
    }, 4);
    // transfer frame buffer
    size_t len = (x_end - x_start) * (y_end - y_start) * hx8369a->bits_per_pixel / 8;
    esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len);

    return ESP_OK;
}

static esp_err_t panel_hx8369a_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
{
    hx8369a_panel_t *hx8369a = __containerof(panel, hx8369a_panel_t, base);
    esp_lcd_panel_io_handle_t io = hx8369a->io;
    int command = 0;
    if (invert_color_data) {
        command = LCD_CMD_INVON;
    } else {
        command = LCD_CMD_INVOFF;
    }
    esp_lcd_panel_io_tx_param(io, command, NULL, 0);
    return ESP_OK;
}

static esp_err_t panel_hx8369a_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
{
    hx8369a_panel_t *hx8369a = __containerof(panel, hx8369a_panel_t, base);
    esp_lcd_panel_io_handle_t io = hx8369a->io;
    if (mirror_x) {
        hx8369a->madctl_val |= LCD_CMD_MX_BIT;
    } else {
        hx8369a->madctl_val &= ~LCD_CMD_MX_BIT;
    }
    if (mirror_y) {
        hx8369a->madctl_val |= LCD_CMD_MY_BIT;
    } else {
        hx8369a->madctl_val &= ~LCD_CMD_MY_BIT;
    }
    esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
        hx8369a->madctl_val
    }, 1);
    return ESP_OK;
}

static esp_err_t panel_hx8369a_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
{
    hx8369a_panel_t *hx8369a = __containerof(panel, hx8369a_panel_t, base);
    esp_lcd_panel_io_handle_t io = hx8369a->io;
    if (swap_axes) {
        hx8369a->madctl_val |= LCD_CMD_MV_BIT;
    } else {
        hx8369a->madctl_val &= ~LCD_CMD_MV_BIT;
    }
    esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]) {
        hx8369a->madctl_val
    }, 1);
    return ESP_OK;
}

static esp_err_t panel_hx8369a_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
{
    hx8369a_panel_t *hx8369a = __containerof(panel, hx8369a_panel_t, base);
    hx8369a->x_gap = x_gap;
    hx8369a->y_gap = y_gap;
    return ESP_OK;
}

static esp_err_t panel_hx8369a_disp_off(esp_lcd_panel_t *panel, bool off)
{
    hx8369a_panel_t *hx8369a = __containerof(panel, hx8369a_panel_t, base);
    esp_lcd_panel_io_handle_t io = hx8369a->io;
    int command = 0;
    if (off) {
        command = LCD_CMD_DISPOFF;
    } else {
        command = LCD_CMD_DISPON;
    }
    esp_lcd_panel_io_tx_param(io, command, NULL, 0);
    return ESP_OK;
}
