intel/display: replace intel_fbdev with drm/kms

Fixes #4806
This commit is contained in:
Alexander Boettcher 2023-03-24 16:01:48 +01:00 committed by Christian Helmuth
parent ae19ab0cff
commit 1f2dc78feb
13 changed files with 540 additions and 341 deletions

View File

@ -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;
}

View File

@ -1 +1 @@
6d981ad90be7e1fc9eb1dfd8513698d4e2a37a45
564e03bbd3b6d72497bed4f3267ebdc1c407517b

View File

@ -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)}

View File

@ -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

View File

@ -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 <linux/fb.h>
#include <lx_emul/fb.h>
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)
{

View File

@ -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))

View File

@ -115,22 +115,6 @@ void * __vmalloc_node_range(unsigned long size,unsigned long align,unsigned long
}
#include <linux/uaccess.h>
unsigned long _copy_to_user(void __user * to,const void * from,unsigned long n)
{
lx_emul_trace_and_stop(__func__);
}
#include <drm/drm_lease.h>
bool _drm_lease_held(struct drm_file * file_priv,int id)
{
lx_emul_trace_and_stop(__func__);
}
#include <linux/printk.h>
int _printk_deferred(const char * fmt,...)
@ -484,30 +468,6 @@ long drm_ioctl(struct file * filp,unsigned int cmd,unsigned long arg)
}
#include <drm/drm_lease.h>
void drm_lease_destroy(struct drm_master * master)
{
lx_emul_trace_and_stop(__func__);
}
#include <drm/drm_lease.h>
struct drm_master * drm_lease_owner(struct drm_master * master)
{
lx_emul_trace_and_stop(__func__);
}
#include <drm/drm_lease.h>
void drm_lease_revoke(struct drm_master * top)
{
lx_emul_trace_and_stop(__func__);
}
#include <drm/drm_ioctl.h>
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)
{

View File

@ -373,3 +373,22 @@ void * kmem_cache_alloc_lru(struct kmem_cache * cachep,struct list_lru * lru,gfp
unsigned long __FIXADDR_TOP = 0xfffff000;
#include <linux/uaccess.h>
unsigned long _copy_from_user(void * to, const void __user * from,
unsigned long n)
{
memcpy(to, from, n);
return 0;
}
#include <linux/uaccess.h>
unsigned long _copy_to_user(void __user * to, const void * from, unsigned long n)
{
memcpy(to, from, n);
return 0;
}

View File

@ -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 <linux/fb.h> /* struct fb_info */
#include <linux/sched/task.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_print.h>
#include <drm/drm_client.h>
#include <drm_crtc_internal.h>
#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__);
}

View File

@ -138,6 +138,26 @@ struct Framebuffer::Driver
if (apply_config)
Genode::Signal_transmitter(config_handler).submit();
}
template <typename T>
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 <typename T>
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);
});

View File

@ -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

View File

@ -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

View File

@ -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