1#if defined(USE_ESP32_VARIANT_ESP32S3) || defined(USE_ESP32_VARIANT_ESP32P4) || defined(USE_ESP32_VARIANT_ESP32S31)
10#include <driver/gpio.h>
11#include <esp_lcd_panel_ops.h>
16static const uint8_t DELAY_FLAG = 0xFF;
19static constexpr size_t MIPI_RGB_MAX_CMD_LOG_BYTES = 64;
20static constexpr uint8_t MADCTL_MY = 0x80;
21static constexpr uint8_t MADCTL_MX = 0x40;
22static constexpr uint8_t MADCTL_MV = 0x20;
23static constexpr uint8_t MADCTL_ML = 0x10;
24static constexpr uint8_t MADCTL_BGR = 0x08;
25static constexpr uint8_t MADCTL_XFLIP = 0x02;
26static constexpr uint8_t MADCTL_YFLIP = 0x01;
32 pin->digital_write(
true);
58 this->
write(value, 9);
70 this->
write(value | 0x100, 9);
86 while (index !=
len) {
87 if (
len - index < 2) {
88 this->
mark_failed(LOG_STR(
"Malformed init sequence"));
91 uint8_t cmd =
seq[index++];
92 uint8_t
x =
seq[index++];
93 if (
x == DELAY_FLAG) {
94 ESP_LOGD(TAG,
"Delay %dms", cmd);
97 uint8_t num_args =
x & 0x7F;
98 if (
len - index < num_args) {
99 this->
mark_failed(LOG_STR(
"Malformed init sequence"));
105 const auto *
ptr =
seq + index;
107 ESP_LOGD(TAG,
"Write command %02X, length %d, byte(s) %s", cmd, num_args,
111 while (num_args-- != 0)
123 LOG_PIN(
" CS Pin: ", this->
cs_);
124 LOG_PIN(
" DC Pin: ", this->
dc_pin_);
125 ESP_LOGCONFIG(TAG,
" SPI Data rate: %uMHz", (
unsigned) (this->
data_rate_ / 1000000));
136 esp_lcd_rgb_panel_config_t config{};
137 config.flags.fb_in_psram = 1;
138 config.bounce_buffer_size_px = this->
width_ * 10;
140 config.timings.h_res = this->
width_;
141 config.timings.v_res = this->
height_;
150 config.clk_src = LCD_CLK_SRC_PLL160M;
152 for (
size_t i = 0; i != data_pin_count; i++) {
153 config.data_gpio_nums[i] =
static_cast<gpio_num_t
>(this->
data_pins_[i]->
get_pin());
155 config.data_width = data_pin_count;
156 config.disp_gpio_num = GPIO_NUM_NC;
160 config.hsync_gpio_num = GPIO_NUM_NC;
165 config.vsync_gpio_num = GPIO_NUM_NC;
168 config.de_gpio_num =
static_cast<gpio_num_t
>(this->
de_pin_->
get_pin());
170 config.de_gpio_num = GPIO_NUM_NC;
173 esp_err_t err = esp_lcd_new_rgb_panel(&config, &this->
handle_);
175 err = esp_lcd_panel_reset(this->
handle_);
177 err = esp_lcd_panel_init(this->
handle_);
180 err = esp_lcd_rgb_panel_get_frame_buffer(this->
handle_, 1, &fb);
181 if (err == ESP_OK && fb !=
nullptr) {
182 this->
fb_ =
reinterpret_cast<uint16_t *
>(fb);
183 }
else if (err == ESP_OK) {
184 err = ESP_ERR_NO_MEM;
188 ESP_LOGE(TAG,
"lcd setup failed: %s", esp_err_to_name(err));
191 ESP_LOGCONFIG(TAG,
"MipiRgb setup complete");
203 }
else if (this->
page_ !=
nullptr) {
222 this->x_low_, this->y_low_, this->
width_ - w - this->x_low_);
224 this->x_low_ = this->
width_;
251 const auto *src_origin =
252 reinterpret_cast<const uint16_t *
>(
ptr) + (
static_cast<size_t>(
y_offset) * stride +
x_offset);
265 esp_err_t err = ESP_OK;
273 for (
int y = 0;
y !=
h;
y++) {
281 ESP_LOGE(TAG,
"lcd_lcd_panel_draw_bitmap failed: %s", esp_err_to_name(err));
293 if (this->
buffer_ ==
nullptr) {
294 this->
mark_failed(LOG_STR(
"Could not allocate buffer for display!"));
302 int x1 = x_start, y1 =
y_start;
303 int x2 = x_start +
w - 1, y2 =
y_start +
h - 1;
306 const int right =
static_cast<int>(this->
width_) - 1;
307 const int bottom =
static_cast<int>(this->
height_) - 1;
308 const int left = std::clamp(std::min(x1, x2), 0, right);
309 const int top = std::clamp(std::min(y1, y2), 0, bottom);
310 x2 = std::clamp(std::max(x1, x2), 0, right);
311 y2 = std::clamp(std::max(y1, y2), 0, bottom);
338 if (this->
buffer_[pos] == new_color)
357 Display::fill(color);
361 auto *ptr_16 =
reinterpret_cast<uint16_t *
>(this->
buffer_);
363 std::fill_n(ptr_16, this->
width_ * this->
height_, new_color);
367 this->
y_high_ = this->height_ - 1;
394[[maybe_unused]]
static const char *get_pin_name(
GPIOPin *pin, std::span<char, GPIO_SUMMARY_MAX_LEN>
buffer) {
403 for (uint8_t i = start; i !=
end; i++) {
405 ESP_LOGCONFIG(TAG,
" %s pin %d: %s", name, offset++, pin_summary);
420 "\n Rotation: %d degrees"
421 "\n PCLK Inverted: %s"
422 "\n HSync Pulse Width: %u"
423 "\n HSync Back Porch: %u"
424 "\n HSync Front Porch: %u"
425 "\n VSync Pulse Width: %u"
426 "\n VSync Back Porch: %u"
427 "\n VSync Front Porch: %u"
428 "\n Invert Colors: %s"
429 "\n Pixel Clock: %uMHz"
439 get_pin_name(this->
de_pin_, de_buf), get_pin_name(this->
pclk_pin_, pclk_buf),
void mark_failed()
Mark this component as failed.
virtual void digital_write(bool value)=0
virtual size_t dump_summary(char *buffer, size_t len) const
Write a summary of this pin to the provided buffer.
An STL allocator that uses SPI or internal RAM.
int int int const uint8_t ColorOrder ColorBitness bitness
virtual void clear()
Clear the entire screen by filling it with OFF pixels.
int int int const uint8_t ColorOrder ColorBitness bool int int y_offset
int int int const uint8_t ColorOrder ColorBitness bool int int int x_pad
int int int const uint8_t * ptr
bool ESPHOME_ALWAYS_INLINE is_point_clipped(int x, int y) const
Whether (x, y) falls outside the active clipping rectangle.
int int int const uint8_t ColorOrder ColorBitness bool big_endian
Rect get_clipping() const
Get the current the clipping rectangle.
int int int const uint8_t ColorOrder ColorBitness bool int x_offset
DisplayRotation rotation_
const display_writer_t & get_writer() const
size_t dump_summary(char *buffer, size_t len) const override
InternalGPIOPin * de_pin_
std::vector< GPIOPin * > enable_pins_
uint16_t hsync_pulse_width_
void flush_dirty_()
Send the region touched since the last flush to the panel.
uint16_t hsync_front_porch_
uint16_t vsync_back_porch_
esp_lcd_panel_handle_t handle_
int get_height() override
InternalGPIOPin * hsync_pin_
void dump_config() override
uint16_t vsync_front_porch_
int get_height_internal() override
uint16_t vsync_pulse_width_
InternalGPIOPin * data_pins_[16]
void draw_pixel_at(int x, int y, Color color) override
void rotate_(int &x, int &y) const
uint16_t to_panel_565_(Color color) const
Pack a colour as the 565 value the panel's byte order expects in memory.
InternalGPIOPin * vsync_pin_
void dump_pins_(uint8_t start, uint8_t end, const char *name, uint8_t offset)
void mark_dirty_(int x_start, int y_start, int w, int h)
void draw_pixels_internal(int x_start, int y_start, int w, int h, const uint8_t *ptr, display::ColorBitness bitness, bool big_endian, int x_offset, int y_offset, int x_pad) override
InternalGPIOPin * pclk_pin_
uint16_t hsync_back_porch_
void fill(Color color) override
int get_width_internal() override
void write_to_display_(int x_start, int y_start, int w, int h, const uint8_t *ptr, int x_offset, int y_offset, int x_pad)
void dump_config() override
void write_data_(uint8_t value)
void write_init_sequence_()
this relies upon the init sequence being well-formed, which is guaranteed by the Python init code.
void write_command_(uint8_t value)
const uint8_t * init_sequence_
size_t init_sequence_len_
void spi_setup() override
void write_byte(uint8_t data)
void write(uint16_t data, size_t num_bits)
@ DISPLAY_ROTATION_0_DEGREES
@ DISPLAY_ROTATION_270_DEGREES
@ DISPLAY_ROTATION_180_DEGREES
@ DISPLAY_ROTATION_90_DEGREES
void blit_rotated_565(uint16_t *fb, int fb_width, int fb_height, display::DisplayRotation rotation, const uint16_t *src_origin, int x_start, int y_start, int w, int h, size_t stride)
char * format_hex_pretty_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length, char separator)
Format byte array as uppercase hex to buffer (base implementation).
constexpr size_t format_hex_pretty_size(size_t byte_count)
Calculate buffer size needed for format_hex_pretty_to with separator: "XX:XX:...:XX\0".
constexpr size_t GPIO_SUMMARY_MAX_LEN
Maximum buffer size for dump_summary output.
void HOT delay(uint32_t ms)