From 1f2dc78feba3dbbbfa96ee5307c936adb123d29b Mon Sep 17 00:00:00 2001 From: Alexander Boettcher Date: Fri, 24 Mar 2023 16:01:48 +0100 Subject: [PATCH] intel/display: replace intel_fbdev with drm/kms Fixes #4806 --- repos/dde_linux/patches/i915_fb_resize.patch | 21 - repos/dde_linux/ports/linux.hash | 2 +- repos/dde_linux/ports/linux.port | 3 +- .../src/drivers/framebuffer/intel/pc/dep.list | 1 + .../pc/src/drivers/framebuffer/intel/pc/fb.c | 36 +- .../pc/src/drivers/framebuffer/intel/pc/gem.c | 14 +- .../framebuffer/intel/pc/generated_dummies.c | 61 -- .../drivers/framebuffer/intel/pc/lx_emul.c | 19 + .../drivers/framebuffer/intel/pc/lx_user.c | 653 +++++++++++++----- .../src/drivers/framebuffer/intel/pc/main.cc | 60 +- .../intel/pc/spec/x86_32/source.list | 5 +- .../intel/pc/spec/x86_64/source.list | 5 +- .../drivers/framebuffer/intel/pc/target.inc | 1 + 13 files changed, 540 insertions(+), 341 deletions(-) delete mode 100644 repos/dde_linux/patches/i915_fb_resize.patch diff --git a/repos/dde_linux/patches/i915_fb_resize.patch b/repos/dde_linux/patches/i915_fb_resize.patch deleted file mode 100644 index d7a8eeb9c6..0000000000 --- a/repos/dde_linux/patches/i915_fb_resize.patch +++ /dev/null @@ -1,21 +0,0 @@ ---- a/drivers/gpu/drm/i915/display/intel_fbdev.c -+++ b/drivers/gpu/drm/i915/display/intel_fbdev.c -@@ -251,6 +251,18 @@ - &view, false, &flags); - if (IS_ERR(vma)) { - ret = PTR_ERR(vma); -+ -+ /* If the partial allocation is not reverted, the next -+ * i915_fb()->funcs->fb_probe (which calls intelfb_create) -+ * will try the old resolution, which failed and fails again, -+ * instead of using the new smaller resolution. -+ */ -+ if (ret == -ENOMEM && ifbdev->fb) { -+ printk("%s -ENOMEM, remove fb\n", __func__); -+ drm_framebuffer_put(&ifbdev->fb->base); -+ ifbdev->fb = NULL; -+ } -+ - goto out_unlock; - } - diff --git a/repos/dde_linux/ports/linux.hash b/repos/dde_linux/ports/linux.hash index e0eba16af2..9dac3aacd8 100644 --- a/repos/dde_linux/ports/linux.hash +++ b/repos/dde_linux/ports/linux.hash @@ -1 +1 @@ -6d981ad90be7e1fc9eb1dfd8513698d4e2a37a45 +564e03bbd3b6d72497bed4f3267ebdc1c407517b diff --git a/repos/dde_linux/ports/linux.port b/repos/dde_linux/ports/linux.port index 309e35c417..1518be3cef 100644 --- a/repos/dde_linux/ports/linux.port +++ b/repos/dde_linux/ports/linux.port @@ -9,12 +9,11 @@ DIR(linux) := src/linux # # Patches # -PATCH_FILES := i915_irq.patch i915_fb_resize.patch \ +PATCH_FILES := i915_irq.patch \ iwlwifi_enable_irq_before_pnvm.patch \ iwlwifi_limit_rx_bufs.patch \ workqueue_deadlock.patch PATCHES += $(addprefix patches/,$(PATCH_FILES)) PATCH_OPT(patches/i915_irq.patch) := -p1 -d${DIR(linux)} -PATCH_OPT(patches/i915_fb_resize.patch) := -p1 -d${DIR(linux)} PATCH_OPT(patches/workqueue_deadlock.patch) := -p1 -d${DIR(linux)} diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/dep.list b/repos/pc/src/drivers/framebuffer/intel/pc/dep.list index 8301dbf120..da5932c39c 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/dep.list +++ b/repos/pc/src/drivers/framebuffer/intel/pc/dep.list @@ -247,6 +247,7 @@ drivers/gpu/drm/i915/i915_sysfs.h drivers/gpu/drm/i915/i915_tasklet.h drivers/gpu/drm/i915/i915_trace.h drivers/gpu/drm/i915/i915_ttm_buddy_manager.h +drivers/gpu/drm/i915/i915_user_extensions.h drivers/gpu/drm/i915/i915_utils.h drivers/gpu/drm/i915/i915_vgpu.h drivers/gpu/drm/i915/i915_vma.h diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/fb.c b/repos/pc/src/drivers/framebuffer/intel/pc/fb.c index 848ba3635b..240021045e 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/fb.c +++ b/repos/pc/src/drivers/framebuffer/intel/pc/fb.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2021 Genode Labs GmbH + * Copyright (C) 2021-2023 Genode Labs GmbH * * This file is distributed under the terms of the GNU General Public License * version 2. @@ -16,40 +16,6 @@ #include #include -struct fb_info * framebuffer_alloc(size_t size,struct device * dev) -{ -#define BYTES_PER_LONG (BITS_PER_LONG/8) -#define PADDING (BYTES_PER_LONG - (sizeof(struct fb_info) % BYTES_PER_LONG)) - int fb_info_size = sizeof(struct fb_info); - struct fb_info *info; - char *p; - - if (size) { - fb_info_size += PADDING; - } - - p = kzalloc(fb_info_size + size, GFP_KERNEL); - - if (!p) - return NULL; - - info = (struct fb_info *) p; - - if (size) - info->par = p + fb_info_size; - - info->device = dev; - info->fbcon_rotate_hint = -1; - -#if IS_ENABLED(CONFIG_FB_BACKLIGHT) - mutex_init(&info->bl_curve_mutex); -#endif - - return info; -#undef PADDING -#undef BYTES_PER_LONG -} - int register_framebuffer(struct fb_info * fb_info) { diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/gem.c b/repos/pc/src/drivers/framebuffer/intel/pc/gem.c index 1e43bd9f1c..0e18a622ba 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/gem.c +++ b/repos/pc/src/drivers/framebuffer/intel/pc/gem.c @@ -5,9 +5,9 @@ */ /* - * Copyright © 2008-2015 Intel Corporation + * Copyright © 2008-2023 Intel Corporation * - * Copyright (C) 2022 Genode Labs GmbH + * Copyright (C) 2022-2023 Genode Labs GmbH * * This file is distributed under the terms of the GNU General Public License * version 2. @@ -242,16 +242,6 @@ int i915_gem_init(struct drm_i915_private *dev_priv) unsigned int i; int ret; - /* request & enforce max resolution if smaller than hardware limits */ - struct genode_mode dummy_mode = { }; - lx_emul_i915_connector_config("dummy", &dummy_mode); - if (dummy_mode.max_width && dummy_mode.max_height) { - if (dev_priv->drm.mode_config.max_width > dummy_mode.max_width) - dev_priv->drm.mode_config.max_width = dummy_mode.max_width; - if (dev_priv->drm.mode_config.max_height > dummy_mode.max_height) - dev_priv->drm.mode_config.max_height = dummy_mode.max_height; - } - /* We need to fallback to 4K pages if host doesn't support huge gtt. */ /* if (intel_vgpu_active(dev_priv) && !intel_vgpu_has_huge_gtt(dev_priv)) diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/generated_dummies.c b/repos/pc/src/drivers/framebuffer/intel/pc/generated_dummies.c index b779c62990..a731a80fb2 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/generated_dummies.c +++ b/repos/pc/src/drivers/framebuffer/intel/pc/generated_dummies.c @@ -115,22 +115,6 @@ void * __vmalloc_node_range(unsigned long size,unsigned long align,unsigned long } -#include - -unsigned long _copy_to_user(void __user * to,const void * from,unsigned long n) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -bool _drm_lease_held(struct drm_file * file_priv,int id) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int _printk_deferred(const char * fmt,...) @@ -484,30 +468,6 @@ long drm_ioctl(struct file * filp,unsigned int cmd,unsigned long arg) } -#include - -void drm_lease_destroy(struct drm_master * master) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -struct drm_master * drm_lease_owner(struct drm_master * master) -{ - lx_emul_trace_and_stop(__func__); -} - - -#include - -void drm_lease_revoke(struct drm_master * top) -{ - lx_emul_trace_and_stop(__func__); -} - - #include int drm_noop(struct drm_device * dev,void * data,struct drm_file * file_priv) @@ -798,20 +758,6 @@ int i915_gem_context_setparam_ioctl(struct drm_device * dev,void * data,struct d } -extern int i915_gem_create_ext_ioctl(struct drm_device * dev,void * data,struct drm_file * file); -int i915_gem_create_ext_ioctl(struct drm_device * dev,void * data,struct drm_file * file) -{ - lx_emul_trace_and_stop(__func__); -} - - -extern int i915_gem_create_ioctl(struct drm_device * dev,void * data,struct drm_file * file); -int i915_gem_create_ioctl(struct drm_device * dev,void * data,struct drm_file * file) -{ - lx_emul_trace_and_stop(__func__); -} - - extern void i915_gem_drain_freed_objects(struct drm_i915_private * i915); void i915_gem_drain_freed_objects(struct drm_i915_private * i915) { @@ -847,13 +793,6 @@ void i915_gem_driver_unregister(struct drm_i915_private * i915) } -extern int i915_gem_dumb_create(struct drm_file * file,struct drm_device * dev,struct drm_mode_create_dumb * args); -int i915_gem_dumb_create(struct drm_file * file,struct drm_device * dev,struct drm_mode_create_dumb * args) -{ - lx_emul_trace_and_stop(__func__); -} - - extern int i915_gem_dumb_mmap_offset(struct drm_file * file,struct drm_device * dev,u32 handle,u64 * offset); int i915_gem_dumb_mmap_offset(struct drm_file * file,struct drm_device * dev,u32 handle,u64 * offset) { diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/lx_emul.c b/repos/pc/src/drivers/framebuffer/intel/pc/lx_emul.c index ba98629ebb..bdb0db11dc 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/lx_emul.c +++ b/repos/pc/src/drivers/framebuffer/intel/pc/lx_emul.c @@ -373,3 +373,22 @@ void * kmem_cache_alloc_lru(struct kmem_cache * cachep,struct list_lru * lru,gfp unsigned long __FIXADDR_TOP = 0xfffff000; + + +#include + +unsigned long _copy_from_user(void * to, const void __user * from, + unsigned long n) +{ + memcpy(to, from, n); + return 0; +} + + +#include + +unsigned long _copy_to_user(void __user * to, const void * from, unsigned long n) +{ + memcpy(to, from, n); + return 0; +} diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/lx_user.c b/repos/pc/src/drivers/framebuffer/intel/pc/lx_user.c index f102e9e567..d9199f6064 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/lx_user.c +++ b/repos/pc/src/drivers/framebuffer/intel/pc/lx_user.c @@ -5,52 +5,95 @@ */ /* - * Copyright (C) 2022 Genode Labs GmbH + * Copyright (C) 2022-2023 Genode Labs GmbH * * This file is distributed under the terms of the GNU General Public License * version 2. */ +#include /* struct fb_info */ #include -#include -#include +#include +#include #include "i915_drv.h" #include "display/intel_backlight.h" #include "display/intel_display_types.h" -#include "display/intel_opregion.h" -#include "display/intel_panel.h" -#include "display/intel_fbdev.h" +#include "display/intel_fb_pin.h" #include "lx_emul.h" enum { MAX_BRIGHTNESS = 100, INVALID_BRIGHTNESS = MAX_BRIGHTNESS + 1 }; -struct task_struct * lx_user_task = NULL; -static struct drm_i915_private *i915 = NULL; + struct task_struct * lx_user_task = NULL; +static struct drm_client_dev * dev_client = NULL; -static struct drm_fb_helper * i915_fb(void) + +static int user_register_fb(struct drm_client_dev const * const dev, + struct fb_info * const info, + struct drm_mode_fb_cmd2 const * const dumb_fb); + + +static int user_attach_fb_to_crtc(struct drm_client_dev * const dev, + struct drm_connector const * const connector, + struct drm_crtc const * const crtc, + struct drm_mode_modeinfo const * const mode, + unsigned const fb_id, + bool const enable); + +static int check_resize_fb(struct drm_client_dev * const dev, + struct drm_mode_create_dumb * const gem_dumb, + struct drm_mode_fb_cmd2 * const dumb_fb, + unsigned const width, + unsigned const height); + + +static inline bool mode_larger(struct drm_display_mode const * const x, + struct drm_display_mode const * const y) { - return i915 ? i915->drm.fb_helper : NULL; + return (uint64_t)x->hdisplay * (uint64_t)x->vdisplay > + (uint64_t)y->hdisplay * (uint64_t)y->vdisplay; +} + + +static inline bool conf_smaller_mode(struct genode_mode const * const g, + struct drm_display_mode const * const p) +{ + return (uint64_t)g->max_width * (uint64_t)g->max_height < + (uint64_t)p->hdisplay * (uint64_t)p->vdisplay; +} + + +static inline bool fb_smaller_mode(struct fb_info const * const info, + struct drm_display_mode const * const mode) +{ + return (uint64_t)info->var.xres * (uint64_t)info->var.yres < + (uint64_t)mode->vdisplay * (uint64_t)mode->hdisplay; } /* * Heuristic to calculate max resolution across all connectors */ -static void preferred_mode(struct drm_display_mode *prefer, uint64_t smaller_as) +static void preferred_mode(struct drm_device const * const dev, + struct drm_display_mode * const prefer, + struct drm_display_mode * const min_mode) { - struct drm_connector *connector = NULL; - struct drm_display_mode *mode = NULL; + struct drm_connector *connector = NULL; + struct drm_display_mode *mode = NULL; + unsigned connector_usable = 0; + struct drm_display_mode max_enforcement = { }; struct drm_connector_list_iter conn_iter; /* read Genode's config per connector */ - drm_connector_list_iter_begin(i915_fb()->dev, &conn_iter); + drm_connector_list_iter_begin(dev, &conn_iter); drm_client_for_each_connector_iter(connector, &conn_iter) { - struct genode_mode conf_mode = { .enabled = 1 }; + struct drm_display_mode smallest = { .hdisplay = ~0, .vdisplay = ~0 }; + struct genode_mode conf_mode = { .enabled = 1 }; + unsigned mode_id = 0; /* check for connector configuration on Genode side */ lx_emul_i915_connector_config(connector->name, &conf_mode); @@ -58,65 +101,92 @@ static void preferred_mode(struct drm_display_mode *prefer, uint64_t smaller_as) if (!conf_mode.enabled) continue; - if (conf_mode.id) { - unsigned mode_id = 0; - list_for_each_entry(mode, &connector->modes, head) { - mode_id ++; + /* look for smallest possible mode or if a specific mode is forced */ + list_for_each_entry(mode, &connector->modes, head) { + mode_id ++; - if (!mode || conf_mode.id != mode_id) - continue; + if (!mode) + continue; - conf_mode.width = mode->hdisplay; - conf_mode.height = mode->vdisplay; - - break; + if (mode_larger(&smallest, mode)) { + smallest.hdisplay = mode->hdisplay; + smallest.vdisplay = mode->vdisplay; } + + if (!conf_mode.id) + continue; + + if (!mode || conf_mode.id != mode_id) + continue; + + conf_mode.width = mode->hdisplay; + conf_mode.height = mode->vdisplay; + + break; } + if (mode_id) + connector_usable ++; + + /* + * If at least on mode is available, store smallest mode if it + * is larger than min_mode of other connectors + */ + if (mode_id && mode_larger(&smallest, min_mode)) + *min_mode = smallest; + /* maximal resolution enforcement */ if (conf_mode.max_width && conf_mode.max_height) { - if (conf_mode.max_width * conf_mode.height < smaller_as) - smaller_as = conf_mode.max_width * conf_mode.max_height + 1; - - if (conf_mode.max_width * conf_mode.height < prefer->hdisplay * prefer->vdisplay) + max_enforcement.hdisplay = conf_mode.max_width; + max_enforcement.vdisplay = conf_mode.max_height; + if (conf_smaller_mode(&conf_mode, prefer)) continue; } if (!conf_mode.width || !conf_mode.height) continue; - if (conf_mode.width * conf_mode.height > prefer->hdisplay * prefer->vdisplay) { + if (!conf_smaller_mode(&conf_mode, prefer)) { prefer->hdisplay = conf_mode.width; prefer->vdisplay = conf_mode.height; } } drm_connector_list_iter_end(&conn_iter); - /* too large check */ - if (smaller_as <= (uint64_t)prefer->hdisplay * prefer->vdisplay) { - prefer->hdisplay = 0; - prefer->vdisplay = 0; - } + /* no modes on any connector, happens during early bootup */ + if (!min_mode->hdisplay || !min_mode->vdisplay) + return; + + /* we got a preferred resolution */ + if (prefer->hdisplay && prefer->vdisplay) + return; /* if too large or nothing configured by Genode's config */ - if (!prefer->hdisplay || !prefer->vdisplay) { - drm_connector_list_iter_begin(i915_fb()->dev, &conn_iter); - drm_client_for_each_connector_iter(connector, &conn_iter) { - list_for_each_entry(mode, &connector->modes, head) { - if (!mode) - continue; + drm_connector_list_iter_begin(dev, &conn_iter); + drm_client_for_each_connector_iter(connector, &conn_iter) { + list_for_each_entry(mode, &connector->modes, head) { + if (!mode) + continue; - if (smaller_as <= (uint64_t)mode->hdisplay * mode->vdisplay) - continue; + if (mode_larger(min_mode, mode)) + continue; - if (mode->hdisplay * mode->vdisplay > prefer->hdisplay * prefer->vdisplay) { - prefer->hdisplay = mode->hdisplay; - prefer->vdisplay = mode->vdisplay; - } + if (max_enforcement.hdisplay && max_enforcement.vdisplay) { + if (mode_larger(mode, &max_enforcement)) + continue; + } + + if (mode_larger(mode, prefer)) { + prefer->hdisplay = mode->hdisplay; + prefer->vdisplay = mode->vdisplay; } } - drm_connector_list_iter_end(&conn_iter); } + drm_connector_list_iter_end(&conn_iter); + + /* handle the "never should happen case" gracefully */ + if (!prefer->hdisplay || !prefer->vdisplay) + *prefer = *min_mode; } @@ -155,68 +225,61 @@ static unsigned get_brightness(struct drm_connector * const connector, } -static bool reconfigure(void * data) +static bool reconfigure(struct drm_client_dev * const dev) { - static uint64_t width_smaller_as = 100000; - static uint64_t height_smaller_as = 100000; + static struct drm_mode_create_dumb gem_dumb = {}; + static struct drm_mode_fb_cmd2 dumb_fb = {}; - struct drm_display_mode *mode = NULL; struct drm_display_mode mode_preferred = {}; + struct drm_display_mode mode_minimum = {}; + struct drm_display_mode mode_real = {}; + struct drm_mode_modeinfo user_mode = {}; + struct drm_display_mode *mode = NULL; struct drm_mode_set *mode_set = NULL; struct fb_info report_fb_info = {}; bool report_fb = false; bool retry = false; - if (!i915_fb()) - return retry; + if (!dev || !dev->dev) + return false; - BUG_ON(!i915_fb()->funcs); - BUG_ON(!i915_fb()->funcs->fb_probe); + preferred_mode(dev->dev, &mode_preferred, &mode_minimum); - preferred_mode(&mode_preferred, width_smaller_as * height_smaller_as); + /* no valid modes on any connector on early boot */ + if (!mode_minimum.hdisplay || !mode_minimum.vdisplay) + return false; - if (mode_preferred.hdisplay && mode_preferred.vdisplay) { - unsigned err = 0; - struct drm_fb_helper_surface_size sizes = {}; + if (mode_larger(&mode_preferred, &mode_minimum)) + mode_real = mode_preferred; + else + mode_real = mode_minimum; - sizes.surface_depth = 24; - sizes.surface_bpp = 32; - sizes.fb_width = mode_preferred.hdisplay; - sizes.fb_height = mode_preferred.vdisplay; - sizes.surface_width = sizes.fb_width; - sizes.surface_height = sizes.fb_height; + { + int const err = check_resize_fb(dev, + &gem_dumb, + &dumb_fb, + mode_real.hdisplay, + mode_real.vdisplay); - err = (*i915_fb()->funcs->fb_probe)(i915_fb(), &sizes); - /* i915_fb()->fb contains adjusted drm_framebuffer object */ - - if (err || !i915_fb()->fbdev) { + if (err) { printk("setting up framebuffer of %ux%u failed - error=%d\n", - mode_preferred.hdisplay, mode_preferred.vdisplay, err); + mode_real.hdisplay, mode_real.vdisplay, err); - if (err == -ENOMEM) { - width_smaller_as = mode_preferred.hdisplay; - height_smaller_as = mode_preferred.vdisplay; - - retry = true; - return retry; - } - } else { - width_smaller_as = 100000; - height_smaller_as = 100000; + return true; } } - if (!i915_fb()->fb) + /* without fb handle created by check_resize_fb we can't proceed */ + if (!dumb_fb.fb_id) return retry; - /* data is adjusted if virtual resolution is not same size as physical fb */ - report_fb_info = *i915_fb()->fbdev; - if (mode_preferred.hdisplay && mode_preferred.vdisplay) { - report_fb_info.var.xres_virtual = mode_preferred.hdisplay; - report_fb_info.var.yres_virtual = mode_preferred.vdisplay; - } + /* prepare fb info for register_framebuffer() evaluated by Genode side */ + report_fb_info.var.xres = mode_real.hdisplay; + report_fb_info.var.yres = mode_real.vdisplay; + report_fb_info.var.xres_virtual = mode_preferred.hdisplay; + report_fb_info.var.yres_virtual = mode_preferred.vdisplay; - drm_client_for_each_modeset(mode_set, &(i915_fb()->client)) { + drm_client_for_each_modeset(mode_set, dev) { struct drm_display_mode *mode_match = NULL; unsigned mode_id = 0; struct drm_connector *connector = NULL; @@ -242,8 +305,7 @@ static bool reconfigure(void * data) continue; /* allocated framebuffer smaller than mode can't be used */ - if (report_fb_info.var.xres * report_fb_info.var.yres < - mode->vdisplay * mode->hdisplay) + if (fb_smaller_mode(&report_fb_info, mode)) continue; /* use mode id if configured and matches exactly */ @@ -280,9 +342,8 @@ static bool reconfigure(void * data) /* apply new mode */ mode_id = 0; list_for_each_entry(mode, &connector->modes, head) { - struct drm_mode_set set; - int err = -1; - bool no_match = false; + int err = -1; + bool no_match = false; mode_id ++; @@ -292,9 +353,8 @@ static bool reconfigure(void * data) /* no matching mode ? */ if (!mode_match) { - /* allocated framebuffer smaller than mode can't be used */ - if (report_fb_info.var.xres * report_fb_info.var.yres < - mode->vdisplay * mode->hdisplay) + /* fb smaller than mode is denied by drm_mode_setcrtc */ + if (fb_smaller_mode(&report_fb_info, mode)) continue; /* use first smaller mode */ @@ -307,36 +367,28 @@ static bool reconfigure(void * data) if (mode_match != mode) continue; - set.crtc = mode_set->crtc; - set.x = 0; - set.y = 0; - set.mode = conf_mode.enabled ? mode : NULL; - set.connectors = &connector; - set.num_connectors = conf_mode.enabled ? 1 : 0; - set.fb = conf_mode.enabled ? i915_fb()->fb : NULL; + /* convert kernel internal mode to user mode expectecd via ioctl */ + drm_mode_convert_to_umode(&user_mode, mode); - if (set.crtc->funcs && set.crtc->funcs->set_config && - drm_drv_uses_atomic_modeset(i915_fb()->dev)) { + /* assign fb & connector to crtc with specified mode */ + err = user_attach_fb_to_crtc(dev, connector, mode_set->crtc, + &user_mode, dumb_fb.fb_id, + conf_mode.enabled); - struct drm_modeset_acquire_ctx ctx; + if (err) + retry = true; + else + report_fb = true; - DRM_MODESET_LOCK_ALL_BEGIN(i915_fb()->dev, ctx, - DRM_MODESET_ACQUIRE_INTERRUPTIBLE, - err); - err = set.crtc->funcs->set_config(&set, &ctx); - - if (!err && conf_mode.enabled && conf_mode.brightness <= MAX_BRIGHTNESS) - set_brightness(conf_mode.brightness, connector); - - DRM_MODESET_LOCK_ALL_END(i915_fb()->dev, ctx, err); - - if (err) - retry = true; - else { - report_fb = true; - } + /* set brightness */ + if (!err && conf_mode.enabled && conf_mode.brightness <= MAX_BRIGHTNESS) { + drm_modeset_lock(&dev->dev->mode_config.connection_mutex, NULL); + set_brightness(conf_mode.enabled ? conf_mode.brightness : 0, + connector); + drm_modeset_unlock(&dev->dev->mode_config.connection_mutex); } + /* diagnostics */ printk("%s: %s name='%s' id=%u %ux%u@%u%s", connector->name ? connector->name : "unnamed", conf_mode.enabled ? " enable" : "disable", @@ -357,7 +409,7 @@ static bool reconfigure(void * data) } if (report_fb) - register_framebuffer(&report_fb_info); + user_register_fb(dev, &report_fb_info, &dumb_fb); return retry; } @@ -368,7 +420,7 @@ static int configure_connectors(void * data) unsigned retry_count = 0; while (true) { - bool retry = reconfigure(data); + bool retry = reconfigure(dev_client); if (retry && retry_count < 3) { retry_count ++; @@ -395,23 +447,6 @@ void lx_user_init(void) } -static int genode_fb_client_hotplug(struct drm_client_dev *client) -{ - /* - * Set deferred_setup to execute codepath of drm_fb_helper_hotplug_event() - * on next connector state change that does not drop modes, which are - * above the current framebuffer resolution. It is required if the - * connected display at runtime is larger than the ones attached already - * during boot. Without this quirk, not all modes are reported on displays - * connected after boot. - */ - i915_fb()->deferred_setup = true; - - lx_emul_i915_hotplug_connector(client); - return 0; -} - - void lx_emul_i915_report(void * lx_data, void * genode_data) { struct drm_client_dev *client = lx_data; @@ -439,6 +474,11 @@ void lx_emul_i915_iterate_modes(void * lx_data, void * genode_data) struct drm_display_mode *prev_mode = NULL; unsigned mode_id = 0; + /* mark modes as unavailable due to max_resolution enforcement */ + struct genode_mode conf_max_mode = { }; + lx_emul_i915_connector_config("dummy", &conf_max_mode); + + list_for_each_entry(mode, &connector->modes, head) { bool skip = false; @@ -456,12 +496,16 @@ void lx_emul_i915_iterate_modes(void * lx_data, void * genode_data) } if (!skip) { - struct genode_mode conf_mode = { .width = mode->hdisplay, - .height = mode->vdisplay, - .preferred = mode->type & (DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DEFAULT), - .hz = drm_mode_vrefresh(mode), - .id = mode_id - }; + bool const max_mode = conf_max_mode.max_width && conf_max_mode.max_height; + + struct genode_mode conf_mode = { + .width = mode->hdisplay, + .height = mode->vdisplay, + .preferred = mode->type & (DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DEFAULT), + .hz = drm_mode_vrefresh(mode), + .id = mode_id, + .enabled = !max_mode || !conf_smaller_mode(&conf_max_mode, mode) + }; static_assert(sizeof(conf_mode.name) == DRM_DISPLAY_MODE_LEN); memcpy(conf_mode.name, mode->name, sizeof(conf_mode.name)); @@ -474,56 +518,283 @@ void lx_emul_i915_iterate_modes(void * lx_data, void * genode_data) } -static const struct drm_client_funcs drm_fbdev_client_funcs = { - .owner = THIS_MODULE, - .hotplug = genode_fb_client_hotplug, -}; - - -static void hotplug_setup(struct drm_device *dev) -{ - struct drm_fb_helper *hotplug_helper; - int ret; - - hotplug_helper = kzalloc(sizeof(*hotplug_helper), GFP_KERNEL); - if (!hotplug_helper) { - drm_err(dev, "Failed to allocate fb_helper\n"); - return; - } - - ret = drm_client_init(dev, &hotplug_helper->client, "fbdev", - &drm_fbdev_client_funcs); - if (ret) { - kfree(hotplug_helper); - drm_err(dev, "Failed to register client: %d\n", ret); - return; - } - - hotplug_helper->preferred_bpp = 32; - - ret = genode_fb_client_hotplug(&hotplug_helper->client); - if (ret) - drm_dbg_kms(dev, "client hotplug ret=%d\n", ret); - - drm_client_register(&hotplug_helper->client); - - hotplug_helper->dev = dev; -} - - int i915_switcheroo_register(struct drm_i915_private *i915_private) { - /* get hold of the function pointers we need for mode setting */ - i915 = i915_private; - - /* register dummy fb_helper to get notifications about hotplug events */ - hotplug_setup(&i915_private->drm); - return 0; } void i915_switcheroo_unregister(struct drm_i915_private *i915) { - lx_emul_trace_and_stop(__func__); + return; +} + + +static int fb_client_hotplug(struct drm_client_dev *client) +{ + /* + * Triggers set up of display pipelines for enabled connectors and + * stores the config in the client's modeset array. + */ + int const result = drm_client_modeset_probe(client, + 0 /* auto width */, + 0 /* auto height */); + if (result) + printk("%s: error on modeset probe %d\n", __func__, result); + + lx_emul_i915_hotplug_connector(client); + + return 0; +} + + +static const struct drm_client_funcs drm_client_funcs = { + .owner = THIS_MODULE, + .hotplug = fb_client_hotplug, +/* + .unregister = + .restore = +*/ +}; + + +static int register_drm_client(struct drm_device * const dev) +{ + int result = -EINVAL; + + dev_client = kzalloc(sizeof(*dev_client), GFP_KERNEL); + if (!dev_client) { + drm_err(dev, "Failed to allocate drm_client_dev\n"); + return -ENOMEM; + } + + result = drm_client_init(dev, dev_client, "genode_client", + &drm_client_funcs); + + /* dev_client->file contains drm_file */ + if (result) { + kfree(dev_client); + drm_err(dev, "Failed to register client: %d\n", result); + return -ENODEV; + } + + drm_client_register(dev_client); + + /* + * Normally set via drm_ioctl() calling 'static int drm_setclientcap()' + * + * Without this feature bit set, drm_mode_setcrtc() denies usage of + * some modes we report as available. + */ + dev_client->file->aspect_ratio_allowed = 1; + + return 0; +} + + +int user_attach_fb_to_crtc(struct drm_client_dev * const dev, + struct drm_connector const * const connector, + struct drm_crtc const * const crtc, + struct drm_mode_modeinfo const * const mode, + unsigned const fb_id, + bool const enable) +{ + int result = -EINVAL; + uint32_t connectors [1] = { connector->base.id }; + struct drm_mode_crtc crtc_req = { + .set_connectors_ptr = (uintptr_t)(&connectors), + .count_connectors = enable ? 1 : 0, + .crtc_id = crtc->base.id, + .fb_id = fb_id, + .x = 0, + .y = 0, /* position on the framebuffer */ + .gamma_size = 0, + .mode_valid = enable, + .mode = *mode, + }; + + result = drm_mode_setcrtc(dev->dev, &crtc_req, dev->file); + if (result) + drm_err(dev->dev, "%s: failed to set crtc %d\n", __func__, result); + + return result; +} + + +static int user_register_fb(struct drm_client_dev const * const dev, + struct fb_info * const info, + struct drm_mode_fb_cmd2 const * const dumb_fb) +{ + intel_wakeref_t wakeref; + + int result = -EINVAL; + struct i915_gtt_view const view = { .type = I915_GTT_VIEW_NORMAL }; + unsigned long flags = 0; + struct i915_vma *vma = NULL; + void __iomem *vaddr = NULL; + struct drm_i915_private *dev_priv = to_i915(dev->dev); + struct drm_framebuffer *fb = drm_framebuffer_lookup(dev->dev, + dev->file, + dumb_fb->fb_id); + + if (!info || !fb) { + printk("%s:%u error setting up info and fb\n", __func__, __LINE__); + return -ENODEV; + } + + wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm); + + /* Pin the GGTT vma for our access via info->screen_base. + * This also validates that any existing fb inherited from the + * BIOS is suitable for own access. + */ + vma = intel_pin_and_fence_fb_obj(fb, false /* phys_cursor */, + &view, false /* use fences */, + &flags); + + if (IS_ERR(vma)) { + intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref); + + result = PTR_ERR(vma); + printk("%s:%u error setting vma %d\n", __func__, __LINE__, result); + return result; + } + + vaddr = i915_vma_pin_iomap(vma); + if (IS_ERR(vaddr)) { + intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref); + + result = PTR_ERR(vaddr); + printk("%s:%u error pin iomap %d\n", __func__, __LINE__, result); + return result; + } + + /* fill framebuffer info for register_framebuffer */ + info->screen_base = vaddr; + info->screen_size = vma->size; + info->fix.line_length = fb->pitches[0]; + info->var.bits_per_pixel = drm_format_info_bpp(fb->format, 0); + + intel_runtime_pm_put(&dev_priv->runtime_pm, wakeref); + + register_framebuffer(info); + + return 0; +} + + +static int check_resize_fb(struct drm_client_dev * const dev, + struct drm_mode_create_dumb * const gem_dumb, + struct drm_mode_fb_cmd2 * const dumb_fb, + unsigned const width, + unsigned const height) +{ + int result = -EINVAL; + + /* paranoia */ + if (!dev || !dev->dev || !dev->file || !gem_dumb || !dumb_fb) + return -ENODEV; + + /* if requested size is smaller, free up current dumb buffer */ + if (gem_dumb->width && gem_dumb->height && + gem_dumb->width * gem_dumb->height < width * height) { + + result = drm_mode_destroy_dumb(dev->dev, gem_dumb->handle, dev->file); + if (result) { + drm_err(dev->dev, "%s: failed to destroy framebuffer %d\n", + __func__, result); + return result; + } + + memset(gem_dumb, 0, sizeof(*gem_dumb)); + memset(dumb_fb, 0, sizeof(*dumb_fb)); + } + + /* allocate dumb framebuffer, on success a GEM object handle is returned */ + if (!gem_dumb->width && !gem_dumb->height) { + gem_dumb->height = height; + gem_dumb->width = width; + gem_dumb->bpp = 32; + gem_dumb->flags = 0; + /* .handle, .pitch, .size written by kernel in gem_dumb */ + + result = drm_mode_create_dumb_ioctl(dev->dev, gem_dumb, dev->file); + if (result) { + drm_err(dev->dev, "%s: failed to create framebuffer %d\n", + __func__, result); + memset(gem_dumb, 0, sizeof(*gem_dumb)); + return -ENODEV; + } + } + + /* bind framwbuffer(GEM object) to drm client */ + if (!dumb_fb->width && !dumb_fb->height) { + /* .fb_id <- written by kernel */ + dumb_fb->width = gem_dumb->width, + dumb_fb->height = gem_dumb->height, + dumb_fb->pixel_format = DRM_FORMAT_XRGB8888, + /* .flags */ + /* up to 4 planes with handle/pitch/offset/modifier can be set */ + dumb_fb->handles[0] = gem_dumb->handle; + dumb_fb->pitches[0] = gem_dumb->pitch; + /* .offsets[4] */ + /* .modifier[4] */ + + result = drm_mode_addfb2_ioctl(dev->dev, dumb_fb, dev->file); + if (result) { + drm_err(dev->dev, "%s: failed to add framebuffer to drm client %d\n", + __func__, result); + memset(dumb_fb, 0, sizeof(*dumb_fb)); + return -ENODEV; + } + } + + return 0; +} + + +int intel_fbdev_init(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = to_i915(dev); + + if (drm_WARN_ON(dev, !HAS_DISPLAY(dev_priv))) + return -ENODEV; + + return register_drm_client(dev); +} + + +void intel_fbdev_fini(struct drm_i915_private *dev_priv) +{ + lx_emul_trace(__func__); +} + + +void intel_fbdev_initial_config_async(struct drm_device *dev) +{ + lx_emul_trace(__func__); +} + + +void intel_fbdev_unregister(struct drm_i915_private *dev_priv) +{ + lx_emul_trace(__func__); +} + + +void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous) +{ + lx_emul_trace(__func__); +} + + +void intel_fbdev_restore_mode(struct drm_device *dev) +{ + lx_emul_trace(__func__); +} + + +void intel_fbdev_output_poll_changed(struct drm_device *dev) +{ + lx_emul_trace(__func__); } diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/main.cc b/repos/pc/src/drivers/framebuffer/intel/pc/main.cc index 66800f75cd..678d5ae4f0 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/main.cc +++ b/repos/pc/src/drivers/framebuffer/intel/pc/main.cc @@ -138,6 +138,26 @@ struct Framebuffer::Driver if (apply_config) Genode::Signal_transmitter(config_handler).submit(); } + + template + void with_max_enforcement(T const &fn) const + { + unsigned max_width = config.xml().attribute_value("max_width", 0u); + unsigned max_height = config.xml().attribute_value("max_height",0u); + + if (max_width && max_height) + fn(max_width, max_height); + } + + template + void with_force(T const &fn) const + { + unsigned force_width = config.xml().attribute_value("force_width", 0u); + unsigned force_height = config.xml().attribute_value("force_height", 0u); + + if (force_width && force_height) + fn(force_width, force_height); + } }; @@ -184,6 +204,17 @@ void Framebuffer::Driver::generate_report(void *lx_data) try { Genode::Reporter::Xml_generator xml(reporter, [&] () { + /* reflect force/max enforcement in report for user clarity */ + with_max_enforcement([&](unsigned width, unsigned height) { + xml.attribute("max_width", width); + xml.attribute("max_height", height); + }); + + with_force([&](unsigned width, unsigned height) { + xml.attribute("force_width", width); + xml.attribute("force_height", height); + }); + lx_emul_i915_report(lx_data, &xml); }); @@ -204,11 +235,6 @@ void Framebuffer::Driver::lookup_config(char const * const name, if (!config.valid()) return; - unsigned force_width = config.xml().attribute_value("force_width", 0u); - unsigned force_height = config.xml().attribute_value("force_height", 0u); - unsigned max_width = config.xml().attribute_value("max_width", 0u); - unsigned max_height = config.xml().attribute_value("max_height", 0u); - /* iterate independently of force* ever to get brightness and hz */ config.xml().for_each_sub_node("connector", [&] (Xml_node &node) { typedef String<32> Name; @@ -229,18 +255,18 @@ void Framebuffer::Driver::lookup_config(char const * const name, mode.id = node.attribute_value("mode_id", 0U); }); - /* enforce forced width/height if configured */ - mode.preferred = force_width && force_height; - if (mode.preferred) { - mode.width = force_width; - mode.height = force_height; - mode.id = 0; - } + mode.preferred = false; + with_force([&](unsigned const width, unsigned const height) { + mode.preferred = true; + mode.width = width; + mode.height = height; + mode.id = 0; + }); - if (max_width && max_height) { - mode.max_width = max_width; - mode.max_height = max_height; - } + with_max_enforcement([&](unsigned const width, unsigned const height) { + mode.max_width = width; + mode.max_height = height; + }); } @@ -319,6 +345,8 @@ void lx_emul_i915_report_modes(void * genode_xml, struct genode_mode *mode) xml.attribute("hz", mode->hz); xml.attribute("mode_id", mode->id); xml.attribute("mode_name", mode->name); + if (!mode->enabled) + xml.attribute("unavailable", true); if (mode->preferred) xml.attribute("preferred", true); }); diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/spec/x86_32/source.list b/repos/pc/src/drivers/framebuffer/intel/pc/spec/x86_32/source.list index cad5239716..c6195d4e38 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/spec/x86_32/source.list +++ b/repos/pc/src/drivers/framebuffer/intel/pc/spec/x86_32/source.list @@ -38,6 +38,7 @@ drivers/gpu/drm/drm_crtc_helper.c drivers/gpu/drm/drm_damage_helper.c drivers/gpu/drm/drm_displayid.c drivers/gpu/drm/drm_drv.c +drivers/gpu/drm/drm_dumb_buffers.c drivers/gpu/drm/drm_edid.c drivers/gpu/drm/drm_encoder.c drivers/gpu/drm/drm_fb_helper.c @@ -45,6 +46,7 @@ drivers/gpu/drm/drm_file.c drivers/gpu/drm/drm_fourcc.c drivers/gpu/drm/drm_framebuffer.c drivers/gpu/drm/drm_gem.c +drivers/gpu/drm/drm_lease.c drivers/gpu/drm/drm_managed.c drivers/gpu/drm/drm_mipi_dsi.c drivers/gpu/drm/drm_mm.c @@ -118,7 +120,6 @@ drivers/gpu/drm/i915/display/intel_dvo.c drivers/gpu/drm/i915/display/intel_fb.c drivers/gpu/drm/i915/display/intel_fb_pin.c drivers/gpu/drm/i915/display/intel_fbc.c -drivers/gpu/drm/i915/display/intel_fbdev.c drivers/gpu/drm/i915/display/intel_fdi.c drivers/gpu/drm/i915/display/intel_fifo_underrun.c drivers/gpu/drm/i915/display/intel_frontbuffer.c @@ -156,6 +157,7 @@ drivers/gpu/drm/i915/display/skl_watermark.c drivers/gpu/drm/i915/display/vlv_dsi.c drivers/gpu/drm/i915/display/vlv_dsi_pll.c drivers/gpu/drm/i915/gem/i915_gem_clflush.c +drivers/gpu/drm/i915/gem/i915_gem_create.c drivers/gpu/drm/i915/gem/i915_gem_domain.c drivers/gpu/drm/i915/gem/i915_gem_internal.c drivers/gpu/drm/i915/gem/i915_gem_lmem.c @@ -190,6 +192,7 @@ drivers/gpu/drm/i915/i915_suspend.c drivers/gpu/drm/i915/i915_sw_fence.c drivers/gpu/drm/i915/i915_sw_fence_work.c drivers/gpu/drm/i915/i915_sysfs.c +drivers/gpu/drm/i915/i915_user_extensions.c drivers/gpu/drm/i915/i915_utils.c drivers/gpu/drm/i915/i915_vma.c drivers/gpu/drm/i915/i915_vma_resource.c diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/spec/x86_64/source.list b/repos/pc/src/drivers/framebuffer/intel/pc/spec/x86_64/source.list index ffa77c24ec..f01b51608b 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/spec/x86_64/source.list +++ b/repos/pc/src/drivers/framebuffer/intel/pc/spec/x86_64/source.list @@ -41,6 +41,7 @@ drivers/gpu/drm/drm_crtc_helper.c drivers/gpu/drm/drm_damage_helper.c drivers/gpu/drm/drm_displayid.c drivers/gpu/drm/drm_drv.c +drivers/gpu/drm/drm_dumb_buffers.c drivers/gpu/drm/drm_edid.c drivers/gpu/drm/drm_encoder.c drivers/gpu/drm/drm_fb_helper.c @@ -48,6 +49,7 @@ drivers/gpu/drm/drm_file.c drivers/gpu/drm/drm_fourcc.c drivers/gpu/drm/drm_framebuffer.c drivers/gpu/drm/drm_gem.c +drivers/gpu/drm/drm_lease.c drivers/gpu/drm/drm_managed.c drivers/gpu/drm/drm_mipi_dsi.c drivers/gpu/drm/drm_mm.c @@ -121,7 +123,6 @@ drivers/gpu/drm/i915/display/intel_dvo.c drivers/gpu/drm/i915/display/intel_fb.c drivers/gpu/drm/i915/display/intel_fb_pin.c drivers/gpu/drm/i915/display/intel_fbc.c -drivers/gpu/drm/i915/display/intel_fbdev.c drivers/gpu/drm/i915/display/intel_fdi.c drivers/gpu/drm/i915/display/intel_fifo_underrun.c drivers/gpu/drm/i915/display/intel_frontbuffer.c @@ -159,6 +160,7 @@ drivers/gpu/drm/i915/display/skl_watermark.c drivers/gpu/drm/i915/display/vlv_dsi.c drivers/gpu/drm/i915/display/vlv_dsi_pll.c drivers/gpu/drm/i915/gem/i915_gem_clflush.c +drivers/gpu/drm/i915/gem/i915_gem_create.c drivers/gpu/drm/i915/gem/i915_gem_domain.c drivers/gpu/drm/i915/gem/i915_gem_internal.c drivers/gpu/drm/i915/gem/i915_gem_lmem.c @@ -193,6 +195,7 @@ drivers/gpu/drm/i915/i915_suspend.c drivers/gpu/drm/i915/i915_sw_fence.c drivers/gpu/drm/i915/i915_sw_fence_work.c drivers/gpu/drm/i915/i915_sysfs.c +drivers/gpu/drm/i915/i915_user_extensions.c drivers/gpu/drm/i915/i915_utils.c drivers/gpu/drm/i915/i915_vma.c drivers/gpu/drm/i915/i915_vma_resource.c diff --git a/repos/pc/src/drivers/framebuffer/intel/pc/target.inc b/repos/pc/src/drivers/framebuffer/intel/pc/target.inc index 50556f5af7..ad88889896 100644 --- a/repos/pc/src/drivers/framebuffer/intel/pc/target.inc +++ b/repos/pc/src/drivers/framebuffer/intel/pc/target.inc @@ -36,6 +36,7 @@ fail endif INC_DIR += $(LX_SRC_DIR)/drivers/gpu/drm/i915 +INC_DIR += $(LX_SRC_DIR)/drivers/gpu/drm # # The generated dummies file pulls in functions without header