save sink EDID
This commit is contained in:
parent
e345b61860
commit
82e5d984bc
138
main.c
138
main.c
|
@ -11,6 +11,7 @@
|
||||||
#include "softi2c_master.h"
|
#include "softi2c_master.h"
|
||||||
|
|
||||||
#define EEPROM_ADDR 0x4000 // EEPROM start address
|
#define EEPROM_ADDR 0x4000 // EEPROM start address
|
||||||
|
static bool eeprom_valid = true; // if the EDID can be read from EEPROM
|
||||||
// LED pin (sink for on)
|
// LED pin (sink for on)
|
||||||
#define LED_PORT GPIO_PA
|
#define LED_PORT GPIO_PA
|
||||||
#define LED_PIN PA3
|
#define LED_PIN PA3
|
||||||
|
@ -102,7 +103,7 @@ structure shall also meet the requirements of CEA-861-D.
|
||||||
* EDID 2.0 with its 256 bytes does not seem to be used in HDMI at all
|
* EDID 2.0 with its 256 bytes does not seem to be used in HDMI at all
|
||||||
* DisplayID with its variable-length structure meant to replace E-EDID only seems to be used in DisplayPort
|
* DisplayID with its variable-length structure meant to replace E-EDID only seems to be used in DisplayPort
|
||||||
*/
|
*/
|
||||||
static uint16_t edid_length(uint8_t* edid)
|
static uint16_t edid_length(const uint8_t* edid)
|
||||||
{
|
{
|
||||||
puts("EDID check: ");
|
puts("EDID check: ");
|
||||||
// check EDID 1.3/1.4 fixed pattern header
|
// check EDID 1.3/1.4 fixed pattern header
|
||||||
|
@ -144,6 +145,36 @@ static uint16_t edid_length(uint8_t* edid)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// modify EDID to indicate firewall
|
||||||
|
static void edid_modify(uint8_t* edid)
|
||||||
|
{
|
||||||
|
// modify EDID to include the character indicating the firewall
|
||||||
|
const char firewall_indicator = '|'; // pipe/wall character to indicate the firewall
|
||||||
|
// ensure descriptor 4 is for Display name
|
||||||
|
if ((0 == edid[108]) && (0 == edid[109]) && (0 == edid[110]) && (0xfc == edid[111]) && (0 == edid[112])) { // ensure descriptor 4 is for Display name
|
||||||
|
uint8_t last_c; // position of last character
|
||||||
|
for (last_c = 113; last_c < 126 && edid[last_c] != '\n'; last_c++); // find position for inserting our character
|
||||||
|
if (firewall_indicator != edid[last_c - 1]) { // the last character is not yet the pipe
|
||||||
|
if (last_c > 125) { // ensure we insert as the last possible character
|
||||||
|
last_c = 125;
|
||||||
|
}
|
||||||
|
edid[last_c++] = firewall_indicator; // insert pipe
|
||||||
|
if (last_c < 126) {
|
||||||
|
edid[last_c++] = '\n'; // insert LF to terminate string
|
||||||
|
}
|
||||||
|
while (last_c < 126) {
|
||||||
|
edid[last_c++] = ' '; // insert padding space
|
||||||
|
}
|
||||||
|
// calculate new checksum
|
||||||
|
uint8_t checksum = 0;
|
||||||
|
for (uint8_t i = 0; i < 127; i++) {
|
||||||
|
checksum += edid[i];
|
||||||
|
}
|
||||||
|
edid[127] = (256 - checksum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void main(void)
|
void main(void)
|
||||||
{
|
{
|
||||||
sim(); // disable interrupts (while we reconfigure them)
|
sim(); // disable interrupts (while we reconfigure them)
|
||||||
|
@ -250,23 +281,19 @@ we will only be able to provide 1 extension block since we can only respond to o
|
||||||
HPD_PORT->ODR.reg |= HPD_PIN; // pull up HPD line to indicate EDID is ready
|
HPD_PORT->ODR.reg |= HPD_PIN; // pull up HPD line to indicate EDID is ready
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if EDID should be copied
|
|
||||||
EDID_PORT->DDR.reg &= ~EDID_PIN; // switch pin to output
|
|
||||||
EDID_PORT->CR1.reg |= EDID_PIN; // enable pull up
|
|
||||||
if (0 == (EDID_PORT->ODR.reg & EDID_PIN)) { // EDID switched on
|
|
||||||
// TODO copy EDID from sink/monitor
|
|
||||||
/*
|
|
||||||
An HDMI Sink shall indicate any change to the contents of the E-EDID by driving a low voltage
|
|
||||||
level pulse on the Hot Plug Detect pin. This pulse shall be at least 100 msec.
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
rim(); // re-enable interrupts
|
rim(); // re-enable interrupts
|
||||||
// even if we don't pull up HPD ourself, we should be able to respond to I²C within 20 ms
|
// even if we don't pull up HPD ourself, we should be able to respond to I²C within 20 ms
|
||||||
puts("I²C ready\r\n");
|
puts("I²C ready\r\n");
|
||||||
|
|
||||||
// read sink EDID
|
// check if EDID should be copied
|
||||||
if (!i2c_fwd) { // ensure we are the only master on the I²C sink lines
|
EDID_PORT->DDR.reg &= ~EDID_PIN; // switch pin to output
|
||||||
|
EDID_PORT->CR1.reg |= EDID_PIN; // enable pull up
|
||||||
|
if (EDID_PORT->IDR.reg & EDID_PIN) { // EDID switched off
|
||||||
|
puts("EDID protected\r\n");
|
||||||
|
} else if (i2c_fwd) { // we are not the only master on the I²C sink lines
|
||||||
|
puts("can't read EDID: I²C lines forwarded\r\n");
|
||||||
|
LED_PORT->ODR.reg &= ~LED_PIN; // switch LED on to indicate error
|
||||||
|
} else {
|
||||||
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||||
puts("reading sink EDID: ");
|
puts("reading sink EDID: ");
|
||||||
softi2c_master_setup(100); // start the I²C master to talk to sink
|
softi2c_master_setup(100); // start the I²C master to talk to sink
|
||||||
|
@ -297,13 +324,81 @@ i2c_end:
|
||||||
if (i2c_success) {
|
if (i2c_success) {
|
||||||
puts("success\r\n");
|
puts("success\r\n");
|
||||||
const uint16_t edid_len = edid_length(edid_sink); // get length
|
const uint16_t edid_len = edid_length(edid_sink); // get length
|
||||||
|
if (edid_len) { // EDID is valid
|
||||||
|
edid_modify(edid_sink); // modify EDID to include firewall indication
|
||||||
|
// compare saved/source and sink EDID
|
||||||
|
bool edid_equal = true;
|
||||||
|
for (uint16_t i = 0; i < edid_len && edid_equal; i++) {
|
||||||
|
if (*(uint8_t*)(EEPROM_ADDR + i) != edid_sink[i]) {
|
||||||
|
edid_equal = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (edid_equal) {
|
||||||
|
puts("EDID not changed\r\n");
|
||||||
|
} else {
|
||||||
|
LED_PORT->ODR.reg &= ~LED_PIN; // switch LED on to indicate we are saving EDID
|
||||||
|
puts("saving EDID: ");
|
||||||
|
// note: the STM8S103 does not support RWW
|
||||||
|
// try to make fast small operations to not stall I²C communication
|
||||||
|
eeprom_valid = false; // invalidate saved EDID while re-programming it
|
||||||
|
// disable DATA (e.g. EEPROM) write protection
|
||||||
|
if (0 == (FLASH_IAPSR & FLASH_IAPSR_DUL)) {
|
||||||
|
FLASH_DUKR = FLASH_DUKR_KEY1;
|
||||||
|
FLASH_DUKR = FLASH_DUKR_KEY2;
|
||||||
|
}
|
||||||
|
// erase EEPROM (we don't do faster block erase since it needs to be done from RAM)
|
||||||
|
for (uint16_t i = 0; i < 256; i += 4U) { // go through word
|
||||||
|
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||||
|
FLASH_CR2 |= FLASH_CR2_WPRG; // set word programming
|
||||||
|
FLASH_NCR2 &= ~FLASH_NCR2_NWPRG; // set word programming
|
||||||
|
*(uint32_t*)(EEPROM_ADDR + i) = 0; // erase word
|
||||||
|
while (FLASH_CR2 & FLASH_CR2_WPRG); // wait for erase to complete
|
||||||
|
// check if programming failed
|
||||||
|
// we don't check for WR_PG_DIS (while checking EOP) because EEPROM isn't (and can't be) write protected
|
||||||
|
if (!(FLASH_IAPSR & FLASH_IAPSR_EOP)) {
|
||||||
|
FLASH_IAPSR &= ~FLASH_IAPSR_DUL; // re-enable write protection
|
||||||
|
puts("failed\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// save need EDID
|
||||||
|
for (uint16_t i = 0; i < edid_len; i += 4U) { // go through word
|
||||||
|
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||||
|
FLASH_CR2 |= FLASH_CR2_WPRG; // set word programming
|
||||||
|
FLASH_NCR2 &= ~FLASH_NCR2_NWPRG; // set word programming
|
||||||
|
*(uint32_t*)(EEPROM_ADDR + i) = *(uint32_t*)(&edid_sink[i]); // write word
|
||||||
|
while (FLASH_CR2 & FLASH_CR2_WPRG); // wait for write to complete
|
||||||
|
// check if programming failed
|
||||||
|
// we don't check for WR_PG_DIS (while checking EOP) because EEPROM isn't (and can't be) write protected
|
||||||
|
if (!(FLASH_IAPSR & FLASH_IAPSR_EOP)) {
|
||||||
|
FLASH_IAPSR &= ~FLASH_IAPSR_DUL; // re-enable write protection
|
||||||
|
puts("failed\r\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FLASH_IAPSR &= ~FLASH_IAPSR_DUL; // re-enable write protection
|
||||||
|
eeprom_valid = true; // re-enable EDID reading
|
||||||
|
LED_PORT->ODR.reg |= LED_PIN; // switch LED off to indicate we are completed saving EDID
|
||||||
|
puts("done\r\n");
|
||||||
|
// indicate there is a new EDID
|
||||||
|
if (!hpd_fwd) { // we have to pull up
|
||||||
|
/*
|
||||||
|
HDMI v1.3 spec:
|
||||||
|
An HDMI Sink shall indicate any change to the contents of the E-EDID by driving a low voltage
|
||||||
|
level pulse on the Hot Plug Detect pin. This pulse shall be at least 100 msec.
|
||||||
|
*/
|
||||||
|
HPD_PORT->ODR.reg &= ~HPD_PIN; // pull down HPD line to indicate EDID change
|
||||||
|
wait_10us(110 * 100); // wait over 100 ms
|
||||||
|
HPD_PORT->ODR.reg |= HPD_PIN; // pull up HPD line to indicate EDID is ready
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
puts("fail\r\n");
|
puts("fail\r\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool action = false; // if an action has been performed
|
bool action = false; // if an action has been performed
|
||||||
LED_PORT->ODR.reg &= ~LED_PIN; // switch LED on to indicate we are ready
|
|
||||||
puts("loop\r\n");
|
puts("loop\r\n");
|
||||||
while (true) {
|
while (true) {
|
||||||
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
IWDG_KR = IWDG_KR_KEY_REFRESH; // reset watchdog
|
||||||
|
@ -337,7 +432,12 @@ void i2c(void) __interrupt(IRQ_I2C) // auto wakeup
|
||||||
const uint8_t sr2 = I2C_SR2;
|
const uint8_t sr2 = I2C_SR2;
|
||||||
const uint8_t sr3 = I2C_SR3; // clears ADDR after reading SR1
|
const uint8_t sr3 = I2C_SR3; // clears ADDR after reading SR1
|
||||||
if (sr1 & I2C_SR1_TXE) { // transmission buffer is empty
|
if (sr1 & I2C_SR1_TXE) { // transmission buffer is empty
|
||||||
I2C_DR = *(uint8_t*)(EEPROM_ADDR + addr++); // transmit next byte
|
// transmit next byte
|
||||||
|
if (eeprom_valid) {
|
||||||
|
I2C_DR = *(uint8_t*)(EEPROM_ADDR + addr++);
|
||||||
|
} else {
|
||||||
|
I2C_DR = 0xff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (sr1 & I2C_SR1_RXNE) { // receive buffer is full
|
if (sr1 & I2C_SR1_RXNE) { // receive buffer is full
|
||||||
const uint8_t data = I2C_DR; // read data (also clears flag)
|
const uint8_t data = I2C_DR; // read data (also clears flag)
|
||||||
|
@ -352,7 +452,11 @@ void i2c(void) __interrupt(IRQ_I2C) // auto wakeup
|
||||||
if (sr1 & I2C_SR1_ADDR) { // our slave address has been selected
|
if (sr1 & I2C_SR1_ADDR) { // our slave address has been selected
|
||||||
i2c_transaction_new = true; // notify main loop transaction started
|
i2c_transaction_new = true; // notify main loop transaction started
|
||||||
if (sr3 & I2C_SR3_TRA) { // start data transmission
|
if (sr3 & I2C_SR3_TRA) { // start data transmission
|
||||||
I2C_DR = *(uint8_t*)(EEPROM_ADDR + addr++); // transmit selected byte
|
if (eeprom_valid) {
|
||||||
|
I2C_DR = *(uint8_t*)(EEPROM_ADDR + addr++); // transmit selected byte
|
||||||
|
} else {
|
||||||
|
I2C_DR = 0xff;
|
||||||
|
}
|
||||||
i2c_input_new = false; // notify we send data
|
i2c_input_new = false; // notify we send data
|
||||||
} else { // we will receive data
|
} else { // we will receive data
|
||||||
I2C_CR2 |= I2C_CR2_ACK; // ACK next received byte
|
I2C_CR2 |= I2C_CR2_ACK; // ACK next received byte
|
||||||
|
|
Loading…
Reference in New Issue