mirror of
https://github.com/lvgl/lvgl.git
synced 2026-05-30 07:06:19 +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/demos/lv_demos.h"
|
||||
#include "lvgl/drivers/drm/lv_linux_drm.h"
|
||||
|
||||
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`.
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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
|
||||
**********************/
|
||||
|
||||
@@ -17,6 +17,7 @@ extern "C" {
|
||||
#include "../../../display/lv_display.h"
|
||||
|
||||
#if LV_USE_LINUX_DRM
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
@@ -26,6 +27,19 @@ extern "C" {
|
||||
* 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
|
||||
**********************/
|
||||
@@ -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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
**********************/
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#if LV_USE_LINUX_DRM
|
||||
|
||||
#include <dirent.h>
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
#include "lv_linux_drm.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*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -621,6 +630,16 @@ static drmModeConnector * drm_get_connector(lv_drm_ctx_t * ctx)
|
||||
static drmModeModeInfo * drm_get_mode(lv_drm_ctx_t * ctx)
|
||||
{
|
||||
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;
|
||||
uint32_t best_area = 0;
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ extern "C" {
|
||||
#include "../../opengles/lv_opengles_texture_private.h"
|
||||
#include "../../opengles/lv_opengles_egl.h"
|
||||
#include "../../opengles/lv_opengles_egl_private.h"
|
||||
#include "lv_linux_drm.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
@@ -50,6 +51,7 @@ typedef struct {
|
||||
struct gbm_bo * gbm_bo_flipped;
|
||||
struct gbm_bo * gbm_bo_presented;
|
||||
|
||||
lv_linux_drm_select_mode_cb_t mode_select_cb;
|
||||
int fd;
|
||||
bool crtc_isset;
|
||||
} lv_drm_ctx_t;
|
||||
|
||||
Reference in New Issue
Block a user