🌐 English

    Other UI Framework Support#

    Oasis can coexist with other UI Frameworks, such as Qt and LVGL.

    LVGL#

    To use LVGL simultaneously with Oasis, perform the following sequence:

    1. Set the hide-unmanaged-ui-layer parameter key to the HLAYER used by LVGL when calling oasis::ui::setup (e.g., "1:2:0").
    2. Retrieve the Display handle by calling oasis::ui::getDisplayHandle.
    3. Create the LVGL driver or LVGL Display object, define the flush_cb callback function, and render inside the flush_cb callback function by referencing the Display object.

    Step 3 may vary depending on the LVGL version and chipset SDK.

    LVGL 6.1.2#

    This section provides an example of using lvgl 6.1.2 and lv_drivers 6.1.1 as the primary application UI framework on the KL730 chipset.

    In the KL730 SDK, the Display handle, screen size, and Layer ID are specified inside the lv_scr_setup function defined in lvgl/lvgl/custom/lv_custom.c:

    void lv_scr_setup(void *handle, uint32_t w, uint32_t h, uint32_t x, uint32_t y, VMF_VDISP_LAYER l)
    

    Actual rendering is processed via the flush_cb function defined in lvgl/lv_drivers/display/v4l2dev.c:

    void fbdev_init(void);
    void fbdev_exit(void);
    void fbdev_flush(lv_disp_drv_t * drv, const lv_area_t * area, lv_color_t * color_p);
    

    Initialize the Oasis UI. Configure the parameters so that the LVGL graphics are processed on Layer 2, Oasis Video YUV streams on Layer 0, and Oasis UI on Layer 1. Specify the HLAYER for LVGL in hide-unmanaged-ui-layer:

    static int32_t oasis_setup()
    {
      int32_t err;
      oasis::key_value_map_t parameters;
    
      parameters["offs-disable"] = "1";
      //parameters["oasis-log-flags"] = std::to_string(OASIS_LOG_DEBUG/*|OASIS_LOG_ENCODE_BITRATE*/);
    
      if(oasis::initialize(parameters) < 0) {
        DLOG(DLOG_ERROR, "Oasis init failed\n");
        return -1;
      }
    
      ////////////////////////////////////////////////////////////////////////////////////////////
      // display setup
      parameters.clear();
    
      parameters["memory-type"] = "ion"; //cma
      parameters["screen-width"] = "480";
      parameters["screen-height"] = "320";
    
      display::setup(parameters);
    
      //////////////////////////////////////////////////////////////////////////////////////////
      // ui setup
      parameters.clear();
    
      parameters["system-font-size"] = "11";
      parameters["system-font-path"] = "/mnt/sd/NanumGothicCoding.ttf";
      parameters["display-rotation"] = "0";
      parameters["enable-touch"] = "1";
      parameters["touch-cal-data-path"] = "/mnt/sd/cal.dat";
    
      parameters["vi0-hlayer"] = "1:0:0"; // VMF_VDISP_L0
      parameters["fb0-hlayer"] = "1:1:0"; // VMF_VDISP_L1
      parameters["hide-unmanaged-ui-layer"] = "1:2:0"; // VMF_VDISP_L2
    
      err = ui::setup(parameters);
      if (err < 0) {
        DLOG(DLOG_ERROR, "Oasis ui::setup failed\n");
        oasis::finalize();
        return -1;
      }
    
      return 0;
    }
    

    Initialize the LVGL library:

    static int32_t lvgl_setup(void *disp_handle)
    {
      static int32_t pool_size = 32;
      static int32_t gui_width = 480;
      static int32_t gui_height = 320;
      static int32_t gui_x = 0;
      static int32_t gui_y = 0;
    
      unsigned int disp_buf_size;
      lv_disp_buf_t disp_buf;
      lv_disp_drv_t disp_drv;
      lv_indev_drv_t indev_drv;
      lv_indev_t *my_indev;
    
    
      lv_init(pool_size);
    
      lv_scr_setup(disp_handle, gui_width, gui_height, gui_x, gui_y, VMF_VDISP_L2);
    
      if(v4l2dev_init()) {
        fprintf(stderr, "v4l2dev_init failed!\n");
        lv_release();
        return -1;
      }
    
      /*Init display buffer, allocate buffer for screen-flushing*/
      disp_buf_size = 10 * lv_scr_get_hor_res();
      lvgl_buf = (lv_color_t*)malloc(disp_buf_size * sizeof *lvgl_buf);
    
      /*Initialize a descriptor for the buffer*/
      lv_disp_buf_init(&disp_buf, lvgl_buf, NULL, disp_buf_size);
    
      /*Initialize and register a display driver*/
      lv_disp_drv_init(&disp_drv);
      disp_drv.buffer = &disp_buf;
      disp_drv.flush_cb = v4l2dev_flush;
      lv_disp_drv_register(&disp_drv);
    
      return 0;
    }
    

    In the code above, disp_handle is obtained by calling oasis::ui::getDisplayHandle:

    void *disp_handle = ui::getDisplayHandle();
    

    After LVGL initialization, construct the UI components using the LVGL library. The following example creates four buttons arranged vertically on the screen:

    static void lvgl_draw_screen(void)
    {
      lv_obj_t *scr = NULL;
      static lv_style_t button_style_rel;
      static lv_style_t button_style_pre;
      uint32_t btn_w = lv_scr_get_hor_res() / 2;
      uint32_t btn_h = lv_scr_get_ver_res() / 8;
    
      static const char *btn_txt[4] = { 
        "page1",    "page2", "page3", "page4"
      };
    
      lv_point_t positons[4];
    
      /*Init screen*/
      scr = lv_disp_get_scr_act(NULL);
      lv_obj_clean(scr);
      lv_obj_set_style(scr, &lv_style_transp);
    
      /*Init button styles*/
      lv_style_copy(&button_style_rel, &lv_style_btn_rel);
      lv_style_copy(&button_style_pre, &lv_style_btn_pr);
      button_style_rel.body.opa = LV_OPA_TRANSP;
      button_style_rel.body.border.opa = LV_OPA_TRANSP;
      button_style_rel.text.color = LV_COLOR_WHITE;
      button_style_pre.body.opa = LV_OPA_TRANSP;
      button_style_pre.body.border.opa = LV_OPA_TRANSP;
      button_style_pre.text.color = LV_COLOR_YELLOW;
    
      for (int i = 0; i < 4; i++) {
        lv_obj_t *btn;
        char name[32];
        lv_obj_t *label;
        uint32_t offset = btn_h * 2 + i * btn_h;
        lv_area_t coords;
    
        btn = lv_btn_create(scr, NULL);
        lv_btn_set_style(btn, LV_BTN_STYLE_REL, &button_style_rel);
        lv_btn_set_style(btn, LV_BTN_STYLE_PR,  &button_style_pre);
        lv_obj_set_size(btn, btn_w, btn_h);
        lv_obj_align(btn, NULL, LV_ALIGN_IN_TOP_MID, 0, offset);
        lv_obj_get_coords(btn, &coords);
        positons[i].x = (coords.x1 + coords.x2) >> 1;
        positons[i].y = (coords.y1 + coords.y2) >> 1;
    
        strncpy(name, btn_txt[i], 31);
        label = lv_label_create(btn, NULL);
        lv_label_set_text(label, name);
      }
    }
    

    To render the Oasis UI components, use the Oasis UI API as shown below. This example renders a text label displaying "Hello, World!" in the center of the screen:

    static void oasis_draw_screen()
    {
      ui::ScreenRef screen = ui::getDefaultScreen();
    
      int32_t screen_width = ui::screenWidth(screen);
      int32_t screen_height = ui::screenHeight(screen);
    
      ui::WindowRef window = ui::createWindow("Hello");
    
      ui::BoxRef vbox = ui::createBox(ui::kOrientationVertical, false, 6);
      ui::setVAlign(vbox, ui::kAlignCenter);
    
      ui::LabelRef label = ui::createLabelWithText("Hello, World!");
      ui::setVAlign(label, ui::kAlignCenter);
      ui::setHAlign(label, ui::kAlignCenter);
      ui::setLabelHorzJustification(label, ui::kJustificationCenter);
      ui::setLabelVertJustification(label, ui::kJustificationCenter);
      ui::setLabelFontPointSize(label, 16);
    
      ui::boxPackStart(vbox, label, true, true, 0);
    
      ui::containerAdd(window, vbox);
      ui::screenAdd(screen, window);
    
      ui::setVisible(window, true);
    }
    

    Note

    Execute LVGL operations inside a separate worker thread rather than on the main application thread.

    Release LVGL resource allocations during application shutdown:

    static void lvgl_cleanup()
    {
      if(lvgl_buf) {
        free(lvgl_buf);
        lvgl_buf = nullptr;
      }
    
      v4l2dev_release();
      lv_release();
    }
    

    Release Oasis resource allocations following the LVGL cleanup sequence:

    static void oasis_cleanup()
    {
      oasis::ui::cleanup();
      oasis::finalize();
    }
    

    The complete example source code is listed below:

    
    #include "OasisAPI.h"
    #include "OasisLog.h"
    #include "OasisUI.h"
    #include "OasisUtil.h"
    #include "OasisDisplay.h"
    
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    #include <dirent.h>
    
    #include <thread>
    #include <mutex>
    #include <memory>
    #include <condition_variable>
    
    #include "lvgl/lvgl.h"
    #include "lvgl/custom/lv_custom.h"
    #include "lv_drivers/display/v4l2dev.h"
    
    #undef DLOG_TAG
    #define DLOG_TAG "Hello-LVGL"
    
    
    using namespace oasis;
    
    static bool got_interrupt = false;
    static lv_color_t *lvgl_buf = nullptr;
    
    #include <signal.h>
    
    void cancel_handler(int sig)
    {
      got_interrupt = true;
    }
    
    static void lvgl_draw_screen(void)
    {
      lv_obj_t *scr = NULL;
      static lv_style_t button_style_rel;
      static lv_style_t button_style_pre;
      uint32_t btn_w = lv_scr_get_hor_res() / 2;
      uint32_t btn_h = lv_scr_get_ver_res() / 8;
    
      static const char *btn_txt[4] = { 
        "page1",    "page2", "page3", "page4"
      };
    
      lv_point_t positons[4];
    
      /*Init screen*/
      scr = lv_disp_get_scr_act(NULL);
      lv_obj_clean(scr);
      lv_obj_set_style(scr, &lv_style_transp);
    
      /*Init button styles*/
      lv_style_copy(&button_style_rel, &lv_style_btn_rel);
      lv_style_copy(&button_style_pre, &lv_style_btn_pr);
      button_style_rel.body.opa = LV_OPA_TRANSP;
      button_style_rel.body.border.opa = LV_OPA_TRANSP;
      button_style_rel.text.color = LV_COLOR_WHITE;
      button_style_pre.body.opa = LV_OPA_TRANSP;
      button_style_pre.body.border.opa = LV_OPA_TRANSP;
      button_style_pre.text.color = LV_COLOR_YELLOW;
    
      for (int i = 0; i < 4; i++) {
        lv_obj_t *btn;
        char name[32];
        lv_obj_t *label;
        uint32_t offset = btn_h * 2 + i * btn_h;
        lv_area_t coords;
    
        btn = lv_btn_create(scr, NULL);
        lv_btn_set_style(btn, LV_BTN_STYLE_REL, &button_style_rel);
        lv_btn_set_style(btn, LV_BTN_STYLE_PR,  &button_style_pre);
        lv_obj_set_size(btn, btn_w, btn_h);
        lv_obj_align(btn, NULL, LV_ALIGN_IN_TOP_MID, 0, offset);
        lv_obj_get_coords(btn, &coords);
        positons[i].x = (coords.x1 + coords.x2) >> 1;
        positons[i].y = (coords.y1 + coords.y2) >> 1;
    
        strncpy(name, btn_txt[i], 31);
        label = lv_label_create(btn, NULL);
        lv_label_set_text(label, name);
      }
    }
    
    
    static int32_t lvgl_setup(void *disp_handle)
    {
      static int32_t pool_size = 32;
      static int32_t gui_width = 480;
      static int32_t gui_height = 320;
      static int32_t gui_x = 0;
      static int32_t gui_y = 0;
    
      unsigned int disp_buf_size;
      lv_disp_buf_t disp_buf;
      lv_disp_drv_t disp_drv;
      lv_indev_drv_t indev_drv;
      lv_indev_t *my_indev;
    
    
      lv_init(pool_size);
    
      lv_scr_setup(disp_handle, gui_width, gui_height, gui_x, gui_y, VMF_VDISP_L2);
    
      if(v4l2dev_init()) {
        fprintf(stderr, "v4l2dev_init failed!\n");
        lv_release();
        return -1;
      }
    
      /*Init display buffer, allocate buffer for screen-flushing*/
      disp_buf_size = 10 * lv_scr_get_hor_res();
      lvgl_buf = (lv_color_t*)malloc(disp_buf_size * sizeof *lvgl_buf);
    
      /*Initialize a descriptor for the buffer*/
      lv_disp_buf_init(&disp_buf, lvgl_buf, NULL, disp_buf_size);
    
      /*Initialize and register a display driver*/
      lv_disp_drv_init(&disp_drv);
      disp_drv.buffer = &disp_buf;
      disp_drv.flush_cb = v4l2dev_flush;
      lv_disp_drv_register(&disp_drv);
    
      return 0;
    }
    
    static void lvgl_cleanup()
    {
      if(lvgl_buf) {
        free(lvgl_buf);
        lvgl_buf = nullptr;
      }
    
      v4l2dev_release();
      lv_release();
    }
    
    static void oasis_draw_screen()
    {
      ui::ScreenRef screen = ui::getDefaultScreen();
    
      int32_t screen_width = ui::screenWidth(screen);
      int32_t screen_height = ui::screenHeight(screen);
    
      ui::WindowRef window = ui::createWindow("Hello");
    
      ui::BoxRef vbox = ui::createBox(ui::kOrientationVertical, false, 6);
      ui::setVAlign(vbox, ui::kAlignCenter);
    
      ui::LabelRef label = ui::createLabelWithText("Hello, World!");
      ui::setVAlign(label, ui::kAlignCenter);
      ui::setHAlign(label, ui::kAlignCenter);
      ui::setLabelHorzJustification(label, ui::kJustificationCenter);
      ui::setLabelVertJustification(label, ui::kJustificationCenter);
      ui::setLabelFontPointSize(label, 16);
    
      ui::boxPackStart(vbox, label, true, true, 0);
    
      ui::containerAdd(window, vbox);
      ui::screenAdd(screen, window);
    
      ui::setVisible(window, true);
    }
    
    static int32_t oasis_setup()
    {
      int32_t err;
      oasis::key_value_map_t parameters;
    
      parameters["offs-disable"] = "1";
      //parameters["oasis-log-flags"] = std::to_string(OASIS_LOG_DEBUG/*|OASIS_LOG_ENCODE_BITRATE*/);
    
      if(oasis::initialize(parameters) < 0) {
        DLOG(DLOG_ERROR, "Oasis init failed\n");
        return -1;
      }
    
      ////////////////////////////////////////////////////////////////////////////////////////////
      // display setup
      parameters.clear();
    
      parameters["memory-type"] = "ion"; //cma
      parameters["screen-width"] = "480";
      parameters["screen-height"] = "320";
    
      display::setup(parameters);
    
      //////////////////////////////////////////////////////////////////////////////////////////
      // ui setup
      parameters.clear();
    
      parameters["system-font-size"] = "11";
      parameters["system-font-path"] = "/mnt/sd/NanumGothicCoding.ttf";
      parameters["display-rotation"] = "0";
      parameters["enable-touch"] = "1";
      parameters["touch-cal-data-path"] = "/mnt/sd/cal.dat";
    
      parameters["vi0-hlayer"] = "1:0:0"; // VMF_VDISP_L0
      parameters["fb0-hlayer"] = "1:1:0"; // VMF_VDISP_L1
      parameters["hide-unmanaged-ui-layer"] = "1:2:0"; // VMF_VDISP_L2
    
      err = ui::setup(parameters);
      if (err < 0) {
        DLOG(DLOG_ERROR, "Oasis ui::setup failed\n");
        oasis::finalize();
        return -1;
      }
    
    
      return 0;
    }
    
    static void oasis_cleanup()
    {
      oasis::ui::cleanup();
      oasis::finalize();
    }
    
    int main(int argc, char *argv[])
    {
      int32_t err;
      std::thread t;
    
      signal(SIGINT, cancel_handler);
    
    
      if(oasis_setup() < 0) {
        fprintf(stderr, "Oasis setup failed\n");
        return -1;
      }
    
      oasis_draw_screen();
    
      t = std::thread([&]() {
    
        void *disp_handle = ui::getDisplayHandle();
        if(!disp_handle) {
          fprintf(stderr, "display handle not found!\n");
          got_interrupt = true;
          return;
        }
    
        if(lvgl_setup(disp_handle) < 0) {
          got_interrupt = true;
          return;
        }
    
        lvgl_draw_screen();
    
        do {
          try {
            lv_task_handler();
          } catch(...) {
            fprintf(stderr, "lvgl got exception!\n");
            got_interrupt = true;
            break;
          }
          usleep(10000);
        } while(got_interrupt == false);
    
        lvgl_cleanup();
      });
    
      fprintf(stdout, "press ctrl+c to exit\n");
    
      do {
        usleep(10000);
      } while(got_interrupt == false);
    
      t.join();
    
    done:
      oasis_cleanup();
    
      return 0;
    }
    
    

    Below is the execution screen. The page1, page2, page3, and page4 buttons belong to the LVGL UI, whereas Hello, World! belongs to the Oasis UI.

    Oasis-LVGL

    Download Example Source Code

    LVGL 9.4#

    This section provides an example of using lvgl 9.4 as the primary application UI framework on the KL730 chipset.

    The target environment assumes a 720x1920 display layout where LVGL operates on a 90-degree rotated 1920x720 canvas.

    In this setup, a device object for the display driver is created, which is subsequently referenced inside the flush_cb handler.

    Only the LVGL modifications distinct from the LVGL 6.1.2 example are described below.

    Define the display global variables:

    #define PIXEL_STRIDE 4
    static int32_t gui_width = 720;
    static int32_t gui_height = 1920;
    static int32_t gui_stride = ALIGN_64B(gui_width*PIXEL_STRIDE);
    static int32_t gui_x = 0;
    static int32_t gui_y = 0;
    

    Configure the LVGL properties. When display rotation is disabled, use the LV_DISPLAY_RENDER_MODE_DIRECT mode to write rendering output directly to the hardware frame buffer. When rotation is enabled, use the LV_DISPLAY_RENDER_MODE_PARTIAL mode to rotate and commit contents to the frame buffer block by block.

    static int32_t lvgl_setup(void *disp_handle)
    {
      lv_display_t *disp = NULL;
      LV_CUSTOM_DRV_T *drvobj = NULL;
      lv_color_format_t cf = LV_COLOR_FORMAT_ARGB8888;
      lv_display_rotation_t rotation = LV_DISPLAY_ROTATION_90;
      lv_init();
      lv_tick_set_cb(lv_kl730_get_ms);
    
      drvobj = lv_kl730_create_drvobj(disp_handle, VMF_VDISP_L2, gui_width, gui_height, gui_stride, cf, rotation);
      if(!drvobj) {
        lv_deinit();
        fprintf(stderr, "lv_kl730_create_drvobj failed\n");
        return -1;
      }
    
      disp = lv_display_create(gui_width, gui_height);
      lv_display_set_rotation(disp, rotation);
      lv_display_set_color_format(disp, cf);
      lv_display_set_driver_data(disp, drvobj);
    
      lvgl_canvas_width = lv_display_get_horizontal_resolution(NULL);
      lvgl_canvas_height = lv_display_get_vertical_resolution(NULL);
      lvgl_canvas_stride = lv_draw_buf_width_to_stride(lvgl_canvas_width, cf);
      lvgl_canvas_size = lvgl_canvas_stride*lvgl_canvas_height;
      lvgl_canvas_buffer = (uint8_t*)calloc(1, lvgl_canvas_size);
    
      if(rotation == LV_DISPLAY_ROTATION_90 || rotation == LV_DISPLAY_ROTATION_270) {
        lv_display_set_buffers_with_stride(disp, drvobj->lvBuffers[0], drvobj->lvBuffers[1], drvobj->dwLvBufferSize, gui_stride, LV_DISPLAY_RENDER_MODE_PARTIAL);
        //lv_display_set_flush_cb(disp, lv_kl730_flush_cb_partial);
        lv_display_set_flush_cb(disp, lv_capture_flush_cb);
      } else {
        lv_display_set_buffers_with_stride(disp, drvobj->lvBuffers[0], drvobj->lvBuffers[1], drvobj->dwLvBufferSize, gui_stride, LV_DISPLAY_RENDER_MODE_DIRECT);
        lv_display_set_flush_cb(disp, lv_kl730_flush_cb_direct);
      }
    
      lvgl_init_style();
    
      return 0;
    }
    
    

    Below is the factory function that creates the display driver object:

    LV_CUSTOM_DRV_T *lv_kl730_create_drvobj(void *disp_handle, VMF_VDISP_LAYER layer, int32_t width, int32_t height, int32_t stride, lv_color_format_t cf, lv_display_rotation_t rotation)
    {
      uint32_t buf_size;
      VMF_DMA_1D_INIT_T dma_init = { 0 };
    
      memset(&lv_disp_drv, 0, sizeof(lv_disp_drv));
    
      lv_disp_drv.ptDisp = disp_handle;
      lv_disp_drv.width = width;
      lv_disp_drv.height = height;
      lv_disp_drv.stride = stride;
      lv_disp_drv.dwBufSize = stride*height;
    
      switch(layer) {
      case VMF_VDISP_L0:
        lv_disp_drv.fnDispProc = VMF_VDISP_ProcessOneFrame;
        lv_disp_drv.fnDispStop = VMF_VDISP_Stop;
        break;
      case VMF_VDISP_L1:
        lv_disp_drv.fnDispProc = VMF_VDISP_PIP_ProcessOneFrame;
        lv_disp_drv.fnDispStop = VMF_VDISP_PIP_Stop;
        break;
      case VMF_VDISP_L2:
        lv_disp_drv.fnDispProc = VMF_VDISP_PIP2_ProcessOneFrame;
        lv_disp_drv.fnDispStop = VMF_VDISP_PIP2_Stop;
        break;
      default:
        return NULL;
      }
    
      int32_t lv_buffer_count = 2;
      if(rotation == LV_DISPLAY_ROTATION_90 || rotation == LV_DISPLAY_ROTATION_270) {
        //display's width,height -> lv display height,width
        int32_t lv_src_stride = lv_draw_buf_width_to_stride(height, cf);
        lv_disp_drv.dwLvBufferSize = lv_src_stride*width;
      } else {
        lv_disp_drv.dwLvBufferSize = stride*height;
      }
      buf_size = lv_disp_drv.dwLvBufferSize;
      for (int32_t i = 0; i < 2; i++) {
        void **buf_ptr = &lv_disp_drv.lvBuffers[i];
        if (!(*buf_ptr = MemBroker_GetMemory(buf_size, VMF_ALIGN_TYPE_DEFAULT))) {
          printf("MemBroker_GetMemory failed\n");
          goto exit;
        }
        memset(*buf_ptr, 0, buf_size);
        MemBroker_CacheCopyBack(*buf_ptr, buf_size);
      }
    
      if(rotation == LV_DISPLAY_ROTATION_90 || rotation == LV_DISPLAY_ROTATION_270) {
        void **buf_ptr = &lv_disp_drv.lvBuffers[2];
        if (!(*buf_ptr = MemBroker_GetMemory(lv_disp_drv.dwBufSize, VMF_ALIGN_TYPE_DEFAULT))) {
          printf("MemBroker_GetMemory failed\n");
          goto exit;
        }
        memset(*buf_ptr, 0, buf_size);
        MemBroker_CacheCopyBack(*buf_ptr, buf_size);
      }
    
      for (int32_t q = 0; q < VMF_VIDEO_DISPLAY_MIN_QUEUE_SIZE; q++) {
        uint8_t **ppbyVirtBuf = &lv_disp_drv.apbyVirtBuf[q];
        uint8_t **ppbyPhysBuf = &lv_disp_drv.apbyPhysBuf[q];
        if (!(*ppbyVirtBuf = MemBroker_GetMemory(buf_size, VMF_ALIGN_TYPE_DEFAULT))) {
          printf("MemBroker_GetMemory failed\n");
          goto exit;
        }
        *ppbyPhysBuf = MemBroker_GetPhysAddr(*ppbyVirtBuf);
        memset(*ppbyVirtBuf, 0, buf_size);
        MemBroker_CacheCopyBack(*ppbyVirtBuf, buf_size);
      }
    
      if (!(lv_disp_drv.ptDma = VMF_DMA_Init(1, 128))) {
        printf("VMF_DMA_Init failed\n");
        goto exit;
      }
    
      if (!(lv_disp_drv.ptDmaDesc = VMF_DMA_Descriptor_Create(DMA_1D, &dma_init))) {
        printf("VMF_DMA_Descriptor_Create failed\n");
        goto exit;
      }
    
      return &lv_disp_drv;
    
    exit:
      lv_kl730_destroy_drvobj(&lv_disp_drv);
      return NULL;
    
    }
    

    Below is the flush_cb callback function utilized when display rotation is enabled. Content rotation is managed using the lv_draw_sw_rotate function:

    void lv_kl730_flush_cb_partial(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map)
    {
      VMF_DMA_ADDR_T tDmaAddr = { 0 };
      unsigned dwEnQCnt = 1;
    
      LV_CUSTOM_DRV_T *ptDispDrv = NULL;
    
      (void) area;
      ptDispDrv = lv_display_get_driver_data(disp);
    
      lv_color_format_t cf = lv_display_get_color_format(disp);
      uint32_t px_size = lv_color_format_get_size(cf);
      lv_display_rotation_t rotation = lv_display_get_rotation(disp);
      lv_area_t rotated_area;
      if(rotation == LV_DISPLAY_ROTATION_90 || rotation == LV_DISPLAY_ROTATION_270) {
        rotated_area = *area;
        lv_display_rotate_area(disp, &rotated_area);
        uint32_t src_stride = lv_draw_buf_width_to_stride(lv_area_get_width(area), cf);
        int32_t src_w = lv_area_get_width(area);
        int32_t src_h = lv_area_get_height(area);
    
        uint8_t *rotate_px_map = ptDispDrv->lvBuffers[2];
        if(!rotate_px_map) {
          return;
        }
        int32_t fb_stride = ptDispDrv->stride;
        uint8_t *fb_start = rotate_px_map;
        fb_start += rotated_area.y1 * fb_stride + rotated_area.x1 * px_size;
    
        lv_draw_sw_rotate(px_map, fb_start, src_w, src_h, src_stride, fb_stride, rotation, cf);
        px_map = rotate_px_map;
      } else {
        //use direct mode
        return;
      }
    
      MemBroker_CacheCopyBack(px_map, ptDispDrv->dwBufSize);
    
      tDmaAddr.pbySrcYPhysAddr = MemBroker_GetPhysAddr(px_map);
      tDmaAddr.dwTransSize = ptDispDrv->dwBufSize;
    
      //! EN-QUEUE ALL BUFFERS TO DISPLAY IN THE BEGINNING
      if (!ptDispDrv->bAllEnQ) {
        dwEnQCnt = VMF_VIDEO_DISPLAY_MIN_QUEUE_SIZE;
        ptDispDrv->bAllEnQ = true;
      }
    
      for (unsigned n = 0; n < dwEnQCnt; n++) {
        VMF_VIDEO_BUF_T tFrameBuf = {
          .apbyVirtAddr[0] = ptDispDrv->apbyVirtBuf[ptDispDrv->dwBufIdx],
          .apbyPhysAddr[0] = ptDispDrv->apbyPhysBuf[ptDispDrv->dwBufIdx],
        };
    
        tDmaAddr.pbyDstYPhysAddr = tFrameBuf.apbyPhysAddr[0];
    
        VMF_DMA_Descriptor_Update_Addr(ptDispDrv->ptDmaDesc, &tDmaAddr);
        VMF_DMA_Setup(ptDispDrv->ptDma, &ptDispDrv->ptDmaDesc, 1);
        VMF_DMA_Process(ptDispDrv->ptDma);
    
        ptDispDrv->fnDispProc(ptDispDrv->ptDisp, &tFrameBuf, &ptDispDrv->dwBufIdx);
      }
    
      lv_display_flush_ready(disp);
    }
    
    

    Below is the execution output displaying a 1920x720 canvas mapped onto a 720x1920 Display device panel.

    Oasis-LVGL-9.4

    Download Example Source Code