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"
|
||||
|
||||
#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)
|
||||
#define LED_PORT GPIO_PA
|
||||
#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
|
||||
* 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: ");
|
||||
// check EDID 1.3/1.4 fixed pattern header
|
||||
|
@ -144,6 +145,36 @@ static uint16_t edid_length(uint8_t* edid)
|
|||
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)
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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");
|
||||
|
||||
// read sink EDID
|
||||
if (!i2c_fwd) { // ensure we are the only master on the I²C sink lines
|
||||
// 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 (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
|
||||
puts("reading sink EDID: ");
|
||||
softi2c_master_setup(100); // start the I²C master to talk to sink
|
||||
|
@ -297,13 +324,81 @@ i2c_end:
|
|||
if (i2c_success) {
|
||||
puts("success\r\n");
|
||||
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 {
|
||||
puts("fail\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
while (true) {
|
||||
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 sr3 = I2C_SR3; // clears ADDR after reading SR1
|
||||
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
|
||||
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
|
||||
i2c_transaction_new = true; // notify main loop transaction started
|
||||
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
|
||||
} else { // we will receive data
|
||||
I2C_CR2 |= I2C_CR2_ACK; // ACK next received byte
|
||||
|
|
Loading…
Reference in New Issue