| // Copyright 2017 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 "hi3660-dsi.h" |
| #include "hidisplay-regs.h" |
| #include <hw/reg.h> |
| #include <unistd.h> |
| |
| namespace hi_display { |
| |
| zx_status_t HiDsi::GetDisplayResolution(uint32_t& width, uint32_t& height) { |
| if (std_disp_timing_ == NULL) { |
| zxlogf(ERROR, "Display not ready \n"); |
| return ZX_ERR_NOT_SUPPORTED; |
| } |
| height = static_cast<uint32_t>(std_disp_timing_->HActive); |
| width = static_cast<uint32_t>(std_disp_timing_->VActive); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t HiDsi::DsiGetDisplayTiming() { |
| zx_status_t status; |
| uint8_t num_dtd = 0; |
| |
| if (&edid_buf_[0] == 0) { |
| zxlogf(ERROR, "%s: No EDID available\n", __FUNCTION__); |
| return ZX_ERR_NOT_FOUND; |
| } |
| |
| std_raw_dtd_ = static_cast<DetailedTiming*>(calloc(1, sizeof(DetailedTiming))); |
| std_disp_timing_ = static_cast<DisplayTiming*>(calloc(1, sizeof(DisplayTiming))); |
| if (std_raw_dtd_ == 0 || std_disp_timing_ == 0) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| edid_->EdidParseStdDisplayTiming(edid_buf_, std_raw_dtd_, std_disp_timing_); |
| |
| if ((status = edid_->EdidGetNumDtd(edid_buf_, &num_dtd)) != ZX_OK) { |
| zxlogf(ERROR, "Something went wrong with reading number of DTD\n"); |
| return status; |
| } |
| |
| if (num_dtd == 0) { |
| zxlogf(ERROR, "No DTD Founds!!\n"); |
| return ZX_ERR_INTERNAL; |
| } |
| |
| zxlogf(INFO, "Number of DTD found was %d\n", num_dtd); |
| raw_dtd_ = static_cast<DetailedTiming*>(calloc(num_dtd, sizeof(DetailedTiming))); |
| disp_timing_ = static_cast<DisplayTiming*>(calloc(num_dtd, sizeof(DisplayTiming))); |
| if (raw_dtd_ == 0 || disp_timing_ == 0) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| edid_->EdidParseDisplayTiming(edid_buf_, raw_dtd_, disp_timing_, num_dtd); |
| |
| return ZX_OK; |
| } |
| |
| /* Unknown DPHY. Use hardcoded values from Android source code for now */ |
| void HiDsi::DsiDphyWrite(uint32_t reg, uint32_t val) { |
| |
| dsiimpl_.PhySendCode((reg | DW_DSI_PHY_TST_CTRL1_TESTEN), val); |
| } |
| |
| void HiDsi::DsiConfigureDphyPll() { |
| uint32_t i; |
| uint32_t tmp = 0; |
| |
| // TODO: Calculate proper configuration based on display resolution |
| // Currently hardcoded configuration to support only 1080p |
| if (std_disp_timing_->HActive == 1920 && std_disp_timing_->VActive == 1080) { |
| DsiDphyWrite(0x15, 0x0d); |
| DsiDphyWrite(0x16, 0x21); |
| DsiDphyWrite(0x1e, 0x29); |
| DsiDphyWrite(0x1f, 0x5a); |
| |
| DsiDphyWrite(0x21, 0x30); |
| DsiDphyWrite(0x22, 0x15); |
| DsiDphyWrite(0x23, 0x04); |
| DsiDphyWrite(0x24, 0x1c); |
| |
| for (i = 0; i < DS_NUM_LANES; i++) { |
| tmp = i << 4; |
| DsiDphyWrite(0x30 + tmp, 0x55); |
| DsiDphyWrite(0x32 + tmp, 0x15); |
| DsiDphyWrite(0x33 + tmp, 0x04); |
| DsiDphyWrite(0x34 + tmp, 0x1c); |
| } |
| } else { |
| zxlogf(INFO, "%d x %d resolution not supported\n", |
| std_disp_timing_->HActive, std_disp_timing_->VActive); |
| } |
| } |
| |
| zx_status_t HiDsi::DsiConfigureDphy() { |
| // Configure PHY PLL values |
| DsiConfigureDphyPll(); |
| |
| // Enable PHY |
| dsiimpl_.PhyPowerUp(); |
| |
| // Wait for PHY to be read |
| zx_status_t status; |
| if ((status = dsiimpl_.PhyWaitForReady()) != ZX_OK) { |
| return status; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t HiDsi::DsiHostConfig(const display_setting_t& disp_setting) { |
| constexpr uint32_t kClkLaneLp2Hs = 0x3f; |
| constexpr uint32_t kClkLaneHs2Lp = 0x3a; |
| constexpr uint32_t kDataLaneLp2Hs = 0x68; |
| constexpr uint32_t kDataLaneHs2Lp = 0x13; |
| |
| dsi_config_t dsi_cfg; |
| dsi_cfg.display_setting = disp_setting; |
| dsi_cfg.video_mode_type = VIDEO_MODE_NON_BURST_PULSE; |
| dsi_cfg.color_coding = COLOR_CODE_PACKED_24BIT_888; |
| |
| designware_config_t dw_cfg; |
| dw_cfg.lp_escape_time = 0x9; |
| dw_cfg.lp_cmd_pkt_size = 4; |
| dw_cfg.phy_timer_clkhs_to_lp = kClkLaneHs2Lp; |
| dw_cfg.phy_timer_clklp_to_hs = kClkLaneLp2Hs; |
| dw_cfg.phy_timer_hs_to_lp = kDataLaneHs2Lp; |
| dw_cfg.phy_timer_lp_to_hs = kDataLaneLp2Hs; |
| dw_cfg.auto_clklane = 0; |
| dsi_cfg.vendor_config_buffer = &dw_cfg; |
| dsiimpl_.Config(&dsi_cfg); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t HiDsi::HiDsiGetDisplaySetting(display_setting_t& disp_setting) { |
| uint32_t hsync_start = std_disp_timing_->HActive + std_disp_timing_->HSyncOffset; |
| uint32_t vsync_start = std_disp_timing_->VActive + std_disp_timing_->VSyncOffset; |
| uint32_t hsync_end = hsync_start + std_disp_timing_->HSyncPulseWidth; |
| uint32_t vsync_end = vsync_start + std_disp_timing_->VSyncPulseWidth; |
| uint32_t htotal = std_disp_timing_->HActive + std_disp_timing_->HBlanking; |
| uint32_t vtotal = std_disp_timing_->VActive + std_disp_timing_->VBlanking; |
| |
| disp_setting.h_active = std_disp_timing_->HActive; |
| disp_setting.v_active = std_disp_timing_-> VActive; |
| disp_setting.vsync_bp = vtotal - vsync_end; |
| disp_setting.hsync_bp = htotal - hsync_end; |
| disp_setting.h_period = htotal; |
| disp_setting.v_period = vtotal; |
| disp_setting.hsync_width = std_disp_timing_->HSyncPulseWidth; |
| disp_setting.vsync_width = std_disp_timing_->VSyncPulseWidth; |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t HiDsi::DsiMipiInit() { |
| dsiimpl_.PowerDown(); |
| |
| DsiConfigureDphy(); |
| |
| // Configure the dsi settings and initialize dsiimpl |
| display_setting_t disp_setting; |
| disp_setting.lane_num = DS_NUM_LANES; |
| HiDsiGetDisplaySetting(disp_setting); |
| DsiHostConfig(disp_setting); |
| |
| /* Waking up Core*/ |
| dsiimpl_.PowerUp(); |
| |
| /* Make sure we are in video mode */ |
| dsiimpl_.SetMode(DSI_MODE_VIDEO); |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t HiDsi::DsiInit(zx_device_t* parent) { |
| pdev_protocol_t pdev; |
| zx_status_t status = device_get_protocol(parent, ZX_PROTOCOL_PDEV, &pdev); |
| if (status != ZX_OK) { |
| zxlogf(ERROR, "Failed to obtain the display protocol\n"); |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| dsiimpl_ = parent; |
| if (!dsiimpl_.is_valid()) { |
| zxlogf(ERROR, "DSI Protocol Not implemented\n"); |
| return ZX_ERR_NO_RESOURCES; |
| } |
| |
| DsiGetDisplayTiming(); |
| DsiMipiInit(); |
| #ifdef DW_DSI_TEST_ENABLE |
| dsiimpl_.PrintDsiRegisters(); |
| while (true) dsiimpl_.EnableBist(0); |
| #endif |
| |
| return ZX_OK; |
| } |
| } // namespace hi_display |