{"id":905,"date":"2022-11-28T15:58:19","date_gmt":"2022-11-28T07:58:19","guid":{"rendered":"https:\/\/swordofmorning.com\/?p=905"},"modified":"2025-10-09T13:55:34","modified_gmt":"2025-10-09T05:55:34","slug":"lvgl-01","status":"publish","type":"post","link":"https:\/\/swordofmorning.com\/index.php\/2022\/11\/28\/lvgl-01\/","title":{"rendered":"LVGL 01 Enable Transparent Plane"},"content":{"rendered":"<p><div class=\"has-toc have-toc\"><\/div><\/p>\n<p>&emsp;&emsp;In this article, we'll trying to overlay a UI layer onto a video layer, which like OSD. Here is my platform information.<\/p>\n<ol>\n<li>System: kernel 4.19<\/li>\n<li>LVGL Version: 8.3<\/li>\n<li>Display Driver: DRM<\/li>\n<li>CPU: RV1126, which have two layers.<\/li>\n<\/ol>\n<h2>\u00a71 Check Plane Info<\/h2>\n<p>&emsp;&emsp;If your CPU's VOP support multiple layer, then you could draw UI and video on difference layer. In this article, I use RV1126 to implement this function. If you use <code>framebuffer<\/code> as display interface, then you can directly draw image or UI on different fb device. Such as, draw video on <code>fb1<\/code>, UI on <code>fb0<\/code>. If you use DRM, using <code>modetest<\/code> to check whether planes support overlay.<\/p>\n<pre><code class=\"language-shell\">[root@DCIR:\/]# modetest\nEncoders:\nid  crtc    type    possible crtcs  possible clones \n58  56  DSI 0x00000001  0x00000000\n\nConnectors:\nid  encoder status      name        size (mm)   modes   encoders\n59  58  connected   DSI-1           68x121      1   58\n  modes:\n    name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot)\n  800x1280 57 800 818 836 882 1280 1310 1314 1328 67000 flags: nhsync, nvsync; type: preferred, driver\n  props:\n    1 EDID:\n        flags: immutable blob\n        blobs:\n\n        value:\n    2 DPMS:\n        flags: enum\n        enums: On=0 Standby=1 Suspend=2 Off=3\n        value: 0\n    5 link-status:\n        flags: enum\n        enums: Good=0 Bad=1\n        value: 0\n    6 non-desktop:\n        flags: immutable range\n        values: 0 1\n        value: 0\n    46 brightness:\n        flags: range\n        values: 0 100\n        value: 50\n    47 contrast:\n        flags: range\n        values: 0 100\n        value: 50\n    50 saturation:\n        flags: range\n        values: 0 100\n        value: 50\n    51 hue:\n        flags: range\n        values: 0 100\n        value: 50\n\nCRTCs:\nid  fb  pos size\n56  60  (0,0)   (800x1280)\n  800x1280 57 800 818 836 882 1280 1310 1314 1328 67000 flags: nhsync, nvsync; type: preferred, driver\n  props:\n    41 left margin:\n        flags: range\n        values: 0 100\n        value: 100\n    42 right margin:\n        flags: range\n        values: 0 100\n        value: 100\n    43 top margin:\n        flags: range\n        values: 0 100\n        value: 100\n    44 bottom margin:\n        flags: range\n        values: 0 100\n        value: 100\n    54 FEATURE:\n        flags: immutable bitmask\n        values: afbdc=0x1\n        value: 0\n    26 GAMMA_LUT:\n        flags: blob\n        blobs:\n\n        value:\n    27 GAMMA_LUT_SIZE:\n        flags: immutable range\n        values: 0 4294967295\n        value: 256\n\nPlanes:\nid  crtc    fb  CRTC x,y    x,y gamma size  possible crtcs\n55  56  60  0,0     0,0 0           0x00000001\n  formats: XR24 AR24 XB24 AB24 RG24 BG24 RG16 BG16\n  props:\n    8 type:\n        flags: immutable enum\n        enums: Overlay=0 Primary=1 Cursor=2\n        value: 1\n    53 FEATURE:\n        flags: immutable bitmask\n        values: scale=0x1 alpha=0x2 hdr2sdr=0x4 sdr2hdr=0x8 afbdc=0x10\n        value: 2\n57  0   0   0,0     0,0 0           0x00000001\n  formats: XR24 AR24 XB24 AB24 RG24 BG24 RG16 BG16 NV12 NV16 NV24\n  props:\n    8 type:\n        flags: immutable enum\n        enums: Overlay=0 Primary=1 Cursor=2\n        value: 0\n    53 FEATURE:\n        flags: immutable bitmask\n        values: scale=0x1 alpha=0x2 hdr2sdr=0x4 sdr2hdr=0x8 afbdc=0x10\n        value: 3\n\nFrame buffers:\nid  size    pitch<\/code><\/pre>\n<h3>1.1 Connectors<\/h3>\n<p>&emsp;&emsp;Connected output port via CPU, here we only have one MIPI-DSI output which id equals 59.<\/p>\n<h3>1.2 CRTCs<\/h3>\n<p>&emsp;&emsp;Video Output Processor, for RV1126, we only have one VOP which id equals 56.<\/p>\n<h3>1.3 Planes<\/h3>\n<p>&emsp;&emsp;i.e. layer, for RV1126, we have two layers. plane-55's props specify it's a primary layer, and plane-57 is a overlay layer. so, we should draw video on primary player, and draw UI on overlay layer.<\/p>\n<h2>\u00a72 LVGL config<\/h2>\n<p>&emsp;&emsp;Here are several steps to enable transparent background:<\/p>\n<h3>2.1 LV_COLOR_DEPTH &amp; LV_COLOR_SCREEN_TRANSP<\/h3>\n<ol>\n<li>In <code>lv_conf.h<\/code>, make sure color depth is 32, eg. <code>#define LV_COLOR_DEPTH 32<\/code>, which allows alpha channel.<\/li>\n<li>In <code>lv_conf.h<\/code>, enable LV_COLOR_SCREEN_TRANSP, eg. <code>#define LV_COLOR_SCREEN_TRANSP 1<\/code>.<\/li>\n<\/ol>\n<h3>2.2 Specify planes<\/h3>\n<p>&emsp;&emsp;Here I manually specify LVGL's plane id as 57. In <code>lv_drivers\/display\/drm.c<\/code>, for function <code>drm_setup<\/code>, we modify:<\/p>\n<pre><code class=\"language-c\">\/\/ ret = find_plane(fourcc, &amp;drm_dev.plane_id, drm_dev.crtc_id, drm_dev.crtc_idx);\n\/\/ if (ret) {\n\/\/  err(&quot;Cannot find plane&quot;);\n\/\/  goto err;\n\/\/ }\ndrm_dev.plane_id = 57;<\/code><\/pre>\n<h3>2.3 Set background opa<\/h3>\n<p>&emsp;&emsp;In main function, we add:<\/p>\n<pre><code class=\"language-c\">lv_style_t style_scr_act;\nlv_style_init(&amp;style_scr_act);\nlv_style_set_bg_opa(&amp;style_scr_act, LV_OPA_TRANSP);\nlv_obj_add_style(lv_scr_act(), &amp;style_scr_act, 0);\n\/\/ lv_disp_set_bg_opa(lv_scr_act(), LV_OPA_TRANSP);<\/code><\/pre>\n<p>The complete main:<\/p>\n<pre><code class=\"language-c\">int main(void)\n{\n\n    DRM_Init();\n\n    \/\/ capture and draw video\n    pthread_t pth_capt;\n\n    pthread_create(&amp;pth_capt, NULL, (void*)V4L2_SP_Streaming, NULL);\n\n#if 1\n\/* ================ *\/\n\/* ===== LVGL ===== *\/\n\/* ================ *\/\n\n    \/*LittlevGL init*\/\n    lv_init();\n\n    \/*Linux frame buffer device init*\/\n    drm_init();\n\n    \/*A small buffer for LittlevGL to draw the screen&#039;s content*\/\n    static lv_color_t buf[DISP_BUF_SIZE], buf1[DISP_BUF_SIZE];\n\n    \/*Initialize a descriptor for the buffer*\/\n    static lv_disp_draw_buf_t disp_buf;\n    \/\/ lv_disp_draw_buf_init(&amp;disp_buf, buf, NULL, DISP_BUF_SIZE);\n    lv_disp_draw_buf_init(&amp;disp_buf, buf, buf1, DISP_BUF_SIZE);\n\n    \/*Initialize and register a display driver*\/\n    static lv_disp_drv_t disp_drv;\n    lv_disp_drv_init(&amp;disp_drv);\n    disp_drv.draw_buf   = &amp;disp_buf;\n    disp_drv.flush_cb   = drm_flush;\n    disp_drv.hor_res    = 800;\n    disp_drv.ver_res    = 1280;\n    disp_drv.screen_transp = 1;\n    lv_disp_drv_register(&amp;disp_drv);\n\n#if 1\n    evdev_init();\n    static lv_indev_drv_t indev_drv_1;\n    lv_indev_drv_init(&amp;indev_drv_1); \/*Basic initialization*\/\n    indev_drv_1.type = LV_INDEV_TYPE_POINTER;\n\n    \/*This function will be called periodically (by the library) to get the mouse position and state*\/\n    indev_drv_1.read_cb = evdev_read;\n    lv_indev_t *mouse_indev = lv_indev_drv_register(&amp;indev_drv_1);\n#endif\n\n#if 1\n    \/*Set a cursor for the mouse*\/\n    LV_IMG_DECLARE(mouse_cursor_icon)\n    lv_obj_t * cursor_obj = lv_img_create(lv_scr_act()); \/*Create an image object for the cursor *\/\n    lv_img_set_src(cursor_obj, &amp;mouse_cursor_icon);           \/*Set the image source*\/\n    lv_indev_set_cursor(mouse_indev, cursor_obj);             \/*Connect the image  object to the driver*\/\n#endif\n\n#if 1\n    lv_style_t style_scr_act;\n    lv_style_init(&amp;style_scr_act);\n    lv_style_set_bg_opa(&amp;style_scr_act, LV_OPA_TRANSP);\n    lv_obj_add_style(lv_scr_act(), &amp;style_scr_act, 0);\n    \/\/ lv_disp_set_bg_opa(lv_scr_act(), LV_OPA_TRANSP);\n#endif\n\n    mybtn();\n\n    \/*Handle LitlevGL tasks (tickless mode)*\/\n    while(1) {\n        lv_timer_handler();\n        usleep(5000);\n    }\n#endif\n\n    pthread_exit(NULL);\n\n    return 0;\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>&emsp;&emsp;In this article, we&#8217;ll trying to overlay a UI layer o &#823","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[54],"tags":[],"amp_enabled":true,"_links":{"self":[{"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/posts\/905"}],"collection":[{"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/comments?post=905"}],"version-history":[{"count":3,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/posts\/905\/revisions"}],"predecessor-version":[{"id":908,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/posts\/905\/revisions\/908"}],"wp:attachment":[{"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/media?parent=905"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/categories?post=905"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/swordofmorning.com\/index.php\/wp-json\/wp\/v2\/tags?post=905"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}