mirror of
https://github.com/genodelabs/genode.git
synced 2024-12-18 21:27:56 +00:00
parent
e69c01aad3
commit
50cc52a091
@ -68,7 +68,12 @@ append config {
|
||||
</default-route>
|
||||
<default caps="100"/>
|
||||
|
||||
<start name="report_rom" caps="100">
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
|
||||
<start name="report_rom" caps="100" priority="-1">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides>
|
||||
<service name="ROM" />
|
||||
@ -87,7 +92,7 @@ append config {
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="acpi" caps="250">
|
||||
<start name="acpi" caps="250" priority="-1">
|
||||
<resource name="RAM" quantum="6M"/>
|
||||
<route>
|
||||
<service name="IO_MEM"> <parent/> </service>
|
||||
@ -100,7 +105,7 @@ append config {
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="pci_decode" caps="350">
|
||||
<start name="pci_decode" caps="350" priority="-1">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<route>
|
||||
<service name="Report">
|
||||
@ -116,7 +121,7 @@ append config {
|
||||
</route>
|
||||
</start>
|
||||
|
||||
<start name="platform" caps="100" managing_system="yes">
|
||||
<start name="platform" caps="100" managing_system="yes" priority="-1">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<provides> <service name="Platform"/> </provides>
|
||||
<route>
|
||||
@ -143,14 +148,20 @@ append config {
|
||||
<pci class="USB"/>
|
||||
</policy>
|
||||
</config>
|
||||
</start>
|
||||
</start>}
|
||||
|
||||
<start name="timer">
|
||||
<resource name="RAM" quantum="1M"/>
|
||||
<provides><service name="Timer"/></provides>
|
||||
</start>
|
||||
append_if $use_top config {
|
||||
<start name="top" priority="-1">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<config period_ms="40000"/>
|
||||
<route>
|
||||
<service name="TRACE"> <parent label=""/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>}
|
||||
|
||||
<start name="init_dynamic" caps="10000">
|
||||
append config {
|
||||
<start name="init_dynamic" caps="10000" priority="-2">
|
||||
<binary name="init"/>
|
||||
<resource name="RAM" quantum="1000M"/>
|
||||
<route>
|
||||
@ -181,16 +192,6 @@ append config {
|
||||
<default caps="100"/>
|
||||
<report init_ram="yes" child_ram="yes" delay_ms="10000"/>}
|
||||
|
||||
append_if $use_top config {
|
||||
<start name="top">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
<config period_ms="40000"/>
|
||||
<route>
|
||||
<service name="TRACE"> <parent label=""/> </service>
|
||||
<any-service> <parent/> <any-child/> </any-service>
|
||||
</route>
|
||||
</start>}
|
||||
|
||||
append config {
|
||||
<start name="report_rom" priority="-1">
|
||||
<resource name="RAM" quantum="2M"/>
|
||||
@ -219,6 +220,13 @@ append config {
|
||||
<inline name="fb.config">
|
||||
<config ld_verbose="yes" apply_on_hotplug="} $apply_on_hotplug {">
|
||||
<report connectors="yes"/>
|
||||
<merge name="mirror">
|
||||
<!-- all connectors in merge node gets mirrored -->
|
||||
<!--
|
||||
<connector name="DP-1"/>
|
||||
<connector name="HDMI-A-1"/>
|
||||
-->
|
||||
</merge>
|
||||
</config>
|
||||
</inline>
|
||||
</import>
|
||||
@ -266,7 +274,7 @@ append_if $use_gpu config {
|
||||
append config {
|
||||
<start name="intel_fb" caps="1000">
|
||||
<binary name="pc_intel_fb"/>
|
||||
<resource name="RAM" quantum="90M"/>
|
||||
<resource name="RAM" quantum="128M"/>
|
||||
<route>}
|
||||
|
||||
append_if $use_gpu config {
|
||||
@ -276,6 +284,18 @@ append config {
|
||||
<service name="ROM" label="config">
|
||||
<child name="config_rom" label="fb.config"/> </service>
|
||||
<service name="Report"> <child name="report_rom"/> </service>
|
||||
|
||||
<service name="Capture" label="eDP-1"> <child name="test-framebuffer-eDP-1"/> </service>
|
||||
<service name="Capture" label="HDMI-A-1"> <child name="test-framebuffer-HDMI-A-1"/> </service>
|
||||
<service name="Capture" label="HDMI-A-2"> <child name="test-framebuffer-HDMI-A-2"/> </service>
|
||||
<service name="Capture" label="HDMI-A-3"> <child name="test-framebuffer-HDMI-A-3"/> </service>
|
||||
<service name="Capture" label="DP-1"> <child name="test-framebuffer-DP-1"/> </service>
|
||||
<service name="Capture" label="DP-2"> <child name="test-framebuffer-DP-2"/> </service>
|
||||
<service name="Capture" label="DP-3"> <child name="test-framebuffer-DP-3"/> </service>
|
||||
<service name="Capture" label="DP-4"> <child name="test-framebuffer-DP-4"/> </service>
|
||||
<service name="Capture" label="VGA-1"> <child name="test-framebuffer-VGA-1"/> </service>
|
||||
<service name="Capture" label="mirror"> <child name="test-framebuffer-mirror"/> </service>
|
||||
|
||||
<any-service> <parent/> <any-child /> </any-service>
|
||||
</route>
|
||||
</start>}
|
||||
@ -309,7 +329,62 @@ append_if $use_fb_controller config {
|
||||
</start>}
|
||||
|
||||
append config {
|
||||
<start name="test-framebuffer" priority="-1">
|
||||
<start name="test-framebuffer-eDP-1" priority="-1">
|
||||
<binary name="test-framebuffer"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
<config/>
|
||||
</start>
|
||||
<start name="test-framebuffer-HDMI-A-1" priority="-1">
|
||||
<binary name="test-framebuffer"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
<config/>
|
||||
</start>
|
||||
<start name="test-framebuffer-HDMI-A-2" priority="-1">
|
||||
<binary name="test-framebuffer"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
<config/>
|
||||
</start>
|
||||
<start name="test-framebuffer-HDMI-A-3" priority="-1">
|
||||
<binary name="test-framebuffer"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
<config/>
|
||||
</start>
|
||||
<start name="test-framebuffer-DP-1" priority="-1">
|
||||
<binary name="test-framebuffer"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
<config/>
|
||||
</start>
|
||||
<start name="test-framebuffer-DP-2" priority="-1">
|
||||
<binary name="test-framebuffer"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
<config/>
|
||||
</start>
|
||||
<start name="test-framebuffer-DP-3" priority="-1">
|
||||
<binary name="test-framebuffer"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
<config/>
|
||||
</start>
|
||||
<start name="test-framebuffer-DP-4" priority="-1">
|
||||
<binary name="test-framebuffer"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
<config/>
|
||||
</start>
|
||||
<start name="test-framebuffer-VGA-1" priority="-1">
|
||||
<binary name="test-framebuffer"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
<config/>
|
||||
</start>
|
||||
<start name="test-framebuffer-mirror" priority="-1">
|
||||
<binary name="test-framebuffer"/>
|
||||
<resource name="RAM" quantum="10M"/>
|
||||
<provides> <service name="Capture"/> </provides>
|
||||
<config/>
|
||||
|
@ -3,62 +3,22 @@ This driver is for Intel i915 compatible graphic cards.
|
||||
Default behaviour
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
When no configuration is provided to the driver, it will switch on all devices
|
||||
connected to the graphics card. It will use the highest resolution as
|
||||
provided by the BIOS or EDID information from the display devices for each
|
||||
connector. The virtual resolution delivered to the client is the maximum in
|
||||
width and height of the active connectors.
|
||||
When no configuration is provided to the driver, it will enable all connectors
|
||||
with attached displays and allocate for each display a discrete framebuffer.
|
||||
It will use the highest resolution as provided by the BIOS or EDID information.
|
||||
For each connector a separate Capture connection will be requested labeled
|
||||
according to the connector name. When newly connected displays are detected
|
||||
by the driver, the new connectors are enabled and another Capture session
|
||||
labeled according to the connector will be requested.
|
||||
|
||||
When newly connected devices are detected by the hardware, the new connectors
|
||||
are enabled, probed, and again the highest resolution across all active
|
||||
connectors will be chosen. By default, the current config of the driver will
|
||||
be re-read and re-applied. This behaviour can be disabled by
|
||||
By default, on hotplug of a display, the current config of the driver will be
|
||||
re-parsed and re-applied. This behaviour can be disabled by
|
||||
|
||||
! <config apply_on_hotplug="no"/>
|
||||
|
||||
Configuration
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Each of the connectors can be configured explicitly in terms of resolution and
|
||||
whether it should be enabled or not. This looks like the following:
|
||||
|
||||
! <config>
|
||||
! <connector name="eDP-1" width="1920" height="1080" hz="60" brightness="75" enabled="true"/>
|
||||
! <connector name="DP-1" mode_id="2" enabled="true"/>
|
||||
! </config>
|
||||
|
||||
The resolution can be configured exactly by the reported mode_id or by
|
||||
the width/height/hz attributes. In the latter case the driver will take the
|
||||
first matching mode out of multiple matching modes potentially.
|
||||
|
||||
When the configuration changes during runtime, the driver will adapt to it. In
|
||||
this case, it will also change the current virtual resolution to the maximum of
|
||||
the configured resolutions in width and height, and it will inform its client
|
||||
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">
|
||||
! </config>
|
||||
|
||||
The amount of memory used by the driver for the accounting of its available
|
||||
buffer space is set by:
|
||||
|
||||
! <config max_framebuffer_memory="64M">
|
||||
! </config>
|
||||
|
||||
The default and minimal value is '64M' and suffices for resolutions of at
|
||||
least '3840x2160'. How much actual memory is used depends on the configured
|
||||
resolution.
|
||||
|
||||
To present all available connectors and their possible resolutions to the user,
|
||||
the driver is able to deliver a corresponding report, which can be enabled
|
||||
in the configuration as follows:
|
||||
@ -89,5 +49,78 @@ in millimeter per connector and if available, also per mode. The values can
|
||||
be used as input to DPI calculations. The currently used mode of the connector
|
||||
is tagged in the report explicitly.
|
||||
|
||||
The brightness attribute is reported only if the hardware supports it.
|
||||
Each of the connectors can be configured a specific mode and
|
||||
whether it should be enabled or not. This looks like the following:
|
||||
|
||||
! <config>
|
||||
! <connector name="eDP-1" enabled="true" width="1920" height="1080" hz="60" brightness="75"/>
|
||||
! <connector name="DP-1" enabled="true" mode_id="2"/>
|
||||
! </config>
|
||||
|
||||
The resolution can be configured exactly by the reported mode_id or by
|
||||
the width/height/hz attributes. In the latter case the driver will take the
|
||||
first matching mode out of multiple matching modes potentially.
|
||||
|
||||
The brightness value is in percent and takes effect only if supported by
|
||||
the hardware.
|
||||
|
||||
The maximal physical resolution across all connectors can be restricted by:
|
||||
|
||||
! <config max_width="2560" max_height="1440">
|
||||
! </config>
|
||||
|
||||
Note: All larger modes still will be reported, but are marked as unusable
|
||||
by an additional attribute 'unavailable' set to true.
|
||||
|
||||
The amount of memory used by the driver for the accounting of its available
|
||||
buffer space is set by:
|
||||
|
||||
! <config max_framebuffer_memory="64M">
|
||||
! </config>
|
||||
|
||||
The default and minimal value is '64M' and suffices for resolutions of at
|
||||
least '3840x2160'. How much actual memory is used depends on the configured
|
||||
resolution.
|
||||
|
||||
|
||||
Non-discrete usage of connectors
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Mirrored usage of connectors can be achieved by moving those connectors into
|
||||
a sub node called 'merge' of the configuration. For all those connectors,
|
||||
exactly one and the same framebuffer will be used internally by the driver.
|
||||
The driver will allocate the framebuffer large enough to accommodate all
|
||||
non-discrete connectors. If some of the modes of the connectors are smaller,
|
||||
than only a subset of the content will be visible on those displays.
|
||||
|
||||
! <connectors>
|
||||
!
|
||||
! <connector name="eDP-1" ...> <!-- discrete, not mirrored -->
|
||||
! ...
|
||||
! </connector>
|
||||
! <connector name="DP-1" ...> <!.. discrete, not mirrored -->
|
||||
! ...
|
||||
! </connector>
|
||||
!
|
||||
! <merge name="mirror">
|
||||
! <connector name="HDMI-A-1" ...>
|
||||
! ...
|
||||
! </connector>
|
||||
! <connector name="VGA--1" ...>
|
||||
! ...
|
||||
! </connector>
|
||||
! </merge>
|
||||
! </connectors>
|
||||
|
||||
Note: If connectors are configured as non-discrete, they will also be
|
||||
reported inside a separate 'merge' node.
|
||||
|
||||
Additionally, the virtual resolution for non-discrete connectors may be
|
||||
restricted via:
|
||||
|
||||
! <merge name="mirror" width="1024" height="768">
|
||||
! ...
|
||||
! </merge>
|
||||
|
||||
Thereby, the driver will open a Genode capture session to the
|
||||
GUI multiplexer with this limited dimension.
|
||||
|
@ -1,27 +0,0 @@
|
||||
/*
|
||||
* \brief Linux kernel framebuffer device support
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2021-05-03
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021-2023 Genode Labs GmbH
|
||||
*
|
||||
* This file is distributed under the terms of the GNU General Public License
|
||||
* version 2.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fb.h>
|
||||
#include <lx_emul/fb.h>
|
||||
|
||||
|
||||
int register_framebuffer(struct fb_info * fb_info)
|
||||
{
|
||||
lx_emul_framebuffer_ready(fb_info->screen_base, fb_info->screen_size,
|
||||
fb_info->var.xres_virtual, fb_info->var.yres_virtual,
|
||||
fb_info->fix.line_length /
|
||||
(fb_info->var.bits_per_pixel / 8), fb_info->var.yres);
|
||||
return 0;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/**
|
||||
* \brief Lx_emul support to register Linux kernel framebuffer
|
||||
* \author Stefan Kalkowski
|
||||
* \date 2021-05-17
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (C) 2021 Genode Labs GmbH
|
||||
*
|
||||
* This file is distributed under the terms of the GNU General Public License
|
||||
* version 2.
|
||||
*/
|
||||
|
||||
#ifndef _LX_EMUL__FB_H_
|
||||
#define _LX_EMUL__FB_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void lx_emul_framebuffer_ready(void * base, unsigned long size,
|
||||
unsigned xres, unsigned yres,
|
||||
unsigned virtual_width, unsigned virtual_height);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _LX_EMUL__FB_H_ */
|
@ -29,11 +29,13 @@ struct genode_mode {
|
||||
unsigned preferred;
|
||||
unsigned inuse;
|
||||
unsigned id;
|
||||
char mirror;
|
||||
char name[32];
|
||||
};
|
||||
|
||||
int lx_emul_i915_blit(void);
|
||||
void lx_emul_i915_report(void * genode_xml);
|
||||
int lx_emul_i915_blit(unsigned);
|
||||
void lx_emul_i915_report_discrete(void * genode_xml);
|
||||
void lx_emul_i915_report_non_discrete(void * genode_xml);
|
||||
void lx_emul_i915_hotplug_connector(void);
|
||||
void lx_emul_i915_report_connector(void * lx_data, void * genode_xml,
|
||||
char const *name, char connected,
|
||||
@ -43,5 +45,14 @@ void lx_emul_i915_iterate_modes(void *lx_data, void * genode_data);
|
||||
void lx_emul_i915_report_modes(void * genode_xml, struct genode_mode *);
|
||||
void lx_emul_i915_connector_config(char * name, struct genode_mode *);
|
||||
int lx_emul_i915_config_done_and_block(void);
|
||||
void lx_emul_i915_framebuffer_ready(unsigned connector_id,
|
||||
char const * const connector_name,
|
||||
void * base,
|
||||
unsigned long size,
|
||||
unsigned xres, unsigned yres,
|
||||
unsigned virtual_width,
|
||||
unsigned virtual_height,
|
||||
unsigned mm_width,
|
||||
unsigned mm_height);
|
||||
|
||||
#endif /* _LX_I915_H_ */
|
||||
|
@ -28,14 +28,15 @@
|
||||
enum { MAX_BRIGHTNESS = 100, INVALID_BRIGHTNESS = MAX_BRIGHTNESS + 1 };
|
||||
|
||||
|
||||
struct task_struct * lx_update_task = NULL;
|
||||
struct task_struct * lx_user_task = NULL;
|
||||
static struct drm_client_dev * dev_client = NULL;
|
||||
struct task_struct * lx_user_task = NULL;
|
||||
static struct task_struct * lx_update_task = NULL;
|
||||
static struct drm_client_dev * dev_client = NULL;
|
||||
|
||||
|
||||
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);
|
||||
struct drm_mode_fb_cmd2 const * const dumb_fb,
|
||||
unsigned width_mm, unsigned height_mm);
|
||||
|
||||
|
||||
static int user_attach_fb_to_crtc(struct drm_client_dev * const dev,
|
||||
@ -85,11 +86,11 @@ static inline bool fb_smaller_mode(struct fb_info const * const info,
|
||||
|
||||
|
||||
/*
|
||||
* Heuristic to calculate max resolution across all connectors
|
||||
* Heuristic to calculate max resolution across all mirrored connectors
|
||||
*/
|
||||
static void preferred_mode(struct drm_device const * const dev,
|
||||
struct drm_display_mode * const prefer,
|
||||
struct drm_display_mode * const min_mode)
|
||||
static void preferred_mirror(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;
|
||||
@ -101,7 +102,7 @@ static void preferred_mode(struct drm_device const * const dev,
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
drm_client_for_each_connector_iter(connector, &conn_iter) {
|
||||
struct drm_display_mode smallest = { .hdisplay = ~0, .vdisplay = ~0 };
|
||||
struct genode_mode conf_mode = { .enabled = 1 };
|
||||
struct genode_mode conf_mode = { };
|
||||
unsigned mode_id = 0;
|
||||
|
||||
/* check for connector configuration on Genode side */
|
||||
@ -144,7 +145,9 @@ static void preferred_mode(struct drm_device const * const dev,
|
||||
if (mode_id && mode_larger(&smallest, min_mode))
|
||||
*min_mode = smallest;
|
||||
|
||||
if (conf_mode.force_width && conf_mode.force_height) {
|
||||
if (conf_mode.mirror &&
|
||||
conf_mode.force_width && conf_mode.force_height) {
|
||||
|
||||
/*
|
||||
* Even so the force_* mode is selected, a configured mode for
|
||||
* a connector is considered, effectively the framebuffer content
|
||||
@ -168,7 +171,7 @@ static void preferred_mode(struct drm_device const * const dev,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!conf_mode.width || !conf_mode.height)
|
||||
if (!conf_mode.width || !conf_mode.height || !conf_mode.mirror)
|
||||
continue;
|
||||
|
||||
if (conf_larger_mode(&conf_mode, prefer)) {
|
||||
@ -189,6 +192,11 @@ static void preferred_mode(struct drm_device const * const dev,
|
||||
/* if too large or nothing configured by Genode's config */
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
drm_client_for_each_connector_iter(connector, &conn_iter) {
|
||||
|
||||
struct genode_mode conf_mode = { };
|
||||
/* check for connector configuration on Genode side */
|
||||
lx_emul_i915_connector_config(connector->name, &conf_mode);
|
||||
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
if (!mode)
|
||||
continue;
|
||||
@ -201,6 +209,9 @@ static void preferred_mode(struct drm_device const * const dev,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (conf_mode.enabled && !conf_mode.mirror)
|
||||
continue;
|
||||
|
||||
if (mode_larger(mode, prefer)) {
|
||||
prefer->hdisplay = mode->hdisplay;
|
||||
prefer->vdisplay = mode->vdisplay;
|
||||
@ -249,35 +260,313 @@ static unsigned get_brightness(struct drm_connector * const connector,
|
||||
return ret * MAX_BRIGHTNESS / panel->backlight.device->props.max_brightness;
|
||||
}
|
||||
|
||||
static struct drm_mode_fb_cmd2 dumb_fb = {};
|
||||
|
||||
static struct drm_mode_fb_cmd2 *mirror_fb_cmd;
|
||||
|
||||
|
||||
static struct drm_framebuffer * lookup_framebuffer(struct drm_crtc *crtc,
|
||||
struct drm_modeset_acquire_ctx *ctx)
|
||||
{
|
||||
struct drm_atomic_state *state;
|
||||
struct drm_plane_state *plane;
|
||||
struct drm_crtc_state *crtc_state;
|
||||
|
||||
state = drm_atomic_state_alloc(crtc->dev);
|
||||
if (!state)
|
||||
return NULL;
|
||||
|
||||
state->acquire_ctx = ctx;
|
||||
|
||||
crtc_state = drm_atomic_get_crtc_state(state, crtc);
|
||||
if (IS_ERR(crtc_state)) {
|
||||
drm_atomic_state_put(state);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
plane = drm_atomic_get_plane_state(state, crtc->primary);
|
||||
|
||||
drm_atomic_state_put(state);
|
||||
|
||||
return plane ? plane->fb : NULL;
|
||||
}
|
||||
|
||||
|
||||
enum { MAX_FBS = 8 };
|
||||
|
||||
|
||||
/* own data structure for tracking dumb buffers, e.g. GEM handles, flags, vma */
|
||||
struct gem_dumb {
|
||||
struct drm_mode_create_dumb fb_dumb;
|
||||
struct drm_mode_fb_cmd2 fb_cmd;
|
||||
struct i915_vma * vma;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
|
||||
/* allocator and lookup helper for our own meta data */
|
||||
static bool _meta_data(struct drm_client_dev const * const dev,
|
||||
struct drm_framebuffer const * const fb,
|
||||
struct drm_mode_create_dumb ** fb_dumb, /* out */
|
||||
struct drm_mode_fb_cmd2 ** fb_cmd, /* out */
|
||||
struct gem_dumb ** gem_dumb) /* out */
|
||||
{
|
||||
static struct gem_dumb gem_dumb_list [MAX_FBS] = { };
|
||||
|
||||
struct gem_dumb * free_slot = NULL;
|
||||
|
||||
if (!dev)
|
||||
return false;
|
||||
|
||||
for (unsigned i = 0; i < MAX_FBS; i++) {
|
||||
struct gem_dumb * slot = &gem_dumb_list[i];
|
||||
struct drm_framebuffer * cmp = NULL;
|
||||
|
||||
if (!slot->fb_cmd.fb_id) {
|
||||
if (!free_slot)
|
||||
free_slot = slot;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!fb)
|
||||
continue;
|
||||
|
||||
cmp = drm_framebuffer_lookup(dev->dev, dev->file,
|
||||
slot->fb_cmd.fb_id);
|
||||
|
||||
if (cmp)
|
||||
drm_framebuffer_put(cmp);
|
||||
|
||||
/* lookup case */
|
||||
if (cmp == fb) {
|
||||
if (fb_dumb) *fb_dumb = &slot->fb_dumb;
|
||||
if (fb_cmd) *fb_cmd = &slot->fb_cmd;
|
||||
if (gem_dumb) *gem_dumb = slot;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* allocate case */
|
||||
if (free_slot) {
|
||||
if (fb_dumb) *fb_dumb = &free_slot->fb_dumb;
|
||||
if (fb_cmd) *fb_cmd = &free_slot->fb_cmd;
|
||||
if (gem_dumb) *gem_dumb = free_slot;
|
||||
}
|
||||
|
||||
return !!free_slot;
|
||||
}
|
||||
|
||||
|
||||
static bool dumb_meta(struct drm_client_dev const * const dev,
|
||||
struct drm_framebuffer const * const fb,
|
||||
struct drm_mode_create_dumb ** fb_dumb, /* out */
|
||||
struct drm_mode_fb_cmd2 ** fb_cmd) /* out */
|
||||
{
|
||||
return _meta_data(dev, fb, fb_dumb, fb_cmd, NULL);
|
||||
}
|
||||
|
||||
|
||||
static bool dumb_gem(struct drm_client_dev const * const dev,
|
||||
struct drm_framebuffer const * const fb,
|
||||
struct gem_dumb ** gem_dumb) /* out */
|
||||
{
|
||||
return _meta_data(dev, fb, NULL, NULL, gem_dumb);
|
||||
}
|
||||
|
||||
|
||||
static void destroy_fb(struct drm_client_dev * const dev,
|
||||
struct drm_mode_create_dumb * const gem_dumb,
|
||||
struct drm_mode_fb_cmd2 * const dumb_fb)
|
||||
{
|
||||
int result = drm_mode_rmfb(dev->dev, dumb_fb->fb_id, dev->file);
|
||||
|
||||
if (result) {
|
||||
drm_err(dev->dev, "%s: failed to remove framebuffer %d\n",
|
||||
__func__, result);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* frees the entry in _meta_data allocator */
|
||||
memset(gem_dumb, 0, sizeof(*gem_dumb));
|
||||
memset(dumb_fb, 0, sizeof(*dumb_fb));
|
||||
}
|
||||
|
||||
|
||||
static int kernel_register_fb(struct fb_info const * const fb_info,
|
||||
unsigned const width_mm,
|
||||
unsigned const height_mm)
|
||||
{
|
||||
lx_emul_i915_framebuffer_ready(fb_info->node,
|
||||
fb_info->par,
|
||||
fb_info->screen_base,
|
||||
fb_info->screen_size,
|
||||
fb_info->var.xres_virtual,
|
||||
fb_info->var.yres_virtual,
|
||||
fb_info->fix.line_length /
|
||||
(fb_info->var.bits_per_pixel / 8),
|
||||
fb_info->var.yres,
|
||||
width_mm, height_mm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct drm_mode_fb_cmd2 fb_of_screen(struct drm_client_dev * const dev,
|
||||
struct genode_mode const * const conf_mode,
|
||||
struct fb_info * const fb_info,
|
||||
struct drm_mode_fb_cmd2 const * const dumb_fb_mirror,
|
||||
struct drm_display_mode const * const mode,
|
||||
struct drm_framebuffer * fb,
|
||||
struct drm_connector const * const connector)
|
||||
{
|
||||
int err = -EINVAL;
|
||||
struct drm_mode_create_dumb *gem_dumb = NULL;
|
||||
struct drm_mode_fb_cmd2 *fb_cmd = NULL;
|
||||
struct drm_framebuffer *fb_mirror = drm_framebuffer_lookup(dev->dev,
|
||||
dev->file,
|
||||
dumb_fb_mirror->fb_id);
|
||||
|
||||
/* during hotplug the mirrored fb is used for non mirrored connectors temporarily */
|
||||
if (fb && !conf_mode->mirror && fb == fb_mirror) {
|
||||
fb = NULL;
|
||||
}
|
||||
|
||||
if (!dumb_meta(dev, fb, &gem_dumb, &fb_cmd) || !gem_dumb || !fb_cmd) {
|
||||
struct drm_mode_fb_cmd2 invalid = { };
|
||||
printk("could not create dumb buffer\n");
|
||||
return invalid;
|
||||
}
|
||||
|
||||
/* notify genode side about switch from connector specific fb to mirror fb */
|
||||
if (fb && conf_mode->mirror && fb != fb_mirror) {
|
||||
struct fb_info info = {};
|
||||
|
||||
info.var.bits_per_pixel = 32;
|
||||
info.node = connector->index;
|
||||
info.par = connector->name;
|
||||
|
||||
kernel_register_fb(&info, mode->width_mm, mode->height_mm);
|
||||
|
||||
destroy_fb(dev, gem_dumb, fb_cmd);
|
||||
}
|
||||
|
||||
if (fb_mirror)
|
||||
drm_framebuffer_put(fb_mirror);
|
||||
|
||||
fb_info->node = connector->index;
|
||||
|
||||
if (!conf_mode->enabled) {
|
||||
struct drm_mode_fb_cmd2 invalid = { };
|
||||
return invalid;
|
||||
}
|
||||
|
||||
if (conf_mode->mirror)
|
||||
return *dumb_fb_mirror;
|
||||
|
||||
err = check_resize_fb(dev, gem_dumb, fb_cmd,
|
||||
mode->hdisplay, mode->vdisplay);
|
||||
|
||||
if (err) {
|
||||
printk("setting up framebuffer of %ux%u failed - error=%d\n",
|
||||
mode->hdisplay, mode->vdisplay, err);
|
||||
return *dumb_fb_mirror;
|
||||
}
|
||||
|
||||
fb_info->var.xres = mode->hdisplay;
|
||||
fb_info->var.yres = mode->vdisplay;
|
||||
fb_info->var.xres_virtual = mode->hdisplay;
|
||||
fb_info->var.yres_virtual = mode->vdisplay;
|
||||
|
||||
return *fb_cmd;
|
||||
}
|
||||
|
||||
|
||||
static void close_unused_captures(struct drm_client_dev * const dev)
|
||||
{
|
||||
/* report disconnected connectors to close capture connections */
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
struct drm_connector *connector = NULL;
|
||||
|
||||
drm_connector_list_iter_begin(dev->dev, &conn_iter);
|
||||
drm_client_for_each_connector_iter(connector, &conn_iter) {
|
||||
bool unused = connector->status != connector_status_connected;
|
||||
|
||||
if (!unused) {
|
||||
unused = !connector->state || !connector->state->crtc;
|
||||
|
||||
if (!unused) {
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
void * fb = NULL;
|
||||
int err = -1;
|
||||
|
||||
DRM_MODESET_LOCK_ALL_BEGIN(dev->dev, ctx,
|
||||
DRM_MODESET_ACQUIRE_INTERRUPTIBLE,
|
||||
err);
|
||||
|
||||
fb = lookup_framebuffer(connector->state->crtc, &ctx);
|
||||
|
||||
DRM_MODESET_LOCK_ALL_END(dev->dev, ctx, err);
|
||||
|
||||
unused = !fb;
|
||||
}
|
||||
}
|
||||
|
||||
if (unused) {
|
||||
struct fb_info fb_info = {};
|
||||
|
||||
fb_info.var.bits_per_pixel = 32;
|
||||
fb_info.node = connector->index;
|
||||
|
||||
kernel_register_fb(&fb_info, 0, 0);
|
||||
}
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
}
|
||||
|
||||
|
||||
static bool reconfigure(struct drm_client_dev * const dev)
|
||||
{
|
||||
static struct drm_mode_create_dumb gem_dumb = {};
|
||||
static struct drm_mode_create_dumb *gem_mirror = NULL;
|
||||
|
||||
struct drm_display_mode mode_preferred = {};
|
||||
struct drm_display_mode mode_minimum = {};
|
||||
struct drm_display_mode framebuffer = {};
|
||||
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;
|
||||
struct drm_display_mode mode_preferred = {};
|
||||
struct drm_display_mode mode_minimum = {};
|
||||
struct drm_display_mode framebuffer = {};
|
||||
struct drm_mode_modeinfo user_mode = {};
|
||||
struct drm_display_mode * mode = NULL;
|
||||
struct drm_mode_set * mode_set = NULL;
|
||||
struct fb_info info = {};
|
||||
bool retry = false;
|
||||
struct {
|
||||
struct fb_info info;
|
||||
unsigned width_mm;
|
||||
unsigned height_mm;
|
||||
bool report;
|
||||
} mirror = { { }, 0, 0, false };
|
||||
|
||||
if (!dev || !dev->dev)
|
||||
if (!gem_mirror) {
|
||||
/* request storage for gem_mirror and mirror_fb_cmd */
|
||||
dumb_meta(dev, NULL, &gem_mirror, &mirror_fb_cmd);
|
||||
}
|
||||
|
||||
if (!dev || !dev->dev || !gem_mirror || !mirror_fb_cmd)
|
||||
return false;
|
||||
|
||||
preferred_mode(dev->dev, &mode_preferred, &mode_minimum);
|
||||
preferred_mirror(dev->dev, &mode_preferred, &mode_minimum);
|
||||
|
||||
if (!mode_minimum.hdisplay || !mode_minimum.vdisplay) {
|
||||
/* no valid modes on any connector on early boot */
|
||||
if (!dumb_fb.fb_id)
|
||||
if (!mirror_fb_cmd->fb_id)
|
||||
return false;
|
||||
|
||||
/* valid connectors but all are disabled by config */
|
||||
mode_minimum.hdisplay = dumb_fb.width;
|
||||
mode_minimum.vdisplay = dumb_fb.height;
|
||||
mode_minimum.hdisplay = mirror_fb_cmd->width;
|
||||
mode_minimum.vdisplay = mirror_fb_cmd->height;
|
||||
mode_preferred = mode_minimum;
|
||||
}
|
||||
|
||||
@ -288,8 +577,8 @@ static bool reconfigure(struct drm_client_dev * const dev)
|
||||
|
||||
{
|
||||
int const err = check_resize_fb(dev,
|
||||
&gem_dumb,
|
||||
&dumb_fb,
|
||||
gem_mirror,
|
||||
mirror_fb_cmd,
|
||||
framebuffer.hdisplay,
|
||||
framebuffer.vdisplay);
|
||||
|
||||
@ -302,21 +591,20 @@ static bool reconfigure(struct drm_client_dev * const dev)
|
||||
}
|
||||
|
||||
/* without fb handle created by check_resize_fb we can't proceed */
|
||||
if (!dumb_fb.fb_id)
|
||||
if (!mirror_fb_cmd->fb_id)
|
||||
return retry;
|
||||
|
||||
/* prepare fb info for register_framebuffer() evaluated by Genode side */
|
||||
report_fb_info.var.xres = framebuffer.hdisplay;
|
||||
report_fb_info.var.yres = framebuffer.vdisplay;
|
||||
report_fb_info.var.xres_virtual = mode_preferred.hdisplay;
|
||||
report_fb_info.var.yres_virtual = mode_preferred.vdisplay;
|
||||
/* prepare fb info for kernel_register_fb() evaluated by Genode side */
|
||||
info.var.xres = framebuffer.hdisplay;
|
||||
info.var.yres = framebuffer.vdisplay;
|
||||
info.var.xres_virtual = mode_preferred.hdisplay;
|
||||
info.var.yres_virtual = mode_preferred.vdisplay;
|
||||
|
||||
drm_client_for_each_modeset(mode_set, dev) {
|
||||
struct drm_display_mode *mode_match = NULL;
|
||||
unsigned mode_id = 0;
|
||||
struct drm_connector *connector = NULL;
|
||||
|
||||
struct genode_mode conf_mode = { };
|
||||
struct drm_display_mode * mode_match = NULL;
|
||||
unsigned mode_id = 0;
|
||||
struct drm_connector * connector = NULL;
|
||||
struct genode_mode conf_mode = {};
|
||||
|
||||
if (!mode_set->connectors || !*mode_set->connectors)
|
||||
continue;
|
||||
@ -337,7 +625,7 @@ static bool reconfigure(struct drm_client_dev * const dev)
|
||||
continue;
|
||||
|
||||
/* allocated framebuffer smaller than mode can't be used */
|
||||
if (fb_smaller_mode(&report_fb_info, mode))
|
||||
if (fb_smaller_mode(&info, mode))
|
||||
continue;
|
||||
|
||||
/* use mode id if configured and matches exactly */
|
||||
@ -374,19 +662,35 @@ static bool reconfigure(struct drm_client_dev * const dev)
|
||||
/* apply new mode */
|
||||
mode_id = 0;
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
int err = -1;
|
||||
bool no_match = false;
|
||||
struct fb_info fb_info = info;
|
||||
int err = -1;
|
||||
bool no_match = false;
|
||||
struct drm_mode_fb_cmd2 fb_cmd = *mirror_fb_cmd;
|
||||
|
||||
mode_id ++;
|
||||
|
||||
if (!mode)
|
||||
continue;
|
||||
|
||||
/* use first mode for non mirrored connector in case of no match */
|
||||
if (!mode_match && !conf_mode.mirror) {
|
||||
|
||||
struct drm_display_mode max = { .hdisplay = conf_mode.max_width,
|
||||
.vdisplay = conf_mode.max_height };
|
||||
|
||||
if (conf_mode.max_width && conf_mode.max_height) {
|
||||
if (conf_larger_mode(&conf_mode, &max))
|
||||
continue;
|
||||
}
|
||||
|
||||
mode_match = mode;
|
||||
}
|
||||
|
||||
/* no matching mode ? */
|
||||
if (!mode_match) {
|
||||
|
||||
/* fb smaller than mode is denied by drm_mode_setcrtc */
|
||||
if (fb_smaller_mode(&report_fb_info, mode))
|
||||
if (fb_smaller_mode(&fb_info, mode))
|
||||
continue;
|
||||
|
||||
/* use first smaller mode */
|
||||
@ -399,19 +703,34 @@ static bool reconfigure(struct drm_client_dev * const dev)
|
||||
if (mode_match != mode)
|
||||
continue;
|
||||
|
||||
/* Genode side prefer to have a name for the connector */
|
||||
fb_info.par = connector->name;
|
||||
|
||||
{
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
|
||||
DRM_MODESET_LOCK_ALL_BEGIN(dev->dev, ctx,
|
||||
DRM_MODESET_ACQUIRE_INTERRUPTIBLE,
|
||||
err);
|
||||
|
||||
fb = lookup_framebuffer(mode_set->crtc, &ctx);
|
||||
|
||||
DRM_MODESET_LOCK_ALL_END(dev->dev, ctx, err);
|
||||
|
||||
/* check for mirrored fb or specific one for connector */
|
||||
fb_cmd = fb_of_screen(dev, &conf_mode, &fb_info, mirror_fb_cmd,
|
||||
mode, fb, connector);
|
||||
}
|
||||
|
||||
/* convert kernel internal mode to user mode expectecd via ioctl */
|
||||
drm_mode_convert_to_umode(&user_mode, mode);
|
||||
|
||||
/* 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,
|
||||
&user_mode, fb_cmd.fb_id,
|
||||
conf_mode.enabled);
|
||||
|
||||
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);
|
||||
@ -420,13 +739,25 @@ static bool reconfigure(struct drm_client_dev * const dev)
|
||||
drm_modeset_unlock(&dev->dev->mode_config.connection_mutex);
|
||||
}
|
||||
|
||||
if (!retry)
|
||||
retry = !!err;
|
||||
|
||||
if (!err && conf_mode.mirror && !mirror.report) {
|
||||
/* use fb_info of first mirrored screen */
|
||||
mirror.report = true;
|
||||
mirror.width_mm = mode->width_mm;
|
||||
mirror.height_mm = mode->height_mm;
|
||||
mirror.info = fb_info;
|
||||
}
|
||||
|
||||
/* diagnostics */
|
||||
printk("%10s: %s name='%9s' id=%u%s mode=%4ux%4u@%u%s fb=%4ux%4u%s",
|
||||
printk("%10s: %s name='%9s' id=%u%s%s mode=%4ux%4u@%u%s fb=%4ux%4u%s",
|
||||
connector->name ? connector->name : "unnamed",
|
||||
conf_mode.enabled ? " enable" : "disable",
|
||||
mode->name ? mode->name : "noname",
|
||||
mode_id, mode_id < 10 ? " " : "", mode->hdisplay,
|
||||
mode->vdisplay, drm_mode_vrefresh(mode),
|
||||
mode_id, mode_id < 10 ? " " : "",
|
||||
conf_mode.mirror ? " mirror " : " discrete",
|
||||
mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode),
|
||||
drm_mode_vrefresh(mode) < 100 ? " ": "",
|
||||
framebuffer.hdisplay, framebuffer.vdisplay,
|
||||
(err || no_match) ? "" : "\n");
|
||||
@ -438,12 +769,21 @@ static bool reconfigure(struct drm_client_dev * const dev)
|
||||
if (err)
|
||||
printk(" - failed, error=%d\n", err);
|
||||
|
||||
if (!err && !conf_mode.mirror)
|
||||
user_register_fb(dev, &fb_info, &fb_cmd,
|
||||
mode->width_mm, mode->height_mm);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (report_fb)
|
||||
user_register_fb(dev, &report_fb_info, &dumb_fb);
|
||||
if (mirror.report) {
|
||||
mirror.info.par = "mirror_capture";
|
||||
user_register_fb(dev, &mirror.info, mirror_fb_cmd, mirror.width_mm,
|
||||
mirror.height_mm);
|
||||
}
|
||||
|
||||
close_unused_captures(dev);
|
||||
|
||||
return retry;
|
||||
}
|
||||
@ -474,9 +814,8 @@ static int configure_connectors(void * data)
|
||||
}
|
||||
|
||||
|
||||
void framebuffer_dirty(void)
|
||||
static void mark_framebuffer_dirty(struct drm_framebuffer * const fb)
|
||||
{
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
struct drm_clip_rect *clips = NULL;
|
||||
struct drm_mode_fb_dirty_cmd r = { };
|
||||
|
||||
@ -484,12 +823,9 @@ void framebuffer_dirty(void)
|
||||
int num_clips = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (!dev_client || !dumb_fb.fb_id)
|
||||
if (!dev_client)
|
||||
return;
|
||||
|
||||
fb = drm_framebuffer_lookup(dev_client->dev, dev_client->file,
|
||||
dumb_fb.fb_id);
|
||||
|
||||
if (!fb || !fb->funcs || !fb->funcs->dirty)
|
||||
return;
|
||||
|
||||
@ -504,9 +840,37 @@ void framebuffer_dirty(void)
|
||||
static int update_content(void *)
|
||||
{
|
||||
while (true) {
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
struct drm_connector *connector = NULL;
|
||||
struct drm_device const *dev = dev_client->dev;
|
||||
|
||||
if (lx_emul_i915_blit())
|
||||
framebuffer_dirty();
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
drm_client_for_each_connector_iter(connector, &conn_iter) {
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
int err = -1;
|
||||
|
||||
if (connector->status != connector_status_connected)
|
||||
continue;
|
||||
|
||||
if (!lx_emul_i915_blit(connector->index))
|
||||
continue;
|
||||
|
||||
if (!connector->state || !connector->state->crtc)
|
||||
continue;
|
||||
|
||||
DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx,
|
||||
DRM_MODESET_ACQUIRE_INTERRUPTIBLE,
|
||||
err);
|
||||
|
||||
fb = lookup_framebuffer(connector->state->crtc, &ctx);
|
||||
|
||||
DRM_MODESET_LOCK_ALL_END(dev, ctx, err);
|
||||
|
||||
if (fb)
|
||||
mark_framebuffer_dirty(fb);
|
||||
}
|
||||
drm_connector_list_iter_end(&conn_iter);
|
||||
|
||||
/* schedule_timeout(jiffes) or hrtimer or msleep */
|
||||
msleep(20);
|
||||
@ -523,12 +887,42 @@ void lx_user_init(void)
|
||||
int pid2 = kernel_thread(update_content, NULL, "lx_update",
|
||||
CLONE_FS | CLONE_FILES);
|
||||
|
||||
lx_user_task = find_task_by_pid_ns(pid, NULL);;
|
||||
lx_update_task = find_task_by_pid_ns(pid2, NULL);;
|
||||
lx_user_task = find_task_by_pid_ns(pid , NULL);
|
||||
lx_update_task = find_task_by_pid_ns(pid2, NULL);
|
||||
}
|
||||
|
||||
|
||||
void lx_emul_i915_report(void * genode_data)
|
||||
static bool mirrored_fb(struct drm_client_dev * client,
|
||||
struct drm_crtc const * const crtc)
|
||||
{
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
struct drm_framebuffer const * fb = NULL;
|
||||
struct drm_framebuffer const * fb_mirror = NULL;
|
||||
int result = -1;
|
||||
|
||||
if (mirror_fb_cmd && mirror_fb_cmd->fb_id)
|
||||
fb_mirror = drm_framebuffer_lookup(client->dev, client->file,
|
||||
mirror_fb_cmd->fb_id);
|
||||
|
||||
if (!fb_mirror || !crtc)
|
||||
return false;
|
||||
|
||||
if (fb_mirror)
|
||||
drm_framebuffer_put(fb_mirror);
|
||||
|
||||
DRM_MODESET_LOCK_ALL_BEGIN(client->dev, ctx,
|
||||
DRM_MODESET_ACQUIRE_INTERRUPTIBLE,
|
||||
result);
|
||||
|
||||
fb = lookup_framebuffer(crtc, &ctx);
|
||||
|
||||
DRM_MODESET_LOCK_ALL_END(client->dev, ctx, result);
|
||||
|
||||
return fb && fb_mirror == fb;
|
||||
}
|
||||
|
||||
|
||||
static void _report_connectors(void * genode_data, bool const discrete)
|
||||
{
|
||||
struct drm_connector_list_iter conn_iter;
|
||||
|
||||
@ -537,6 +931,22 @@ void lx_emul_i915_report(void * genode_data)
|
||||
|
||||
drm_connector_list_iter_begin(dev, &conn_iter);
|
||||
drm_client_for_each_connector_iter(connector, &conn_iter) {
|
||||
|
||||
bool mirror = connector->state && connector->state->crtc &&
|
||||
mirrored_fb(dev_client, connector->state->crtc);
|
||||
|
||||
if (!mirror && (!connector->state || !connector->state->crtc)) {
|
||||
struct genode_mode conf_mode = { };
|
||||
|
||||
/* check for connector configuration on Genode side */
|
||||
lx_emul_i915_connector_config(connector->name, &conf_mode);
|
||||
|
||||
mirror = conf_mode.mirror;
|
||||
}
|
||||
|
||||
if ((discrete && mirror) || (!discrete && !mirror))
|
||||
continue;
|
||||
|
||||
lx_emul_i915_report_connector(connector, genode_data,
|
||||
connector->name,
|
||||
connector->status != connector_status_disconnected,
|
||||
@ -548,6 +958,18 @@ void lx_emul_i915_report(void * genode_data)
|
||||
}
|
||||
|
||||
|
||||
void lx_emul_i915_report_discrete(void * genode_data)
|
||||
{
|
||||
_report_connectors(genode_data, true /* discrete */);
|
||||
}
|
||||
|
||||
|
||||
void lx_emul_i915_report_non_discrete(void * genode_data)
|
||||
{
|
||||
_report_connectors(genode_data, false /* non discrete */);
|
||||
}
|
||||
|
||||
|
||||
void lx_emul_i915_iterate_modes(void * lx_data, void * genode_data)
|
||||
{
|
||||
struct drm_connector *connector = lx_data;
|
||||
@ -582,6 +1004,8 @@ void lx_emul_i915_iterate_modes(void * lx_data, void * genode_data)
|
||||
bool const inuse = connector->state && connector->state->crtc &&
|
||||
connector->state->crtc->state &&
|
||||
drm_mode_equal(&connector->state->crtc->state->mode, mode);
|
||||
bool const mirror = connector->state && connector->state->crtc &&
|
||||
mirrored_fb(dev_client, connector->state->crtc);
|
||||
|
||||
struct genode_mode conf_mode = {
|
||||
.width = mode->hdisplay,
|
||||
@ -591,6 +1015,7 @@ void lx_emul_i915_iterate_modes(void * lx_data, void * genode_data)
|
||||
.preferred = mode->type & (DRM_MODE_TYPE_PREFERRED |
|
||||
DRM_MODE_TYPE_DEFAULT),
|
||||
.inuse = inuse,
|
||||
.mirror = mirror,
|
||||
.hz = drm_mode_vrefresh(mode),
|
||||
.id = mode_id,
|
||||
.enabled = !max_mode ||
|
||||
@ -622,12 +1047,13 @@ void i915_switcheroo_unregister(struct drm_i915_private *i915)
|
||||
|
||||
static int fb_client_hotplug(struct drm_client_dev *client)
|
||||
{
|
||||
struct drm_mode_set *modeset = NULL;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
int result = -EINVAL;
|
||||
struct drm_mode_set *modeset = NULL;
|
||||
struct drm_framebuffer *fb_mirror = NULL;
|
||||
int result = -EINVAL;
|
||||
|
||||
if (dumb_fb.fb_id)
|
||||
fb = drm_framebuffer_lookup(client->dev, client->file, dumb_fb.fb_id);
|
||||
if (mirror_fb_cmd && mirror_fb_cmd->fb_id)
|
||||
fb_mirror = drm_framebuffer_lookup(client->dev, client->file,
|
||||
mirror_fb_cmd->fb_id);
|
||||
|
||||
/*
|
||||
* Triggers set up of display pipelines for connectors and
|
||||
@ -644,22 +1070,75 @@ static int fb_client_hotplug(struct drm_client_dev *client)
|
||||
* (Re-)assign framebuffer to modeset (lost due to modeset_probe) and
|
||||
* commit the change.
|
||||
*/
|
||||
if (fb) {
|
||||
bool mode_too_large = false;
|
||||
if (fb_mirror) {
|
||||
struct drm_framebuffer * free_fbs[MAX_FBS] = { };
|
||||
struct drm_modeset_acquire_ctx ctx;
|
||||
|
||||
bool mode_too_large = false;
|
||||
unsigned fb_count = 0;
|
||||
|
||||
DRM_MODESET_LOCK_ALL_BEGIN(client->dev, ctx,
|
||||
DRM_MODESET_ACQUIRE_INTERRUPTIBLE,
|
||||
result);
|
||||
mutex_lock(&client->modeset_mutex);
|
||||
drm_client_for_each_modeset(modeset, client) {
|
||||
if (!modeset || !modeset->num_connectors)
|
||||
struct drm_connector *connector = NULL;
|
||||
struct drm_framebuffer *fb = NULL;
|
||||
|
||||
if (!modeset)
|
||||
continue;
|
||||
|
||||
if (!mode_too_large && modeset->mode &&
|
||||
if (modeset->crtc)
|
||||
fb = lookup_framebuffer(modeset->crtc, &ctx);
|
||||
|
||||
if (!mode_too_large && fb && modeset->mode &&
|
||||
(modeset->mode->hdisplay > fb->width ||
|
||||
modeset->mode->vdisplay > fb->height))
|
||||
mode_too_large = true;
|
||||
|
||||
modeset->fb = fb;
|
||||
if (!modeset->num_connectors || !modeset->connectors || !*modeset->connectors) {
|
||||
|
||||
struct drm_mode_fb_cmd2 *fb_cmd = NULL;
|
||||
struct drm_mode_create_dumb *fb_dumb = NULL;
|
||||
|
||||
if (!fb || fb == fb_mirror)
|
||||
continue;
|
||||
|
||||
if (!dumb_meta(client, fb, &fb_dumb, &fb_cmd) || !fb_dumb || !fb_cmd)
|
||||
continue;
|
||||
|
||||
if (fb_count >= MAX_FBS) {
|
||||
printk("leaking framebuffer memory\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
free_fbs[fb_count++] = fb;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* set connector */
|
||||
connector = *modeset->connectors;
|
||||
|
||||
modeset->fb = fb ? fb : fb_mirror;
|
||||
}
|
||||
mutex_unlock(&client->modeset_mutex);
|
||||
DRM_MODESET_LOCK_ALL_END(client->dev, ctx, result);
|
||||
|
||||
for (unsigned i = 0; i < fb_count; i++) {
|
||||
struct drm_mode_fb_cmd2 *fb_cmd = NULL;
|
||||
struct drm_mode_create_dumb *fb_dumb = NULL;
|
||||
|
||||
if (!free_fbs[i])
|
||||
continue;
|
||||
|
||||
if (!dumb_meta(client, free_fbs[i], &fb_dumb, &fb_cmd) || !fb_dumb || !fb_cmd)
|
||||
continue;
|
||||
|
||||
destroy_fb(client, fb_dumb, fb_cmd);
|
||||
|
||||
free_fbs[i] = NULL;
|
||||
}
|
||||
|
||||
/* triggers disablement of encoders attached to disconnected ports */
|
||||
result = drm_client_modeset_commit(client);
|
||||
@ -673,8 +1152,8 @@ static int fb_client_hotplug(struct drm_client_dev *client)
|
||||
/* notify Genode side */
|
||||
lx_emul_i915_hotplug_connector();
|
||||
|
||||
if (fb)
|
||||
drm_framebuffer_put(fb);
|
||||
if (fb_mirror)
|
||||
drm_framebuffer_put(fb_mirror);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -755,15 +1234,16 @@ int user_attach_fb_to_crtc(struct drm_client_dev * const dev,
|
||||
|
||||
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)
|
||||
struct drm_mode_fb_cmd2 const * const dumb_fb,
|
||||
unsigned const width_mm,
|
||||
unsigned const height_mm)
|
||||
{
|
||||
intel_wakeref_t wakeref;
|
||||
|
||||
int result = -EINVAL;
|
||||
struct i915_gtt_view const view = { .type = I915_GTT_VIEW_NORMAL };
|
||||
static struct i915_vma *vma = NULL;
|
||||
static unsigned long flags = 0;
|
||||
void __iomem *vaddr = NULL;
|
||||
struct gem_dumb *gem_dumb = NULL;
|
||||
struct drm_i915_private *dev_priv = to_i915(dev->dev);
|
||||
struct drm_framebuffer *fb = drm_framebuffer_lookup(dev->dev,
|
||||
dev->file,
|
||||
@ -774,49 +1254,61 @@ static int user_register_fb(struct drm_client_dev const * const dev,
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
wakeref = intel_runtime_pm_get(&dev_priv->runtime_pm);
|
||||
|
||||
if (vma) {
|
||||
intel_unpin_fb_vma(vma, flags);
|
||||
|
||||
vma = NULL;
|
||||
flags = 0;
|
||||
if (!dumb_gem(dev, fb, &gem_dumb) || !gem_dumb) {
|
||||
printk("%s:%u error looking up fb and vma\n", __func__, __LINE__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (gem_dumb->vma) {
|
||||
intel_unpin_fb_vma(gem_dumb->vma, gem_dumb->flags);
|
||||
|
||||
gem_dumb->vma = NULL;
|
||||
gem_dumb->flags = 0;
|
||||
}
|
||||
|
||||
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);
|
||||
gem_dumb->vma = intel_pin_and_fence_fb_obj(fb, false /* phys_cursor */,
|
||||
&view, false /* use fences */,
|
||||
&gem_dumb->flags);
|
||||
|
||||
if (IS_ERR(vma)) {
|
||||
if (IS_ERR(gem_dumb->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);
|
||||
result = PTR_ERR(gem_dumb->vma);
|
||||
|
||||
printk("%s:%u error setting vma %d\n", __func__, __LINE__, result);
|
||||
|
||||
gem_dumb->vma = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
vaddr = i915_vma_pin_iomap(vma);
|
||||
vaddr = i915_vma_pin_iomap(gem_dumb->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);
|
||||
|
||||
intel_unpin_fb_vma(gem_dumb->vma, gem_dumb->flags);
|
||||
gem_dumb->vma = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* fill framebuffer info for register_framebuffer */
|
||||
/* fill framebuffer info for kernel_register_fb */
|
||||
info->screen_base = vaddr;
|
||||
info->screen_size = vma->size;
|
||||
info->screen_size = gem_dumb->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);
|
||||
kernel_register_fb(info, width_mm, height_mm);
|
||||
|
||||
if (fb)
|
||||
drm_framebuffer_put(fb);
|
||||
@ -841,20 +1333,7 @@ static int check_resize_fb(struct drm_client_dev * const dev,
|
||||
if (gem_dumb->width && gem_dumb->height &&
|
||||
(gem_dumb->width < width || gem_dumb->height < height)) {
|
||||
|
||||
result = drm_mode_rmfb(dev->dev, dumb_fb->fb_id, dev->file);
|
||||
if (result) {
|
||||
drm_err(dev->dev, "%s: failed to remove framebufer %d\n",
|
||||
__func__, result);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
memset(gem_dumb, 0, sizeof(*gem_dumb));
|
||||
memset(dumb_fb, 0, sizeof(*dumb_fb));
|
||||
destroy_fb(dev, gem_dumb, dumb_fb);
|
||||
}
|
||||
|
||||
/* allocate dumb framebuffer, on success a GEM object handle is returned */
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
/* emulation includes */
|
||||
#include <lx_emul/init.h>
|
||||
#include <lx_emul/fb.h>
|
||||
#include <lx_emul/task.h>
|
||||
#include <lx_kit/env.h>
|
||||
#include <lx_kit/init.h>
|
||||
@ -45,6 +44,7 @@ struct Framebuffer::Driver
|
||||
using Attached_rom_system = Constructible<Attached_rom_dataspace>;
|
||||
|
||||
Env &env;
|
||||
Heap heap { env.ram(), env.rm() };
|
||||
Attached_rom_dataspace config { env, "config" };
|
||||
Attached_rom_system system { };
|
||||
Expanding_reporter reporter { env, "connectors", "connectors" };
|
||||
@ -61,67 +61,88 @@ struct Framebuffer::Driver
|
||||
bool disable_all { false };
|
||||
bool disable_report_once { false };
|
||||
|
||||
class Fb
|
||||
{
|
||||
private:
|
||||
Capture::Connection::Label merge_label { "mirror" };
|
||||
|
||||
Capture::Connection _capture;
|
||||
Capture::Area const _size;
|
||||
Capture::Area const _size_phys;
|
||||
Capture::Connection::Screen _captured_screen;
|
||||
void * _base;
|
||||
struct Connector {
|
||||
using Space = Id_space<Connector>;
|
||||
using Id = Space::Id;
|
||||
|
||||
/*
|
||||
* Non_copyable
|
||||
*/
|
||||
Fb(const Fb&);
|
||||
Fb & operator=(const Fb&);
|
||||
Space::Element id_element;
|
||||
|
||||
public:
|
||||
Connector(Space &space, Id id) : id_element(*this, space, id) { }
|
||||
|
||||
bool paint()
|
||||
{
|
||||
using Pixel = Capture::Pixel;
|
||||
addr_t base { };
|
||||
Capture::Area size { };
|
||||
Capture::Area size_phys { };
|
||||
Capture::Area size_mm { };
|
||||
|
||||
bool dirty = false;
|
||||
|
||||
_captured_screen.with_texture([&] (Texture<Pixel> const &texture) {
|
||||
|
||||
auto const affected = _capture.capture_at(Capture::Point(0, 0));
|
||||
|
||||
affected.for_each_rect([&] (Capture::Rect const rect) {
|
||||
|
||||
Surface<Pixel> surface((Pixel*)_base, _size_phys);
|
||||
|
||||
surface.clip(rect);
|
||||
|
||||
Blit_painter::paint(surface, texture, Capture::Point(0, 0));
|
||||
|
||||
dirty = true;
|
||||
});
|
||||
});
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
Fb(Env & env, void * base, Capture::Area size,
|
||||
Capture::Area size_phys)
|
||||
:
|
||||
_capture(env),
|
||||
_size(size),
|
||||
_size_phys(size_phys),
|
||||
_captured_screen(_capture, env.rm(), { .px = _size, .mm = { } }),
|
||||
_base(base) {}
|
||||
|
||||
bool same_setup(void * base, Capture::Area &size,
|
||||
Capture::Area &size_phys)
|
||||
{
|
||||
return ((base == _base) && (size == _size) &&
|
||||
(size_phys == _size_phys));
|
||||
}
|
||||
Constructible<Capture::Connection> capture { };
|
||||
Constructible<Capture::Connection::Screen> screen { };
|
||||
};
|
||||
|
||||
Constructible<Fb> fb {};
|
||||
Connector::Space ids { };
|
||||
|
||||
bool capture(Connector::Space &ids, Connector::Id const &id)
|
||||
{
|
||||
using Pixel = Capture::Pixel;
|
||||
|
||||
bool dirty = false;
|
||||
|
||||
ids.apply<Connector>(id, [&](Connector &connector) {
|
||||
|
||||
if (!connector.capture.constructed() ||
|
||||
!connector.screen.constructed())
|
||||
return;
|
||||
|
||||
connector.screen->with_texture([&] (Texture<Pixel> const &texture) {
|
||||
|
||||
auto const affected = connector.capture->capture_at({ 0, 0});
|
||||
|
||||
affected.for_each_rect([&] (Capture::Rect const rect) {
|
||||
|
||||
Surface<Pixel> surface((Pixel*)connector.base,
|
||||
connector.size_phys);
|
||||
|
||||
surface.clip(rect);
|
||||
Blit_painter::paint(surface, texture, Capture::Point(0, 0));
|
||||
|
||||
dirty = true;
|
||||
});
|
||||
});
|
||||
}, [&](){ /* unknown connector id */ });
|
||||
|
||||
return dirty;
|
||||
}
|
||||
|
||||
bool update(Connector &conn, addr_t const base,
|
||||
Capture::Area &size, Capture::Area &size_phys,
|
||||
Capture::Area const mm,
|
||||
auto const &label)
|
||||
{
|
||||
bool same = (base == conn.base) &&
|
||||
(size == conn.size) &&
|
||||
(size_phys == conn.size_phys) &&
|
||||
(mm == conn.size_mm);
|
||||
|
||||
if (same)
|
||||
return same;
|
||||
|
||||
conn.base = base;
|
||||
conn.size = size;
|
||||
conn.size_phys = size_phys;
|
||||
conn.size_mm = mm;
|
||||
|
||||
if (conn.size.valid()) {
|
||||
Capture::Connection::Screen::Attr attr = { .px = conn.size, .mm = conn.size_mm };
|
||||
conn.capture.construct(env, label);
|
||||
conn.screen .construct(*conn.capture, env.rm(), attr);
|
||||
} else {
|
||||
conn.screen .destruct();
|
||||
conn.capture.destruct();
|
||||
}
|
||||
|
||||
return same;
|
||||
}
|
||||
|
||||
void config_update();
|
||||
void system_update();
|
||||
@ -178,8 +199,7 @@ struct Framebuffer::Driver
|
||||
return apply_config;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void with_max_enforcement(T const &fn) const
|
||||
void with_max_enforcement(auto const &fn) const
|
||||
{
|
||||
unsigned max_width = config.xml().attribute_value("max_width", 0u);
|
||||
unsigned max_height = config.xml().attribute_value("max_height",0u);
|
||||
@ -188,11 +208,10 @@ struct Framebuffer::Driver
|
||||
fn(max_width, max_height);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void with_force(T const &fn) const
|
||||
void with_force(auto const &node, auto const &fn) const
|
||||
{
|
||||
unsigned force_width = config.xml().attribute_value("force_width", 0u);
|
||||
unsigned force_height = config.xml().attribute_value("force_height", 0u);
|
||||
unsigned force_width = node.attribute_value("width", 0u);
|
||||
unsigned force_height = node.attribute_value("height", 0u);
|
||||
|
||||
if (force_width && force_height)
|
||||
fn(force_width, force_height);
|
||||
@ -247,6 +266,11 @@ void Framebuffer::Driver::config_update()
|
||||
if (!config.valid() || !lx_user_task)
|
||||
return;
|
||||
|
||||
config.xml().with_optional_sub_node("merge", [&](auto const &node) {
|
||||
if (node.has_attribute("name"))
|
||||
merge_label = node.attribute_value("name", String<160>(merge_label));
|
||||
});
|
||||
|
||||
if (config.xml().attribute_value("system", false)) {
|
||||
system.construct(Lx_kit::env().env, "system");
|
||||
system->sigh(system_handler);
|
||||
@ -310,12 +334,18 @@ void Framebuffer::Driver::generate_report()
|
||||
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_discrete(&xml);
|
||||
|
||||
lx_emul_i915_report(&xml);
|
||||
xml.node("merge", [&] () {
|
||||
node.with_optional_sub_node("merge", [&](auto const &merge) {
|
||||
with_force(merge, [&](unsigned width, unsigned height) {
|
||||
xml.attribute("width", width);
|
||||
xml.attribute("height", height);
|
||||
});
|
||||
});
|
||||
|
||||
lx_emul_i915_report_non_discrete(&xml);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -326,36 +356,58 @@ void Framebuffer::Driver::generate_report()
|
||||
void Framebuffer::Driver::lookup_config(char const * const name,
|
||||
struct genode_mode &mode)
|
||||
{
|
||||
bool mirror_node = false;
|
||||
|
||||
/* default settings, possibly overridden by explicit configuration below */
|
||||
mode.enabled = !disable_all;
|
||||
mode.brightness = 70 /* percent */;
|
||||
mode.brightness = 70; /* percent */
|
||||
mode.mirror = false;
|
||||
|
||||
if (!config.valid() || disable_all)
|
||||
return;
|
||||
|
||||
/* iterate independently of force* ever to get brightness and hz */
|
||||
config.xml().for_each_sub_node("connector", [&] (Xml_node &node) {
|
||||
auto for_each_node = [&](auto const &node, bool const mirror){
|
||||
using Name = String<32>;
|
||||
Name const con_policy = node.attribute_value("name", Name());
|
||||
if (con_policy != name)
|
||||
return;
|
||||
|
||||
mode.mirror = mirror;
|
||||
mode.enabled = node.attribute_value("enabled", true);
|
||||
|
||||
if (!mode.enabled)
|
||||
return;
|
||||
|
||||
mode.width = node.attribute_value("width" , 0U);
|
||||
mode.height = node.attribute_value("height" , 0U);
|
||||
mode.hz = node.attribute_value("hz" , 0U);
|
||||
mode.id = node.attribute_value("mode_id", 0U);
|
||||
mode.brightness = node.attribute_value("brightness",
|
||||
unsigned(MAX_BRIGHTNESS + 1));
|
||||
};
|
||||
|
||||
mode.width = node.attribute_value("width", 0U);
|
||||
mode.height = node.attribute_value("height", 0U);
|
||||
mode.hz = node.attribute_value("hz", 0U);
|
||||
mode.id = node.attribute_value("mode_id", 0U);
|
||||
/* lookup config of discrete connectors */
|
||||
config.xml().for_each_sub_node("connector", [&] (Xml_node const &conn) {
|
||||
for_each_node(conn, false);
|
||||
});
|
||||
|
||||
with_force([&](unsigned const width, unsigned const height) {
|
||||
mode.force_width = width;
|
||||
mode.force_height = height;
|
||||
/* lookup config of mirrored connectors */
|
||||
config.xml().for_each_sub_node("merge", [&] (Xml_node const &merge) {
|
||||
if (mirror_node) {
|
||||
error("only one mirror node supported");
|
||||
return;
|
||||
}
|
||||
|
||||
merge.for_each_sub_node("connector", [&] (Xml_node const &conn) {
|
||||
for_each_node(conn, true);
|
||||
});
|
||||
|
||||
with_force(merge, [&](unsigned const width, unsigned const height) {
|
||||
mode.force_width = width;
|
||||
mode.force_height = height;
|
||||
});
|
||||
|
||||
mirror_node = true;
|
||||
});
|
||||
|
||||
with_max_enforcement([&](unsigned const width, unsigned const height) {
|
||||
@ -372,41 +424,72 @@ unsigned long long driver_max_framebuffer_memory(void)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Can be called already as side-effect of `lx_emul_start_kernel`,
|
||||
* that's why the Driver object needs to be constructed already here.
|
||||
*/
|
||||
extern "C" void lx_emul_framebuffer_ready(void * base, unsigned long,
|
||||
unsigned xres, unsigned yres,
|
||||
unsigned phys_width,
|
||||
unsigned phys_height)
|
||||
void lx_emul_i915_framebuffer_ready(unsigned const connector_id,
|
||||
char const * const conn_name,
|
||||
void * const base,
|
||||
unsigned long,
|
||||
unsigned const xres,
|
||||
unsigned const yres,
|
||||
unsigned const phys_width,
|
||||
unsigned const phys_height,
|
||||
unsigned const mm_width,
|
||||
unsigned const mm_height)
|
||||
{
|
||||
auto &env = Lx_kit::env().env;
|
||||
auto &drv = driver(env);
|
||||
auto &fb = drv.fb;
|
||||
|
||||
Capture::Area area(xres, yres);
|
||||
Capture::Area area_phys(phys_width, phys_height);
|
||||
using namespace Genode;
|
||||
|
||||
if (fb.constructed()) {
|
||||
if (fb->same_setup(base, area, area_phys))
|
||||
typedef Framebuffer::Driver::Connector Connector;
|
||||
|
||||
auto const id = Connector::Id { connector_id };
|
||||
|
||||
/* allocate new id for new connector */
|
||||
drv.ids.apply<Connector>(id, [&](Connector &) { /* known id */ }, [&](){
|
||||
/* ignore unused connector - don't need a object for it */
|
||||
if (!base)
|
||||
return;
|
||||
|
||||
fb.destruct();
|
||||
}
|
||||
new (drv.heap) Connector (drv.ids, id);
|
||||
});
|
||||
|
||||
/* clear artefacts */
|
||||
if (area != area_phys)
|
||||
Genode::memset(base, 0, area_phys.count() * 4);
|
||||
drv.ids.apply<Connector>(id, [&](Connector &conn) {
|
||||
|
||||
fb.construct(env, base, area, area_phys);
|
||||
Capture::Area area (xres, yres);
|
||||
Capture::Area area_phys(phys_width, phys_height);
|
||||
|
||||
Genode::log("framebuffer reconstructed - virtual=", xres, "x", yres,
|
||||
" physical=", phys_width, "x", phys_height);
|
||||
auto const prev_size = conn.size;
|
||||
|
||||
auto label = !conn_name
|
||||
? Capture::Connection::Label(conn.id_element)
|
||||
: Capture::Connection::Label(conn_name) == "mirror_capture"
|
||||
? drv.merge_label
|
||||
: Capture::Connection::Label(conn_name);
|
||||
|
||||
bool const same = drv.update(conn, Genode::addr_t(base), area,
|
||||
area_phys, { mm_width, mm_height}, label);
|
||||
|
||||
if (same)
|
||||
return;
|
||||
|
||||
/* clear artefacts */
|
||||
if (base && (area != area_phys))
|
||||
Genode::memset(base, 0, area_phys.count() * 4);
|
||||
|
||||
if (conn.size.valid())
|
||||
log("setup - framebuffer ",
|
||||
" - connector id=", conn.id_element.id().value,
|
||||
", virtual=", xres, "x", yres,
|
||||
", physical=", phys_width, "x", phys_height);
|
||||
else
|
||||
log("free - framebuffer ",
|
||||
" - connector id=", conn.id_element.id().value,
|
||||
", ", prev_size);
|
||||
}, [](){ /* unknown id */ });
|
||||
}
|
||||
|
||||
|
||||
extern "C" void lx_emul_i915_hotplug_connector()
|
||||
void lx_emul_i915_hotplug_connector()
|
||||
{
|
||||
Genode::Env &env = Lx_kit::env().env;
|
||||
driver(env).generate_report();
|
||||
@ -422,8 +505,8 @@ void lx_emul_i915_report_connector(void * lx_data, void * genode_xml,
|
||||
|
||||
xml.node("connector", [&] ()
|
||||
{
|
||||
xml.attribute("name", name);
|
||||
xml.attribute("connected", !!connected);
|
||||
xml.attribute("name", name);
|
||||
if (width_mm)
|
||||
xml.attribute("width_mm" , width_mm);
|
||||
if (height_mm)
|
||||
@ -466,14 +549,13 @@ void lx_emul_i915_report_modes(void * genode_xml, struct genode_mode *mode)
|
||||
}
|
||||
|
||||
|
||||
int lx_emul_i915_blit()
|
||||
int lx_emul_i915_blit(unsigned connector_id)
|
||||
{
|
||||
auto &drv = driver(Lx_kit::env().env);
|
||||
|
||||
if (!drv.fb.constructed())
|
||||
return false;
|
||||
auto const id = Framebuffer::Driver::Connector::Id { connector_id };
|
||||
|
||||
return drv.fb->paint();
|
||||
return drv.capture(drv.ids, id);
|
||||
}
|
||||
|
||||
|
||||
|
@ -15,7 +15,6 @@ SRC_C += dummies.c
|
||||
SRC_C += pci.c
|
||||
SRC_C += lx_emul.c
|
||||
SRC_C += $(notdir $(wildcard $(REL_PRG_DIR)/generated_dummies.c))
|
||||
SRC_C += fb.c
|
||||
SRC_C += lx_user.c
|
||||
SRC_C += gem.c
|
||||
SRC_C += lx_emul/common_dummies.c
|
||||
|
Loading…
Reference in New Issue
Block a user