Post Go back to editing

adv7480: failed to detect hdmi signal

Category: Choose a category
Product Number: adv748x, adv7480

Hi,

I am trying to use adv748x linux driver (kernel 5.10.35). On top of the kernel we have modified a  bit to enable configuring tmds as per the recommended settings. This driver was earlier used for adv7482 chip and works fine for the hdmi detection.

I am observing that the regs written are not the same if I try to do readback (reg 5A, 9C), but it is unclear as what these reg means from the hardware manual rev.PrB

It seems to fail to read  hdmi_rx[87]:[2]. As per the "the Poll Reg 0x87[2] in HDMI Rx Map until value read back is 0b1 or timeout occurs. Timeout should occur after at least 100 ms."

Is there any config diff for register compared to ADV7482 and ADV7480?

Thanks,



Incorrect part selected earlier
[edited by: nish at 7:27 AM (GMT -4) on 19 Sep 2022]
  • Hi,

       ADV7480 and ADV7482 are almost have same input and output functionality so there is no difference on the register setting between ADV7480 and ADV7482.

       If Possible, Could you please share your register configuration with us.

    Thanks,

    Poornima

  • Hi Poornima,

    PFA initial regconfig and the function to adv748x_hdmi_init_equalizer (written after referring to the manuals).

    /* Initialize CP Core with RGB888 format. */
    static const struct adv748x_reg_value adv748x_init_hdmi[] = {
    	/* Disable chip powerdown & Enable HDMI Rx block */
    	{ADV748X_PAGE_IO, 0x00, 0x00, 0x40},
    
    	{ADV748X_PAGE_REPEATER, 0x40, 0x00, 0x83},/* Enable HDCP 1.1 */
    
    	{ADV748X_PAGE_HDMI, 0x00, 0x00, 0x08},/* Foreground Channel = A */
    	{ADV748X_PAGE_HDMI, 0x98, 0x00, 0xff},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x99, 0x00, 0xa3},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x9a, 0x00, 0x00},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x9b, 0x00, 0x0a},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x9d, 0x00, 0x40},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0xcb, 0x00, 0x09},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x3d, 0x00, 0x10},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x3e, 0x00, 0x7b},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x3f, 0x00, 0x5e},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x4e, 0x00, 0xfe},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x4f, 0x00, 0x18},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x57, 0x00, 0xa3},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x58, 0x00, 0x04},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x85, 0x00, 0x10},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x89, 0x01, 0x01},/* Enable dynamic HDMI equalizer */
    	{ADV748X_PAGE_HDMI, 0x8a, 0x01, 0xa1},/* Set equalizer frequency range */
    
    	{ADV748X_PAGE_HDMI, 0x83, 0x00, 0x00},/* Enable All Terminations */
    	{ADV748X_PAGE_HDMI, 0xa3, 0x00, 0x01},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0xbe, 0x00, 0x00},/* ADI Required Write */
    
    	{ADV748X_PAGE_HDMI, 0x6c, 0x00, 0x02},/* HPA Manual Enable */
    	{ADV748X_PAGE_HDMI, 0xf8, 0x00, 0x01},/* HPA Asserted */
    	{ADV748X_PAGE_HDMI, 0x0f, 0x00, 0x00},/* Audio Mute Speed Set to Fastest */
    	/* (Smallest Step Size) */
    
    	{ADV748X_PAGE_IO, 0x04, 0x00, 0x02},	/* RGB Out of CP */
    	{ADV748X_PAGE_IO, 0x12, 0x00, 0xf0},	/* CSC Depends on ip Packets, SDR 444 */
    	{ADV748X_PAGE_IO, 0x17, 0x00, 0x80},	/* Luma & Chroma can reach 254d */
    	{ADV748X_PAGE_IO, 0x03, 0x00, 0x86},	/* CP-Insert_AV_Code */
    
    	{ADV748X_PAGE_CP, 0x7c, 0x00, 0x00},	/* ADI Required Write */
    
    	{ADV748X_PAGE_IO, 0x0c, 0x00, 0xe0},	/* Enable LLC_DLL & Double LLC Timing */
    	{ADV748X_PAGE_IO, 0x0e, 0x00, 0xdd},	/* LLC/PIX/SPI PINS TRISTATED AUD */
    
    	{ADV748X_PAGE_EOR, 0xff, 0xff, 0xff}	/* End of register table */
    };
    
    /* Initialize AFE core with YUV8 format. */
    static const struct adv748x_reg_value adv748x_init_afe[] = {
    	{ADV748X_PAGE_IO, 0x00, 0x00, 0x30},	/* Disable chip powerdown Rx */
    	{ADV748X_PAGE_IO, 0xf2, 0x00, 0x01},	/* Enable I2C Read Auto-Increment */
    
    	{ADV748X_PAGE_IO, 0x0e, 0x00, 0xff},	/* LLC/PIX/AUD/SPI PINS TRISTATED */
    	{ADV748X_PAGE_SDP, 0x0f, 0x00, 0x00},	/* Exit Power Down Mode */
    	{ADV748X_PAGE_SDP, 0x52, 0x00, 0xcd},	/* ADI Required Write */
    
    	{ADV748X_PAGE_SDP, 0x0e, 0x00, 0x80},	/* ADI Required Write */
    	{ADV748X_PAGE_SDP, 0x9c, 0x00, 0x00},	/* ADI Required Write */
    	{ADV748X_PAGE_SDP, 0x9c, 0x00, 0xff},	/* ADI Required Write */
    	{ADV748X_PAGE_SDP, 0x0e, 0x00, 0x00},	/* ADI Required Write */
    
    	/* ADI recommended writes for improved video quality */
    	{ADV748X_PAGE_SDP, 0x80, 0x00, 0x51},	/* ADI Required Write */
    	{ADV748X_PAGE_SDP, 0x81, 0x00, 0x51},	/* ADI Required Write */
    	{ADV748X_PAGE_SDP, 0x82, 0x00, 0x68},	/* ADI Required Write */
    
    	{ADV748X_PAGE_SDP, 0x03, 0x00, 0x42},	/* Tri-S Output , PwrDwn 656 pads */
    	{ADV748X_PAGE_SDP, 0x04, 0x00, 0xb5},	/* ITU-R BT.656-4 compatible */
    	{ADV748X_PAGE_SDP, 0x13, 0x00, 0x00},	/* ADI Required Write */
    
    	{ADV748X_PAGE_SDP, 0x17, 0x00, 0x41},	/* Select SH1 */
    	{ADV748X_PAGE_SDP, 0x31, 0x00, 0x12},	/* ADI Required Write */
    	{ADV748X_PAGE_SDP, 0xe6, 0x00, 0x4f},  /* V bit end pos manually in NTSC */
    
    	/* Interrupts */
    	{ADV748X_PAGE_IO, 0x40, 0xc0, 0xc0},	/* intrq_dur_sel */
    	{ADV748X_PAGE_IO, 0x75, 0xa8, 0xa8},	/* tmdspll_lck_a_mb1, hdmi_encrpt_a_mb1, tmds_clk_a_mb1 */
    	{ADV748X_PAGE_IO, 0x75, 0x01, 0x01},	/* de_regen_lck_mb1 */
    	{ADV748X_PAGE_IO, 0x47, 0x01, 0x01},	/* sd_int_mb1 */
    	{ADV748X_PAGE_SDP, 0x0e, 0x00, 0x20},	/* SDP Map 1 */
    	{ADV748X_PAGE_SDP, 0x44, 0x03, 0x03},	/* sd_unlock_mskb, sd_lock_mskb */
    	{ADV748X_PAGE_SDP, 0x4c, 0x00, 0x00},   /* sd_h_lock_chng_mskb, sd_v_lock_chng_mskb */
    	{ADV748X_PAGE_SDP, 0x0e, 0x00, 0x00},	/* ADI Required Write */
    
    	{ADV748X_PAGE_EOR, 0xff, 0xff, 0xff}	/* End of register table */
    };
    
    
    static const struct adv748x_reg_value adv748x_init_hdmi_rx_regs[] = {
    	{ADV748X_PAGE_HDMI, 0x00, 0x00, 0x08},/* Foreground Channel = A */
    	{ADV748X_PAGE_HDMI, 0x98, 0x00, 0xff},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x99, 0x00, 0xa3},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x9a, 0x00, 0x00},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x9b, 0x00, 0x0a},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x9d, 0x00, 0x40},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0xcb, 0x00, 0x09},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x3d, 0x00, 0x10},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x3e, 0x00, 0x7b},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x3f, 0x00, 0x5e},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x4e, 0x00, 0xfe},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x4f, 0x00, 0x18},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x57, 0x00, 0xa3},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x58, 0x00, 0x04},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x85, 0x00, 0x10},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0x83, 0x00, 0x00},/* Enable All Terminations */
    	{ADV748X_PAGE_HDMI, 0xa3, 0x00, 0x01},/* ADI Required Write */
    	{ADV748X_PAGE_HDMI, 0xbe, 0x00, 0x00},/* ADI Required Write */
    
    	{ADV748X_PAGE_EOR, 0xff, 0xff, 0xff}	/* End of register table */
    };
    
    /* The equalizer tuning sequence is described in ADV748x Recommended Settings,
     * page 5.
     */
    static void adv748x_hdmi_init_equalizer(struct adv748x_state *state)
    {
    	int a = 16 * (hdmi_read(state, 0xda) & 0x01);
    	int b = a + (hdmi_read(state, 0xd6) & 0x0f);
    	int c = 160 * b;
    	int init_val = hdmi_read(state, 0x86) & 0x0f;
    	bool config_failed = false;
    
    	adv_dbg(state, "c = %d\n", c);
    
    	if (c < 630) {
    		int reg;
    		int n;
    		const int list1[] = { -1, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14,
    				      15, 28, 29, 30, 31 };
    		const int list2[] = { -1, 2, 3 };
    		const int list3[] = { -1, 13, 13 };
    		int retries = 0;
    
    		/* Enable HDMI dynamic equalizer mode */
    		hdmi_clrset(state, 0x89, 0x01, 0x01);
    
    		/* Manual GCTRL loop from Fig. 2 */
    		hdmi_clrset(state, 0x85, 0x07, 0x06);
    		hdmi_clrset(state, 0x87, 0x80, 0x00);
    		hdmi_clrset(state, 0x9c, 0x80, 0x80);
    		hdmi_clrset(state, 0xcb, 0x01, 0x01);
    
    		/* Start of the loop in Fig 2 */
    		for (n = 1; n <= 16; n++) {
    			hdmi_clrset(state, 0x92, 0x1f, list1[n]);
    			hdmi_clrset(state, 0x5a, 0x80, 0x80);
    			hdmi_clrset(state, 0x9c, 0x40, 0x00);
    			hdmi_clrset(state, 0x9c, 0x40, 0x40);
    
    			while (!(reg = hdmi_read(state, 0x87) & 0x04) &&
    			       retries < 10) {
    				schedule_timeout_interruptible(
    					msecs_to_jiffies(10));
    				retries++;
    			}
    
    			if (retries >= 10) {
    				config_failed = true;
    				break;
    			} else {
    				hdmi_clrset(state, 0x87, 0x60, 0x40);
    				reg = hdmi_read(state, 0x88);
    				if (reg >= 2) {
    					break;
    				}
    			}
    		}
    
    		/* HDMI manual ZCTRL loop from Fig. 3 */
    		if (!config_failed) {
    			int m;
    
    			hdmi_clrset(state, 0x85, 0x07, 0x03);
    			hdmi_clrset(state, 0x87, 0x80, 0x80);
    			hdmi_clrset(state, 0x9c, 0x80, 0x80);
    			hdmi_clrset(state, 0x9c, 0x40, 0x00);
    			hdmi_clrset(state, 0xcb, 0x01, 0x01);
    
    			for (m = 1; m <= 2; m++) {
    				hdmi_write(state, 0x93, list2[m]);
    				hdmi_clrset(state, 0x91, 0x0f, list3[m]);
    				hdmi_clrset(state, 0x86, 0x0f, list3[m]);
    				hdmi_clrset(state, 0x5a, 0x80, 0x80);
    				hdmi_clrset(state, 0x9c, 0x40, 0x00);
    				hdmi_clrset(state, 0x9c, 0x40, 0x40);
    
    				retries = 0;
    				while (!(reg = hdmi_read(state, 0x87) & 0x04) &&
    				       retries < 10) {
    					schedule_timeout_interruptible(
    						msecs_to_jiffies(10));
    					retries++;
    				}
    
    				if (retries >= 10) {
    					config_failed = true;
    					break;
    				} else {
    					hdmi_clrset(state, 0x87, 0x60, 0x40);
    					reg = hdmi_read(state, 0x88);
    					if (reg <= 2)
    						break;
    				}
    			}
    		}
    
    		hdmi_clrset(state, 0x86, 0x0f, init_val);
    
    		/* HDMI GCTRL increase flow from Fig. 5 */
    		if (!config_failed) {
    			n = min(n + 3, 16);
    			hdmi_clrset(state, 0x92, 0x1f, list1[n]);
    			hdmi_clrset(state, 0x5a, 0x80, 0x80);
    		}
    	} else {
    		/* Disable HDMI dynamic equalizer mode */
    		hdmi_clrset(state, 0x89, 0x01, 0x00);
    
    		adv748x_init_hdmi_rx(state);
    	}
    }
    

    Thanks!

  • Hi Poornima,

    Some more updates, It looks like failing to lock the PLL, added some prints as per the recommended settings

    [   13.180625] adv748x 4-0070: Before init equalizer
    [   13.187129] adv748x 4-0070: de_regen_lck_raw|v_locked_raw|tmdspll_lck_a_raw, raw 0x40, exp=83
    [   13.196950] adv748x 4-0070: stdi_dvalid_ch1, raw 0x3f, exp=80
    
    ...
    
    [   13.691989] adv748x 4-0070: read after loop [87] m=04, o=0x0
    [   13.699577] adv748x 4-0070: tmds config failed in GCTRL
    [   13.709468] adv748x 4-0070: After init equalizer
    [   13.717602] adv748x 4-0070: de_regen_lck_raw|v_locked_raw|tmdspll_lck_a_raw, raw 0x40, exp=83
    [   13.730043] adv748x 4-0070: stdi_dvalid_ch1, raw 0x3f, exp=80

    Is there anyway to debug if is is hardware or software issue, any registers to check why PLL locking is not happening, even though the HDMI_CABLE_DET_RAW is detecting the cable.

    And this line is failing hdmi_read(state, 0x87) & 0x04)  where bit6 is not set, not sure what that means?

  • Hi,

    Please ensure with below things for PLL unlock issue,

      1. Ensure that the pad underneath is making good connection to GND.

      2. Ensure the grounding of the ADV7480 whether Gnd-Pads are soldered properly.

      3. Is your PVDD isolated from the other supplies, PVDD is very important for maintaining the video stability.  Ideally it should be ferrite bead isolated from other supplies.

    All power rails can affect the output images and PVDD is most sensitive to noise.
    So at first we need to check PVDD noise around the horizontal rates.  If noise is coupled into PVDD then the PLL might lose lock.

      4. Ensure your PCB layout is correct at HDMI Layout Guideline However, if you are working on a new board, you will need to verify that the ADV7480 is connected correctly to the HDMI connector and that the PCB layout is correct.

      5. PLL locks depend on the incoming video CLK line.  So the CLK line must be stable, without any jitter. 

      Also it seems some of the below ADI required register configuration are not available in your shared script. Please enable these configuration and let us know the result.

      94 DB 13 ; ADI Required Write
      94 D6 07 ; ADI Required Write
      94 C4 0A ; ADI Required Write
      94 71 33 ; ADI Required Write
      94 72 11 ; ADI Required Write

        If those writes are not included,the part would not expect to work reliably (unless those values are default anyway).Those registers are generally internal settings that let us handle process variation which is why they are determined during characterization. 

    Thanks,

    Poornima

  • Hi Poornima,

    In these there is only one diff which is reg value 0xDB -> 0x13. We have these configs enabled as separate API

    /* -----------------------------------------------------------------------------
     * TXA and TXB
     */
    
    static int adv748x_power_up_tx(struct adv748x_csi2 *tx)
    {
            struct adv748x_state *state = tx->state;
            u8 page = is_txa(tx) ? ADV748X_PAGE_TXA : ADV748X_PAGE_TXB;
            int ret = 0;
    
            /* Enable n-lane MIPI */
            adv748x_write_check(state, page, 0x00, 0x80 | tx->active_lanes, &ret);
    
            /* Set Auto DPHY Timing */
            adv748x_write_check(state, page, 0x00, 0xa0 | tx->active_lanes, &ret);
    
            /* ADI Required Write */
            if (tx->src == &state->hdmi.sd) {
                    adv748x_write_check(state, page, 0xdb, 0x10, &ret);
                    adv748x_write_check(state, page, 0xd6, 0x07, &ret);
            } else {
                    adv748x_write_check(state, page, 0xd2, 0x40, &ret);
            }
    
            adv748x_write_check(state, page, 0xc4, 0x0a, &ret);
            adv748x_write_check(state, page, 0x71, 0x33, &ret);
            adv748x_write_check(state, page, 0x72, 0x11, &ret);
    
            /* i2c_dphy_pwdn - 1'b0 */
            adv748x_write_check(state, page, 0xf0, 0x00, &ret);
    
            /* ADI Required Writes*/
            adv748x_write_check(state, page, 0x31, 0x82, &ret);
            adv748x_write_check(state, page, 0x1e, 0x40, &ret);
    
            /* i2c_mipi_pll_en - 1'b1 */
            adv748x_write_check(state, page, 0xda, 0x01, &ret);
            usleep_range(2000, 2500);
    
            /* Power-up CSI-TX */
            adv748x_write_check(state, page, 0x00, 0x20 | tx->active_lanes, &ret);
            usleep_range(1000, 1500);
    
            /* ADI Required Writes */
            adv748x_write_check(state, page, 0xc1, 0x2b, &ret);
            usleep_range(1000, 1500);
            adv748x_write_check(state, page, 0x31, 0x80, &ret);
    
            return ret;
    }
    

    Other than software, will check on the hardware, as it is the new board.

    Thanks!

  • Hi,

     Please check with hardware side and give first preference to ensure your PCB layout HDMI Layout Guideline 

    Thanks,

    Poornima

  • Hi Poornima,

    Yes, we are checking on hardware. Will let you know if we find anything on hardware.

    Thanks!

  • Hi Poornima,

    Some updates form our hardware team. Out team has done some HW mods to reduce AC coupling and connected insulation ground to common ground, and with that it started to work on adv7482(which was also not working earlier on the new hardware)

    ADV7480/2 has TMDS termination power of 3.3V which is provided on our hardware. Internal termination scheme is not well documented by all documents available to us. However, it's likely similar to the following drawing.

    And we want to tweak ADV7480/2's internal termination settings to: 1. Add DC bias voltage. 2. improve signal quality

    So we were thinking of tweaking auto termination,

    And we have following in the reg map

            {ADV748X_PAGE_HDMI, 0x83, 0x00, 0x00},/* Enable All Terminations */

    Thanks!

  • Hi,

     Please look at the HDMI specification to see how HDMI TMDS are terminated. 

     HDMI receivers must provide proper termination on the TMDS lines. Normally this is done internally so you can turn off the termination to save power and indicate to the transmitter the receiver is not set up yet. 

     Usually transmitters will not transmit until they see the correct termination.  Transmitters monitor the dc voltage on the TMDS clock line for something greater then 2V (All TMDS lines when operating properly should be at ~3V with a 300mVpp signal on them) and if it see this then it assumes it is properly terminated and allows you to turn on the transmitter drivers.  Of course you can override this and force it to transmit. 

    As per expert comment the optimal solution would be,

         1) Design for a 50 Ohm board (55 +- 10% can be done easily at most board fabricators)

         2) Use 22-47 series resistors in the control and pixel buses.  If the traces are short enough you may not need the series resistors and control the integrity with the drive settings only.

         3) Do signal integrity analysts using the IBIS files and tweaking the series resistor and drive strength as needed for best signal integrity.

        We are in the High Performance Media group, are designing boards based on 50 Ohm trace impedances.  This better matches design requirements for HDMI, USB, DDRs, SDRAMs and DSPs we use on our boards.  For the pixel bus we generally use 33 Ohms and tweak the output drive strengths to get good signal integrity.

    Note: Normally receivers have on-die termination resistors.  For schematics you can do point to point routing as long as you maintain the 50 Ohms, 100 Ohm differential layout for the TMDS pairs

    Thanks,

    Poornima