| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "lcd.h" |
| #include <ddk/debug.h> |
| #include <ddktl/device.h> |
| #include <lib/mipi-dsi/mipi-dsi.h> |
| #include "common.h" |
| |
| #define DELAY_CMD (0xFF) |
| #define DCS_CMD (0xFE) |
| #define GEN_CMD (0xFD) |
| |
| constexpr uint8_t kId1Reg = 0xDA; |
| constexpr uint8_t kId2Reg = 0xDC; |
| constexpr uint16_t panel1_id = 0xA1A1; |
| constexpr uint16_t panel2_id = 0xB1B1; |
| |
| namespace mt8167s_display { |
| |
| namespace { |
| // Based on Vendor datasheet |
| // <CMD TYPE><LENGTH><DATA...> |
| // <DELAY_CMD><DELAY (ms)> |
| constexpr uint8_t lcd_shutdown_sequence[] = { |
| DELAY_CMD,5, |
| DCS_CMD,1,0x28, |
| DELAY_CMD, 30, |
| DCS_CMD,1,0x10, |
| DELAY_CMD,150, |
| }; |
| |
| constexpr uint8_t lcd_init_sequence_ST7701S_1[] = { |
| DCS_CMD,6,0xFF,0x77,0x01,0x00,0x00,0x00, |
| DCS_CMD,1,0x11, |
| DELAY_CMD,120, |
| DCS_CMD,6,0xFF,0x77,0x01,0x00,0x00,0x10, |
| DCS_CMD,17,0xB0,0x40,0xC9,0x8F,0x0D,0x11,0x07,0x02,0x09,0x09,0x1F,0x04,0x50,0x0F,0xE4,0x29,0xDF, |
| DCS_CMD,17,0xB1,0x40,0xCB,0xD3,0x11,0x8F,0x04,0x00,0x08,0x07,0x1C,0x06,0x53,0x12,0x63,0xEB,0xDF, |
| DCS_CMD,6,0xFF,0x77,0x01,0x00,0x00,0x00, |
| DCS_CMD,1,0x29, |
| DELAY_CMD,20, |
| }; |
| |
| constexpr uint8_t lcd_init_sequence_ST7701S_2[] = { |
| DCS_CMD,1,0x11, |
| DELAY_CMD,120, |
| DCS_CMD,1,0x29, |
| DELAY_CMD,20, |
| }; |
| |
| constexpr uint8_t lcd_init_sequence_ILI9881C[] = { |
| DCS_CMD,4,0xFF,0x98,0x81,0x03, |
| DCS_CMD,2,0x01,0x00, |
| DCS_CMD,2,0x02,0x00, |
| DCS_CMD,2,0x03,0x53, |
| DCS_CMD,2,0x04,0x13, |
| DCS_CMD,2,0x05,0x13, |
| DCS_CMD,2,0x06,0x06, |
| DCS_CMD,2,0x07,0x00, |
| DCS_CMD,2,0x08,0x04, |
| DCS_CMD,2,0x09,0x00, |
| DCS_CMD,2,0x0a,0x00, |
| DCS_CMD,2,0x0b,0x00, |
| DCS_CMD,2,0x0c,0x00, |
| DCS_CMD,2,0x0d,0x00, |
| DCS_CMD,2,0x0e,0x00, |
| DCS_CMD,2,0x0f,0x00, |
| DCS_CMD,2,0x10,0x00, |
| DCS_CMD,2,0x11,0x00, |
| DCS_CMD,2,0x12,0x00, |
| DCS_CMD,2,0x13,0x00, |
| DCS_CMD,2,0x14,0x00, |
| DCS_CMD,2,0x15,0x00, |
| DCS_CMD,2,0x16,0x00, |
| DCS_CMD,2,0x17,0x00, |
| DCS_CMD,2,0x18,0x00, |
| DCS_CMD,2,0x19,0x00, |
| DCS_CMD,2,0x1a,0x00, |
| DCS_CMD,2,0x1b,0x00, |
| DCS_CMD,2,0x1c,0x00, |
| DCS_CMD,2,0x1d,0x00, |
| DCS_CMD,2,0x1e,0xC0, |
| DCS_CMD,2,0x1f,0x80, |
| DCS_CMD,2,0x20,0x04, |
| DCS_CMD,2,0x21,0x0B, |
| DCS_CMD,2,0x22,0x00, |
| DCS_CMD,2,0x23,0x00, |
| DCS_CMD,2,0x24,0x00, |
| DCS_CMD,2,0x25,0x00, |
| DCS_CMD,2,0x26,0x00, |
| DCS_CMD,2,0x27,0x00, |
| DCS_CMD,2,0x28,0x55, |
| DCS_CMD,2,0x29,0x03, |
| DCS_CMD,2,0x2a,0x00, |
| DCS_CMD,2,0x2b,0x00, |
| DCS_CMD,2,0x2c,0x00, |
| DCS_CMD,2,0x2d,0x00, |
| DCS_CMD,2,0x2e,0x00, |
| DCS_CMD,2,0x2f,0x00, |
| DCS_CMD,2,0x30,0x00, |
| DCS_CMD,2,0x31,0x00, |
| DCS_CMD,2,0x32,0x00, |
| DCS_CMD,2,0x33,0x00, |
| DCS_CMD,2,0x34,0x04, |
| DCS_CMD,2,0x35,0x05, |
| DCS_CMD,2,0x36,0x05, |
| DCS_CMD,2,0x37,0x00, |
| DCS_CMD,2,0x38,0x3C, |
| DCS_CMD,2,0x39,0x00, |
| DCS_CMD,2,0x3a,0x40, |
| DCS_CMD,2,0x3b,0x40, |
| DCS_CMD,2,0x3c,0x00, |
| DCS_CMD,2,0x3d,0x00, |
| DCS_CMD,2,0x3e,0x00, |
| DCS_CMD,2,0x3f,0x00, |
| DCS_CMD,2,0x40,0x00, |
| DCS_CMD,2,0x41,0x00, |
| DCS_CMD,2,0x42,0x00, |
| DCS_CMD,2,0x43,0x00, |
| DCS_CMD,2,0x44,0x00, |
| DCS_CMD,2,0x50,0x01, |
| DCS_CMD,2,0x51,0x23, |
| DCS_CMD,2,0x52,0x45, |
| DCS_CMD,2,0x53,0x67, |
| DCS_CMD,2,0x54,0x89, |
| DCS_CMD,2,0x55,0xAB, |
| DCS_CMD,2,0x56,0x01, |
| DCS_CMD,2,0x57,0x23, |
| DCS_CMD,2,0x58,0x45, |
| DCS_CMD,2,0x59,0x67, |
| DCS_CMD,2,0x5A,0x89, |
| DCS_CMD,2,0x5B,0xAB, |
| DCS_CMD,2,0x5C,0xCD, |
| DCS_CMD,2,0x5D,0xEF, |
| DCS_CMD,2,0x5E,0x01, |
| DCS_CMD,2,0x5F,0x14, |
| DCS_CMD,2,0x60,0x15, |
| DCS_CMD,2,0x61,0x0C, |
| DCS_CMD,2,0x62,0x0D, |
| DCS_CMD,2,0x63,0x0E, |
| DCS_CMD,2,0x64,0x0F, |
| DCS_CMD,2,0x65,0x10, |
| DCS_CMD,2,0x66,0x11, |
| DCS_CMD,2,0x67,0x08, |
| DCS_CMD,2,0x68,0x02, |
| DCS_CMD,2,0x69,0x0A, |
| DCS_CMD,2,0x6A,0x02, |
| DCS_CMD,2,0x6B,0x02, |
| DCS_CMD,2,0x6C,0x02, |
| DCS_CMD,2,0x6D,0x02, |
| DCS_CMD,2,0x6E,0x02, |
| DCS_CMD,2,0x6F,0x02, |
| DCS_CMD,2,0x70,0x02, |
| DCS_CMD,2,0x71,0x02, |
| DCS_CMD,2,0x72,0x06, |
| DCS_CMD,2,0x73,0x02, |
| DCS_CMD,2,0x74,0x02, |
| DCS_CMD,2,0x75,0x14, |
| DCS_CMD,2,0x76,0x15, |
| DCS_CMD,2,0x77,0x11, |
| DCS_CMD,2,0x78,0x10, |
| DCS_CMD,2,0x79,0x0F, |
| DCS_CMD,2,0x7A,0x0E, |
| DCS_CMD,2,0x7B,0x0D, |
| DCS_CMD,2,0x7C,0x0C, |
| DCS_CMD,2,0x7D,0x06, |
| DCS_CMD,2,0x7E,0x02, |
| DCS_CMD,2,0x7F,0x0A, |
| DCS_CMD,2,0x80,0x02, |
| DCS_CMD,2,0x81,0x02, |
| DCS_CMD,2,0x82,0x02, |
| DCS_CMD,2,0x83,0x02, |
| DCS_CMD,2,0x84,0x02, |
| DCS_CMD,2,0x85,0x02, |
| DCS_CMD,2,0x86,0x02, |
| DCS_CMD,2,0x87,0x02, |
| DCS_CMD,2,0x88,0x08, |
| DCS_CMD,2,0x89,0x02, |
| DCS_CMD,2,0x8A,0x02, |
| DCS_CMD,4,0xFF,0x98,0x81,0x04, |
| DCS_CMD,2,0x6C,0x15, |
| DCS_CMD,2,0x6E,0x3B, |
| DCS_CMD,2,0x6F,0x53, |
| DCS_CMD,2,0x3A,0xA4, |
| DCS_CMD,2,0x8D,0x15, |
| DCS_CMD,2,0x87,0xBA, |
| DCS_CMD,2,0x26,0x76, |
| DCS_CMD,2,0xB2,0xD1, |
| DCS_CMD,2,0x88,0x0B, |
| DCS_CMD,4,0xFF,0x98,0x81,0x01, |
| DCS_CMD,2,0x22,0x0A, |
| DCS_CMD,2,0x31,0x00, |
| DCS_CMD,2,0x53,0x96, |
| DCS_CMD,2,0x55,0x88, |
| DCS_CMD,2,0x50,0x96, |
| DCS_CMD,2,0x51,0x96, |
| DCS_CMD,2,0x60,0x14, |
| DCS_CMD,2,0xA0,0x08, |
| DCS_CMD,2,0xA1,0x1C, |
| DCS_CMD,2,0xA2,0x29, |
| DCS_CMD,2,0xA3,0x13, |
| DCS_CMD,2,0xA4,0x16, |
| DCS_CMD,2,0xA5,0x28, |
| DCS_CMD,2,0xA6,0x1C, |
| DCS_CMD,2,0xA7,0x1D, |
| DCS_CMD,2,0xA8,0x80, |
| DCS_CMD,2,0xA9,0x1a, |
| DCS_CMD,2,0xAA,0x27, |
| DCS_CMD,2,0xAB,0x6A, |
| DCS_CMD,2,0xAC,0x1a, |
| DCS_CMD,2,0xAD,0x19, |
| DCS_CMD,2,0xAE,0x4b, |
| DCS_CMD,2,0xAF,0x21, |
| DCS_CMD,2,0xB0,0x25, |
| DCS_CMD,2,0xB1,0x4A, |
| DCS_CMD,2,0xB2,0x59, |
| DCS_CMD,2,0xB3,0x2C, |
| DCS_CMD,2,0xC0,0x08, |
| DCS_CMD,2,0xC1,0x1C, |
| DCS_CMD,2,0xC2,0x29, |
| DCS_CMD,2,0xC3,0x13, |
| DCS_CMD,2,0xC4,0x17, |
| DCS_CMD,2,0xC5,0x28, |
| DCS_CMD,2,0xC6,0x1C, |
| DCS_CMD,2,0xC7,0x1D, |
| DCS_CMD,2,0xC8,0x80, |
| DCS_CMD,2,0xC9,0x1a, |
| DCS_CMD,2,0xCA,0x27, |
| DCS_CMD,2,0xCB,0x6A, |
| DCS_CMD,2,0xCC,0x1A, |
| DCS_CMD,2,0xCD,0x19, |
| DCS_CMD,2,0xCE,0x4b, |
| DCS_CMD,2,0xCF,0x21, |
| DCS_CMD,2,0xD0,0x25, |
| DCS_CMD,2,0xD1,0x4A, |
| DCS_CMD,2,0xD2,0x5B, |
| DCS_CMD,2,0xD3,0x2C, |
| DCS_CMD,4,0xFF,0x98,0x81,0x00, |
| DCS_CMD,2,0x35,0x00, |
| DCS_CMD,1,0x11, |
| DELAY_CMD,120, |
| DCS_CMD,1,0x29, |
| DELAY_CMD,20, |
| }; |
| } // namespace |
| |
| zx_status_t Lcd::GetDisplayId(uint16_t& id) { |
| |
| uint8_t id1; |
| uint8_t id2; |
| zx_status_t status = ZX_OK; |
| |
| // Create the command using mipi-dsi library |
| mipi_dsi_cmd_t cmd[2]; |
| status = mipi_dsi::MipiDsi::CreateCommand(&kId1Reg, 1, &id1, 1, COMMAND_DCS, &cmd[0]); |
| if (status != ZX_OK) { |
| DISP_ERROR("Invalid command (%d)\n", status); |
| return status; |
| } |
| |
| status = mipi_dsi::MipiDsi::CreateCommand(&kId2Reg, 1, &id2, 1, COMMAND_DCS, &cmd[1]); |
| if (status != ZX_OK) { |
| DISP_ERROR("Invalid command (%d)\n", status); |
| return status; |
| } |
| |
| if ((status = dsiimpl_.SendCmd(cmd, 2)) != ZX_OK) { |
| DISP_ERROR("Could not read out Display ID\n"); |
| return status; |
| } |
| id = static_cast<uint16_t>((id1 << 8) | id2); |
| DISP_INFO("Display ID: 0x%x\n", id); |
| return status; |
| } |
| |
| zx_status_t Lcd::LoadInitTable(const uint8_t* buffer, size_t size) { |
| zx_status_t status = ZX_OK; |
| size_t i; |
| i = 0; |
| bool isDCS = false; |
| while (i < size) { |
| switch (buffer[i]) { |
| case DELAY_CMD: |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(buffer[i + 1]))); |
| i += 2; |
| break; |
| case DCS_CMD: |
| isDCS = true; |
| __FALLTHROUGH; |
| case GEN_CMD: |
| default: |
| // Create the command using mipi-dsi library |
| mipi_dsi_cmd_t cmd; |
| status = mipi_dsi::MipiDsi::CreateCommand(&buffer[i+2], buffer[i+1], |
| NULL, 0, |
| isDCS, &cmd); |
| if (status == ZX_OK) { |
| if ((status = dsiimpl_.SendCmd(&cmd, 1)) != ZX_OK) { |
| DISP_ERROR("Error loading LCD init table. Aborting %d\n", status); |
| return status; |
| } |
| } else { |
| DISP_ERROR("Invalid command (%d). Skipping\n", status); |
| } |
| // increment by payload length |
| i += buffer[i + 1] + 2; // the 2 includes current plus size field |
| isDCS = false; |
| break; |
| } |
| } |
| return status; |
| } |
| |
| zx_status_t Lcd::Disable() { |
| if (!enabled_) { |
| return ZX_OK; |
| } |
| // First send shutdown command to LCD |
| enabled_ = false; |
| return LoadInitTable(lcd_shutdown_sequence, sizeof(lcd_shutdown_sequence)); |
| } |
| |
| void Lcd::PowerOn() { |
| if (gpio_.is_valid()) { |
| gpio_.ConfigOut(1); |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); |
| gpio_.Write(0); |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(200))); |
| gpio_.Write(1); |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(50))); |
| } |
| } |
| |
| void Lcd::PowerOff() { |
| if (gpio_.is_valid()) { |
| gpio_.Write(0); |
| zx_nanosleep(zx_deadline_after(ZX_MSEC(120))); |
| } |
| } |
| |
| zx_status_t Lcd::Enable() { |
| if (enabled_) { |
| return ZX_OK; |
| } |
| |
| // load table |
| zx_status_t status; |
| if (panel_type_ == PANEL_ILI9881C) { |
| status = LoadInitTable(lcd_init_sequence_ILI9881C, |
| sizeof(lcd_init_sequence_ILI9881C)); |
| } else if (panel_type_ == PANEL_ST7701S) { |
| // There are two types are panels. Identify them |
| uint16_t id; |
| status = GetDisplayId(id); |
| if (status == ZX_OK) { |
| if (id == panel1_id) { |
| status = LoadInitTable(lcd_init_sequence_ST7701S_1, |
| sizeof(lcd_init_sequence_ST7701S_1)); |
| } else if (id == panel2_id) { |
| status = LoadInitTable(lcd_init_sequence_ST7701S_2, |
| sizeof(lcd_init_sequence_ST7701S_2)); |
| } else { |
| status = ZX_ERR_NOT_SUPPORTED; |
| } |
| } else { |
| DISP_ERROR("Could not read display ID\n"); |
| } |
| } else { |
| status = ZX_ERR_NOT_SUPPORTED; |
| } |
| |
| if (status == ZX_OK) { |
| // LCD is on now. |
| enabled_ = true; |
| } else { |
| DISP_ERROR("Unsupported panel detected\n"); |
| } |
| |
| return status; |
| } |
| |
| } // namespace mt8167s_display |