Skip to content

Commit e56eeef

Browse files
video: Fix HDMI black screen / frozen boot on DE10-Nano clones (MiSTer-devel#1215) (MiSTer-devel#1217)
* video: bound EDID read and don't ingest garbage on DDC timeout (MiSTer-devel#1213) * video: keep last-good EDID and skip redundant reinit on failed re-read (MiSTer-devel#1215) * i2c: pin ADV7513 EDID/SPD/CEC to the main chip's bus to avoid a wrong-bus hang (MiSTer-devel#1215) --------- Co-authored-by: TheJesusFish <TheJesusFish@users.noreply.github.com>
1 parent 0a1bddb commit e56eeef

4 files changed

Lines changed: 92 additions & 38 deletions

File tree

hdmi_cec.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,8 @@ bool cec_init()
10181018

10191019
cec_deinit();
10201020

1021-
cec_main_fd = i2c_open(ADV7513_MAIN_ADDR, 0);
1021+
int adv_bus = -1;
1022+
cec_main_fd = i2c_open(ADV7513_MAIN_ADDR, 0, -1, &adv_bus);
10221023
if (cec_main_fd < 0)
10231024
{
10241025
return false;
@@ -1030,7 +1031,9 @@ bool cec_init()
10301031
return false;
10311032
}
10321033

1033-
cec_fd = i2c_open(ADV7513_CEC_ADDR, 0);
1034+
// CEC is a sub-map of the same chip: pin it to the main bus rather than
1035+
// rescanning, so a phantom on another bus can't capture the handle.
1036+
cec_fd = i2c_open(ADV7513_CEC_ADDR, 0, adv_bus);
10341037
if (cec_fd < 0)
10351038
{
10361039
cec_deinit();

smbus.cpp

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,10 +209,18 @@ int i2c_smbus_block_process_call(int file, uint8_t command, uint8_t length, uint
209209
return data.block[0];
210210
}
211211

212-
int i2c_open(int dev_address, int is_smbus)
212+
int i2c_open(int dev_address, int is_smbus, int force_bus, int *found_bus)
213213
{
214214
char str[16];
215-
for (int bus = 0; bus < 3; bus++)
215+
// only /dev/i2c-0..2 exist; an out-of-range pin hint means "not found"
216+
if (force_bus > 2)
217+
{
218+
printf("i2c_open: invalid bus %d for device 0x%X\n", force_bus, dev_address);
219+
return -1;
220+
}
221+
int bus_first = (force_bus >= 0) ? force_bus : 0;
222+
int bus_last = (force_bus >= 0) ? force_bus : 2;
223+
for (int bus = bus_first; bus <= bus_last; bus++)
216224
{
217225
int fd;
218226
sprintf(str, "/dev/i2c-%d", bus);
@@ -250,6 +258,8 @@ int i2c_open(int dev_address, int is_smbus)
250258
}
251259

252260
printf("Opened %s for device 0x%X\n", str, dev_address);
261+
// report the matched bus so sibling chip addresses can be pinned to it
262+
if (found_bus) *found_bus = bus;
253263
return fd;
254264
}
255265

smbus.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#ifndef SMBUS_H
44
#define SMBUS_H
55

6-
int i2c_open(int dev_address, int is_smbus);
6+
int i2c_open(int dev_address, int is_smbus, int force_bus = -1, int *found_bus = NULL);
77
void i2c_close(int fd);
88

99
int i2c_smbus_write_quick(int file, uint8_t value);

video.cpp

Lines changed: 74 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,23 +1444,27 @@ static void hdmi_config_init()
14441444

14451445
if (hdmi_main_fd < 0)
14461446
{
1447-
hdmi_main_fd = i2c_open(0x39, 0);
1447+
int adv_bus = -1;
1448+
hdmi_main_fd = i2c_open(0x39, 0, -1, &adv_bus);
14481449
if (hdmi_main_fd < 0)
14491450
{
14501451
printf("ADV7513 not found on i2c bus! HDMI won't be available!\n");
14511452
return;
14521453
}
14531454
else
14541455
{
1456+
// EDID/SPD are sub-maps of the same chip: pin them to the main bus
1457+
// instead of rescanning, or a phantom that ACKs on another bus can
1458+
// capture the handle and wedge the i2c controller on a real transfer.
14551459
if (hdmi_edid_fd < 0)
14561460
{
1457-
hdmi_edid_fd = i2c_open(0x3f, 0);
1461+
hdmi_edid_fd = i2c_open(0x3f, 0, adv_bus);
14581462
if (hdmi_edid_fd < 0) printf("ADV7513: cannot find EDID registers.\n");
14591463
}
14601464

14611465
if (hdmi_spd_fd < 0)
14621466
{
1463-
hdmi_spd_fd = i2c_open(0x38, 0);
1467+
hdmi_spd_fd = i2c_open(0x38, 0, adv_bus);
14641468
if (hdmi_spd_fd < 0) printf("ADV7513: cannot find SPD registers.\n");
14651469
}
14661470
}
@@ -1806,20 +1810,25 @@ static int find_edid_vrr_capability()
18061810

18071811
}
18081812

1809-
static int is_edid_valid()
1813+
static int is_edid_valid_buf(const uint8_t *buf)
18101814
{
18111815
static const uint8_t magic[] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 };
18121816
if (sizeof(edid) < sizeof(magic)) return 0;
1813-
return !memcmp(edid, magic, sizeof(magic));
1817+
return !memcmp(buf, magic, sizeof(magic));
1818+
}
1819+
1820+
static int is_edid_valid()
1821+
{
1822+
return is_edid_valid_buf(edid);
18141823
}
18151824

1816-
static void cache_raw_edid_mfg_id()
1825+
static void cache_raw_edid_mfg_id(const uint8_t *buf)
18171826
{
1818-
raw_edid_mfg_id = (edid[0x08] << 8) | edid[0x09];
1827+
raw_edid_mfg_id = (buf[0x08] << 8) | buf[0x09];
18191828
raw_edid_mfg_id_valid = true;
18201829
}
18211830

1822-
static void read_edid_segment(uint8_t segment, uint8_t *buf)
1831+
static bool read_edid_segment(uint8_t segment, uint8_t *buf)
18231832
{
18241833
i2c_smbus_write_byte_data(hdmi_main_fd, 0xC4, segment);
18251834
i2c_smbus_write_byte_data(hdmi_main_fd, 0xC9, 0x03);
@@ -1829,28 +1838,28 @@ static void read_edid_segment(uint8_t segment, uint8_t *buf)
18291838
unsigned long timeout = GetTimer(500);
18301839
while (!CheckTimer(timeout))
18311840
{
1832-
if (i2c_smbus_read_byte_data(hdmi_main_fd, 0x96) & 4)
1841+
int status = i2c_smbus_read_byte_data(hdmi_main_fd, 0x96);
1842+
if (status >= 0 && (status & 4))
18331843
{
18341844
i2c_smbus_write_byte_data(hdmi_main_fd, 0x96, 4);
1835-
break;
1845+
for (uint16_t i = 0; i < 256; i++)
1846+
{
1847+
int value = i2c_smbus_read_byte_data(hdmi_edid_fd, (uint8_t)i);
1848+
buf[i] = (value < 0) ? 0 : (uint8_t)value;
1849+
}
1850+
return true;
18361851
}
18371852
usleep(10000);
18381853
}
18391854

1840-
for (uint16_t i = 0; i < 256; i++)
1841-
{
1842-
int value = i2c_smbus_read_byte_data(hdmi_edid_fd, (uint8_t)i);
1843-
buf[i] = (value < 0) ? 0 : (uint8_t)value;
1844-
}
1855+
return false;
18451856
}
18461857

18471858
static int read_edid(bool force = false)
18481859
{
18491860
if (hdmi_main_fd < 0 || hdmi_edid_fd < 0) return 0;
18501861
if (is_edid_valid() && !force) return 1;
18511862

1852-
memset(edid, 0, sizeof(edid));
1853-
18541863
//Test if adv7513 senses hdmi clock. If not, don't bother with the edid query
18551864
int hpd_state = i2c_smbus_read_byte_data(hdmi_main_fd, 0x42);
18561865
if (hpd_state < 0 || !(hpd_state & 0x20))
@@ -1859,35 +1868,57 @@ static int read_edid(bool force = false)
18591868
return 0;
18601869
}
18611870

1871+
// read into scratch; replace live edid[] only on a valid read, so a transient failure can't blank it
1872+
uint8_t buf[sizeof(edid)] = {};
1873+
bool ddc_responded = false;
1874+
18621875
// waiting for valid EDID
18631876
for (int k = 0; k < 20; k++)
18641877
{
1865-
read_edid_segment(0, edid);
1866-
if (is_edid_valid()) break;
1878+
int current = i2c_smbus_read_byte_data(hdmi_main_fd, 0x42);
1879+
if (current < 0 || !(current & 0x20))
1880+
{
1881+
raw_edid_mfg_id_valid = false;
1882+
return 0;
1883+
}
1884+
bool got_interrupt = read_edid_segment(0, buf);
1885+
if (got_interrupt) ddc_responded = true;
1886+
if (is_edid_valid_buf(buf)) break;
1887+
if (!got_interrupt) break; // no DDC response — display has no EDID, don't retry
18671888
usleep(100000);
18681889
}
18691890

1870-
if (is_edid_valid())
1891+
if (!is_edid_valid_buf(buf))
18711892
{
1872-
uint8_t max_blocks = sizeof(edid) / 256;
1873-
uint8_t blocks = (2 + edid[126]) / 2; // each block is 128 bytes
1874-
if (blocks > max_blocks) blocks = max_blocks;
1875-
for (uint8_t i = 1; i < blocks; i++) read_edid_segment(i, edid + (i * 256));
1893+
if (ddc_responded)
1894+
{
1895+
// header bad but DDC answered: still cache raw mfg id for non-conformant DAC EDIDs
1896+
cache_raw_edid_mfg_id(buf);
1897+
printf("Invalid EDID: incorrect header.\n");
1898+
}
1899+
else
1900+
{
1901+
printf("Invalid EDID: no DDC response.\n");
1902+
}
1903+
// stored edid[], not buf: only when a prior valid read is being kept
1904+
if (is_edid_valid()) printf("Invalid EDID: keeping last valid EDID.\n");
1905+
1906+
return 0;
18761907
}
18771908

1909+
uint8_t max_blocks = sizeof(edid) / 256;
1910+
uint8_t blocks = (2 + buf[126]) / 2; // each block is 128 bytes
1911+
if (blocks > max_blocks) blocks = max_blocks;
1912+
for (uint8_t i = 1; i < blocks; i++) read_edid_segment(i, buf + (i * 256));
1913+
1914+
memcpy(edid, buf, sizeof(edid));
1915+
18781916
printf("EDID:\n");
18791917
uint8_t n = edid[126] + 1;
18801918
if (n > sizeof(edid) / 128) n = sizeof(edid) / 128;
18811919
hexdump(edid, n*128, 0);
18821920

1883-
cache_raw_edid_mfg_id();
1884-
1885-
if (!is_edid_valid())
1886-
{
1887-
printf("Invalid EDID: incorrect header.\n");
1888-
bzero(edid, sizeof(edid));
1889-
return 0;
1890-
}
1921+
cache_raw_edid_mfg_id(edid);
18911922

18921923
edid_version++;
18931924
return 1;
@@ -2686,10 +2717,20 @@ void video_init()
26862717

26872718
void video_reinit()
26882719
{
2720+
int prev_ver = edid_version;
2721+
read_edid(true);
2722+
2723+
// re-read gave nothing new but a valid EDID is still held: the mode is unchanged,
2724+
// so don't bounce a working link (some clones can't re-lock mid-operation)
2725+
if (edid_version == prev_ver && is_edid_valid())
2726+
{
2727+
printf("*** Video re-init skipped: EDID unchanged.\n");
2728+
return;
2729+
}
2730+
26892731
printf("*** Video re-initialization.\n");
26902732

26912733
hdmi_config_init();
2692-
read_edid(true);
26932734
hdmi_config_set_hdr();
26942735

26952736
support_FHD = 0;

0 commit comments

Comments
 (0)