mirror of
https://github.com/lvgl/lvgl.git
synced 2026-06-02 01:18:04 +08:00
feat(drm): add ability to select display mode (#8992)
This commit is contained in:
@@ -55,7 +55,6 @@ Basic Usage
|
|||||||
|
|
||||||
#include "lvgl/lvgl.h"
|
#include "lvgl/lvgl.h"
|
||||||
#include "lvgl/demos/lv_demos.h"
|
#include "lvgl/demos/lv_demos.h"
|
||||||
#include "lvgl/drivers/drm/lv_linux_drm.h"
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
@@ -152,3 +151,116 @@ No special setup is required beyond the basic DRM initialization shown in :ref:`
|
|||||||
|
|
||||||
For a detailed overview of EGL usage and configuration, see :ref:`egl_driver`.
|
For a detailed overview of EGL usage and configuration, see :ref:`egl_driver`.
|
||||||
|
|
||||||
|
|
||||||
|
Selecting Display Mode
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
Custom mode selection is currently only supported when using DRM with EGL
|
||||||
|
(``LV_LINUX_DRM_USE_EGL`` enabled). When using DRM without EGL, the driver
|
||||||
|
will always use the preferred display mode.
|
||||||
|
|
||||||
|
By default, the DRM driver automatically selects the preferred display mode for the connected display. However, you can customize this behavior by providing a mode selection callback.
|
||||||
|
|
||||||
|
Custom Mode Selection
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
To implement custom mode selection logic, define a callback function and register it with :cpp:func:`lv_linux_drm_set_mode_cb`:
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
#include "lvgl/lvgl.h"
|
||||||
|
|
||||||
|
/* Custom mode selection callback */
|
||||||
|
size_t my_mode_selector(lv_display_t * disp, const lv_linux_drm_mode_t * modes, size_t mode_count)
|
||||||
|
{
|
||||||
|
/* Example: Select the first 1920x1080@60Hz mode */
|
||||||
|
for(size_t i = 0; i < mode_count; i++) {
|
||||||
|
int32_t width = lv_linux_drm_mode_get_horizontal_resolution(&modes[i]);
|
||||||
|
int32_t height = lv_linux_drm_mode_get_vertical_resolution(&modes[i]);
|
||||||
|
int32_t refresh = lv_linux_drm_mode_get_refresh_rate(&modes[i]);
|
||||||
|
|
||||||
|
if(width == 1920 && height == 1080 && refresh == 60) {
|
||||||
|
return i; /* Return the index of the selected mode */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fallback: return the first mode */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
lv_init();
|
||||||
|
|
||||||
|
lv_display_t * disp = lv_linux_drm_create();
|
||||||
|
|
||||||
|
/* Set custom mode selection callback */
|
||||||
|
lv_linux_drm_set_mode_cb(disp, my_mode_selector);
|
||||||
|
|
||||||
|
lv_linux_drm_set_file(disp, "/dev/dri/card0", -1);
|
||||||
|
|
||||||
|
/* ... rest of your application ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
The callback receives an array of available modes and must return the index of the desired mode.
|
||||||
|
|
||||||
|
Mode Information API
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The following functions are available to query mode properties:
|
||||||
|
|
||||||
|
- :cpp:func:`lv_linux_drm_mode_get_horizontal_resolution` - Get width in pixels
|
||||||
|
- :cpp:func:`lv_linux_drm_mode_get_vertical_resolution` - Get height in pixels
|
||||||
|
- :cpp:func:`lv_linux_drm_mode_get_refresh_rate` - Get refresh rate in Hz
|
||||||
|
- :cpp:func:`lv_linux_drm_mode_is_preferred` - Check if mode is the display's preferred/native mode
|
||||||
|
|
||||||
|
Example: Selecting Preferred Mode
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
size_t select_preferred_mode(lv_display_t * disp, const lv_linux_drm_mode_t * modes, size_t mode_count)
|
||||||
|
{
|
||||||
|
/* Find and select the preferred mode */
|
||||||
|
for(size_t i = 0; i < mode_count; i++) {
|
||||||
|
if(lv_linux_drm_mode_is_preferred(&modes[i])) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If no preferred mode found, return the first mode */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Example: Selecting Highest Resolution
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. code-block:: c
|
||||||
|
|
||||||
|
size_t select_highest_resolution(lv_display_t * disp, const lv_linux_drm_mode_t * modes, size_t mode_count)
|
||||||
|
{
|
||||||
|
size_t best_index = 0;
|
||||||
|
int32_t max_pixels = 0;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < mode_count; i++) {
|
||||||
|
int32_t width = lv_linux_drm_mode_get_horizontal_resolution(&modes[i]);
|
||||||
|
int32_t height = lv_linux_drm_mode_get_vertical_resolution(&modes[i]);
|
||||||
|
int32_t pixels = width * height;
|
||||||
|
|
||||||
|
if(pixels > max_pixels) {
|
||||||
|
max_pixels = pixels;
|
||||||
|
best_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return best_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
Notes
|
||||||
|
~~~~~
|
||||||
|
|
||||||
|
- The mode selection callback is called before the display is initialized.
|
||||||
|
- If no callback is set, the driver uses the preferred mode by default.
|
||||||
|
- Ensure the callback always returns a valid index (0 to ``mode_count - 1``).
|
||||||
|
- To restore default behavior, call :cpp:func:`lv_linux_drm_set_mode_cb` with ``NULL`` as the callback.
|
||||||
|
|||||||
@@ -251,6 +251,12 @@ void lv_linux_drm_set_file(lv_display_t * disp, const char * file, int64_t conne
|
|||||||
hor_res, ver_res, lv_display_get_dpi(disp));
|
hor_res, ver_res, lv_display_get_dpi(disp));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lv_linux_drm_set_mode_cb(lv_display_t * disp, lv_linux_drm_select_mode_cb_t callback)
|
||||||
|
{
|
||||||
|
LV_UNUSED(disp);
|
||||||
|
LV_UNUSED(callback);
|
||||||
|
LV_LOG_WARN("DRM without EGL support doesn't currently support setting a mode selection callback");
|
||||||
|
}
|
||||||
/**********************
|
/**********************
|
||||||
* STATIC FUNCTIONS
|
* STATIC FUNCTIONS
|
||||||
**********************/
|
**********************/
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ extern "C" {
|
|||||||
#include "../../../display/lv_display.h"
|
#include "../../../display/lv_display.h"
|
||||||
|
|
||||||
#if LV_USE_LINUX_DRM
|
#if LV_USE_LINUX_DRM
|
||||||
|
#include <xf86drmMode.h>
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
* DEFINES
|
* DEFINES
|
||||||
@@ -26,6 +27,19 @@ extern "C" {
|
|||||||
* TYPEDEFS
|
* TYPEDEFS
|
||||||
**********************/
|
**********************/
|
||||||
|
|
||||||
|
typedef drmModeModeInfo lv_linux_drm_mode_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function type for selecting a DRM display mode
|
||||||
|
* @param disp pointer to the display object
|
||||||
|
* @param modes array of available DRM modes
|
||||||
|
* @param mode_count number of modes in the array
|
||||||
|
* @return index of the selected mode from the modes array
|
||||||
|
*/
|
||||||
|
typedef size_t (*lv_linux_drm_select_mode_cb_t)(lv_display_t * disp,
|
||||||
|
const lv_linux_drm_mode_t * modes,
|
||||||
|
size_t mode_count);
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* GLOBAL PROTOTYPES
|
* GLOBAL PROTOTYPES
|
||||||
**********************/
|
**********************/
|
||||||
@@ -64,6 +78,45 @@ void lv_linux_drm_set_file(lv_display_t * disp, const char * file, int64_t conne
|
|||||||
*/
|
*/
|
||||||
char * lv_linux_drm_find_device_path(void);
|
char * lv_linux_drm_find_device_path(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a callback function for custom DRM mode selection to override the default mode selection behavior
|
||||||
|
*
|
||||||
|
* The default mode selection behavior is selecting the native mode
|
||||||
|
*
|
||||||
|
* @param disp pointer to the display object
|
||||||
|
* @param callback function to be called when a display mode needs to be selected,
|
||||||
|
* or NULL to use the default mode selection behavior
|
||||||
|
*/
|
||||||
|
void lv_linux_drm_set_mode_cb(lv_display_t * disp, lv_linux_drm_select_mode_cb_t callback);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the horizontal resolution of a DRM mode
|
||||||
|
* @param mode pointer to the DRM mode object
|
||||||
|
* @return horizontal resolution in pixels, or 0 if mode is invalid
|
||||||
|
*/
|
||||||
|
int32_t lv_linux_drm_mode_get_horizontal_resolution(const lv_linux_drm_mode_t * mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the vertical resolution of a DRM mode
|
||||||
|
* @param mode pointer to the DRM mode object
|
||||||
|
* @return vertical resolution in pixels, or 0 if mode is invalid
|
||||||
|
*/
|
||||||
|
int32_t lv_linux_drm_mode_get_vertical_resolution(const lv_linux_drm_mode_t * mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the refresh rate of a DRM mode
|
||||||
|
* @param mode pointer to the DRM mode object
|
||||||
|
* @return refresh rate in Hz, or 0 if mode is invalid
|
||||||
|
*/
|
||||||
|
int32_t lv_linux_drm_mode_get_refresh_rate(const lv_linux_drm_mode_t * mode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a DRM mode is the preferred mode for the display
|
||||||
|
* @param mode pointer to the DRM mode object
|
||||||
|
* @return true if this is the preferred/native mode, false otherwise
|
||||||
|
*/
|
||||||
|
bool lv_linux_drm_mode_is_preferred(const lv_linux_drm_mode_t * mode);
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* MACROS
|
* MACROS
|
||||||
**********************/
|
**********************/
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#if LV_USE_LINUX_DRM
|
#if LV_USE_LINUX_DRM
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <xf86drmMode.h>
|
||||||
|
|
||||||
#include "lv_linux_drm.h"
|
#include "lv_linux_drm.h"
|
||||||
#include "../../../stdlib/lv_sprintf.h"
|
#include "../../../stdlib/lv_sprintf.h"
|
||||||
@@ -92,4 +93,36 @@ static char * find_by_class(void)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32_t lv_linux_drm_mode_get_horizontal_resolution(const lv_linux_drm_mode_t * mode)
|
||||||
|
{
|
||||||
|
if(!mode) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return mode->hdisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t lv_linux_drm_mode_get_vertical_resolution(const lv_linux_drm_mode_t * mode)
|
||||||
|
{
|
||||||
|
if(!mode) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return mode->vdisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t lv_linux_drm_mode_get_refresh_rate(const lv_linux_drm_mode_t * mode)
|
||||||
|
{
|
||||||
|
if(!mode) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return mode->vrefresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool lv_linux_drm_mode_is_preferred(const lv_linux_drm_mode_t * mode)
|
||||||
|
{
|
||||||
|
if(!mode) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (mode->type & DRM_MODE_TYPE_PREFERRED) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /*LV_USE_LINUX_DRM*/
|
#endif /*LV_USE_LINUX_DRM*/
|
||||||
|
|||||||
@@ -141,6 +141,15 @@ void lv_linux_drm_set_file(lv_display_t * display, const char * file, int64_t co
|
|||||||
lv_display_add_event_cb(ctx->display, event_cb, LV_EVENT_DELETE, NULL);
|
lv_display_add_event_cb(ctx->display, event_cb, LV_EVENT_DELETE, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lv_linux_drm_set_mode_cb(lv_display_t * disp, lv_linux_drm_select_mode_cb_t callback)
|
||||||
|
{
|
||||||
|
if(!disp) {
|
||||||
|
LV_LOG_ERROR("Cannot set a mode select callback on a NULL display");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lv_drm_ctx_t * ctx = lv_display_get_driver_data(disp);
|
||||||
|
ctx->mode_select_cb = callback;
|
||||||
|
}
|
||||||
|
|
||||||
/**********************
|
/**********************
|
||||||
* STATIC FUNCTIONS
|
* STATIC FUNCTIONS
|
||||||
@@ -621,6 +630,16 @@ static drmModeConnector * drm_get_connector(lv_drm_ctx_t * ctx)
|
|||||||
static drmModeModeInfo * drm_get_mode(lv_drm_ctx_t * ctx)
|
static drmModeModeInfo * drm_get_mode(lv_drm_ctx_t * ctx)
|
||||||
{
|
{
|
||||||
LV_ASSERT_NULL(ctx->drm_connector);
|
LV_ASSERT_NULL(ctx->drm_connector);
|
||||||
|
if(ctx->mode_select_cb) {
|
||||||
|
size_t mode_index = ctx->mode_select_cb(ctx->display, (lv_linux_drm_mode_t *)ctx->drm_connector->modes,
|
||||||
|
(size_t)ctx->drm_connector->count_modes);
|
||||||
|
if(mode_index >= (size_t)ctx->drm_connector->count_modes) {
|
||||||
|
LV_LOG_ERROR("Failed to select drm mode. User select callback return an invalid mode index");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return &ctx->drm_connector->modes[mode_index];
|
||||||
|
}
|
||||||
|
|
||||||
drmModeModeInfo * best_mode = NULL;
|
drmModeModeInfo * best_mode = NULL;
|
||||||
uint32_t best_area = 0;
|
uint32_t best_area = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ extern "C" {
|
|||||||
#include "../../opengles/lv_opengles_texture_private.h"
|
#include "../../opengles/lv_opengles_texture_private.h"
|
||||||
#include "../../opengles/lv_opengles_egl.h"
|
#include "../../opengles/lv_opengles_egl.h"
|
||||||
#include "../../opengles/lv_opengles_egl_private.h"
|
#include "../../opengles/lv_opengles_egl_private.h"
|
||||||
|
#include "lv_linux_drm.h"
|
||||||
|
|
||||||
/*********************
|
/*********************
|
||||||
* DEFINES
|
* DEFINES
|
||||||
@@ -50,6 +51,7 @@ typedef struct {
|
|||||||
struct gbm_bo * gbm_bo_flipped;
|
struct gbm_bo * gbm_bo_flipped;
|
||||||
struct gbm_bo * gbm_bo_presented;
|
struct gbm_bo * gbm_bo_presented;
|
||||||
|
|
||||||
|
lv_linux_drm_select_mode_cb_t mode_select_cb;
|
||||||
int fd;
|
int fd;
|
||||||
bool crtc_isset;
|
bool crtc_isset;
|
||||||
} lv_drm_ctx_t;
|
} lv_drm_ctx_t;
|
||||||
|
|||||||
Reference in New Issue
Block a user