blob: 8db2c793a8980c4d7193af2b0c62cde6be27c697 [file] [log] [blame]
// 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 "adv7533.h"
#include "common.h"
#include "edid.h"
#include "hidisplay-regs.h"
#include "hidisplay.h"
#include <ddk/protocol/i2c-lib.h>
#include <lib/edid/edid.h>
namespace hi_display {
uint8_t edid_buf_[256];
constexpr uint32_t kAdvDelay = 1000;
constexpr uint32_t kAdvTimeout = 100000;
void Adv7533::HdmiGpioInit() {
gpio_config_out(&gpios[GPIO_MUX], 0);
gpio_config_out(&gpios[GPIO_PD], 0);
gpio_config_in(&gpios[GPIO_INT], GPIO_NO_PULL);
gpio_write(&gpios[GPIO_MUX], 0);
}
/* Helper functions for reading/writing to single registers */
void Adv7533::Adv7533MainChannelWrite(uint8_t d1, uint8_t d2) {
write_buf_[0] = d1;
write_buf_[1] = d2;
i2c_write_read_sync(&i2c_dev.i2c_main, write_buf_, 2, NULL, 0);
}
void Adv7533::Adv7533MainChannelRead(uint8_t d1, uint8_t len) {
write_buf_[0] = d1;
i2c_write_read_sync(&i2c_dev.i2c_main, write_buf_, 1, write_buf_, len);
}
void Adv7533::Adv7533CecChannelWrite(uint8_t d1, uint8_t d2) {
write_buf_[0] = d1;
write_buf_[1] = d2;
i2c_write_read_sync(&i2c_dev.i2c_cec, write_buf_, 2, NULL, 0);
}
void Adv7533::Adv7533EdidChannelRead(uint8_t d1, uint8_t len) {
write_buf_[0] = d1;
i2c_write_read_sync(&i2c_dev.i2c_edid, write_buf_, 1, write_buf_, len);
}
zx_status_t Adv7533::Adv7533Init(pdev_protocol_t* pdev_) {
uint8_t g = 0;
size_t actual;
zx_status_t status;
if ((status = pdev_get_protocol(pdev_, ZX_PROTOCOL_I2C, 0, &i2c_dev.i2c_main,
sizeof(i2c_dev.i2c_main), &actual)) != ZX_OK) {
zxlogf(ERROR, "%s: Could not obtain I2C Protocol\n", __FUNCTION__);
return status;
}
if ((status = pdev_get_protocol(pdev_, ZX_PROTOCOL_I2C, 1, &i2c_dev.i2c_cec,
sizeof(i2c_dev.i2c_cec), &actual)) != ZX_OK) {
zxlogf(ERROR, "%s: Could not obtain I2C Protocol\n", __FUNCTION__);
return status;
}
if ((status = pdev_get_protocol(pdev_, ZX_PROTOCOL_I2C, 2, &i2c_dev.i2c_edid,
sizeof(i2c_dev.i2c_edid), &actual)) != ZX_OK) {
zxlogf(ERROR, "%s: Could not obtain I2C Protocol\n", __FUNCTION__);
return status;
}
/* Obtain the GPIO devices */
for (uint32_t i = 0; i < countof(gpios); i++) {
if ((status = pdev_get_protocol(pdev_, ZX_PROTOCOL_GPIO, i, &gpios[i],
sizeof(gpios[i]), &actual)) != ZX_OK) {
zxlogf(ERROR, "%s: Could not obtain GPIO Protocol\n", __FUNCTION__);
return status;
}
}
HdmiGpioInit();
Adv7533MainChannelRead(ADV7533_REG_CHIP_REVISION, 1);
zxlogf(INFO, "%s: HDMI Ver 0x%x\n", __FUNCTION__, write_buf_[0]);
/* Write ADV7533 fixed register values */
for (uint32_t i = 0; i < sizeof(kAdv7533FixedRegs); i += 2) {
Adv7533MainChannelWrite(kAdv7533FixedRegs[i], kAdv7533FixedRegs[i + 1]);
}
/* Write EDID I2C Slave Address */
Adv7533MainChannelWrite(ADV7533_REG_EDID_I2C_ADDR, EDID_I2C_ADDR);
/* Write packet i2c address */
Adv7533MainChannelWrite(ADV7533_REG_PACKET_I2C_ADDR, PACKET_I2C_ADDR);
/* Write cec i2c address */
Adv7533MainChannelWrite(ADV7533_REG_CEC_I2C_ADDR, CEC_I2C_ADDR);
/* Disable packet_enable1 */
Adv7533MainChannelWrite(ADV7533_REG_PACKET_ENABLE1, PACKET_ENABLE_DISABLE);
/* Write ADV7533 CEC fixed register values */
for (size_t i = 0; i < sizeof(kAdv7533CecFixedRegs); i += 2) {
Adv7533MainChannelWrite(kAdv7533CecFixedRegs[i], kAdv7533CecFixedRegs[i + 1]);
}
/* ADV7533_REG_CEC_CTRL write 1 */
Adv7533MainChannelWrite(ADV7533_REG_CEC_CTRL, 0x1);
/* Spec doc missing for these magic registers */
Adv7533CecChannelWrite(0x3, 0xb);
Adv7533CecChannelWrite(0x27, 0xb);
Adv7533MainChannelRead(ADV7533_REG_STATUS, 1);
if ((write_buf_[0] & REG_STATUS_HPD_DET) == 0) {
zxlogf(INFO, "%s: No external disp_intf_ detected\n", __FUNCTION__);
return ZX_ERR_IO_NOT_PRESENT;
}
/* Clear HPD INTR */
Adv7533MainChannelWrite(ADV7533_REG_INT0, REG_INT0_HPD);
/* Power up interface */
Adv7533MainChannelWrite(ADV7533_REG_POWER, REG_POWER_PWR_UP);
/* Enable HPD and EDID RDY Interrupt */
Adv7533MainChannelWrite(ADV7533_REG_INT0_ENABLE,
(REG_INT0_ENABLE_HPD | REG_INT0_ENABLE_EDID_RDY));
/* Enable DDC Errors */
Adv7533MainChannelWrite(ADV7533_REG_INT1_ENABLE, REG_INT1_ENABLE_DDC_ERR);
/* Assume HPD is always HIGH (ignore HPD line) */
Adv7533MainChannelWrite(ADV7533_REG_POWER2, REG_POWER2_HPD_ALWAYS_HIGH);
/* Set EDID I2C Slave Address */
Adv7533MainChannelWrite(ADV7533_REG_EDID_I2C_ADDR, EDID_I2C_ADDR);
/* Wait EDID once ready */
int timeout = kAdvTimeout;
gpio_read(&gpios[GPIO_INT], &g);
while (g && timeout--) {
zx_nanosleep(zx_deadline_after(ZX_USEC(kAdvDelay)));
if (timeout <= 0) {
zxlogf(ERROR, "Adv7533 EDID not ready\n");
return ZX_ERR_TIMED_OUT;
}
}
/* Interrupt fired. Let's see if EDID is ready to be read */
Adv7533MainChannelRead(ADV7533_REG_DDC_STATUS, 1);
if (write_buf_[0] != REG_DDC_STATUS_EDID_READY) {
zxlogf(ERROR, "%s: EDID not ready!!!!\n", __FUNCTION__);
Adv7533MainChannelRead(ADV7533_REG_INT0, 2);
zxlogf(ERROR, "%s: INTR REGS: ADV7533_REG_INT0 = 0x%x, ADV7533_REG_INT1 = 0x%x\n",
__FUNCTION__, write_buf_[0], write_buf_[1]);
return ZX_ERR_INTERNAL;
}
// Save EDID value. Read first 128 first
for (int h = 0; h < 128; h += 32) {
Adv7533EdidChannelRead((uint8_t)h, 32);
memcpy(&edid_buf_[h], &write_buf_[0], 32);
}
edid::BaseEdid* EdidBuf = (edid::BaseEdid*)edid_buf_;
if (EdidBuf->num_extensions) {
zxlogf(INFO, "EDID has extension. Continue Reading\n");
for (int h = 128; h < 256; h += 32) {
Adv7533EdidChannelRead((uint8_t)h, 32);
memcpy(&edid_buf_[h], &write_buf_[0], 32);
}
}
/* Power down interface for now */
Adv7533MainChannelWrite(ADV7533_REG_POWER, REG_POWER_PWR_DWN);
/* Enable HDMI Mode */
Adv7533MainChannelWrite(ADV7533_REG_HDCP_HDMI_CFG, REG_HDCP_HDMI_CFG_ENB_HDMI);
HdmiInit();
return ZX_OK;
}
void Adv7533::Adv7533EnableTestMode() {
/* enable internal timing generator */
Adv7533CecChannelWrite(0x27, 0xcb);
Adv7533CecChannelWrite(0x27, 0x8b);
Adv7533CecChannelWrite(0x27, 0xcb);
/* enable hdmi */
Adv7533CecChannelWrite(0x3, 0x89);
/* enable test mode */
Adv7533CecChannelWrite(0x55, 0x80);
Adv7533CecChannelWrite(0x03, 0x89);
Adv7533CecChannelWrite(0xAF, 0x16);
}
void Adv7533::HdmiInit() {
/* Power up the interface */
Adv7533MainChannelWrite(ADV7533_REG_POWER, REG_POWER_PWR_UP);
/* Enable HPD and EDID RDY Interrupt */
Adv7533MainChannelWrite(ADV7533_REG_INT0_ENABLE,
(REG_INT0_ENABLE_HPD | REG_INT0_ENABLE_EDID_RDY));
/* Enable DDC Errors */
Adv7533MainChannelWrite(ADV7533_REG_INT1_ENABLE, REG_INT1_ENABLE_DDC_ERR);
/* Write ADV7533 fixed register values */
for (size_t i = 0; i < sizeof(kAdv7533FixedRegs); i += 2) {
Adv7533MainChannelWrite(kAdv7533FixedRegs[i], kAdv7533FixedRegs[i + 1]);
}
/* Write EDID I2C Slave Address */
Adv7533MainChannelWrite(ADV7533_REG_EDID_I2C_ADDR, EDID_I2C_ADDR);
/* Write packet i2c address */
Adv7533MainChannelWrite(ADV7533_REG_PACKET_I2C_ADDR, PACKET_I2C_ADDR);
/* Write cec i2c address */
Adv7533MainChannelWrite(ADV7533_REG_CEC_I2C_ADDR, CEC_I2C_ADDR);
/* Enable HDMI Mode */
Adv7533MainChannelWrite(ADV7533_REG_HDCP_HDMI_CFG, REG_HDCP_HDMI_CFG_ENB_HDMI);
/* Assume HPD is always HIGH (ignore HPD line) */
Adv7533MainChannelWrite(ADV7533_REG_POWER2, REG_POWER2_HPD_ALWAYS_HIGH);
/* set number of disp_intf_ lanes */
Adv7533CecChannelWrite(0x1c, 0x40);
/* disable internal timing generator */
Adv7533CecChannelWrite(0x27, 0xb);
/* enable hdmi */
Adv7533CecChannelWrite(0x3, 0x89);
/* disable test mode */
Adv7533CecChannelWrite(0x55, 0x0);
#ifdef DW_DSI_TEST_ENABLE
Adv7533EnableTestMode();
#endif
/* Write ADV7533 CEC fixed register values */
for (size_t i = 0; i < sizeof(kAdv7533CecFixedRegs); i += 2) {
Adv7533MainChannelWrite(kAdv7533CecFixedRegs[i], kAdv7533CecFixedRegs[i + 1]);
}
}
} // namespace hi_display