This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

DS90UB941AS-Q1: i2c transfer error during built-in driver probe on some of our board

Part Number: DS90UB941AS-Q1

Hi,

HW: DS90UB941

Platform: NXP I.MX8MP

Our DS90UB941 (connected with DS90UB948) has a problem.

When Kernel init FPD-LINK driver, if it was on built-in driver, 3 piece of our DS90UB941 got i2c R/W error to DS90UB948(NAK). (3/8)

Those piece didn't encounter this issue when set to module driver.


We have tried several tests: 

  • At the same time i2c R/W retry won't help.
  • While in u-boot, i2c transfer always success.
  • Add defer probe, i2c transfer success once the drm/mipi-dsi/panel init [see append 1]
  • Init using shell script, i2c transfer always success.
  • rmmod/ modprobe i2c transfer always success.

Please note that not all of our piece encounter this issue.

We also found that the i2c waveform is quite weird on i2c R/W error.

The stop bit is not followed by NAK bit.

and the SCL LOW time is uncertain...

And here is the i2c regs of DS90UB941 on different timestamp. [see append 2]


append 1:

[    0.188848] platform 32e80000.lcd-controller: Fixing up cyclic dependency with 32e60000.mipi_dsi
[    2.419937] [drm] Initialized vivante 1.0.0 20170808 for 40000000.mix_gpu_ml on minor 0
[    3.231945] dummy 4-002c: REGMAP_RETRY_WRITE: failed: reg 0x01, value 0x03, retry =1
[    3.309179] ds90ub94x 4-000c: ds90ub94x probe failed with CRC_ERROR_COUNT = 0x01bb

====> first probe, failed


[    4.207681] dummy 4-002c: REGMAP_RETRY_WRITE: failed: reg 0x01, value 0x03, retry =1
[    4.284238] ds90ub94x 4-000c: ds90ub94x probe failed with CRC_ERROR_COUNT = 0x01b2

====> defer probe 1 times, failed


[    4.323765] imx-drm display-subsystem: bound imx-lcdifv3-crtc.0 (ops 0xffff8000093cf378)
[    4.332014] imx-drm display-subsystem: bound imx-lcdifv3-crtc.1 (ops 0xffff8000093cf378)
[    4.340196] imx-drm display-subsystem: bound imx-lcdifv3-crtc.2 (ops 0xffff8000093cf378)
[    4.348563] imx_sec_dsim_drv 32e60000.mipi_dsi: version number is 0x1060200
[    4.356000] panel-simple-dsi 32e60000.mipi_dsi.0: supply power not found, using dummy regulator
[    4.365050] imx-drm display-subsystem: bound 32e60000.mipi_dsi (ops 0xffff8000093bd1f0)
[    4.373174] imx-drm display-subsystem: bound 32fd8000.hdmi (ops 0xffff8000093bc830)
[    4.382007] [drm] Initialized imx-drm 1.0.0 20120507 for display-subsystem on minor 1
[    4.462027] imx-drm display-subsystem: [drm] fb0: imx-drmdrmfb frame buffer device
[    5.017223] ds90ub94x 4-000c: Using Single channel lvds
[    5.023186] ds90ub94x 4-000c: ds90ub94x probe success with CRC_ERROR_COUNT = 0x0008

=====> defer probe 2 times, success


[    6.175793] systemd-sysv-generator[196]: SysV service '/etc/init.d/sendsigs' lacks a native systemd unit file. Automatically generating a unit file for compatibility. Please update package to include a native systemd unit file, in order to make it more safe and robust.
[    7.391969] systemd[1]: Starting Load Kernel Module drm...


Append 2:

============= built-in driver, probe --> failed =============

ds90ub94x 4-000c: >>>> dump reg
>>>> dump_text=      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0e 0f
>>>> dump_text= 00   18 00 00 92 20 00 58 00 00 01 00 00 01 30 00
>>>> dump_text= 10   00 00 01 30 00 00 00 00 00 cb 00 00 fe 1e 7f
>>>> dump_text= 20   00 00 fe 1e 7f 7f 01 00 21 00 01 00 0b 00 25
>>>> dump_text= 30   01 00 0b 00 25 00 00 00 00 00 01 20 20 a0 00
>>>> dump_text= 40   01 20 20 a0 00 00 a5 5a 00 b9 00 05 0c 00 00
>>>> dump_text= 50   00 05 0c 00 00 00 00 00 00 00 00 00 81 02 10
>>>> dump_text= 60   00 00 81 02 10 94 00 00 00 00 00 00 00 00 00
>>>> dump_text= 70   00 00 00 00 00 00 00 00 00 0c 16 00 00 00 02
>>>> dump_text= 80   16 00 00 00 02 00 00 02 00 00 92 00 07 06 44
>>>> dump_text= 90   92 00 07 06 44 00 22 02 00 00 10 00 00 00 00
>>>> dump_text= a0   10 00 00 00 00 00 00 00 00 00 20 00 00 00 00
>>>> dump_text= b0   20 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>>> dump_text= c0   00 00 00 00 00 00 80 00 00 00 00 00 00 00 00
>>>> dump_text= d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>>> dump_text= e0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

============= built-in driver, defer probe 1 --> failed =============

ds90ub94x 4-000c: >>>> dump reg
>>>> dump_text=      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0e 0f
>>>> dump_text= 00   18 00 00 92 20 00 58 00 00 01 00 00 05 30 00
>>>> dump_text= 10   00 00 05 30 00 00 00 00 00 cb 00 00 fe 1e 7f
>>>> dump_text= 20   00 00 fe 1e 7f 7f 01 00 23 00 01 00 0b 00 25
>>>> dump_text= 30   01 00 0b 00 25 00 00 00 00 00 01 20 20 a0 00
>>>> dump_text= 40   01 20 20 a0 00 00 a5 5a 00 b9 00 05 0c 00 00
>>>> dump_text= 50   00 05 0c 00 00 00 00 00 00 00 00 00 81 02 10
>>>> dump_text= 60   00 00 81 02 10 94 00 00 00 00 00 00 00 00 00
>>>> dump_text= 70   00 00 00 00 00 00 00 00 00 0c 16 00 00 00 02
>>>> dump_text= 80   16 00 00 00 02 00 00 02 00 00 08 00 07 06 44
>>>> dump_text= 90   09 00 07 06 44 3b 22 02 00 00 10 00 00 00 00
>>>> dump_text= a0   10 00 00 00 00 00 00 00 00 00 20 00 00 00 00
>>>> dump_text= b0   20 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>>> dump_text= c0   00 00 00 00 00 00 80 00 00 00 00 00 00 00 00
>>>> dump_text= d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>>> dump_text= e0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

============= built-in driver, defer probe 2 --> success =============

ds90ub94x 4-000c: >>>> dump reg
>>>> dump_text=      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0e 0f
>>>> dump_text= 00   18 00 00 92 20 00 58 00 00 01 00 00 05 30 00
>>>> dump_text= 10   00 00 65 30 00 00 00 00 00 cb 00 00 fe 1e 7f
>>>> dump_text= 20   00 00 fe 1e 7f 7f 01 00 27 00 01 00 0b 00 25
>>>> dump_text= 30   01 00 0b 00 25 00 00 00 00 00 01 20 20 a0 00
>>>> dump_text= 40   01 20 20 a0 00 00 a5 5a 00 b9 00 05 0c 00 00
>>>> dump_text= 50   00 05 0c 00 00 00 00 00 00 00 00 00 81 02 10
>>>> dump_text= 60   00 00 81 02 10 94 00 00 00 00 00 00 00 00 00
>>>> dump_text= 70   00 00 00 00 00 00 00 00 00 0c 16 00 00 00 02
>>>> dump_text= 80   16 00 00 00 02 00 00 02 00 00 d9 00 07 06 44
>>>> dump_text= 90   d9 00 07 06 44 44 22 02 00 00 10 00 00 00 00
>>>> dump_text= a0   10 00 00 00 00 00 00 00 00 00 20 00 00 00 00
>>>> dump_text= b0   20 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>>> dump_text= c0   00 00 00 00 00 00 80 00 00 00 00 00 00 00 00
>>>> dump_text= d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>>> dump_text= e0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

============= module driver, probe --> success =============

ds90ub94x 4-000c: >>>> dump reg
>>>> dump_text=      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0e 0f
>>>> dump_text= 00   18 00 00 92 20 00 58 00 00 01 00 00 65 30 00
>>>> dump_text= 10   00 00 65 30 00 00 00 00 00 cb 00 00 fe 1e 7f
>>>> dump_text= 20   00 00 fe 1e 7f 7f 01 00 2f 00 01 00 0b 00 25
>>>> dump_text= 30   01 00 0b 00 25 00 00 00 00 00 01 20 20 a0 00
>>>> dump_text= 40   01 20 20 a0 00 00 a5 5a 00 b9 00 05 0c 00 00
>>>> dump_text= 50   00 05 0c 00 00 00 00 00 00 00 00 00 81 02 10
>>>> dump_text= 60   00 00 81 02 10 94 00 00 00 00 00 00 00 00 00
>>>> dump_text= 70   00 00 00 00 00 00 00 00 00 0c 16 00 00 00 02
>>>> dump_text= 80   16 00 00 00 02 00 00 02 00 00 d9 00 07 06 44
>>>> dump_text= 90   d9 00 07 06 44 44 22 02 00 00 10 00 00 00 00
>>>> dump_text= a0   10 00 00 00 00 00 00 00 00 00 20 00 00 00 00
>>>> dump_text= b0   20 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>>> dump_text= c0   00 00 00 00 00 00 7f 00 00 00 00 00 00 00 00
>>>> dump_text= d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
>>>> dump_text= e0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

  • Hi Joe,

    Thank you for providing the register dumps and preliminary information on your issue. 

    In the reg dumps for the UB941, I saw that the I2C_PASS_THROUGH bit in the GENERAL_CFG (0x3) register was not set. In order to communicate to remote devices, the I2C_PASS_THROUGH bit needs to be enabled.

    Set register 0x3 to 0x9A to enable I2C pass through.

    Best,

    Jack

  • Hi Jack,

    I assume the error comes from the error state of DS90UB941 when powering up.

    So the reg dump above does not have any i2c data written.

    If you need, there is the reg map "after" i2c data setting to DS90UB941.

    ============= built-in driver, probe --> failed =============

    ds90ub94x 4-000c: >>>> dump reg
    >>>> dump_text=      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
    >>>> dump_text= 00   18 00 00 9a 00 00 58 54 54 01 de 00 07 33 33 03
    >>>> dump_text= 01   00 00 00 cb 00 00 fe 1e 7f 7f 01 00 e1 01 01 00
    >>>> dump_text= 02   0b 00 25 00 00 00 00 00 01 20 20 a0 00 00 a5 5a
    >>>> dump_text= 03   00 b9 00 05 0c 00 00 00 00 00 00 00 00 00 81 02
    >>>> dump_text= 04   10 94 00 00 00 00 00 00 00 00 00 00 00 00 00 8c
    >>>> dump_text= 05   16 00 00 00 02 00 00 02 00 00 08 21 07 06 44 1b
    >>>> dump_text= 06   22 02 00 00 10 00 00 00 00 00 00 00 00 00 20 00
    >>>> dump_text= 07   ac 00 00 00 00 00 00 ac 00 00 00 00 00 00 80 00
    >>>> dump_text= 08   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 09   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0a   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0b   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0c   00 00 82 00 58 00 21 44 40 00 00 00 00 02 ff 00
    >>>> dump_text= 0d   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0e   00 00 82 00 48 08 21 44 00 00 00 00 00 02 00 00
    >>>> dump_text= 0f   5f 55 42 39 34 31 00 00 00 00 00 00 00 00 00 00

    ============= built-in driver, defer probe 1 --> failed =============

    ds90ub94x 4-000c: >>>> dump reg
    >>>> dump_text=      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
    >>>> dump_text= 00   18 00 00 9a 00 00 58 54 54 01 99 00 07 33 33 03
    >>>> dump_text= 01   00 00 00 cb 00 00 fe 1e 7f 7f 01 00 e7 01 01 00
    >>>> dump_text= 02   0b 00 25 00 00 00 00 00 01 20 20 a0 00 00 a5 5a
    >>>> dump_text= 03   00 b9 00 05 0c 00 00 00 00 00 00 00 00 00 81 02
    >>>> dump_text= 04   10 94 00 00 00 00 00 00 00 00 00 00 00 00 00 8c
    >>>> dump_text= 05   16 00 00 00 02 00 00 02 00 00 98 21 07 06 44 4e
    >>>> dump_text= 06   22 02 00 00 10 00 00 00 00 00 00 00 00 00 20 00
    >>>> dump_text= 07   ac 00 00 00 00 00 00 ac 00 00 00 00 00 00 80 00
    >>>> dump_text= 08   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 09   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0a   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0b   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0c   00 00 82 00 58 00 21 44 40 00 00 00 00 02 ff 00
    >>>> dump_text= 0d   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0e   00 00 82 00 48 08 21 44 00 00 00 00 00 02 00 00
    >>>> dump_text= 0f   5f 55 42 39 34 31 00 00 00 00 00 00 00 00 00 00

    ============= built-in driver, defer probe 2 --> success =============

    ds90ub94x 4-000c: >>>> dump reg
    >>>> dump_text=      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
    >>>> dump_text= 00   18 00 00 9a 00 00 58 54 54 01 00 00 65 33 33 03
    >>>> dump_text= 01   00 00 00 cb 00 00 fe 1e 7f 7f 01 00 e7 01 01 00
    >>>> dump_text= 02   0b 00 25 00 00 00 00 00 01 20 20 a0 00 00 a5 5a
    >>>> dump_text= 03   00 b9 00 05 0c 00 00 00 00 00 00 00 00 00 81 02
    >>>> dump_text= 04   10 94 00 00 00 00 00 00 00 00 00 00 00 00 00 8c
    >>>> dump_text= 05   16 00 00 00 02 00 00 02 00 00 d9 21 07 06 44 40
    >>>> dump_text= 06   22 02 00 00 10 00 00 00 00 00 00 00 00 00 20 00
    >>>> dump_text= 07   ac 00 00 00 00 00 00 ac 00 00 00 00 00 00 80 00
    >>>> dump_text= 08   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 09   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0a   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0b   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0c   00 00 82 00 78 00 21 44 40 00 00 00 00 02 ff 00
    >>>> dump_text= 0d   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0e   00 00 82 00 68 08 21 00 00 00 00 00 00 02 00 00
    >>>> dump_text= 0f   5f 55 42 39 34 31 00 00 00 00 00 00 00 00 00 00

    ============= module driver, probe --> success =============

    ds90ub94x 4-000c: >>>> dump reg
    >>>> dump_text=      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
    >>>> dump_text= 00   18 00 00 9a 00 00 58 54 54 01 00 00 65 33 33 03
    >>>> dump_text= 01   00 00 00 cb 00 00 fe 1e 7f 7f 01 00 ef 01 01 00
    >>>> dump_text= 02   0b 00 25 00 00 00 00 00 01 20 20 a0 00 00 a5 5a
    >>>> dump_text= 03   00 b9 00 05 0c 00 00 00 00 00 00 00 00 00 81 02
    >>>> dump_text= 04   10 94 00 00 00 00 00 00 00 00 00 00 00 00 00 8c
    >>>> dump_text= 05   16 00 00 00 02 00 00 02 00 00 d9 21 07 06 44 40
    >>>> dump_text= 06   22 02 00 00 10 00 00 00 00 00 00 00 00 00 20 00
    >>>> dump_text= 07   ac 00 00 00 00 00 00 ac 00 00 00 00 00 00 80 00
    >>>> dump_text= 08   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 09   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0a   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0b   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0c   00 00 82 00 78 00 21 44 40 00 00 00 00 02 ff 00
    >>>> dump_text= 0d   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0e   00 00 82 00 68 08 21 00 00 00 00 00 00 02 00 00
    >>>> dump_text= 0f   5f 55 42 39 34 31 00 00 00 00 00 00 00 00 00 00

  • Hi Joe,

    Thank you for providing the reg dumps for the failed and successful cases. We can rule out I2C pass through causing this issue.

    When I compared the registers I noticed that the CRC errors (Register 0xC) were increased on the failed cases and that the DSI frequency was not stable (Register 0x5A) as well. The incoming DSI signal drives the link so it is important for it to be stable during the 941AS-948 operation.

    Are you using custom hardware or TI EVMs? Is the recommended power sequence in section 10.2 followed? There is a minimum requirement of 1µs to allow pixel clock to stabilize before device initialization.

    Best,

    Jack

  • Hi Jack,

    We also notice CRC/ DSI_STS error, but we can't figure out the root cause.

    I dump another piece that does not probe failed:

    (before or after the i2c data written)

    ============= built-in driver, probe (before)--> success ============

    ds90ub94x 4-000c: >>>> dump reg
    >>>> dump_text=      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
    >>>> dump_text= 00   18 00 00 92 20 00 58 00 00 01 00 00 01 30 00 00
    >>>> dump_text= 01   00 00 00 cb 00 00 fe 1e 7f 7f 01 00 e1 01 01 00
    >>>> dump_text= 02   0b 00 25 00 00 00 00 00 01 20 20 a0 00 00 a5 5a
    >>>> dump_text= 03   00 b9 00 05 0c 00 00 00 00 00 00 00 00 00 81 02
    >>>> dump_text= 04   10 94 00 00 00 00 00 00 00 00 00 00 00 00 00 0c
    >>>> dump_text= 05   16 00 00 00 02 00 00 02 00 00 92 00 07 06 44 00
    >>>> dump_text= 06   22 02 00 00 10 00 00 00 00 00 00 00 00 00 20 00
    >>>> dump_text= 07   00 00 00 00 00 00 00 00 00 00 00 00 00 00 7f 00
    >>>> dump_text= 08   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 09   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0a   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0b   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0c   00 00 82 00 78 00 00 40 40 00 00 00 00 02 ff 00
    >>>> dump_text= 0d   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0e   00 00 82 00 68 08 00 00 00 00 00 00 00 02 00 00
    >>>> dump_text= 0f   5f 55 42 39 34 31 00 00 00 00 00 00 00 00 00 00

    ============= built-in driver, probe (after)--> success =============

    ds90ub94x 4-000c: >>>> dump reg
    >>>> dump_text=      00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
    >>>> dump_text= 00   18 00 00 9a 00 00 58 54 54 01 00 00 01 33 33 03
    >>>> dump_text= 01   00 00 00 cb 00 00 fe 1e 7f 7f 01 00 e1 01 01 00
    >>>> dump_text= 02   0b 00 25 00 00 00 00 00 01 20 20 a0 00 00 a5 5a
    >>>> dump_text= 03   00 b9 00 05 0c 00 00 00 00 00 00 00 00 00 81 02
    >>>> dump_text= 04   10 94 00 00 00 00 00 00 00 00 00 00 00 00 00 8c
    >>>> dump_text= 05   16 00 00 00 02 00 00 02 00 00 92 21 07 06 44 00
    >>>> dump_text= 06   22 02 00 00 10 00 00 00 00 00 00 00 00 00 20 00
    >>>> dump_text= 07   ac 00 00 00 00 00 00 ac 00 00 00 00 00 00 7f 00
    >>>> dump_text= 08   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 09   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0a   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0b   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0c   00 00 82 00 78 00 21 40 40 00 00 00 00 02 ff 00
    >>>> dump_text= 0d   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
    >>>> dump_text= 0e   00 00 82 00 68 08 21 00 00 00 00 00 00 02 00 00
    >>>> dump_text= 0f   5f 55 42 39 34 31 00 00 00 00 00 00 00 00 00 00


    And here is my driver, check "ds90ub941_probe_config" and "ds90ub948_probe_config" for all my

    i2c data written to device.

    We use DSI auto switch, thus we don't need to set 0x40, 0x41, 0x42.

    The initial sequence is telling that we need to enable DSI first before we init DS90UB941.

    Is that necessary?

    If does, why does some of piece don't need that?


    pass: 0x92, 0xd9

    failed: 0x08, 0x98

    For my observation, 0x5A can't tell the true error(non of the bits is only set/ unset on on side).

  • sorry, the above comment doesn't have my driver

    see here

    #include <linux/i2c.h>
    #include <linux/module.h>
    #include <linux/of.h>
    #include <linux/delay.h>
    #include <linux/regmap.h>
    
    #define DS90UB941_I2C_ADDR 0x0c
    #define DS90UB948_I2C_ADDR 0x2c
    
    #define DETECT_SER_REGMAP ds90ub941->regmap
    #define DETECT_DES_REGMAP ds90ub941->regmap
    #define GENERAL_STS	0x0c
    #define I2C_DEVICE_ID	0x00
    #define LINK_DETECT	BIT(0)
    
    #define XNOR(x, y)	~(x ^ y)
    
    #define RESET_MDELAY 50
    #define I2C_RW_MDELAY 50
    
    #define RETRY_TIL_ZERO_COUNT 1
    #define REGMAP_RETRY_COUNT 1
    
    #define RETRY_TIL_ZERO( exp )				\
    ({							\
    	int _attemptc_ = RETRY_TIL_ZERO_COUNT;		\
    	int _resultb_;					\
    	while ( _attemptc_-- ){				\
    		_resultb_ = (exp);			\
    		if (( _resultb_ == 0 )) break;		\
    		printk("%s: RETRY_TIL_ZERO = %d times.\n", __FILE__, (RETRY_TIL_ZERO_COUNT - _attemptc_));	\
    		msleep(I2C_RW_MDELAY);			\
    	}						\
    	_resultb_;					\
    })
    
    #define REGMAP_RETRY_READ( ds90ub94x, reg, val, mask)	\
    ({							\
    	int _attemptc_ = REGMAP_RETRY_COUNT;		\
    	int val_read, check_val;			\
    	int _resultb_;					\
    	while ( _attemptc_-- ){				\
    		_resultb_ = 0;				\
    		regmap_read(ds90ub94x->regmap, reg, &val_read);	\
    		dev_dbg(ds90ub94x->dev, "REGMAP_RETRY_READ: reg 0x%02x, value 0x%02x\n", reg, val_read); \
    		check_val = XNOR(val, val_read);	\
    		check_val &= mask;			\
    		if ( mask == check_val ) break;		\
    		dev_err(ds90ub94x->dev, "REGMAP_RETRY_READ: failed: reg 0x%02x, value 0x%02x, mask 0x%02x, retry = %d\n", reg, val, mask, (REGMAP_RETRY_COUNT - _attemptc_)); \
    		_resultb_ = -1;				\
    		msleep(I2C_RW_MDELAY);			\
    	}						\
    	_resultb_;					\
    })
    
    #define REGMAP_RETRY_WRITE( ds90ub94x, reg, val)	\
    ({							\
    	int _attemptc_ = REGMAP_RETRY_COUNT;		\
    	int _resultb_;					\
    	while ( _attemptc_-- ){				\
    		_resultb_ = 0;				\
    		_resultb_ = regmap_write(ds90ub94x->regmap, reg, val);	\
    		dev_dbg(ds90ub94x->dev, "REGMAP_RETRY_WRITE: reg 0x%02x, value 0x%02x\n", reg, val);	\
    		if (( _resultb_ >= 0 )) break;		\
    		dev_err(ds90ub94x->dev, "REGMAP_RETRY_WRITE: failed: reg 0x%02x, value 0x%02x, retry =%d \n", reg, val, (REGMAP_RETRY_COUNT - _attemptc_));	\
    		msleep(I2C_RW_MDELAY);			\
    	}						\
    	_resultb_;					\
    })
    
    static const struct regmap_config config = {
    	.reg_bits = 8,
    	.val_bits = 8,
    	.max_register = 0xFF,
    };
    
    struct ds90ub94x {
    	struct device *dev;
    	struct regmap *regmap;
    	struct i2c_config *reset;
    	struct i2c_config *probe_info;
    	struct i2c_config *dual_channel;
    	int reset_size;
    	int probe_info_size;
    	int dual_channel_size;
    };
    
    struct i2c_config{
    	unsigned char i2c_reg;
    	unsigned char i2c_val;
    	unsigned char check_mask;
    };
    
    struct i2c_config ds90ub941_reset[] = {
    	{0x01, 0x0E, 0x00},
    	{0x04, 0x20, 0x00}, //clear CRC_ERROR count
    };
    
    struct i2c_config ds90ub948_reset[] = {
    	{0x01, 0x03, 0x00},
    };
    
    struct i2c_config ds90ub941_dual_channel[] = {
    	{0x5B, 0x29, 0x08},
    };
    
    struct i2c_config ds90ub948_dual_channel[] = {
    	{0x49, 0x60, 0x02},
    };
    
    struct i2c_config ds90ub941_probe_config[] = {
    	{0x04, 0x00, 0x33}, //start CRC_ERROR count
    	{0x03, 0x9A, 0xFA}, //Enable FPD-Link I2C pass through, DSI clock auto switch
    
    	{0x1E, 0x01, 0x07}, //Select FPD-Link III Port 0
    	{0x5B, 0x21, 0xFF}, //FPD3_TX_MODE=single, Reset PLL
    	{0x4F, 0x8C, 0xFE}, //DSI Continuous Clock Mode,DSI 4 lanes
    	{0x0D, 0x03, 0x0F}, //GPIO0, MIPI_BL_EN
    	{0x0E, 0x33, 0xFF}, //GPIO1, MIPI_VDDEN; GPIO2, MIPI_BL_PWM
    	{0x0F, 0x03, 0x0F}, //Reset touch interrupt
    
    	{0x01, 0x00, 0x0F}, //Release DSI/DIGITLE reset
    
    	{0x07, 0x54, 0xFE}, //SlaveID_0: touch panel, 0x2A << 1
    	{0x08, 0x54, 0xFE}, //SlaveAlias_0: touch panel, 0x2A << 1
    	{0x70, 0xAC, 0xFE}, //SlaveID_1: EEPROM, 0x56 << 1
    	{0x77, 0xAC, 0xFE}, //SlaveAlias_1: EEPROM, 0x56 << 1
    
    	{0xC6, 0x21, 0xFF}, //REM_INTB the same as INTB_IN on UB948, Global Interrupt Enable 
    };
    
    struct i2c_config ds90ub948_probe_config[] = {
    	{0x03, 0xF8, 0xFE}, //enable CRC, i2c pass through
    	{0x49, 0x62, 0xE3}, //Set FPD_TX_MODE, MAPSEL=1(SPWG), Single OLDI output
    	{0x34, 0x01, 0xFB}, //Select FPD-Link III Port 0
    	{0x26, 0x19, 0xFF}, //SCL_HIGH_TIME: 1.5 us (50 ns * 0x19)
    	{0x27, 0x19, 0xFF}, //SCL_LOW_TIME: 1.5 us (50 ns * 0x19)
    	{0x1D, 0x15, 0x0F}, //GPIO0, MIPI_BL_EN
    	{0x1E, 0x55, 0xFF}, //GPIO1, MIPI_VDDEN; GPIO2, MIPI_BL_PWM
    	{0x1F, 0x05, 0x0F}, //Reset touch interrupt
    
    	{0x08, 0x54, 0xFE}, //TargetID_0: touch panel, 0x2A << 1
    	{0x10, 0x54, 0xFE}, //TargetALIAS_0: touch panel, 0x2A << 1
    	{0x09, 0xAC, 0xFE}, //TargetID_1: EEPROM, 0x56 << 1
    	{0x11, 0xAC, 0xFE}, //TargetALIAS_1: EEPROM, 0x56 << 1
    };
    
    static int regmap_i2c_rw_check_retry(struct ds90ub94x *ds90ub94x, struct i2c_config *i2c_config, int i2c_config_size)
    {
    	int i;
    	unsigned int reg, val, mask;
    
    	//continue write
    	for ( i = 0; i < i2c_config_size ; i++ )
    	{
    		reg = i2c_config[i].i2c_reg;
    		val = i2c_config[i].i2c_val;
    
    		if (REGMAP_RETRY_WRITE(ds90ub94x, reg, val) < 0)
    			return -EIO;
    	}
    
    	//continue read check
    	for ( i = 0; i < i2c_config_size ; i++ )
    	{
    		reg = i2c_config[i].i2c_reg;
    		val = i2c_config[i].i2c_val;
    		mask = i2c_config[i].check_mask;
    
    		if (REGMAP_RETRY_READ(ds90ub94x, reg, val, mask) != 0)
    			return -EIO;
    	}
    	return 0;
    }
    
    static int ds90ub94x_init(struct ds90ub94x *ds90ub94x)
    {
    	int i;
    	unsigned char reg, val;
    
    	// reset reg can't read back the same val
    	for ( i = 0 ; i < ds90ub94x->reset_size ; i++ )
    	{
    		reg = ds90ub94x->reset[i].i2c_reg;
    		val = ds90ub94x->reset[i].i2c_val;
    
    		if (REGMAP_RETRY_WRITE(ds90ub94x, reg, val) < 0)
    			return -EIO;
    	}
    
    	msleep(RESET_MDELAY);
    	if ( RETRY_TIL_ZERO(regmap_i2c_rw_check_retry(ds90ub94x, ds90ub94x->probe_info, ds90ub94x->probe_info_size)) != 0 ) {
    		dev_err(ds90ub94x->dev, "ds90ub94x init fail\n");
    		return -EIO;
    	}
    
    	return 0;
    }
    
    static int ds90ub94x_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
    	struct ds90ub94x *ds90ub941, *ds90ub948;
    	struct i2c_client *dummy_client;
    	struct gpio_desc *reset_gpio;
    	int ret_ser, ret_des, i, crc_error_1, crc_error_2, val_read;
    	int ret = 0;
    
    	ds90ub941 = devm_kzalloc(&client->dev, sizeof(struct ds90ub94x), GFP_KERNEL);
    	ds90ub948 = devm_kzalloc(&client->dev, sizeof(struct ds90ub94x), GFP_KERNEL);
    
    	if ( !ds90ub941 || !ds90ub948 ) {
    		ret = -ENOMEM;
    		goto req_failed;
    	}
    
    	i2c_set_clientdata(client, ds90ub941);
    	//add new i2c_device for UB948
    	dummy_client = devm_i2c_new_dummy_device(&client->dev, client->adapter, DS90UB948_I2C_ADDR);
    
    	ds90ub941->dev = &client->dev;
    	ds90ub941->regmap = devm_regmap_init_i2c(client, &config);
    	ds90ub941->reset = ds90ub941_reset;
    	ds90ub941->probe_info = ds90ub941_probe_config;
    	ds90ub941->dual_channel = ds90ub941_dual_channel;
    	ds90ub941->reset_size = ARRAY_SIZE(ds90ub941_reset);
    	ds90ub941->probe_info_size = ARRAY_SIZE(ds90ub941_probe_config);
    	ds90ub941->dual_channel_size = ARRAY_SIZE(ds90ub941_dual_channel);
    
    	ds90ub948->dev = &dummy_client->dev;
    	ds90ub948->regmap = devm_regmap_init_i2c(dummy_client, &config);
    	ds90ub948->reset = ds90ub948_reset;
    	ds90ub948->probe_info = ds90ub948_probe_config;
    	ds90ub948->dual_channel = ds90ub948_dual_channel;
    	ds90ub948->reset_size = ARRAY_SIZE(ds90ub948_reset);
    	ds90ub948->probe_info_size = ARRAY_SIZE(ds90ub948_probe_config);
    	ds90ub948->dual_channel_size = ARRAY_SIZE(ds90ub948_dual_channel);
    
    	if (IS_ERR(ds90ub941->regmap)) {
    		ret = PTR_ERR(ds90ub941->regmap);
    		goto req_failed;
    	}
    	else if (IS_ERR(ds90ub948->regmap)) {
    		ret = PTR_ERR(ds90ub948->regmap);
    		goto req_failed;
    	}
    
    	reset_gpio = devm_gpiod_get_optional(&client->dev, "reset",
    					     GPIOD_OUT_HIGH);
    
    	if (! reset_gpio) {
    		dev_info(ds90ub941->dev,"reset-gpio get failed\n");
    		dev_info(ds90ub941->dev,"not using HW-PDB reset");
    	} else {
    		gpiod_direction_output(reset_gpio, 0);
    		msleep(RESET_MDELAY);
    		gpiod_direction_output(reset_gpio, 1);
    		msleep(RESET_MDELAY);
    		gpiod_direction_output(reset_gpio, 0);
    		msleep(RESET_MDELAY);
    	}
    
    	for ( i = 0; i<=RETRY_TIL_ZERO_COUNT; i++) {
    		if ( i == RETRY_TIL_ZERO_COUNT ) {
    			ret = -EIO;
    			goto err_out;
    		}
    
    
    
    		ret = regmap_read(DETECT_SER_REGMAP, I2C_DEVICE_ID, &val_read);
    		if (!ret)
    			ret_ser = ds90ub94x_init(ds90ub941);
    		else {
    			dev_err(ds90ub941->dev, "failed to find ds90ub941(serializer) ...");
    			ret= -ENXIO;
    			goto connection_failed;
    		}
    
    		msleep(RESET_MDELAY);
    
    		ret = regmap_read(DETECT_DES_REGMAP, GENERAL_STS, &val_read);
    		if (( (val_read & LINK_DETECT ) == LINK_DETECT ))
    			ret_des = ds90ub94x_init(ds90ub948);
    		else {
    			dev_err(ds90ub948->dev, "failed to find ds90ub948(deserializer) ...");
    			ret= -ENXIO;
    			goto connection_failed;
    		}
    
    		if ( (! ret_ser) && (! ret_des) )
    			break;
    
    		if ( ret_ser )
    			dev_err(ds90ub941->dev,"=====> init failed, full_retry = %d times\n", i);
    		if ( ret_des )
    			dev_err(ds90ub948->dev,"=====> init failed, full_retry = %d times\n", i);
    	}
    
    	//read attribute to set dual lvds channel
            if (ds90ub941->dev->of_node) {
                    struct device_node *dev_node = ds90ub941->dev->of_node;
                    if (of_property_read_bool(dev_node, "vizionpanel-dual-lvds-channel")) {
    			dev_info(ds90ub941->dev, "Using Dual channel lvds\n");
    
    			if ( RETRY_TIL_ZERO(regmap_i2c_rw_check_retry(ds90ub941, ds90ub941->dual_channel, ds90ub941->dual_channel_size)) < 0 ) {
    				ret = -EIO;
    				goto err_out;
    			}
    			msleep(I2C_RW_MDELAY);
    			if ( RETRY_TIL_ZERO(regmap_i2c_rw_check_retry(ds90ub948, ds90ub948->dual_channel, ds90ub948->dual_channel_size)) < 0 ) {
    				ret = -EIO;
    				goto err_out;
    			}
                    } else
    			dev_info(ds90ub941->dev, "Using Single channel lvds\n");
            }
    
    	regmap_read(ds90ub941->regmap, 0x0A, &crc_error_1);
    	regmap_read(ds90ub941->regmap, 0x0B, &crc_error_2);
    	dev_info(ds90ub941->dev, "ds90ub94x probe success with CRC_ERROR_COUNT = 0x%02x%02x\n", crc_error_2, crc_error_1);
    	return ret;
    
    err_out:
    	regmap_read(ds90ub941->regmap, 0x0A, &crc_error_1);
    	regmap_read(ds90ub941->regmap, 0x0B, &crc_error_2);
    	dev_err(ds90ub941->dev, "ds90ub94x probe failed with CRC_ERROR_COUNT = 0x%02x%02x\n", crc_error_2, crc_error_1);
    	return -EPROBE_DEFER;
    
    req_failed:
    	dev_err(ds90ub941->dev, "request memery/regmap failed\n");
    	return ret;
    
    connection_failed:
    	dev_err(ds90ub941->dev, "ds90ub94x failed connect to each other\n");
    	return ret;
    }
    
    static int ds90ub94x_remove(struct i2c_client *client)
    {
    	return 0;
    }
    
    static const struct of_device_id ds90ub94x_of_match[] = {
    	{
    		.compatible = "ti,ds90ub94x"
    	},
    	{ /* sentinel */ }
    };
    MODULE_DEVICE_TABLE(of, ds90ub94x_of_match);
    
    static struct i2c_driver ds90ub94x_driver = {
    	.driver = {
    		.name = "ds90ub94x",
    		.of_match_table = ds90ub94x_of_match,
    	},
    	.probe = ds90ub94x_probe,
    	.remove = ds90ub94x_remove,
    };
    module_i2c_driver(ds90ub94x_driver);
    
    MODULE_DESCRIPTION("TI. DS90UB941/DS90UB948 SerDer bridge");
    MODULE_LICENSE("GPL");
    

  • Hi Joe,

    We use DSI auto switch, thus we don't need to set 0x40, 0x41, 0x42.

    Are you running the UB941AS in external REFCLK mode? The UB941AS relies on either the DSI clock or an external reference clock to generate the link between the UB941AS and the 948.

    The initial sequence is telling that we need to enable DSI first before we init DS90UB941.

    Is that necessary?

    It is recommended to enable DSI first before initializing the UB941AS to allow time for stabilization. 

    Those piece didn't encounter this issue when set to module driver.

    What is different with the module driver? Is there a difference in the initialization?

    Best,

    Jack

  • Are you running the UB941AS in external REFCLK mode? The UB941AS relies on either the DSI clock or an external reference clock to generate the link between the UB941AS and the 948.

    Yes, this had been verified and can display screen on DS90UB948.

    It is recommended to enable DSI first before initializing the UB941AS to allow time for stabilization. 

    OK, I'll try to modify the driver probe sequence or defer until ready.

    What is different with the module driver? Is there a difference in the initialization?

    No, Just the init timestamp is different.

    Does the DS90UB941 detect the DSI clock is ready or not?

    I found 0x5A shows nothing if we don't init DS90UB941.(0x00)

    But the power sequence tell me that we should init DS90UB941 later.

    So we must check the DSI clock "OUTSIDE" the DS90UB94x driver, am I right?

  • Hey Joe,

    1. Is this a new application? 
    2. What is the reproduction rate of the issue?
      1. Is this in the vehicle or on the bench (production failure) or early prototype ?
      2. How many units/systems have been produced/manufactured ?
      3. How many units/systems have failed in total ?
      4. How many power cycles were completed to see the failure?
      5. What is the failure mode occurrence - "x" number of failure in how many power-cycles , etc.?
    3. What is the failure mode?
    4. Are you getting errors on video output on the display or you're having flickering or display issue?
    5. Is the issue only with I2C communication? If yes, what type of issue are you seeing (local I2C) or (remote I2C - communication from Ser to Des through forward channel or form display through back channel to Ser) ?

    Here are some answers to your questions:

    If does, why does some of piece don't need that?

    Some parts may have higher tolerance than others, but you must follow the recommended power-up sequence provided in section "10 Power Supply Recommendations" datasheet to have a deterministic power-up on all units across PVT. 

    We use DSI auto switch, thus we don't need to set 0x40, 0x41, 0x42.

    That doesn't follow the device recommendation. Regardless of what DSI mode you use whether auto-switch or other functions are applied, from our device side you need to apply the recommended DSI Clock settings to 941AS at start-up to have a deterministic power-up. Both Sequence A & B require the "Initialize internal DSI clock settings" to be applied at power-up.

    No, Just the init timestamp is different.

    Does the DS90UB941 detect the DSI clock is ready or not?

    I found 0x5A shows nothing if we don't init DS90UB941.(0x00)

    But the power sequence tell me that we should init DS90UB941 later.

    So we must check the DSI clock "OUTSIDE" the DS90UB94x driver, am I right?

    Looking at your good vs bad reg dump I see that you are having back channel CRC error on the bad runs on reg 0xA and 0xB. Back channel CRC error could corrupt I2C functionality for the back channel remote transactions. 

    Potential Root causes for BC CRC Errors: Signal integrity issue with new layout, connector, power-supply noise, power-up sequence, poor capacitor filter network, cable length, cable type, quality of cable, etc.

    One thing you can try is to clear initial CRC errors by setting 0x04[5] = 1, and then 0x04[5] = 0. After that, monitor the system until this issue happens and the read back the number of CRC errors from 0x0A and 0x0B to see if there are any error accumulating by dumping reg 0xA and 0xB multiple times over time. 

    Regards,
    Fadi A.

  • Hi Fadi,

    As I research more detail of bridge driver, I think I can understand that DS90UB941 need DSI to be stable to transfer data to DS90UB948.

    And we can establish on u-boot is because we didn't set 0x4f on it, which close the DSI function.

    So, if we want to find devices on DS90UB948, we can reset 0x4f to do that.

    But once we open 0x4f, we need to make sure the DSI is stable before we talk to DS90UB948.