4k UHD HDMI-out on Android 11?

Which system do you use? Android, Ubuntu, OOWOW or others?

Android 11

Which version of system do you use? Khadas official images, self built images, or others?

self built images based on Khadas Android 11

Please describe your issue below:

  • I would like to have 4k UHD HDMI-out on the Vim4. Currently Vx1 is 4k UHD and HDMI-out is FHD. Spec states on https://www.khadas.com/vim4 state that hardware is capable of “HDMI OUTPUT x1, Type-A Female, HDMI 2.1 up to 4Kp60 HDR Video”.
  • Does the kernel display driver/device tree support 4k?
  • Can resolution auto-detect be enabled? i.e. can the Vim4 switch between FHD and UHD based on the connected display through DDC/EDID info?

Post a console log of your issue below:

None.

@xavier VIM4 itself supports 3840 × 2160@60hz Output. Can you confirm first if there is a problem with the HDMI cable?

I have verified that “VIM4 to Display” data can be 4k/2160p@60Hz and that the VIM4 and the display can negotiate resolution using EDID information. The following logcat output seems to confirm this, first enable all pr_info() kernel messages echo 6 > /proc/sys/kernel/printk and then filter logcat on “tag:HDMI”:

2023-10-10 10:08:26.621     0-0     [    7.943...3]  hdmitx kernel                               I  recalc before 2160p60hz 60 1, frac 0
2023-10-10 10:08:26.621     0-0     [    8.029...et_drm_pkt kernel                               I  tf=1, cf=1, colormetry=0
2023-10-10 10:08:26.622     0-0     [    8.122...RX]-hdmirx kernel                               W  driver probe ok
2023-10-10 10:08:26.624     0-0     [    8.409...0]  hdmitx kernel                               I  edid: EDID Parser:
2023-10-10 10:08:26.624     0-0     [    8.410...0]  hdmitx kernel                               I  i=0 VIC=97
2023-10-10 10:08:26.624     0-0     [    8.411...0]  hdmitx kernel                               I  i=1 VIC=18
2023-10-10 10:08:26.624     0-0     [    8.413...0]  hdmitx kernel                               I  i=2 VIC=3
2023-10-10 10:08:26.624     0-0     [    8.413...0]  hdmitx kernel                               I  i=3 VIC=19
2023-10-10 10:08:26.624     0-0     [    8.415...0]  hdmitx kernel                               I  i=4 VIC=4
2023-10-10 10:08:26.624     0-0     [    8.415...0]  hdmitx kernel                               I  i=5 VIC=32
2023-10-10 10:08:26.624     0-0     [    8.416...0]  hdmitx kernel                               I  i=6 VIC=34
2023-10-10 10:08:26.624     0-0     [    8.416...0]  hdmitx kernel                               I  i=7 VIC=31
2023-10-10 10:08:26.624     0-0     [    8.417...0]  hdmitx kernel                               I  i=8 VIC=16
2023-10-10 10:08:26.624     0-0     [    8.417...0]  hdmitx kernel                               I  i=9 VIC=95
2023-10-10 10:08:26.624     0-0     [    8.417...0]  hdmitx kernel                               I  i=10 VIC=96
2023-10-10 10:08:26.624     0-0     [    8.418...0]  hdmitx kernel                               I  i=11 VIC=93
2023-10-10 10:08:26.624     0-0     [    8.418...0]  hdmitx kernel                               I  i=12 VIC=94
2023-10-10 10:08:26.624     0-0     [    8.419...0]  hdmitx kernel                               I  edid: get dtd0 vic: 16
2023-10-10 10:08:26.624     0-0     [    8.419...0]  hdmitx kernel                               I  edid: get dtd1 vic: 31
2023-10-10 10:08:26.624     0-0     [    8.420...0]  hdmitx kernel                               I  edid: get dtd2 vic: 4
2023-10-10 10:08:26.624     0-0     [    8.420...0]  hdmitx kernel                               I  edid: get dtd3 vic: 789
2023-10-10 10:08:26.624     0-0     [    8.421...0]  hdmitx kernel                               I  edid: get dtd4 vic: 97
2023-10-10 10:08:26.624     0-0     [    8.421...0]  hdmitx kernel                               I  edid: change preferred_mode from 16 to 97
2023-10-10 10:08:26.624     0-0     [    8.422...0]  hdmitx kernel                               I  edid: find IEEEOUT
2023-10-10 10:08:26.624     0-0     [    8.423...0]  hdmitx kernel                               I  edid: check sum valid
2023-10-10 10:08:26.624     0-0     [    8.423...0]  hdmitx kernel                               I  edid: check sum valid
2023-10-10 10:08:26.624     0-0     [    8.424...0]  hdmitx kernel                               I  edid: check sum valid
2023-10-10 10:08:26.624     0-0     [    8.424...0]  hdmitx kernel                               I  edid: check sum valid
2023-10-10 10:08:26.624     0-0     [    8.425...0]  hdmitx kernel                               I  edid: blk0 raw data
2023-10-10 10:08:26.624     0-0     [    8.425...0]  hdmitx kernel                               I  edid:
2023-10-10 10:08:26.624     0-0     [    8.430...0]  hdmitx kernel                               I  edid: blk1 raw data
2023-10-10 10:08:26.624     0-0     [    8.430...0]  hdmitx kernel                               I  edid:
2023-10-10 10:08:26.624     0-0     [    8.436...0]  hdmitx kernel                               I  hw: set audio
2023-10-10 10:08:26.624     0-0     [    8.438...0]  hdmitx kernel                               I  clk_div = 18
2023-10-10 10:08:26.624     0-0     [    8.439...0]  hdmitx kernel                               I  audio_param->channel_num = 1
2023-10-10 10:08:26.624     0-0     [    8.440...0]  hdmitx kernel                               I  hw: hdmitx tx_aud_src = 0
2023-10-10 10:08:26.624     0-0     [    8.442...0]  hdmitx kernel                               I  fs = 3, cd = 5, tmds_clk = 594000
2023-10-10 10:08:26.624     0-0     [    8.443...0]  hdmitx kernel                               I  hw: aud_n_para = 6144

Note that switching 1080p<->2160p is possible, 1440p is not supported it falls back to 1080p.

But the internal rendering seems to have a bottleneck for HDMI output, so that the HDMI output is just up-scaled 1080p content… I would like the whole pipeline to be 2160p.

adb shell dumpsys SurfaceFlinger returns some interesting results:

The Vx1 display:

 DisplayDevice{0, internal, primary, "Internal display"}
   powerMode=On (2), activeConfig=0,    Composition Display State: ["Internal display"]
   isVirtual=false hwcId=0 
   isEnabled=true isSecure=true usesClientComposition=true usesDeviceComposition=false flipClientTarget=false reusedClientComposition=false layerStack=0 layerStackInternal=true 
   transform 0x00000000 (ROT_0 ) 0x00 (IDENTITY )
    1.0000  0.0000  -0.0000
    0.0000  1.0000  -0.0000
    0.0000  0.0000  1.0000

   bounds=[0 0 3840 2160] frame=[0 0 3840 2160] viewport=[0 0 3840 2160] sourceClip=[0 0 3840 2160] destinationClip=[0 0 3840 2160] needsFiltering=false 
   colorMode=NATIVE (0) renderIntent=COLORIMETRIC (0) dataspace=UNKNOWN (0) colorTransformMatrix=[[1.000,0.000,0.000,0.000][0.000,1.000,0.000,0.000][0.000,0.000,1.000,0.000][0.000,0.000,0.000,1.000]]target dataspace=UNKNOWN (0) 
   Composition Display Color State:
   HWC Support: wideColorGamut=false hdr10plus=false hdr10=true hlg=true dv=false metadata=0 
   Composition RenderSurface State:
   size=[3840 2160] ANativeWindow=0xb4000077e39c81a0 (format 1) flips=1062 

Note that size is 3840x2160
And then:

MesonHwc2 state(DEBUG):
--------------------------------------------------------------------------------------
Display (Tablet-21, HW-Comp) 
Power: (1-0) 
Calibration: (3840x2160)->(0x0,3840x2160)

No upscaling is needed for Vx1

The HDMI display:

+ DisplayDevice{1, external, "External display"}
   powerMode=On (2), activeConfig=0,    Composition Display State: ["External display"]
   isVirtual=false hwcId=1 
   isEnabled=true isSecure=true usesClientComposition=true usesDeviceComposition=false flipClientTarget=false reusedClientComposition=false layerStack=0 layerStackInternal=false 
   transform 0x00000000 (ROT_0 ) 0x04 (SCALE )
    0.5000  0.0000  0.0000
    0.0000  0.5000  0.0000
    0.0000  0.0000  1.0000

   bounds=[0 0 1920 1080] frame=[0 0 1920 1080] viewport=[0 0 3840 2160] sourceClip=[0 0 3840 2160] destinationClip=[0 0 1920 1080] needsFiltering=true 
   colorMode=NATIVE (0) renderIntent=COLORIMETRIC (0) dataspace=UNKNOWN (0) colorTransformMatrix=[[1.000,0.000,0.000,0.000][0.000,1.000,0.000,0.000][0.000,0.000,1.000,0.000][0.000,0.000,0.000,1.000]]target dataspace=UNKNOWN (0) 
   Composition Display Color State:
   HWC Support: wideColorGamut=false hdr10plus=false hdr10=false hlg=false dv=false metadata=0 
   Composition RenderSurface State:
   size=[1920 1080] ANativeWindow=0xb4000077e39ce7d0 (format 1) flips=975 

Note that size is 1920x1080
And then:

Display (HDMI-20, HW-Comp) 
Power: (1-0) 
Calibration: (1920x1080)->(0x0,3840x2160)

Upscaling is performed for HDMI, this should not be needed if the entire pipeline was 4k

@xavier My HDMI monitor is 4K, and when switching resolution, my HDMI monitor will have 2 seconds to directly display the current resolution on the screen. When I insert it into the VIM4 board, the screen displays "3840 × 2160@60hz ”Equal characters. Then ADB executes the command above and outputs the same information as you. So I think we should not be misled by superficial information. It is actually already 4K. This is an unquestionable matter.

+ DisplayDevice{1, external, "External display"}
   powerMode=On (2), activeConfig=0,    Composition Display State: ["External display"]
   isVirtual=false hwcId=1
   isEnabled=true isSecure=true usesClientComposition=true usesDeviceComposition=false flipClientTarget=false reusedClientComposition=false layerStack=0 layerStackInternal=false
   transform 0x00000000 (ROT_0 ) 0x04 (SCALE )
    0.5000  0.0000  0.0000
    0.0000  0.5000  0.0000
    0.0000  0.0000  1.0000

   bounds=[0 0 1920 1080] frame=[0 0 1920 1080] viewport=[0 0 3840 2160] sourceClip=[0 0 3840 2160] destinationClip=[0 0 1920 1080] needsFiltering=true
   colorMode=NATIVE (0) renderIntent=COLORIMETRIC (0) dataspace=UNKNOWN (0) colorTransformMatrix=[[1.000,0.000,0.000,0.000][0.000,1.000,0.000,0.000][0.000,0.000,1.000,0.000][0.000,0.000,0.000,1.000]]target dataspace=UNKNOWN (0)
   Composition Display Color State:
   HWC Support: wideColorGamut=false hdr10plus=false hdr10=false hlg=false dv=false metadata=0
   Composition RenderSurface State:
   size=[1920 1080] ANativeWindow=0xb40000796a1705c0 (format 1) flips=867

I am not convinced that the HDMI pipeline is 4k internally in VIM4/Android.

I have made a simple Android application that draws single pixel wide horizontal and vertical lines (leaving 20 pixels buffer between the horizontal and vertical lines).

class Lines(context: Context?) : View(context) {

   private val paint = Paint()
    init {
        paint.color = Color.BLACK
        this.setBackgroundColor(Color.WHITE)
    }

    override fun onDraw(canvas: Canvas) {
        val startX : Int = 0
        val stopX : Int = 1910

        for (y in 0 until 1080 step 2) {
            canvas.drawLine(
                startX.toFloat(), y.toFloat(),
                stopX.toFloat(), y.toFloat(),
                paint)
        }
        val startY : Int = 0
        val stopY : Int = 1080

        for (x in 1930 until 3840 step 2) {
            canvas.drawLine(
                x.toFloat(), startY.toFloat(),
                x.toFloat(), stopY.toFloat(),
                paint)
        }
    }
}
 
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val lines = Lines(this)
        setContentView(lines);
    }
}

The Android Studio “Running Devices” windows that mirrors the actual VIM4 output over USB-C (zoomed in):

Next, I have connected the VIM4 using both Vx1 and HDMI to the same panel and switched sources to produce the images below. The data paths are not entirely equal, but should be close enough for this test:

  • Vx1 path: VIM4 → Vx1 → panel’s TCON board
  • HDMI path: VIM4 → HDMI → panel’s scaler board → Vx1 → panel’s TCON board

The Vx1 4k output (zoomed in):

The HDMI 4k output (zoomed in):

I believe that the HDMI pipeline down-scales the 4k surface to 1080p internally, and then up-scales the 1080p back to 4k in hardware before it is sent to the display over HDMI. This would explain why the lines in the HDMI output have been blurred to a uniform grey color. I.e. down-scaling to half resolution takes one white and one black pixel and merges them to a grey pixel, then up-scaling the grey pixel back to 4k just duplicates the grey pixel producing a uniform grey surface over HDMI.

@xavier I will follow up on this issue.

@xavier Vx1(4K) + HDMITX(4K) dual 4K output is not supported.
Support: Vx1(4K) + HDMITX(1080p) (currently the default firmware mode)
Support: HDMITX(4K) + Vx1(1080p) (requires additional patches)

Which is the reason for not supporting dual 4K (Vx1 + HDMI)? Is it because of hardware limitations or lacking Linux/Android support?

So, the only currently defined way of enabling 4K HDMI (and no Vx1) is to define BOARD_COMPILE_HDMITX_ONLY := true in device/khadas/kvim4/BoardConfig.mk?

The source code available around new year 2022/2023 + BOARD_COMPILE_HDMITX_ONLY := true enabled HDMITX(4K) (Vx1 not tested at that time). But the currently available source code fails to enable HDMITX(4K).

Expected result:

  • Booting VIM4 with HDMI cable attached → HDMITX(4K)
  • Booting VIM4 without HDMI cable attached, then attaching cable after VIM4 boot → HDMITX(4K)

Actual result:

  • Booting VIM4 with HDMI cable attached → “KHADAS” logo permanently on both HDMITX AND on Vx1. Android Studio’s “Running Devices” mirror shows Android launcher in 4K.
  • Booting VIM4 without HDMI cable attached, then attaching cable after VIM4 boot → HDMITX always black, Vx1 is still enabled and shows “KHADAS” logo permanently.

Why does HDMITX not show Android launcher?
Why is VX1 still enabled?

hardware limitations

Support: HDMITX(4K) + Vx1(1080p) (requires additional patches) Can this situation meet your needs?

We need two different configurations. Preferably they can exist on the same branch and only differ by build time configuration:

  1. Vx1(4K) - we don’t care about HDMI. This can be achieved with the default configuration of Khadas Android 11 build.

  2. HDMI(4K) - we don’t care about Vx1.
    a. This could be achieved by setting BOARD_COMPILE_HTMITX_ONLY, but it does not work, see previous posts above.
    b. This could be achieved with HDMITX(4K) + Vx1(1080p) (requires additional patches). But does this need to live on a separate branch or can it be a compile time setting?

Due to the compatibility of many types of mipi screens with our firmware, it is a bit complicated to modify. Do you need to use a mipi screen? If not needed, I will try configuring it as HTMITX_ONLY next week.

MIPI screens are out of scope for us, so we don’t need support for them. I assume you can publish the patchset so that we can continue building the Android images ourselves…

@xavier HTMITX_ONLY config:

bootloader/uboot$ git diff
diff --git a/board/khadas/configs/kvim4.h b/board/khadas/configs/kvim4.h
index 30a955359b..1dfabafab3 100644
--- a/board/khadas/configs/kvim4.h
+++ b/board/khadas/configs/kvim4.h
@@ -390,7 +390,7 @@
         "loadaddr_kernel=0x01080000\0"\
                "dv_fw_addr=0xa00000\0"\
         "otg_device=1\0" \
-        "panel1_type=vbyone_1\0" \
+        "panel1_type=1080p60hz\0" \
         "panel2_type=lvds_1\0" \
         "t7c_check_camera=0\0" \
         "lcd_ctrl=0x00000000\0" \
@@ -469,9 +469,8 @@
         "storeargs="\
             "get_bootloaderversion;" \
             "setenv bootargs ${initargs} otg_device=${otg_device} "\
-                "logo=${display_layer},loaded,${fb_addr} powermode=${powermode}  vout=${outputmode},enable vout2=${outputmode2},enable "\
+                "logo=${display_layer},loaded,${fb_addr} powermode=${powermode}  vout=${outputmode},enable "\
                 "panel_type=${panel_type} lcd_ctrl=${lcd_ctrl} lcd_debug=${lcd_debug} "\
-                "panel1_type=${panel1_type} lcd1_ctrl=${lcd1_ctrl} panel2_type=${panel2_type} lcd2_ctrl=${lcd2_ctrl} "\
                 "hdmimode=${hdmimode} outputmode=${outputmode} "\
                        "hdmichecksum=${hdmichecksum} dolby_vision_on=${dolby_vision_on} " \
                        "hdmitx=${cecconfig},${colorattribute} "\
@@ -802,11 +801,10 @@
 * logo image path: device/amlogic/$(proj_name)/logo_img_files/
 */
 #define CONFIG_DUAL_LOGO \
-    "setenv outputmode2 ${hdmimode};"\
        "setenv fb_width 1920;setenv fb_height 1080;"\
        "setenv fb_width 1920;setenv fb_height 1080;"\
-    "setenv display_layer viu2_osd0;vout2 prepare ${outputmode2};"\
-    "osd open;osd clear;run load_bmp_logo;vout2 output ${outputmode2};bmp scale;"\
+    "setenv display_layer viu2_osd0;"\
+    "osd open;osd clear;run load_bmp_logo;bmp scale;"\
        "if test ${khadas_mipi_id} = 1 || test ${khadas_mipi_id} = 3; then "\
         "setenv fb_width 1080;setenv fb_height 1920;"\
         "setenv display_width 1080;setenv display_height 1920;"\
@@ -824,11 +822,10 @@
 
 /* dual logo, factory_reset boot, recovery always displays on panel */
 #define CONFIG_RECOVERY_DUAL_LOGO \
-    "setenv outputmode2 ${hdmimode};"\
        "setenv fb_width 1920;setenv fb_height 1080;"\
        "setenv fb_width 1920;setenv fb_height 1080;"\
-    "setenv display_layer viu2_osd0;vout2 prepare ${outputmode2};"\
-    "osd open;osd clear;run load_bmp_logo;vout2 output ${outputmode2};bmp scale;"\
+    "setenv display_layer viu2_osd0;"\
+    "osd open;osd clear;run load_bmp_logo;bmp scale;"\
        "if test ${khadas_mipi_id} = 1 || test ${khadas_mipi_id} = 3; then "\

device/khadas$ git diff
diff --git a/kvim4/BoardConfig.mk b/kvim4/BoardConfig.mk
index 739662a..5f44906 100644
--- a/kvim4/BoardConfig.mk
+++ b/kvim4/BoardConfig.mk
@@ -64,11 +64,11 @@ TARGET_USE_DEFAULT_HDR_PROPERTY := true
 
 #MESONHWC CONFIG
 USE_HWC2 := true
-
+BOARD_COMPILE_HDMITX_ONLY := true
 # 1.device type [MID | MBOX | TV]
 ifeq ($(BOARD_COMPILE_HDMITX_ONLY), true)
 HWC_DISPLAY_NUM := 1
-SYSTEMCONTROL_DISPLAY_TYPE := TV
+SYSTEMCONTROL_DISPLAY_TYPE := MID
 else

system/core$ git diff
diff --git a/init/property_service.cpp b/init/property_service.cpp
index b8f290603..d329f16d2 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -1024,7 +1024,7 @@ static void export_lcd_status() {
        return;
     }
     read(fd, buf, sizeof(buf) - 1);
-    if(strstr(buf,"vout=panel1") != NULL) {
+    if(1) {
         InitPropertySet("sys.lcd.reverse", "0");
         InitPropertySet("persist.vendor.hwc.lcdpath", "1");