2020-11-18 21:24:30 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms and conditions of the GNU General Public License,
|
|
|
|
* version 2, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
|
|
* more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include <exosphere.hpp>
|
|
|
|
#include "fatal_device_page_table.hpp"
|
2020-11-20 09:05:45 +00:00
|
|
|
#include "fatal_registers_di.hpp"
|
2020-11-18 21:24:30 +00:00
|
|
|
|
|
|
|
namespace ams::secmon::fatal {
|
|
|
|
|
2020-11-20 09:05:45 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
#include "fatal_display_config.inc"
|
|
|
|
|
2020-11-18 21:24:30 +00:00
|
|
|
}
|
|
|
|
|
2020-11-20 09:05:45 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
/* Helpful defines. */
|
|
|
|
constexpr size_t FrameBufferHeight = 768;
|
|
|
|
constexpr size_t FrameBufferWidth = 1280;
|
|
|
|
constexpr size_t FrameBufferSize = FrameBufferHeight * FrameBufferWidth * sizeof(u32);
|
|
|
|
|
|
|
|
constexpr int DsiWaitForCommandMilliSecondsMax = 250;
|
|
|
|
constexpr int DsiWaitForCommandCompletionMilliSeconds = 5;
|
|
|
|
constexpr int DsiWaitForHostControlMilliSecondsMax = 150;
|
|
|
|
|
|
|
|
constexpr inline int I2cAddressMax77620Pmic = 0x3C;
|
|
|
|
|
|
|
|
constexpr size_t GPIO_PORT3_CNF_0 = 0x200;
|
|
|
|
constexpr size_t GPIO_PORT3_OE_0 = 0x210;
|
|
|
|
constexpr size_t GPIO_PORT3_OUT_0 = 0x220;
|
|
|
|
|
|
|
|
constexpr size_t GPIO_PORT6_CNF_1 = 0x504;
|
|
|
|
constexpr size_t GPIO_PORT6_OE_1 = 0x514;
|
|
|
|
constexpr size_t GPIO_PORT6_OUT_1 = 0x524;
|
|
|
|
|
|
|
|
/* Globals. */
|
|
|
|
constexpr inline const uintptr_t PMC = secmon::MemoryRegionVirtualDevicePmc .GetAddress();
|
|
|
|
constexpr inline const uintptr_t g_disp1_regs = secmon::MemoryRegionVirtualDeviceDisp1 .GetAddress();
|
|
|
|
constexpr inline const uintptr_t g_dsi_regs = secmon::MemoryRegionVirtualDeviceDsi .GetAddress();
|
|
|
|
constexpr inline const uintptr_t g_clk_rst_regs = secmon::MemoryRegionVirtualDeviceClkRst .GetAddress();
|
|
|
|
constexpr inline const uintptr_t g_gpio_regs = secmon::MemoryRegionVirtualDeviceGpio .GetAddress();
|
|
|
|
constexpr inline const uintptr_t g_apb_misc_regs = secmon::MemoryRegionVirtualDeviceApbMisc.GetAddress();
|
|
|
|
constexpr inline const uintptr_t g_mipi_cal_regs = secmon::MemoryRegionVirtualDeviceMipiCal.GetAddress();
|
|
|
|
|
|
|
|
constinit u32 *g_frame_buffer = nullptr;
|
|
|
|
|
|
|
|
inline void DoRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes, size_t num_writes) {
|
|
|
|
for (size_t i = 0; i < num_writes; i++) {
|
|
|
|
reg::Write(base_address + reg_writes[i].offset, reg_writes[i].value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void DoSocDependentRegisterWrites(uintptr_t base_address, const RegisterWrite *reg_writes_erista, size_t num_writes_erista, const RegisterWrite *reg_writes_mariko, size_t num_writes_mariko) {
|
|
|
|
switch (GetSocType()) {
|
|
|
|
case fuse::SocType_Erista: DoRegisterWrites(base_address, reg_writes_erista, num_writes_erista); break;
|
|
|
|
case fuse::SocType_Mariko: DoRegisterWrites(base_address, reg_writes_mariko, num_writes_mariko); break;
|
|
|
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void DoSleepOrRegisterWrites(uintptr_t base_address, const SleepOrRegisterWrite *reg_writes, size_t num_writes) {
|
|
|
|
for (size_t i = 0; i < num_writes; i++) {
|
|
|
|
switch (reg_writes[i].kind) {
|
|
|
|
case SleepOrRegisterWriteKind_Write:
|
|
|
|
reg::Write(base_address + sizeof(u32) * reg_writes[i].offset, reg_writes[i].value);
|
|
|
|
break;
|
|
|
|
case SleepOrRegisterWriteKind_Sleep:
|
|
|
|
util::WaitMicroSeconds(reg_writes[i].offset * UINT64_C(1000));
|
|
|
|
break;
|
|
|
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaitDsiTrigger() {
|
|
|
|
const u32 timeout = util::GetMicroSeconds() + (DsiWaitForCommandMilliSecondsMax * 1000u);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (util::GetMicroSeconds() >= timeout) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (reg::Read(g_dsi_regs + sizeof(u32) * DSI_TRIGGER) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
util::WaitMicroSeconds(DsiWaitForCommandCompletionMilliSeconds * 1000u);
|
|
|
|
}
|
|
|
|
|
|
|
|
void WaitDsiHostControl() {
|
|
|
|
const u32 timeout = util::GetMicroSeconds() + (DsiWaitForHostControlMilliSecondsMax * 1000u);
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
if (util::GetMicroSeconds() >= timeout) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ((reg::Read(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL) & DSI_HOST_CONTROL_IMM_BTA) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void EnableBacklightForVendor2050ForHardwareTypeFive(int brightness) {
|
|
|
|
/* Enable FRAME_END_INT */
|
|
|
|
reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_ENABLE, 2);
|
|
|
|
|
|
|
|
/* Configure DSI_LINE_TYPE as FOUR */
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 1);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 9);
|
|
|
|
|
|
|
|
/* Set and wait for FRAME_END_INT */
|
|
|
|
reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS, 2);
|
|
|
|
while ((reg::Read(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS) & 2) != 0) { /* ... */ }
|
|
|
|
|
|
|
|
/* Configure display brightness. */
|
|
|
|
const u32 brightness_val = ((0x7FF * brightness) / 100);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x339);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, (brightness_val & 0x700) | ((brightness_val & 0xFF) << 16) | 0x51);
|
|
|
|
|
|
|
|
/* Set and wait for FRAME_END_INT */
|
|
|
|
reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS, 2);
|
|
|
|
while ((reg::Read(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS) & 2) != 0) { /* ... */ }
|
|
|
|
|
|
|
|
/* Set client sync point block reset. */
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_INCR_SYNCPT_CNTRL, 1);
|
|
|
|
util::WaitMicroSeconds(300'000ul);
|
|
|
|
|
|
|
|
/* Clear client sync point block resest. */
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_INCR_SYNCPT_CNTRL, 0);
|
|
|
|
util::WaitMicroSeconds(300'000ul);
|
|
|
|
|
|
|
|
/* Clear DSI_LINE_TYPE config. */
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_VIDEO_MODE_CONTROL, 0);
|
|
|
|
|
|
|
|
/* Disable FRAME_END_INT */
|
|
|
|
reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_ENABLE, 0);
|
|
|
|
reg::Write(g_disp1_regs + sizeof(u32) * DC_CMD_INT_STATUS, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
void EnableBacklightForGeneric(int brightness) {
|
|
|
|
AMS_UNUSED(brightness);
|
|
|
|
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define DO_REGISTER_WRITES(base_address, writes) DoRegisterWrites(base_address, writes, util::size(writes))
|
|
|
|
#define DO_SOC_DEPENDENT_REGISTER_WRITES(base_address, writes) DoSocDependentRegisterWrites(base_address, writes##Erista, util::size(writes##Erista), writes##Mariko, util::size(writes##Mariko))
|
|
|
|
#define DO_SLEEP_OR_REGISTER_WRITES(base_address, writes) DoSleepOrRegisterWrites(base_address, writes, util::size(writes))
|
|
|
|
|
|
|
|
void InitializeFrameBuffer() {
|
|
|
|
if (g_frame_buffer != nullptr) {
|
|
|
|
std::memset(g_frame_buffer, 0, FrameBufferSize);
|
|
|
|
hw::FlushDataCache(g_frame_buffer, FrameBufferSize);
|
|
|
|
} else {
|
|
|
|
/* Clear the frame buffer. */
|
|
|
|
g_frame_buffer = secmon::MemoryRegionDramDcFramebuffer.GetPointer<u32>();
|
|
|
|
std::memset(g_frame_buffer, 0, FrameBufferSize);
|
|
|
|
hw::FlushDataCache(g_frame_buffer, FrameBufferSize);
|
|
|
|
|
|
|
|
/* Attach the frame buffer to DC. */
|
|
|
|
InitializeDevicePageTableForDc();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FinalizeFrameBuffer() {
|
|
|
|
/* We don't actually support finalizing the framebuffer, so do nothing here. */
|
|
|
|
}
|
|
|
|
|
2020-11-18 21:24:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void FinalizeDisplay() {
|
2020-11-20 09:05:45 +00:00
|
|
|
FinalizeFrameBuffer();
|
2020-11-18 21:24:30 +00:00
|
|
|
/* TODO */
|
|
|
|
AMS_SECMON_LOG("FinalizeDisplay not yet implemented\n");
|
|
|
|
}
|
|
|
|
|
2020-11-20 09:05:45 +00:00
|
|
|
void InitializeDisplay() {
|
|
|
|
/* Ensure that the display is finalized. */
|
|
|
|
/* TODO */
|
|
|
|
|
|
|
|
/* Setup the framebuffer. */
|
|
|
|
InitializeFrameBuffer();
|
|
|
|
|
|
|
|
/* Get the hardware type. */
|
|
|
|
const auto hw_type = fuse::GetHardwareType();
|
|
|
|
|
|
|
|
/* Turn on DSI/voltage rail. */
|
|
|
|
{
|
|
|
|
if (GetSocType() == fuse::SocType_Mariko) {
|
|
|
|
i2c::SendByte(i2c::Port_5, I2cAddressMax77620Pmic, 0x18, 0x3A);
|
|
|
|
i2c::SendByte(i2c::Port_5, I2cAddressMax77620Pmic, 0x18, 0x3A);
|
|
|
|
}
|
|
|
|
i2c::SendByte(i2c::Port_5, I2cAddressMax77620Pmic, 0x23, 0xD0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Enable MIPI CAL, DSI, DISP1, HOST1X, UART_FST_MIPI_CAL, DSIA LP clocks. */
|
|
|
|
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_H_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_H_CLR_CLR_MIPI_CAL_RST, ENABLE),
|
|
|
|
CLK_RST_REG_BITS_ENUM(RST_DEV_H_CLR_CLR_DSI_RST, ENABLE));
|
|
|
|
|
|
|
|
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_H_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_H_SET_SET_CLK_ENB_MIPI_CAL, ENABLE),
|
|
|
|
CLK_RST_REG_BITS_ENUM(CLK_ENB_H_SET_SET_CLK_ENB_DSI, ENABLE));
|
|
|
|
|
|
|
|
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_HOST1X_RST, ENABLE),
|
|
|
|
CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_DISP1_RST, ENABLE));
|
|
|
|
|
|
|
|
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_L_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_L_SET_SET_CLK_ENB_HOST1X, ENABLE),
|
|
|
|
CLK_RST_REG_BITS_ENUM(CLK_ENB_L_SET_SET_CLK_ENB_DISP1, ENABLE));
|
|
|
|
|
|
|
|
|
|
|
|
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_X_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_X_SET_SET_CLK_ENB_UART_FST_MIPI_CAL, ENABLE));
|
|
|
|
|
|
|
|
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL, CLK_RST_REG_BITS_VALUE(CLK_SOURCE_UART_FST_MIPI_CAL_UART_FST_MIPI_CAL_CLK_DIVISOR, 10),
|
|
|
|
CLK_RST_REG_BITS_ENUM (CLK_SOURCE_UART_FST_MIPI_CAL_UART_FST_MIPI_CAL_CLK_SRC, PLLP_OUT3));
|
|
|
|
|
|
|
|
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_ENB_W_SET, CLK_RST_REG_BITS_ENUM(CLK_ENB_W_SET_SET_CLK_ENB_DSIA_LP, ENABLE));
|
|
|
|
|
|
|
|
reg::Write(g_clk_rst_regs + CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP, CLK_RST_REG_BITS_VALUE(CLK_SOURCE_DSIA_LP_DSIA_LP_CLK_DIVISOR, 10),
|
|
|
|
CLK_RST_REG_BITS_ENUM (CLK_SOURCE_DSIA_LP_DSIA_LP_CLK_SRC, PLLP_OUT0));
|
|
|
|
|
|
|
|
/* Set IO_DPD_REQ to DPD_OFF. */
|
|
|
|
reg::ReadWrite(PMC + APBDEV_PMC_IO_DPD_REQ, PMC_REG_BITS_ENUM(IO_DPD_REQ_CODE, DPD_OFF));
|
|
|
|
reg::ReadWrite(PMC + APBDEV_PMC_IO_DPD2_REQ, PMC_REG_BITS_ENUM(IO_DPD2_REQ_CODE, DPD_OFF));
|
|
|
|
|
|
|
|
/* Configure LCD pinmux tristate + passthrough. */
|
|
|
|
reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_NFC_EN, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE)));
|
|
|
|
reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_NFC_INT, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE)));
|
|
|
|
reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_LCD_BL_PWM, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE)));
|
|
|
|
reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_LCD_BL_EN, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE)));
|
|
|
|
reg::ClearBits(g_apb_misc_regs + PINMUX_AUX_LCD_RST, reg::EncodeMask(PINMUX_REG_BITS_MASK(AUX_TRISTATE)));
|
|
|
|
|
|
|
|
if (hw_type == fuse::HardwareType_Five) {
|
|
|
|
/* Configure LCD backlight. */
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x4);
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT6_OE_1, 0x4);
|
|
|
|
} else {
|
|
|
|
/* Configure LCD power, VDD. */
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT3_CNF_0, 0x3);
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT3_OE_0, 0x3);
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x1);
|
|
|
|
util::WaitMicroSeconds(10'000ul);
|
|
|
|
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT3_OUT_0, 0x2);
|
|
|
|
util::WaitMicroSeconds(10'000ul);
|
|
|
|
|
|
|
|
/* Configure LCD backlight. */
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT6_CNF_1, 0x7);
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT6_OE_1, 0x7);
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Configure display interface and display. */
|
|
|
|
reg::Write(g_mipi_cal_regs + MIPI_CAL_MIPI_BIAS_PAD_CFG2, 0);
|
|
|
|
if (GetSocType() == fuse::SocType_Mariko) {
|
|
|
|
reg::Write(g_mipi_cal_regs + MIPI_CAL_MIPI_BIAS_PAD_CFG0, 0);
|
|
|
|
reg::Write(g_apb_misc_regs + APB_MISC_GP_DSI_PAD_CONTROL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Execute configs. */
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld01);
|
|
|
|
DO_SLEEP_OR_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc01);
|
|
|
|
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init01);
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init02);
|
|
|
|
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init03);
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init04);
|
|
|
|
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init05);
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
|
|
|
|
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init06);
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
|
|
|
|
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init07);
|
|
|
|
util::WaitMicroSeconds(10'000ul);
|
|
|
|
|
|
|
|
/* Enable backlight reset. */
|
|
|
|
reg::SetBits(g_gpio_regs + GPIO_PORT6_OUT_1, 0x4);
|
|
|
|
util::WaitMicroSeconds(60'000ul);
|
|
|
|
|
|
|
|
if (hw_type == fuse::HardwareType_Five) {
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_BTA_TIMING, 0x40103);
|
|
|
|
} else {
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_BTA_TIMING, 0x50204);
|
|
|
|
}
|
|
|
|
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x337);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
WaitDsiTrigger();
|
|
|
|
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x406);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
WaitDsiTrigger();
|
|
|
|
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_HOST_CONTROL, DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_IMM_BTA | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC);
|
|
|
|
WaitDsiHostControl();
|
|
|
|
util::WaitMicroSeconds(5'000ul);
|
|
|
|
|
|
|
|
/* Parse LCD vendor. */
|
|
|
|
{
|
|
|
|
u32 host_response[3];
|
|
|
|
for (size_t i = 0; i < util::size(host_response); i++) {
|
|
|
|
host_response[i] = reg::Read(g_dsi_regs + sizeof(u32) * DSI_RD_DATA);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The last word from host response is:
|
|
|
|
Bits 0-7: FAB
|
|
|
|
Bits 8-15: REV
|
|
|
|
Bits 16-23: Minor REV
|
|
|
|
*/
|
|
|
|
u32 lcd_vendor;
|
|
|
|
if ((host_response[2] & 0xFF) == 0x10) {
|
|
|
|
lcd_vendor = 0;
|
|
|
|
} else {
|
|
|
|
lcd_vendor = (host_response[2] >> 8) & 0xFF00;
|
|
|
|
}
|
|
|
|
lcd_vendor = (lcd_vendor & 0xFFFFFF00) | (host_response[2] & 0xFF);
|
|
|
|
|
|
|
|
AMS_ASSERT(lcd_vendor == GetLcdVendor());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* LCD vendor specific configuration. */
|
|
|
|
switch (GetLcdVendor()) {
|
|
|
|
case 0x10: /* Japan Display Inc screens. */
|
|
|
|
DO_SLEEP_OR_REGISTER_WRITES(g_dsi_regs, DisplayConfigJdiSpecificInit01);
|
|
|
|
break;
|
|
|
|
case 0xF20: /* Innolux first revision screens. */
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
util::WaitMicroSeconds(180'000ul);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
util::WaitMicroSeconds(5'000ul);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x751548B1);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
util::WaitMicroSeconds(5'000ul);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
break;
|
|
|
|
case 0xF30: /* AUO first revision screens. */
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
util::WaitMicroSeconds(180'000ul);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x439);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x9483FFB9);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
util::WaitMicroSeconds(5'000ul);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x739);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x711148B1);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x143209);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
util::WaitMicroSeconds(5'000ul);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
break;
|
|
|
|
case 0x2050: /* Unknown (hardware type 5) screen. */
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
util::WaitMicroSeconds(180'000ul);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0xA015);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x205315);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x339);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x51);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
util::WaitMicroSeconds(5'000ul);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
break;
|
|
|
|
case 0x1020: /* Innolux second revision screen. */
|
|
|
|
case 0x1030: /* AUO second revision screen. */
|
|
|
|
case 0x1040: /* Unknown second revision screen. */
|
|
|
|
default:
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x1105);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
util::WaitMicroSeconds(120'000ul);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_WR_DATA, 0x2905);
|
|
|
|
reg::Write(g_dsi_regs + sizeof(u32) * DSI_TRIGGER, DSI_TRIGGER_HOST);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
util::WaitMicroSeconds(20'000ul);
|
|
|
|
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_clk_rst_regs, DisplayConfigPlld02);
|
|
|
|
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init08);
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsiPhyTiming);
|
|
|
|
DO_SLEEP_OR_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init09);
|
|
|
|
|
|
|
|
reg::Write(g_disp1_regs + sizeof(u32) * DC_DISP_DISP_CLOCK_CONTROL, SHIFT_CLK_DIVIDER(4));
|
|
|
|
DO_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init10);
|
|
|
|
util::WaitMicroSeconds(10'000ul);
|
|
|
|
|
|
|
|
/* Configure MIPI CAL. */
|
|
|
|
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal01);
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal02);
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11);
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03);
|
|
|
|
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04);
|
|
|
|
if (GetSocType() == fuse::SocType_Mariko) {
|
|
|
|
/* On Mariko the above configurations are executed twice, for some reason. */
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal02);
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_dsi_regs, DisplayConfigDsi01Init11);
|
|
|
|
DO_SOC_DEPENDENT_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal03);
|
|
|
|
DO_REGISTER_WRITES(g_mipi_cal_regs, DisplayConfigMipiCal04);
|
|
|
|
}
|
|
|
|
util::WaitMicroSeconds(10'000ul);
|
|
|
|
|
|
|
|
/* Write DISP1, FrameBuffer config. */
|
|
|
|
DO_SLEEP_OR_REGISTER_WRITES(g_disp1_regs, DisplayConfigDc02);
|
|
|
|
DO_SLEEP_OR_REGISTER_WRITES(g_disp1_regs, DisplayConfigFrameBuffer);
|
|
|
|
if (GetLcdVendor() != 0x2050) {
|
|
|
|
util::WaitMicroSeconds(35'000ul);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ShowDisplay(const ams::impl::FatalErrorContext *f_ctx, const Result save_result) {
|
|
|
|
/* Draw the image to the screen. */
|
|
|
|
std::memset(g_frame_buffer, 0, FrameBufferSize);
|
|
|
|
{
|
|
|
|
/* TODO: Actually print the contents of the report. */
|
|
|
|
AMS_UNUSED(f_ctx, save_result);
|
|
|
|
for (size_t n = 0; n < 8; n++) {
|
|
|
|
const size_t x = n * (FrameBufferWidth / 8);
|
|
|
|
for (size_t cur_y = 0; cur_y < FrameBufferHeight; cur_y++) {
|
|
|
|
for (size_t cur_x = 0; cur_x < (FrameBufferWidth / 8); cur_x++) {
|
|
|
|
g_frame_buffer[(FrameBufferWidth - (x + cur_x)) * FrameBufferHeight + 0 + cur_y] = ((0x20 * n) | 0x1F);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hw::FlushDataCache(g_frame_buffer, FrameBufferSize);
|
|
|
|
|
|
|
|
/* Enable backlight. */
|
|
|
|
constexpr auto DisplayBrightness = 100;
|
|
|
|
if (GetLcdVendor() == 0x2050) {
|
|
|
|
EnableBacklightForVendor2050ForHardwareTypeFive(DisplayBrightness);
|
|
|
|
} else {
|
|
|
|
EnableBacklightForGeneric(DisplayBrightness);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-18 21:24:30 +00:00
|
|
|
}
|