mirror of
https://github.com/genodelabs/genode.git
synced 2025-02-20 17:52:52 +00:00
intel_fb: handle too large framebuffer allocation
In case the allocation fails for the largest possible connector mode, try smaller modes. Additional add option to specify maximal resolution. Issue #4659
This commit is contained in:
parent
72217a6771
commit
eb318d5ceb
@ -16,6 +16,12 @@ if {[get_cmd_switch --autopilot] && [have_board linux]} {
|
||||
|
||||
set use_gpu 1
|
||||
set use_top 0
|
||||
set use_fb_controller 0
|
||||
if {$use_fb_controller} {
|
||||
set apply_on_hotplug "no"
|
||||
} else {
|
||||
set apply_on_hotplug "yes"
|
||||
}
|
||||
|
||||
set build_components {
|
||||
core init timer
|
||||
@ -193,7 +199,7 @@ append config {
|
||||
<ram/>
|
||||
<import>
|
||||
<inline name="fb_drv.config">
|
||||
<config ld_verbose="yes">
|
||||
<config ld_verbose="yes" apply_on_hotplug="} $apply_on_hotplug {">
|
||||
<report connectors="yes"/>
|
||||
</config>
|
||||
</inline>
|
||||
@ -253,11 +259,12 @@ append config {
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
<any-service> <parent/> <any-child /> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
</start>}
|
||||
|
||||
append_if $use_fb_controller {
|
||||
<start name="intel_fb_controller" priority="-1">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<config artifical_update_ms="0"> <!-- off -->
|
||||
<config>
|
||||
<vfs> <fs/> </vfs>
|
||||
</config>
|
||||
<route>
|
||||
@ -265,8 +272,9 @@ append config {
|
||||
<service name="ROM" label="connectors"> <child name="report_rom"/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>
|
||||
</start>}
|
||||
|
||||
append config {
|
||||
<start name="test-framebuffer" priority="-1">
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
|
@ -39,6 +39,11 @@ about the change in resolution.
|
||||
The brightness value is in percent and takes effect only if supported by
|
||||
the hardware.
|
||||
|
||||
The maximal physical resolution can be enforced by:
|
||||
|
||||
! <config max_width="2560" max_height="1440">
|
||||
! </config>
|
||||
|
||||
The virtual resolution can be enforced by:
|
||||
|
||||
! <config force_width="1024" force_height="768">
|
||||
|
@ -20,6 +20,9 @@
|
||||
|
||||
void *emul_alloc_shmem_file_buffer(unsigned long size)
|
||||
{
|
||||
if (!size)
|
||||
return nullptr;
|
||||
|
||||
auto &buffer = Lx_kit::env().memory.alloc_buffer(size);
|
||||
return reinterpret_cast<void *>(buffer.virt_addr());
|
||||
}
|
||||
|
@ -246,6 +246,16 @@ int i915_gem_init(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
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))
|
||||
|
@ -521,14 +521,6 @@ bool file_ns_capable(const struct file * file,struct user_namespace * ns,int cap
|
||||
}
|
||||
|
||||
|
||||
#include <linux/file.h>
|
||||
|
||||
void fput(struct file * file)
|
||||
{
|
||||
lx_emul_trace_and_stop(__func__);
|
||||
}
|
||||
|
||||
|
||||
#include <linux/fb.h>
|
||||
|
||||
void framebuffer_release(struct fb_info * info)
|
||||
|
@ -108,6 +108,9 @@ struct file *shmem_file_setup(char const *name, loff_t size,
|
||||
struct shmem_file_buffer *private_data;
|
||||
loff_t const nrpages = (size / PAGE_SIZE) + ((size % (PAGE_SIZE)) ? 1 : 0);
|
||||
|
||||
if (!size)
|
||||
return (struct file*)ERR_PTR(-EINVAL);
|
||||
|
||||
f = kzalloc(sizeof (struct file), 0);
|
||||
if (!f) {
|
||||
return (struct file*)ERR_PTR(-ENOMEM);
|
||||
@ -165,6 +168,35 @@ err_inode:
|
||||
}
|
||||
|
||||
|
||||
static void _free_file(struct file *file)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct address_space *mapping;
|
||||
struct shmem_file_buffer *private_data;
|
||||
|
||||
mapping = file->f_mapping;
|
||||
inode = file->f_inode;
|
||||
private_data = mapping->private_data;
|
||||
|
||||
lx_emul_forget_pages(private_data->addr, mapping->nrpages << 12);
|
||||
emul_free_shmem_file_buffer(private_data->addr);
|
||||
|
||||
kfree(private_data);
|
||||
kfree(mapping);
|
||||
kfree(inode);
|
||||
kfree(file->f_path.dentry);
|
||||
kfree(file);
|
||||
}
|
||||
|
||||
|
||||
void fput(struct file *file)
|
||||
{
|
||||
if (atomic_long_sub_and_test(1, &file->f_count)) {
|
||||
_free_file(file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct page *shmem_read_mapping_page_gfp(struct address_space *mapping,
|
||||
pgoff_t index, gfp_t gfp)
|
||||
{
|
||||
|
@ -29,6 +29,7 @@ struct dma_fence_work_ops;
|
||||
void lx_emul_time_udelay(unsigned long usec);
|
||||
|
||||
void *emul_alloc_shmem_file_buffer(unsigned long);
|
||||
void emul_free_shmem_file_buffer(void *);
|
||||
|
||||
void * intel_io_mem_map(unsigned long offset, unsigned long size);
|
||||
|
||||
|
@ -17,6 +17,8 @@
|
||||
struct genode_mode {
|
||||
unsigned width;
|
||||
unsigned height;
|
||||
unsigned max_width;
|
||||
unsigned max_height;
|
||||
unsigned hz;
|
||||
unsigned brightness;
|
||||
unsigned enabled;
|
||||
|
@ -36,7 +36,7 @@ static struct drm_fb_helper * i915_fb(void) { return &i915->fbdev->helper; }
|
||||
/*
|
||||
* Heuristic to calculate max resolution across all connectors
|
||||
*/
|
||||
static void preferred_mode(struct drm_display_mode *prefer)
|
||||
static void preferred_mode(struct drm_display_mode *prefer, uint64_t smaller_as)
|
||||
{
|
||||
struct drm_connector *connector = NULL;
|
||||
struct drm_display_mode *mode = NULL;
|
||||
@ -68,6 +68,15 @@ static void preferred_mode(struct drm_display_mode *prefer)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!conf_mode.width || !conf_mode.height)
|
||||
continue;
|
||||
|
||||
@ -78,7 +87,13 @@ static void preferred_mode(struct drm_display_mode *prefer)
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
|
||||
/* if nothing was configured by Genode's config, apply heuristic */
|
||||
/* too large check */
|
||||
if (smaller_as <= (uint64_t)prefer->hdisplay * prefer->vdisplay) {
|
||||
prefer->hdisplay = 0;
|
||||
prefer->vdisplay = 0;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
@ -86,6 +101,9 @@ static void preferred_mode(struct drm_display_mode *prefer)
|
||||
if (!mode)
|
||||
continue;
|
||||
|
||||
if (smaller_as <= (uint64_t)mode->hdisplay * mode->vdisplay)
|
||||
continue;
|
||||
|
||||
if (mode->hdisplay * mode->vdisplay > prefer->hdisplay * prefer->vdisplay) {
|
||||
prefer->hdisplay = mode->hdisplay;
|
||||
prefer->vdisplay = mode->vdisplay;
|
||||
@ -134,6 +152,9 @@ static unsigned get_brightness(struct drm_connector * const connector,
|
||||
|
||||
static bool reconfigure(void * data)
|
||||
{
|
||||
static uint64_t width_smaller_as = 100000;
|
||||
static uint64_t height_smaller_as = 100000;
|
||||
|
||||
struct drm_display_mode *mode = NULL;
|
||||
struct drm_display_mode mode_preferred = {};
|
||||
struct drm_mode_set *mode_set = NULL;
|
||||
@ -147,7 +168,7 @@ static bool reconfigure(void * data)
|
||||
BUG_ON(!i915_fb()->funcs);
|
||||
BUG_ON(!i915_fb()->funcs->fb_probe);
|
||||
|
||||
preferred_mode(&mode_preferred);
|
||||
preferred_mode(&mode_preferred, width_smaller_as * height_smaller_as);
|
||||
|
||||
if (mode_preferred.hdisplay && mode_preferred.vdisplay) {
|
||||
unsigned err = 0;
|
||||
@ -161,10 +182,44 @@ static bool reconfigure(void * data)
|
||||
sizes.surface_height = sizes.fb_height;
|
||||
|
||||
err = (*i915_fb()->funcs->fb_probe)(i915_fb(), &sizes);
|
||||
/* i915_fb()->fb contains adjusted drm_frambuffer object */
|
||||
/* i915_fb()->fb contains adjusted drm_framebuffer object */
|
||||
|
||||
if (err || !i915_fb()->fbdev)
|
||||
printk("setting up framebuffer failed - error=%d\n", err);
|
||||
if (err || !i915_fb()->fbdev) {
|
||||
printk("setting up framebuffer of %ux%u failed - error=%d\n",
|
||||
mode_preferred.hdisplay, mode_preferred.vdisplay, err);
|
||||
|
||||
if (err == -ENOMEM) {
|
||||
/*
|
||||
* roll back code for intelfb_create() in
|
||||
* drivers/gpu/drm/i915/display/intel_fbdev.c:
|
||||
*
|
||||
* vma = intel_pin_and_fence_fb_obj(&ifbdev->fb->base, false,
|
||||
* &view, false, &flags);
|
||||
* if (IS_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.
|
||||
*/
|
||||
struct intel_fbdev *ifbdev =
|
||||
container_of(i915_fb(), struct intel_fbdev, helper);
|
||||
|
||||
if (ifbdev && ifbdev->fb) {
|
||||
drm_framebuffer_put(&ifbdev->fb->base);
|
||||
ifbdev->fb = NULL;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (!i915_fb()->fb)
|
||||
@ -203,6 +258,11 @@ static bool reconfigure(void * data)
|
||||
if (!mode)
|
||||
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)
|
||||
continue;
|
||||
|
||||
/* use mode id if configured and matches exactly */
|
||||
if (conf_mode.id) {
|
||||
if (conf_mode.id != mode_id)
|
||||
@ -248,7 +308,13 @@ static bool reconfigure(void * data)
|
||||
|
||||
/* no matching mode ? */
|
||||
if (!mode_match) {
|
||||
/* use first mode */
|
||||
|
||||
/* allocated framebuffer smaller than mode can't be used */
|
||||
if (report_fb_info.var.xres * report_fb_info.var.yres <
|
||||
mode->vdisplay * mode->hdisplay)
|
||||
continue;
|
||||
|
||||
/* use first smaller mode */
|
||||
mode_match = mode;
|
||||
|
||||
if (conf_mode.enabled)
|
||||
|
@ -192,6 +192,8 @@ void Framebuffer::Driver::lookup_config(char const * const name,
|
||||
|
||||
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) {
|
||||
@ -220,6 +222,11 @@ void Framebuffer::Driver::lookup_config(char const * const name,
|
||||
mode.height = force_height;
|
||||
mode.id = 0;
|
||||
}
|
||||
|
||||
if (max_width && max_height) {
|
||||
mode.max_width = max_width;
|
||||
mode.max_height = max_height;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user