Solved How to get I2C work, to read the monitors' EDID information?

I am trying to find out how to read the EDID data of the monitors connected to the computers' video outputs.
I'd rather prefer to dispatch the EDID stuff before starting X, thus I would like to avoid using xrandr to get this data.

Afaik there are two ways to do this: either by using the real mode Vesa VBE BIOS Read EDID function, which is not a very reliable method, or directly by reading the monitor data via I2C.
Code:
% ll /boot/kernel/iic* /boot/kernel/smb*
-r-xr-xr-x  1 root  wheel   17464 Feb  6 17:07 /boot/kernel/iic.ko*
-r-xr-xr-x  1 root  wheel   24240 Feb  6 17:07 /boot/kernel/iicbb.ko*
-r-xr-xr-x  1 root  wheel   47136 Feb  6 17:07 /boot/kernel/iicbus.ko*
-r-xr-xr-x  1 root  wheel   12240 Feb  6 17:07 /boot/kernel/iicmux.ko*
-r-xr-xr-x  1 root  wheel   16216 Feb  6 17:07 /boot/kernel/iicsmb.ko*
-r-xr-xr-x  1 root  wheel   15136 Feb  6 17:07 /boot/kernel/smb.ko*
-r-xr-xr-x  1 root  wheel  225880 Feb  6 17:07 /boot/kernel/smbfs.ko*
-r-xr-xr-x  1 root  wheel   10144 Feb  6 17:07 /boot/kernel/smbios.ko*
-r-xr-xr-x  1 root  wheel   12480 Feb  6 17:07 /boot/kernel/smbus.ko*
%
I kldloaded all these modules, but the sysutils/read-edid get-edid program still says it cannot see any busses that have an EDID, when run as root.
ll /dev/i* does not show anything iic or i2c.

Trying this on a freshly installed FreeBSD-12-RELEASE-p2 on a HP Z400 test machine using a GeForce 8400GS.

What am I doing wrong?
Any idea or advice?
 
They're on different busses. Same protocol, yes. But iic(4) is tied to a controller on the mainboard, typically used for monitoring temperature and more recently a mouse/touchpad. The controller that talks to your monitor using IIC for EDID is on your graphics card. Not sure if you can access that hardware directly, it's probably hidden behind the graphics card controller (which is basically a small computer in its own right) and thus can only be accessed via the card's firmware (aka VBE BIOS).
 
You are right... looking into the source code of that get-edid revealed it used the Linux /dev/i2c... No idea how Linux handles this. Maybe the program only worked on systems with onboard graphics where the VGA shared the system I2C bus, unless Linux somehow finds all I2C buses on the system.

So things are a bit more difficult.
But there is a freedesktop HOWTO... now curious what I can learn from that, when I look at the DDC code.
 
Maybe the program only worked on systems with onboard graphics where the VGA shared the system I2C bus, unless Linux somehow finds all I2C buses on the system.
I very much doubt these two busses are connected. So I suspect Linux has a way to access the other controller. That does suggest there's a way to access that hardware directly instead of having to go through the video card controller.

IIC (or I2C, officially I2C) is a fairly simple protocol that allows a 'master' to talk to various 'slave' devices (I'm sure that nomenclature is going to change someday) using two wires; a data and a clock signal line. It's quite fun to code for with a micro-controller for example, you can fairly easily bit bang it. And there's a plethora of chips you can control using only those two signal lines.
 
I now decided that instead of getting rid of it, I will install Linux on that PC with the bad ACPI/AML code which fails to load FreeBSD (resp.vice versa).
So I can look how this /dev/i2c/... is managed there, and also get an acpidump for a PR.

Maybe it only works for some typical VGA chipsets? I am curious already.
I am also going to buy an old AGP PC to check out the behavior of old graphics cards, as I found a box with almost a dozen 32-128MB AGP cards beneath the trash can.
So there will be plenty of research material :)

In case Linux indeed finds these I2C controllers, then it would probably be interesting to find out how it does this.
In particular, the question would be whether it actually finds all these, as some freedesktop drivers seem to use their own routines, instead of using the "common" I2C routines, apparently just passing an address where to inout the controller.

The author of the sysutils/read-edid also noted that there are cards which only give EDID info through Vesa BIOS extensions, but not by I2C. Sadly he didn't detail which ones, but some Freedesktop drivers do so also. So the I2C method seems not too "compatible".

So it might be easier and less fail-prone to use xrandr, which probably directly asks the drivers, after Xorg is up with the main screen/monitor.
This also would make sure that even monitors with only the old DDC serial protocol would be detected.
 
To be more helpful, I found a post on Stack Overflow that said xrandr --props contains the hex encoded values of the EDID information for the monitor, which makes sense, because the drivers for the cards are more sophisticated in X than a framebuffer, so you'd have this information exposed.

It looks like read-edid in ports uses the same mechanism.
 
xrandr --props
Yes, or even more human-readable: xrandr --verbose

It looks like read-edid in ports uses the same mechanism.
I am not sure about this, as for nvidia cards (x11-drivers/xf86-video-nv) for example, Xorg just calls the real mode Vesa BIOS to get the EDID data.
The method depends on the cards, apparently particularly for older cards.
In the nv driver there are several different methods used for this, depending on their generation.
This makes me doubt that the I2C approach is good.

I guess I will use randr if experiments confirm that my fear is true that I won't find the nvidia I2C on Linux /dev/i2c/, and thus it doesn't help trying to find out how Linux gets this info.

BTW, the Arch Wiki is surprisingly helpful here... it even advises one to wake up sleeping monitors by knocking a few times before asking, to make sure they will answer their EDID data...
 
It's no secret how Linux does it:
Thank you very much for this info!
This basically says that Linux does it only with help from Xorg (which obviously must then be running)
So no need to explore any further into that direction, because...

I suppose you could expose it as a device through the driver by doing a bunch of work but people might ask "why?"
...my intention was to find a way to add the monitors/screens to the Xorg configuration before starting Xorg.
But it is not a real problem, just a cosmetical thing, if the detection and configuration of extra monitors happens after starting Xorg.
The user will it just see as continuing the installation in the graphics mode anyway.
 
Marking this thread as solved, as there is probably no easy way of getting the monitor information without the help from Xorg.

(At least unless some text about the highly esoteric topic of the various I2C controllers (and talking to them) in PCs appears somewhere and happens to be found, which I consider highly unlikely to happen...)
 
The nvidia driver is a thing in its own. Until / unless nVidia (or maybe someone else) updates its driver for FreeBSD to expose I2C hardware in its GPUs, you won't be able to access it.

If you unpack the driver you can find this source file in it: src/nvidia/nvidia_i2c.c
But it's hooked only to Linux build and its code is compatible only with Linux.
Code for FreeBSD is not there.
 
# 221028
I am trying to read EDID from monitor on FreeBSD 13.1-STABLE

Code:
# pciconf -lv | grep -A4 vga
vgapci0@pci0:14:0:0:    class=0x030000 rev=0xd8 hdr=0x00 vendor=0x1002 device=0x1636 subvendor=0x1043 subdevice=0x87e1
    vendor     = 'Advanced Micro Devices, Inc. [AMD/ATI]'
    device     = 'Renoir'
    class      = display
    subclass   = VGA

# dmesg | grep -i drm
[drm] amdgpu kernel modesetting enabled.                                                                                                                                               [34/327]
drmn0: <drmn> on vgapci0
vgapci0: child drmn0 requested pci_enable_io
vgapci0: child drmn0 requested pci_enable_io
[drm] initializing kernel modesetting (RENOIR 0x1002:0x1636 0x1043:0x87E1 0xD8).
drmn0: Trusted Memory Zone (TMZ) feature disabled as experimental (default)
[drm] register mmio base: 0xFCF00000
[drm] register mmio size: 524288
[drm] add ip block number 0 <soc15_common>
[drm] add ip block number 1 <gmc_v9_0>
[drm] add ip block number 2 <vega10_ih>
[drm] add ip block number 3 <psp>
[drm] add ip block number 4 <smu>
[drm] add ip block number 5 <gfx_v9_0>
[drm] add ip block number 6 <sdma_v4_0>
[drm] add ip block number 7 <dm>
[drm] add ip block number 8 <vcn_v2_0>
[drm] add ip block number 9 <jpeg_v2_0>
[drm] BIOS signature incorrect 0 0
drmn0: Fetched VBIOS from ROM BAR
drmn0: successfully loaded firmware image 'amdgpu/renoir_sdma.bin'
[drm] VCN decode is enabled in VM mode
[drm] VCN encode is enabled in VM mode
[drm] JPEG decode is enabled in VM mode
[drm] vm size is 262144 GB, 4 levels, block size is 9-bit, fragment size is 9-bit
drmn0: VRAM: 2048M 0x000000F400000000 - 0x000000F47FFFFFFF (2048M used)
drmn0: GART: 1024M 0x0000000000000000 - 0x000000003FFFFFFF
drmn0: AGP: 267419648M 0x000000F800000000 - 0x0000FFFFFFFFFFFF
[drm] Detected VRAM RAM=2048M, BAR=2048M
[drm] RAM width 128bits DDR4
[drm] amdgpu: 2048M of VRAM memory ready
[drm] amdgpu: 3072M of GTT memory ready.
[drm] GART: num cpu pages 262144, num gpu pages 262144
[drm] PCIE GART of 1024M enabled (table at 0x000000F400900000).
drmn0: successfully loaded firmware image 'amdgpu/renoir_asd.bin'
drmn0: successfully loaded firmware image 'amdgpu/renoir_pfp.bin'
drmn0: successfully loaded firmware image 'amdgpu/renoir_me.bin'
drmn0: successfully loaded firmware image 'amdgpu/renoir_ce.bin'
drmn0: successfully loaded firmware image 'amdgpu/renoir_rlc.bin'
drmn0: successfully loaded firmware image 'amdgpu/renoir_mec.bin'
drmn0: successfully loaded firmware image 'amdgpu/renoir_mec2.bin'
drmn0: successfully loaded firmware image 'amdgpu/renoir_dmcub.bin'
[drm] Loading DMUB firmware via PSP: version=0x01000000
drmn0: successfully loaded firmware image 'amdgpu/renoir_vcn.bin'
[drm] Found VCN firmware Version ENC: 1.7 DEC: 4 VEP: 0 Revision: 17
drmn0: Will use PSP to load VCN firmware
[drm] reserve 0x400000 from 0xf47f800000 for PSP TMR
drmn0: SMU is initialized successfully!
[drm] kiq ring mec 2 pipe 1 q 0
[drm] Display Core initialized with v3.2.104!
[drm] DMUB hardware initialized: version=0x01000000
lkpi_iic0: <LinuxKPI I2C> on drmn0
lkpi_iic1: <LinuxKPI I2C> on drmn0
[drm] VCN decode and encode initialized successfully(under DPG Mode).
[drm] JPEG decode initialized successfully.
drmn0: SE 1, SH per SE 1, CU per SH 8, active_cu_number 8
[drm] fb mappable at 0x50CF3000
[drm] vram apper at 0x50000000
[drm] size 8294400
[drm] fb depth is 24
[drm]    pitch is 7680
name=drmn0 flags=0x0 stride=7680 bpp=32
drmn0: ring gfx uses VM inv eng 0 on hub 0
drmn0: ring comp_1.0.0 uses VM inv eng 1 on hub 0
drmn0: ring comp_1.1.0 uses VM inv eng 4 on hub 0
drmn0: ring comp_1.2.0 uses VM inv eng 5 on hub 0
drmn0: ring comp_1.3.0 uses VM inv eng 6 on hub 0
drmn0: ring comp_1.0.1 uses VM inv eng 7 on hub 0
drmn0: ring comp_1.1.1 uses VM inv eng 8 on hub 0
drmn0: ring comp_1.2.1 uses VM inv eng 9 on hub 0
drmn0: ring comp_1.3.1 uses VM inv eng 10 on hub 0
drmn0: ring kiq_2.1.0 uses VM inv eng 11 on hub 0
drmn0: ring sdma0 uses VM inv eng 0 on hub 1
drmn0: ring vcn_dec uses VM inv eng 1 on hub 1
drmn0: ring vcn_enc0 uses VM inv eng 4 on hub 1
drmn0: ring vcn_enc1 uses VM inv eng 5 on hub 1
drmn0: ring jpeg_dec uses VM inv eng 6 on hub 1
vgapci0: child drmn0 requested pci_get_powerstate
lkpi_iic2: <LinuxKPI I2C> on drm1
[drm] Initialized amdgpu 3.40.0 20150101 for drmn0 on minor 0

# kldstat -v | grep iic
                132 iichb/iicbus
                131 iicbb/iicbus
                419 lkpi_iicbb/iicbb
                418 drm/lkpi_iicbb
                417 drmn/lkpi_iicbb
                416 lkpi_iic/iicbus
                415 drm/lkpi_iic
                414 drmn/lkpi_iic
                522 drmn/acpi_iicbus
                521 drmn/iicbus
10    1 0xffffffff83785000     22b0 iic.ko (/boot/kernel/iic.ko)
                519 iicbus/iic
                543 iichid/hidbus

# ls -lh /dev/iic*
crw-------  1 root  wheel  0x1d5 Oct 29 10:44 /dev/iic0
crw-------  1 root  wheel  0x1d6 Oct 29 10:44 /dev/iic1
crw-------  1 root  wheel  0x1d7 Oct 29 10:44 /dev/iic2

Using command from https://reviews.freebsd.org/D33053 doesn't help
Code:
# i2c -f /dev/iic0 -a 0x28 -c 128 -d r
ioctl: error sending start condition
zsh: exit 1     i2c -f /dev/iic0 -a 0x28 -c 128 -d r

# truss i2c -f /dev/iic0 -a 0x28 -c 128 -d r
...
openat(AT_FDCWD,"/dev/iic0",O_RDWR,00)           = 3 (0x3)
ioctl(3,I2CSTART,0xd968cb65ea0)                  ERR#19 'Operation not supported by device'
ioctl(3,I2CSTOP,0xffffffffffffffff)              = 0 (0x0)
ioctl: error sending start condition
write(2,"ioctl: error sending start condi"...,37) = 37 (0x25)
close(3)                                         = 0 (0x0)
exit(0x1)
process exit, rval = 1

Same for /dev/iic1

i2c scan doesn't find anything
Code:
# i2c -f /dev/iic0 -s
Hardware may not support START/STOP scanning; trying less-reliable read method.
Scanning I2C devices on /dev/iic0: <none found>

# i2c -f /dev/iic1 -s
Hardware may not support START/STOP scanning; trying less-reliable read method.
Scanning I2C devices on /dev/iic1: <none found>

GPU is integrated into CPU:
Code:
CPU: AMD Ryzen 7 PRO 4750G with Radeon Graphics (3600.09-MHz K8-class CPU)

Trying anything with /dev/iic2 results in kernel panic
Code:
grep Panic /var/crash/info.1
  Panic String: device_busy: called for unattached device

Monitor is connected over DP, working, DDC is enabled on the monitor.
 
Back
Top