Some notes about the HDMI driver, found in the specific 3.18 kernel at drivers/gpu/drm/jz4780. The newer generic driver is found in more recent kernels at drivers/gpu/drm/bridge/synopsys/dw-hdmi.c. Meanwhile, the older HDMI driver functionality can be found in the specific 3.0.8 kernel at drivers/video/jz4780-hdmi.
In dwc_hdmi.h, struct dwc_hdmi retains state for various things:
The probe operation in dwc_hdmi.c obtains device details from the device tree:
Some other operations occur:
The 3.0.8 driver sets the following parameters in hdmi_init, with parameter documentation in core/audioParams.h and core/videoParams.h:
Audio interface type | I2S |
Audio coding type | PCM |
Audio channel allocation | 0 |
Audio packet type (audio sample or high bit rate) | AUDIO_SAMPLE |
Audio sample size | 16 |
Audio sampling frequency | 44100 |
Audio level shift value | 0 |
Audio down mix inhibit flag | 0 |
Audio input clock frequency scaling factor | 64 |
Video input encoding | RGB |
Video output encoding | RGB |
The 3.18 device tree in arch/mips/boot/dts/jz4780.dtsi has a node setting the clock frequency to 27MHz. In the 3.0.8 kernel driver, the probe mechanism in jz4780_hdmi.c sets the same frequency.
The 3.18 device tree in arch/mips/boot/dts/ci20.dts configures an active-high, always-on fixed regulator on GPIO A25.
The 3.0.8 kernel obtains a reference to the vhdmi regulator but does not seem to manipulate it. This regulator is defined in various board definitions in arch/mips/xburst/soc-4780/board.
Power control is actually performed by a separate regulator IC whose 3.0.8 and 3.18 drivers are found in drivers/regulator/act8600-regulator.c.
The power supply to the PHY is set in the dwc_hdmi_phy_enable_power function which sets the PDZ flag in the HDMI_PHY_CONF0 register.
The 3.0.8 driver defines register access primitives in bsp/access.c that employ 16-bit addresses (relative to the peripheral base address) and 8-bit values.
The 3.18 kernel maintains this style of access with the hdmi_readb and hdmi_writeb functions.
More recent kernels maintain this style of access, too. See drivers/gpu/drm/bridge/synopsys/dw-hdmi.c.
From dwc_hdmi_regs.h in 3.18. More recent kernels have a more comprehensive set of definitions in drivers/gpu/drm/bridge/synopsys/dw-hdmi.h.
Register | Offset |
HDMI_DESIGN_ID | 0x0000 |
HDMI_REVISION_ID | 0x0001 |
HDMI_PRODUCT_ID0 | 0x0002 |
HDMI_PRODUCT_ID1 | 0x0003 |
Register | Offset | Description |
HDMI_PHY_CONF0 | 0x3000 | PHY configuration |
HDMI_PHY_I2CM_SLAVE_ADDR | 0x3020 | Device address |
HDMI_PHY_I2CM_ADDRESS_ADDR | 0x3021 | Output data register |
HDMI_PHY_I2CM_DATAO_1_ADDR | 0x3022 | Output data high byte |
HDMI_PHY_I2CM_DATAO_0_ADDR | 0x3023 | Output data low byte |
HDMI_PHY_I2CM_OPERATION_ADDR | 0x3026 | Operation code |
Flag | Bit | Description |
WRITE | 1 | Write data |
READ | 0 | Read data |
Flag | Bit | Description |
PDZ | 7 | Power enable |
ENTMDS | 6 | Enable TMDS |
SPARECTRL | 5 | |
GEN2_PDDQ | 4 | "gen2 pddq" |
GEN2_TXPWRON | 3 | "gen2" transmitter power on |
GEN2_ENHPDRXSENSE | 2 | |
SELDATAENPOL | 1 | "sel" data enable polarity? |
SELDIPIF | 0 | "sel" interface control |
Register | Offset | Description |
HDMI_IH_MUTE | 0x01FF | Top-level interrupt disable |
Flag | Bit | Description |
... | ... | ... |
WAKEUP_INTERRUPT | 1 | Wakeup interrupt disable |
ALL_INTERRUPT | 0 | Disable all interrupts |
Presumably, the ALL_INTERRUPT flag prevents specific interrupt conditions from occurring if set, even if those are enabled individually.
Register | Offset | Description |
HDMI_IH_PHY_STAT0 | 0x0104 | PHY interrupt status |
HDMI_IH_I2CMPHY_STAT0 | 0x0108 | PHY I2C interrupt status |
HDMI_IH_MUTE_PHY_STAT0 | 0x0184 | PHY interrupt disable |
HDMI_IH_MUTE_I2CMPHY_STAT0 | 0x0188 | PHY I2C interrupt disable |
HDMI_PHY_MASK0 | 0x3006 | PHY interrupt mask |
HDMI_PHY_POL0 | 0x3007 | PHY condition polarity? |
Flag | Bit | Description |
... | ... | ... |
RX_SENSE3 | 5 | |
RX_SENSE2 | 4 | |
RX_SENSE1 | 3 | |
RX_SENSE0 | 2 | |
TX_PHY_LOCK | 1 | |
HPD | 0 | Hotplug interrupt status (set to clear) |
Flag | Bit | Description |
... | ... | ... |
I2CMPHYDONE | 1 | Command done (set to clear) |
I2CMPHYERROR | 0 | Command error (set to clear) |
Flag | Bit | Description |
... | ... | ... |
HPD | 0 | Hotplug interrupt (set to disable) |
Flag | Bit | Description |
... | ... | ... |
I2CMPHYDONE | 1 | Command done (set to disable) |
I2CMPHYERROR | 0 | Command error (set to disable) |
Register | Offset | Description |
HDMI_IH_FC_STAT2 | 0x0102 | Overflow interrupt status |
HDMI_IH_MUTE_FC_STAT2 | 0x0182 | Overflow interrupt disable |
HDMI_FC_MASK2 | 0x10DA | Overflow interrupt mask |
FC stands for Frame Composer.
There is a connection employing I2C to the PHY that involves the hdmi_phy_i2c_write function. This uses the HDMI peripheral's own I2C functionality, not the JZ4780 I2C peripherals. (Note that this peripheral can also be used to query the DDC for EDID information.)
The hdmi_phy_wait_i2c_done function is used after each write operation to test whether the transfer was successful by testing the lower two bits of HDMI_IH_I2CMPHY_STAT0 every millisecond until one of the bits is set. The overall timeout in the __hdmi_phy_i2c_write function is one second.
The __hdmi_phy_i2c_write function does the following:
The pixel clock appears to be converted to two quantities: n and cts.
According to the driver, the JZ4780 uses "automatic CTS/N generation" and clears the HDMI_AUD_CTS3_CTS_MANUAL bit of HDMI_AUD_CTS3. According to the newer driver, CTS only needs setting for other SoCs if "internal AHB audio" is used (discovered by testing the HDMI_CONFIG3_AHBAUDDMA bit of HDMI_CONFIG3_ID).
The older driver seems to test pixel clock and sample rate combinations, returning various predefined results, whereas the newer driver calculates it as follows:
cts = (pixel_clock * n) / (128 * sample_rate)
For n, both drivers appear to test pixel clock and sample rate combinations, returning an appropriate value.
According to dwc_hdmi_regs.h, the hardware appears to provide three byte-sized registers together supporting a 20-bit value for n:
Register | Offset | Description |
HDMI_AUD_N1 | 0x3200 | n bits 7..0 |
HDMI_AUD_N2 | 0x3201 | n bits 15..8 |
HDMI_AUD_N3 | 0x3202 | n bits 19..16 |
The different portions of n are presented to these registers, and a field is cleared in the HDMI_AUD_CTS3 register to assert HDMI_AUD_CTS3_N_SHIFT_1, which perhaps causes n to be shifted by 1.
In the 3.18 driver, the HDMI output is enabled when one of the following occurs:
These cause the dwc_hdmi_poweron function to be called which in turn calls dwc_hdmi_setup. This performs a number of operations as described below.
I2C operations involve setting the device to HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2 and performing configuration operations using I2C writes. The following registers are involved:
Address | Register |
0x05 | CKCALCTRL |
0x06 | CPCE_CTRL |
0x09 | CKSYMTXCTRL |
0x0E | VLEVCTRL |
0x10 | CURRCTRL |
0x13 | PLLPHBYCTRL |
0x15 | GMPCTRL |
0x17 | MSM_CTRL |
0x19 | TXTERM |
Address 0x06 (CPCE_CTRL) is written with a value that appears to set various bits according to the selected colour depth/resolution:
Value | Colour Depth |
0x0000 | 8 |
0x2001 | 10 |
0x4002 | 12 |
This value is combined with a value that appears to be connected to the pixel clock frequency.
Value | Frequency |
0x01e0 | <= 45.25MHz |
0x0140 | <= 92.50MHz |
0x00a0 | <= 148.50MHz |
0x0000 | <= 216.00MHz |
For a colour depth of 8, the final value is given as 0x00a0 in the 3.18 and more recent drivers without explanation.
Address 0x15 (GMPCTRL) is written with a value that apparently corresponds to the pixel clock frequency range:
Value | Frequency |
0x0000 | <= 45.25MHz |
0x0005 | <= 92.50MHz |
0x000a | <= 148.50MHz |
0x000f | <= 216.00MHz |
For a colour depth of 8, the final value is given as 0x000a in the 3.18 and more recent drivers without explanation.
Address 0x10 (CURRCTRL) appears to use values related to colour resolution and frequency range in various combinations.