blob: 0184023bdc74589571cd4ca5b14c0a32bd8b7f51 [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 <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/platform-defs.h>
#include <ddk/protocol/platform/device.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <hw/reg.h>
#include <ddk/protocol/i2c.h>
#include <ddk/protocol/i2c-lib.h>
#include <ddk/protocol/gpio.h>
#include "hi-display.h"
#include "adv7533.h"
#include "edid.h"
#define TRACE zxlogf(INFO, "%s %d\n", __FUNCTION__, __LINE__);
static const uint8_t adv7533_fixed_registers[] = {
0x16, 0x20,
0x9a, 0xe0,
0xba, 0x70,
0xde, 0x82,
0xe4, 0x40,
0xe5, 0x80
};
static const uint8_t adv7533_cec_fixed_registers[] = {
0x15, 0xd0 ,
0x17, 0xd0 ,
0x24, 0x20 ,
0x57, 0x11 ,
0x05, 0xc8
};
uint8_t edid_buf[256];
bool edid_complete = false;
uint8_t* adv7533_get_edid_buffer(void) {
if (edid_complete) {
return edid_buf;
}
return 0;
}
/* Helper functions for reading/writing to single registers */
static void adv7533_mainchn_write(display_t* display, uint8_t d1, uint8_t d2) {
display->write_buf[0] = d1;
display->write_buf[1] = d2;
i2c_write_read_sync(&display->i2c_dev.i2c_main, display->write_buf, 2, NULL, 0);
}
static void adv7533_mainchn_read(display_t* display, uint8_t d1, uint8_t len) {
display->write_buf[0] = d1;
i2c_write_read_sync(&display->i2c_dev.i2c_main, display->write_buf, 1, display->write_buf, len);
}
static void adv7533_cecchn_write(display_t* display, uint8_t d1, uint8_t d2) {
display->write_buf[0] = d1;
display->write_buf[1] = d2;
i2c_write_read_sync(&display->i2c_dev.i2c_cec, display->write_buf, 2, NULL, 0);
}
static void adv7533_edidchn_read(display_t* display, uint8_t d1, uint8_t len) {
display->write_buf[0] = d1;
i2c_write_read_sync(&display->i2c_dev.i2c_edid, display->write_buf, 1, display->write_buf, len);
}
zx_status_t adv7533_init(display_t* display) {
gpio_protocol_t* gpios = display->hdmi_gpio.gpios;
adv7533_mainchn_read(display, ADV7533_REG_CHIP_REVISION, 1);
zxlogf(INFO, "%s: HDMI Ver 0x%x\n", __FUNCTION__, display->write_buf[0]);
/* Write ADV7533 fixed register values */
for (size_t i = 0; i < sizeof(adv7533_fixed_registers); i += 2) {
adv7533_mainchn_write(display, adv7533_fixed_registers[i], adv7533_fixed_registers[i + 1]);
}
/* Write EDID I2C Slave Address */
adv7533_mainchn_write(display, ADV7533_REG_EDID_I2C_ADDR, EDID_I2C_ADDR);
/* Write packet i2c address */
adv7533_mainchn_write(display, ADV7533_REG_PACKET_I2C_ADDR, PACKET_I2C_ADDR);
/* Write cec i2c address */
adv7533_mainchn_write(display, ADV7533_REG_CEC_I2C_ADDR, CEC_I2C_ADDR);
/* Disable packet_enable1 */
adv7533_mainchn_write(display, ADV7533_REG_PACKET_ENABLE1, PACKET_ENABLE_DISABLE);
/* Write ADV7533 CEC fixed register values */
for (size_t i = 0; i < sizeof(adv7533_cec_fixed_registers); i += 2) {
adv7533_mainchn_write(display, adv7533_cec_fixed_registers[i],
adv7533_cec_fixed_registers[i + 1]);
}
/* ADV7533_REG_CEC_CTRL write 1 */
adv7533_mainchn_write(display, ADV7533_REG_CEC_CTRL, 0x1);
/* Power off display */
/* Spec doc missing for these magic registers */
adv7533_cecchn_write(display, 0x3, 0xb);
adv7533_cecchn_write(display, 0x27, 0xb);
/* Detect display */
/* TODO: Once GPIO IRQ support is added, we can properly implement hotplug detection */
adv7533_mainchn_read(display, ADV7533_REG_STATUS, 1);
if ( (display->write_buf[0] & REG_STATUS_HPD_DET) == 0) {
zxlogf(INFO, "%s: No external display detected\n", __FUNCTION__);
return ZX_ERR_IO_NOT_PRESENT;
}
/* Clear HPD INTR */
adv7533_mainchn_write(display, ADV7533_REG_INT0, REG_INT0_HPD);
/* Power up interface */
adv7533_mainchn_write(display, ADV7533_REG_POWER, REG_POWER_PWR_UP);
/* Enable HPD and EDID RDY Interrupt */
adv7533_mainchn_write(display, ADV7533_REG_INT0_ENABLE,
(REG_INT0_ENABLE_HPD | REG_INT0_ENABLE_EDID_RDY));
/* Enable DDC Errors */
adv7533_mainchn_write(display, ADV7533_REG_INT1_ENABLE, REG_INT1_ENABLE_DDC_ERR);
/* Assume HPD is always HIGH (ignore HPD line) */
adv7533_mainchn_write(display, ADV7533_REG_POWER2, REG_POWER2_HPD_ALWAYS_HIGH);
/* Set EDID I2C Slave Address */
adv7533_mainchn_write(display, ADV7533_REG_EDID_I2C_ADDR, EDID_I2C_ADDR);
/* Wait EDID once ready */
//TODO: Use GPIO IRQ once it is implemented
uint8_t g = 0;
do {
gpio_read(&gpios[GPIO_INT], &g);
} while(g);
/* Interrupt fired. Let's see if EDID is ready to be read */
adv7533_mainchn_read(display, ADV7533_REG_DDC_STATUS, 1);
if (display->write_buf[0] != REG_DDC_STATUS_EDID_READY) {
zxlogf(ERROR, "%s: EDID not ready!!!!\n", __FUNCTION__);
adv7533_mainchn_read(display, ADV7533_REG_INT0, 2);
zxlogf(ERROR, "%s: INTR REGS: ADV7533_REG_INT0 = 0x%x, ADV7533_REG_INT1 = 0x%x\n",
__FUNCTION__, display->write_buf[0], display->write_buf[1]);
return ZX_ERR_INTERNAL;
}
// Save EDID value. Read first 128 first
for (int h = 0; h < 128; h += 32) {
adv7533_edidchn_read(display, h, 32);
memcpy(&edid_buf[h], &display->write_buf[0], 32);
}
if (edid_has_extension(edid_buf)) {
zxlogf(INFO, "EDID has extension. Continue Reading\n");
for (int h = 128; h < 256; h += 32) {
adv7533_edidchn_read(display, h, 32);
memcpy(&edid_buf[h], &display->write_buf[0], 32);
}
}
edid_complete = true;
/* Power down interface for now */
adv7533_mainchn_write(display, ADV7533_REG_POWER, REG_POWER_PWR_DWN);
/* Enable HDMI Mode */
adv7533_mainchn_write(display, ADV7533_REG_HDCP_HDMI_CFG, REG_HDCP_HDMI_CFG_ENB_HDMI);
return ZX_OK;
}
void hdmi_init(display_t* display) {
/* Power up the interface */
adv7533_mainchn_write(display, ADV7533_REG_POWER, REG_POWER_PWR_UP);
/* Enable HPD and EDID RDY Interrupt */
adv7533_mainchn_write(display, ADV7533_REG_INT0_ENABLE,
(REG_INT0_ENABLE_HPD | REG_INT0_ENABLE_EDID_RDY));
/* Enable DDC Errors */
adv7533_mainchn_write(display, ADV7533_REG_INT1_ENABLE, REG_INT1_ENABLE_DDC_ERR);
/* Write ADV7533 fixed register values */
for (size_t i = 0; i < sizeof(adv7533_fixed_registers); i += 2) {
adv7533_mainchn_write(display, adv7533_fixed_registers[i], adv7533_fixed_registers[i + 1]);
}
/* Write EDID I2C Slave Address */
adv7533_mainchn_write(display, ADV7533_REG_EDID_I2C_ADDR, EDID_I2C_ADDR);
/* Write packet i2c address */
adv7533_mainchn_write(display, ADV7533_REG_PACKET_I2C_ADDR, PACKET_I2C_ADDR);
/* Write cec i2c address */
adv7533_mainchn_write(display, ADV7533_REG_CEC_I2C_ADDR, CEC_I2C_ADDR);
/* Enable HDMI Mode */
adv7533_mainchn_write(display, ADV7533_REG_HDCP_HDMI_CFG, REG_HDCP_HDMI_CFG_ENB_HDMI);
/* Assume HPD is always HIGH (ignore HPD line) */
adv7533_mainchn_write(display, ADV7533_REG_POWER2, REG_POWER2_HPD_ALWAYS_HIGH);
/* set number of display lanes */
adv7533_cecchn_write(display, 0x1c, 0x40);
/* disable internal timing generator */
adv7533_cecchn_write(display, 0x27, 0xb);
/* enable hdmi */
adv7533_cecchn_write(display, 0x3, 0x89);
/* disable test mode */
adv7533_cecchn_write(display, 0x55, 0x0);
/* Write ADV7533 CEC fixed register values */
for (size_t i = 0; i < sizeof(adv7533_cec_fixed_registers); i += 2) {
adv7533_mainchn_write(display, adv7533_cec_fixed_registers[i],
adv7533_cec_fixed_registers[i + 1]);
}
}