#include #include #include #include #include #include #include #include #include #include #define TAG "lvgl" void lvgl_devices_attach() { lvgl_lock(); lv_disp_t* lvgl_display = NULL; bool display_updates_slowly = false; struct Device* kernel_display_device = NULL; device_get_first_by_type(&DISPLAY_TYPE, &kernel_display_device); // Placeholder drivers (boards not yet migrated to the kernel display driver) register with a // NULL api: they exist so the devicetree node resolves, but have nothing for LVGL to bind to. if (kernel_display_device != NULL && device_get_driver(kernel_display_device)->api == NULL) { device_put(kernel_display_device); kernel_display_device = NULL; } if (kernel_display_device != NULL) { uint16_t vres = display_get_resolution_y(kernel_display_device); enum DisplayColorFormat color_format = display_get_color_format(kernel_display_device); bool swap_bytes = color_format == DISPLAY_COLOR_FORMAT_RGB565_SWAPPED || color_format == DISPLAY_COLOR_FORMAT_BGR565_SWAPPED || color_format == DISPLAY_COLOR_FORMAT_BGR565; bool display_requires_full_frame = display_has_capability(kernel_display_device, DISPLAY_CAPABILITY_REQUIRES_FULL_FRAME); display_updates_slowly = display_has_capability(kernel_display_device, DISPLAY_CAPABILITY_SLOW_REFRESH); // Without CAP_SWAP_XY the driver can't rotate 90/270 in hardware (display_swap_xy() is // null and silently skipped by lvgl_display_apply_rotation()) - LVGL would still switch // its own logical w/h for those rotations, mismatching the panel's fixed physical // orientation (e.g. RGB/DPI panels, whose video timing is fixed at panel-init time). // sw_rotate makes LVGL rotate the rendered pixels in software instead, so the driver // itself is never asked to do something it can't. bool can_hw_rotate = display_has_capability(kernel_display_device, DISPLAY_CAPABILITY_CAP_SWAP_XY) && display_has_capability(kernel_display_device, DISPLAY_CAPABILITY_CAP_MIRROR); struct LvglDisplayConfig lvgl_display_config = { .buffer_height = vres > 10 ? vres / 10 : vres, .sw_rotate = !can_hw_rotate, .swap_bytes = swap_bytes, .force_full_frame = display_requires_full_frame }; if (lvgl_display_add(kernel_display_device, &lvgl_display_config, &lvgl_display) == ERROR_NONE) { LOG_I(TAG, "Bound %s to LVGL", kernel_display_device->name); } else { LOG_E(TAG, "Failed to bind %s to LVGL", kernel_display_device->name); } device_put(kernel_display_device); } struct Device* kernel_pointer_device = NULL; device_get_first_by_type(&POINTER_TYPE, &kernel_pointer_device); if (kernel_pointer_device != NULL && device_get_driver(kernel_pointer_device)->api == NULL) { device_put(kernel_pointer_device); kernel_pointer_device = NULL; } if (kernel_pointer_device != NULL) { lv_indev_t* lvgl_pointer_device; if (lvgl_pointer_add(kernel_pointer_device, lvgl_display, &lvgl_pointer_device) == ERROR_NONE) { LOG_I(TAG, "Bound %s to LVGL", kernel_pointer_device->name); // Slow panels cause taps to be missed due to the long update time, prevent that if (display_updates_slowly ) { lv_indev_set_long_press_time(lvgl_pointer_device, 2000); } } else { LOG_E(TAG, "Failed to bind %s to LVG", kernel_pointer_device->name); } device_put(kernel_pointer_device); } struct Device* kernel_keyboard_device = NULL; device_get_first_by_type(&KEYBOARD_TYPE, &kernel_keyboard_device); lv_indev_t* lvgl_keyboard_device; if (kernel_keyboard_device != NULL) { if (lvgl_keyboard_add(kernel_keyboard_device, lvgl_display, &lvgl_keyboard_device) == ERROR_NONE) { LOG_I(TAG, "Bound %s to LVGL", kernel_keyboard_device->name); } else { LOG_E(TAG, "Failed to bind %s to LVGL", kernel_keyboard_device->name); } device_put(kernel_keyboard_device); } lvgl_unlock(); } void lvgl_devices_detach() { lvgl_lock(); lv_indev_t* device = lv_indev_get_next(NULL); while (device != NULL) { lv_indev_type_t type = lv_indev_get_type(device); if (type == LV_INDEV_TYPE_POINTER) { lvgl_pointer_remove(device); } else if (type == LV_INDEV_TYPE_KEYPAD) { lvgl_keyboard_remove(device); } else { lv_indev_delete(device); } // Always get the first item, because getting the next one doesn't work as the current pointer just became corrupt device = lv_indev_get_next(NULL); } lv_disp_t* display = lv_disp_get_next(NULL); while (display != NULL) { lv_display_delete(display); display = lv_disp_get_next(NULL); } lvgl_lock(); }