blob: c068de9c6a0971091e7a5c01dd40f438cc8a7612 [file] [log] [blame]
// 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