* [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback
@ 2018-04-12 16:12 Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 13/25] drm/fb-helper: Remove struct drm_fb_helper_crtc Noralf Trønnes
` (11 more replies)
0 siblings, 12 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel; +Cc: daniel.vetter, intel-gfx, laurent.pinchart, mstaudt
As part of moving the modesetting code out of drm_fb_helper and into
drm_client, the drm_fb_helper_funcs->initial_config callback needs to go.
Replace it with a drm_driver->initial_client_display callback that can
work for all in-kernel clients.
TODO:
- Add a patch that moves the function out of intel_fbdev.c since it's not
fbdev specific anymore.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_fb_helper.c | 19 +++++--
drivers/gpu/drm/i915/i915_drv.c | 1 +
drivers/gpu/drm/i915/intel_drv.h | 11 ++++
drivers/gpu/drm/i915/intel_fbdev.c | 113 ++++++++++++++++++-------------------
include/drm/drm_drv.h | 21 +++++++
5 files changed, 104 insertions(+), 61 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index b992f59dad30..5407bf6dc8c0 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -2103,6 +2103,20 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
/* prevent concurrent modification of connector_count by hotplug */
lockdep_assert_held(&fb_helper->lock);
+ mutex_lock(&dev->mode_config.mutex);
+ if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)
+ DRM_DEBUG_KMS("No connectors reported connected with modes\n");
+
+ if (dev->driver->initial_client_display) {
+ display = dev->driver->initial_client_display(dev, width, height);
+ if (display) {
+ drm_client_display_free(fb_helper->display);
+ fb_helper->display = display;
+ mutex_unlock(&dev->mode_config.mutex);
+ return;
+ }
+ }
+
crtcs = kcalloc(fb_helper->connector_count,
sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
modes = kcalloc(fb_helper->connector_count,
@@ -2120,9 +2134,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
if (IS_ERR(display))
goto out;
- mutex_lock(&fb_helper->dev->mode_config.mutex);
- if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)
- DRM_DEBUG_KMS("No connectors reported connected with modes\n");
drm_enable_connectors(fb_helper, enabled);
if (!(fb_helper->funcs->initial_config &&
@@ -2144,7 +2155,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
}
- mutex_unlock(&fb_helper->dev->mode_config.mutex);
/* need to set the modesets up here for use later */
/* fill out the connector<->crtc mappings into the modesets */
@@ -2182,6 +2192,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
drm_client_display_free(fb_helper->display);
fb_helper->display = display;
out:
+ mutex_unlock(&dev->mode_config.mutex);
kfree(crtcs);
kfree(modes);
kfree(offsets);
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index 07c07d55398b..b746c0cbaa4b 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -2857,6 +2857,7 @@ static struct drm_driver driver = {
.dumb_create = i915_gem_dumb_create,
.dumb_map_offset = i915_gem_mmap_gtt,
+ .initial_client_display = i915_initial_client_display,
.ioctls = i915_ioctls,
.num_ioctls = ARRAY_SIZE(i915_ioctls),
.fops = &i915_driver_fops,
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index d4368589b355..f77f510617c5 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -1720,6 +1720,9 @@ extern void intel_fbdev_fini(struct drm_i915_private *dev_priv);
extern void intel_fbdev_set_suspend(struct drm_device *dev, int state, bool synchronous);
extern void intel_fbdev_output_poll_changed(struct drm_device *dev);
extern void intel_fbdev_restore_mode(struct drm_device *dev);
+struct drm_client_display *
+i915_initial_client_display(struct drm_device *dev, unsigned int width,
+ unsigned int height);
#else
static inline int intel_fbdev_init(struct drm_device *dev)
{
@@ -1749,6 +1752,14 @@ static inline void intel_fbdev_output_poll_changed(struct drm_device *dev)
static inline void intel_fbdev_restore_mode(struct drm_device *dev)
{
}
+
+static inline struct drm_client_display *
+i915_initial_client_display(struct drm_device *dev, unsigned int width,
+ unsigned int height)
+{
+ return NULL;
+}
+
#endif
/* intel_fbc.c */
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index a4ab8575a72e..b7f44c9475a8 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -38,6 +38,7 @@
#include <linux/vga_switcheroo.h>
#include <drm/drmP.h>
+#include <drm/drm_client.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fb_helper.h>
#include "intel_drv.h"
@@ -287,18 +288,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
return ret;
}
-static struct drm_fb_helper_crtc *
-intel_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc)
-{
- int i;
-
- for (i = 0; i < fb_helper->crtc_count; i++)
- if (fb_helper->crtc_info[i].mode_set.crtc == crtc)
- return &fb_helper->crtc_info[i];
-
- return NULL;
-}
-
/*
* Try to read the BIOS display configuration and use it for the initial
* fb configuration.
@@ -326,44 +315,48 @@ intel_fb_helper_crtc(struct drm_fb_helper *fb_helper, struct drm_crtc *crtc)
* is in VGA mode we need to recalculate watermarks and set a new high-res
* framebuffer anyway.
*/
-static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
- struct drm_fb_helper_crtc **crtcs,
- struct drm_display_mode **modes,
- struct drm_fb_offset *offsets,
- bool *enabled, int width, int height)
+struct drm_client_display *
+i915_initial_client_display(struct drm_device *dev, unsigned int width,
+ unsigned int height)
{
- struct drm_i915_private *dev_priv = to_i915(fb_helper->dev);
+ struct drm_i915_private *dev_priv = to_i915(dev);
unsigned long conn_configured, conn_seq, mask;
- unsigned int count = min(fb_helper->connector_count, BITS_PER_LONG);
- int i, j;
- bool *save_enabled;
- bool fallback = true, ret = true;
+ bool fallback = true, *enabled = NULL;
+ struct drm_client_display *display;
+ struct drm_connector **connectors;
+ int i, connector_count;
+ unsigned int count;
int num_connectors_enabled = 0;
int num_connectors_detected = 0;
struct drm_modeset_acquire_ctx ctx;
- save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL);
- if (!save_enabled)
- return false;
+ display = drm_client_display_create(dev);
+ if (IS_ERR(display))
+ return NULL;
drm_modeset_acquire_init(&ctx, 0);
- while (drm_modeset_lock_all_ctx(fb_helper->dev, &ctx) != 0)
+ while (drm_modeset_lock_all_ctx(dev, &ctx) != 0)
drm_modeset_backoff(&ctx);
- memcpy(save_enabled, enabled, count);
+ connector_count = drm_connector_get_all(dev, &connectors);
+ if (connector_count < 1)
+ goto bail;
+
+ enabled = drm_connector_get_enabled_status(connectors, connector_count);
+ if (!enabled)
+ goto bail;
+
+ count = min(connector_count, BITS_PER_LONG);
mask = GENMASK(count - 1, 0);
conn_configured = 0;
retry:
conn_seq = conn_configured;
for (i = 0; i < count; i++) {
- struct drm_fb_helper_connector *fb_conn;
- struct drm_connector *connector;
+ struct drm_connector *connector = connectors[i];
+ struct drm_display_mode *mode;
+ struct drm_mode_set *modeset;
struct drm_encoder *encoder;
- struct drm_fb_helper_crtc *new_crtc;
-
- fb_conn = fb_helper->connector_info[i];
- connector = fb_conn->connector;
if (conn_configured & BIT(i))
continue;
@@ -402,16 +395,13 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
num_connectors_enabled++;
- new_crtc = intel_fb_helper_crtc(fb_helper,
- connector->state->crtc);
-
/*
* Make sure we're not trying to drive multiple connectors
* with a single CRTC, since our cloning support may not
* match the BIOS.
*/
- for (j = 0; j < count; j++) {
- if (crtcs[j] == new_crtc) {
+ drm_client_display_for_each_modeset(modeset, display) {
+ if (modeset->connectors[0] == connector) {
DRM_DEBUG_KMS("fallback: cloned configuration\n");
goto bail;
}
@@ -421,28 +411,26 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
connector->name);
/* go for command line mode first */
- modes[i] = drm_connector_pick_cmdline_mode(connector);
+ mode = drm_connector_pick_cmdline_mode(connector);
/* try for preferred next */
- if (!modes[i]) {
+ if (!mode) {
DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n",
connector->name, connector->has_tile);
- modes[i] = drm_connector_has_preferred_mode(connector,
- width,
- height);
+ mode = drm_connector_has_preferred_mode(connector,
+ width, height);
}
/* No preferred mode marked by the EDID? Are there any modes? */
- if (!modes[i] && !list_empty(&connector->modes)) {
+ if (!mode && !list_empty(&connector->modes)) {
DRM_DEBUG_KMS("using first mode listed on connector %s\n",
connector->name);
- modes[i] = list_first_entry(&connector->modes,
- struct drm_display_mode,
- head);
+ mode = list_first_entry(&connector->modes,
+ struct drm_display_mode, head);
}
/* last resort: use current mode */
- if (!modes[i]) {
+ if (!mode) {
/*
* IMPORTANT: We want to use the adjusted mode (i.e.
* after the panel fitter upscaling) as the initial
@@ -458,16 +446,26 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
*/
DRM_DEBUG_KMS("looking for current mode on connector %s\n",
connector->name);
- modes[i] = &connector->state->crtc->mode;
+ mode = &connector->state->crtc->mode;
}
- crtcs[i] = new_crtc;
+
+ modeset = drm_client_display_find_modeset(display, connector->state->crtc);
+ if (WARN_ON(!modeset))
+ goto bail;
+
+ modeset->mode = drm_mode_duplicate(dev, mode);
+ drm_connector_get(connector);
+ modeset->connectors[0] = connector;
+ modeset->num_connectors = 1;
+ modeset->x = 0;
+ modeset->y = 0;
DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n",
connector->name,
connector->state->crtc->base.id,
connector->state->crtc->name,
- modes[i]->hdisplay, modes[i]->vdisplay,
- modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" :"");
+ mode->hdisplay, mode->vdisplay,
+ mode->flags & DRM_MODE_FLAG_INTERLACE ? "i" : "");
fallback = false;
conn_configured |= BIT(i);
@@ -492,19 +490,20 @@ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper,
if (fallback) {
bail:
DRM_DEBUG_KMS("Not using firmware configuration\n");
- memcpy(enabled, save_enabled, count);
- ret = false;
+ drm_client_display_free(display);
+ display = NULL;
}
drm_modeset_drop_locks(&ctx);
drm_modeset_acquire_fini(&ctx);
- kfree(save_enabled);
- return ret;
+ drm_connector_put_all(connectors, connector_count);
+ kfree(enabled);
+
+ return display;
}
static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
- .initial_config = intel_fb_initial_config,
.fb_probe = intelfb_create,
};
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index 7e545f5f94d3..13356e6fd40c 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -32,6 +32,7 @@
#include <drm/drm_device.h>
+struct drm_client_display;
struct drm_file;
struct drm_gem_object;
struct drm_master;
@@ -553,6 +554,26 @@ struct drm_driver {
struct drm_device *dev,
uint32_t handle);
+ /**
+ * @initial_client_config:
+ *
+ * Driver callback to setup an initial fbdev display configuration.
+ * Drivers can use this callback to tell the fbdev emulation what the
+ * preferred initial configuration is. This is useful to implement
+ * smooth booting where the fbdev (and subsequently all userspace) never
+ * changes the mode, but always inherits the existing configuration.
+ *
+ * This callback is optional.
+ *
+ * RETURNS:
+ *
+ * The driver should return true if a suitable initial configuration has
+ * been filled out and false when the fbdev helper should fall back to
+ * the default probing logic.
+ */
+ struct drm_client_display *(*initial_client_display)(struct drm_device *dev,
+ unsigned int width, unsigned int height);
+
/**
* @gem_vm_ops: Driver private ops for this object
*/
--
2.15.1
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 13/25] drm/fb-helper: Remove struct drm_fb_helper_crtc
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 14/25] drm/fb-helper: Remove struct drm_fb_helper_connector Noralf Trønnes
` (10 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel
Cc: daniel.vetter, intel-gfx, Noralf Trønnes, laurent.pinchart,
mstaudt, dh.herrmann
The stage is now set for a clean removal of drm_fb_helper_crtc.
struct drm_client_display is doing its job now.
Also remove the drm_fb_helper_funcs->initial_config which has been
superseded by drm_driver->initial_client_display.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_fb_helper.c | 124 +++++++++-------------------------------
include/drm/drm_fb_helper.h | 31 ----------
2 files changed, 26 insertions(+), 129 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 5407bf6dc8c0..ce38eadcb346 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -443,24 +443,6 @@ int drm_fb_helper_blank(int blank, struct fb_info *info)
}
EXPORT_SYMBOL(drm_fb_helper_blank);
-static void drm_fb_helper_modeset_release(struct drm_fb_helper *helper,
- struct drm_mode_set *modeset)
-{
- int i;
-
- for (i = 0; i < modeset->num_connectors; i++) {
- drm_connector_put(modeset->connectors[i]);
- modeset->connectors[i] = NULL;
- }
- modeset->num_connectors = 0;
-
- drm_mode_destroy(helper->dev, modeset->mode);
- modeset->mode = NULL;
-
- /* FIXME should hold a ref? */
- modeset->fb = NULL;
-}
-
static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
{
int i;
@@ -470,14 +452,6 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
kfree(helper->connector_info[i]);
}
kfree(helper->connector_info);
-
- for (i = 0; i < helper->crtc_count; i++) {
- struct drm_mode_set *modeset = &helper->crtc_info[i].mode_set;
-
- drm_fb_helper_modeset_release(helper, modeset);
- kfree(modeset->connectors);
- }
- kfree(helper->crtc_info);
}
static void drm_fb_helper_resume_worker(struct work_struct *work)
@@ -552,48 +526,18 @@ int drm_fb_helper_init(struct drm_device *dev,
struct drm_fb_helper *fb_helper,
int max_conn_count)
{
- struct drm_crtc *crtc;
- struct drm_mode_config *config = &dev->mode_config;
- int i;
-
if (!drm_fbdev_emulation) {
dev->fb_helper = fb_helper;
return 0;
}
- if (!max_conn_count)
- return -EINVAL;
-
- fb_helper->crtc_info = kcalloc(config->num_crtc, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
- if (!fb_helper->crtc_info)
- return -ENOMEM;
-
- fb_helper->crtc_count = config->num_crtc;
fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
- if (!fb_helper->connector_info) {
- kfree(fb_helper->crtc_info);
+ if (!fb_helper->connector_info)
return -ENOMEM;
- }
+
fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
fb_helper->connector_count = 0;
- for (i = 0; i < fb_helper->crtc_count; i++) {
- fb_helper->crtc_info[i].mode_set.connectors =
- kcalloc(max_conn_count,
- sizeof(struct drm_connector *),
- GFP_KERNEL);
-
- if (!fb_helper->crtc_info[i].mode_set.connectors)
- goto out_free;
- fb_helper->crtc_info[i].mode_set.num_connectors = 0;
- }
-
- i = 0;
- drm_for_each_crtc(crtc, dev) {
- fb_helper->crtc_info[i].mode_set.crtc = crtc;
- i++;
- }
-
fb_helper->display = drm_client_display_create(dev);
if (IS_ERR(fb_helper->display))
goto out_free;
@@ -1830,7 +1774,7 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
struct drm_display_mode *dmt_mode, *mode;
/* only contemplate cloning in the single crtc case */
- if (fb_helper->crtc_count > 1)
+ if (fb_helper->dev->mode_config.num_crtc > 1)
return false;
count = 0;
@@ -1997,16 +1941,18 @@ static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
}
static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
- struct drm_fb_helper_crtc **best_crtcs,
+ struct drm_crtc **best_crtcs,
struct drm_display_mode **modes,
int n, int width, int height)
{
- int c, o;
+ struct drm_client_display *display = fb_helper->display;
+ struct drm_device *dev = display->dev;
+ int o, my_score, best_score, score;
struct drm_connector *connector;
const struct drm_connector_helper_funcs *connector_funcs;
+ struct drm_mode_set *modeset;
struct drm_encoder *encoder;
- int my_score, best_score, score;
- struct drm_fb_helper_crtc **crtcs, *crtc;
+ struct drm_crtc **crtcs;
struct drm_fb_helper_connector *fb_helper_conn;
if (n == fb_helper->connector_count)
@@ -2020,8 +1966,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
if (modes[n] == NULL)
return best_score;
- crtcs = kcalloc(fb_helper->connector_count,
- sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
+ crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL);
if (!crtcs)
return best_score;
@@ -2053,10 +1998,10 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
* select a crtc for this connector and then attempt to configure
* remaining connectors
*/
- for (c = 0; c < fb_helper->crtc_count; c++) {
- crtc = &fb_helper->crtc_info[c];
+ drm_client_display_for_each_modeset(modeset, display) {
+ struct drm_crtc *crtc = modeset->crtc;
- if ((encoder->possible_crtcs & (1 << c)) == 0)
+ if ((encoder->possible_crtcs & drm_crtc_mask(crtc)) == 0)
continue;
for (o = 0; o < n; o++)
@@ -2065,7 +2010,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
if (o < n) {
/* ignore cloning unless only a single crtc */
- if (fb_helper->crtc_count > 1)
+ if (dev->mode_config.num_crtc > 1)
continue;
if (!drm_mode_equal(modes[o], modes[n]))
@@ -2073,14 +2018,13 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
}
crtcs[n] = crtc;
- memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
+ memcpy(crtcs, best_crtcs, n * sizeof(*crtcs));
score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
width, height);
if (score > best_score) {
best_score = score;
memcpy(best_crtcs, crtcs,
- fb_helper->connector_count *
- sizeof(struct drm_fb_helper_crtc *));
+ fb_helper->connector_count * sizeof(*crtcs));
}
}
out:
@@ -2093,9 +2037,9 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
{
struct drm_device *dev = fb_helper->dev;
struct drm_client_display *display;
- struct drm_fb_helper_crtc **crtcs;
struct drm_display_mode **modes;
struct drm_fb_offset *offsets;
+ struct drm_crtc **crtcs;
bool *enabled;
int i;
@@ -2117,8 +2061,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
}
}
- crtcs = kcalloc(fb_helper->connector_count,
- sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
+ crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL);
modes = kcalloc(fb_helper->connector_count,
sizeof(struct drm_display_mode *), GFP_KERNEL);
offsets = kcalloc(fb_helper->connector_count,
@@ -2136,43 +2079,28 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
drm_enable_connectors(fb_helper, enabled);
- if (!(fb_helper->funcs->initial_config &&
- fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
- offsets,
- enabled, width, height))) {
- memset(modes, 0, fb_helper->connector_count*sizeof(modes[0]));
- memset(crtcs, 0, fb_helper->connector_count*sizeof(crtcs[0]));
- memset(offsets, 0, fb_helper->connector_count*sizeof(offsets[0]));
+ if (!drm_target_cloned(fb_helper, modes, offsets, enabled, width, height) &&
+ !drm_target_preferred(fb_helper, modes, offsets, enabled, width, height))
+ DRM_ERROR("Unable to find initial modes\n");
- if (!drm_target_cloned(fb_helper, modes, offsets,
- enabled, width, height) &&
- !drm_target_preferred(fb_helper, modes, offsets,
- enabled, width, height))
- DRM_ERROR("Unable to find initial modes\n");
+ DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
- DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
- width, height);
-
- drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
- }
+ drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
/* need to set the modesets up here for use later */
/* fill out the connector<->crtc mappings into the modesets */
- for (i = 0; i < fb_helper->crtc_count; i++)
- drm_fb_helper_modeset_release(fb_helper,
- &fb_helper->crtc_info[i].mode_set);
drm_fb_helper_for_each_connector(fb_helper, i) {
struct drm_display_mode *mode = modes[i];
- struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
+ struct drm_crtc *crtc = crtcs[i];
struct drm_fb_offset *offset = &offsets[i];
- if (mode && fb_crtc) {
+ if (mode && crtc) {
struct drm_connector *connector =
fb_helper->connector_info[i]->connector;
struct drm_mode_set *modeset;
- modeset = drm_client_display_find_modeset(display, fb_crtc->mode_set.crtc);
+ modeset = drm_client_display_find_modeset(display, crtc);
if (WARN_ON(!modeset)) {
drm_client_display_free(display);
goto out;
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index f379ef6d6085..408931f7613f 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -45,12 +45,6 @@ struct drm_fb_offset {
int x, y;
};
-struct drm_fb_helper_crtc {
- struct drm_mode_set mode_set;
- struct drm_display_mode *desired_mode;
- int x, y;
-};
-
/**
* struct drm_fb_helper_surface_size - describes fbdev size and scanout surface size
* @fb_width: fbdev width
@@ -101,29 +95,6 @@ struct drm_fb_helper_funcs {
*/
int (*fb_probe)(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes);
-
- /**
- * @initial_config:
- *
- * Driver callback to setup an initial fbdev display configuration.
- * Drivers can use this callback to tell the fbdev emulation what the
- * preferred initial configuration is. This is useful to implement
- * smooth booting where the fbdev (and subsequently all userspace) never
- * changes the mode, but always inherits the existing configuration.
- *
- * This callback is optional.
- *
- * RETURNS:
- *
- * The driver should return true if a suitable initial configuration has
- * been filled out and false when the fbdev helper should fall back to
- * the default probing logic.
- */
- bool (*initial_config)(struct drm_fb_helper *fb_helper,
- struct drm_fb_helper_crtc **crtcs,
- struct drm_display_mode **modes,
- struct drm_fb_offset *offsets,
- bool *enabled, int width, int height);
};
struct drm_fb_helper_connector {
@@ -163,8 +134,6 @@ struct drm_fb_helper {
*/
struct drm_client_display *display;
- int crtc_count;
- struct drm_fb_helper_crtc *crtc_info;
int connector_count;
int connector_info_alloc_count;
/**
--
2.15.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 14/25] drm/fb-helper: Remove struct drm_fb_helper_connector
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 13/25] drm/fb-helper: Remove struct drm_fb_helper_crtc Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 15/25] drm/fb-helper: Move modeset config code to drm_client Noralf Trønnes
` (9 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel
Cc: daniel.vetter, intel-gfx, Noralf Trønnes, laurent.pinchart,
mstaudt, dh.herrmann
No need to maintain a list of registered connectors. Just use the
connector iterator.
TODO: Remove:
- drm_fb_helper_add_one_connector()
- drm_fb_helper_single_add_all_connectors()
- drm_fb_helper_remove_one_connector()
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_fb_helper.c | 359 ++++++++++------------------------------
include/drm/drm_fb_helper.h | 13 --
2 files changed, 86 insertions(+), 286 deletions(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index ce38eadcb346..6ee61f195321 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -112,59 +112,10 @@ static DEFINE_MUTEX(kernel_fb_helper_lock);
* deferred I/O (coupled with drm_fb_helper_fbdev_teardown()).
*/
-#define drm_fb_helper_for_each_connector(fbh, i__) \
- for (({ lockdep_assert_held(&(fbh)->lock); }), \
- i__ = 0; i__ < (fbh)->connector_count; i__++)
-
-static int __drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
- struct drm_connector *connector)
-{
- struct drm_fb_helper_connector *fb_conn;
- struct drm_fb_helper_connector **temp;
- unsigned int count;
-
- if (!drm_fbdev_emulation)
- return 0;
-
- lockdep_assert_held(&fb_helper->lock);
-
- count = fb_helper->connector_count + 1;
-
- if (count > fb_helper->connector_info_alloc_count) {
- size_t size = count * sizeof(fb_conn);
-
- temp = krealloc(fb_helper->connector_info, size, GFP_KERNEL);
- if (!temp)
- return -ENOMEM;
-
- fb_helper->connector_info_alloc_count = count;
- fb_helper->connector_info = temp;
- }
-
- fb_conn = kzalloc(sizeof(*fb_conn), GFP_KERNEL);
- if (!fb_conn)
- return -ENOMEM;
-
- drm_connector_get(connector);
- fb_conn->connector = connector;
- fb_helper->connector_info[fb_helper->connector_count++] = fb_conn;
-
- return 0;
-}
-
int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
struct drm_connector *connector)
{
- int err;
-
- if (!fb_helper)
- return 0;
-
- mutex_lock(&fb_helper->lock);
- err = __drm_fb_helper_add_one_connector(fb_helper, connector);
- mutex_unlock(&fb_helper->lock);
-
- return err;
+ return 0;
}
EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
@@ -184,87 +135,14 @@ EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
*/
int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
{
- struct drm_device *dev;
- struct drm_connector *connector;
- struct drm_connector_list_iter conn_iter;
- int i, ret = 0;
-
- if (!drm_fbdev_emulation || !fb_helper)
- return 0;
-
- dev = fb_helper->dev;
-
- mutex_lock(&fb_helper->lock);
- drm_connector_list_iter_begin(dev, &conn_iter);
- drm_for_each_connector_iter(connector, &conn_iter) {
- ret = __drm_fb_helper_add_one_connector(fb_helper, connector);
- if (ret)
- goto fail;
- }
- goto out;
-
-fail:
- drm_fb_helper_for_each_connector(fb_helper, i) {
- struct drm_fb_helper_connector *fb_helper_connector =
- fb_helper->connector_info[i];
-
- drm_connector_put(fb_helper_connector->connector);
-
- kfree(fb_helper_connector);
- fb_helper->connector_info[i] = NULL;
- }
- fb_helper->connector_count = 0;
-out:
- drm_connector_list_iter_end(&conn_iter);
- mutex_unlock(&fb_helper->lock);
-
- return ret;
-}
-EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
-
-static int __drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
- struct drm_connector *connector)
-{
- struct drm_fb_helper_connector *fb_helper_connector;
- int i, j;
-
- if (!drm_fbdev_emulation)
- return 0;
-
- lockdep_assert_held(&fb_helper->lock);
-
- drm_fb_helper_for_each_connector(fb_helper, i) {
- if (fb_helper->connector_info[i]->connector == connector)
- break;
- }
-
- if (i == fb_helper->connector_count)
- return -EINVAL;
- fb_helper_connector = fb_helper->connector_info[i];
- drm_connector_put(fb_helper_connector->connector);
-
- for (j = i + 1; j < fb_helper->connector_count; j++)
- fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
-
- fb_helper->connector_count--;
- kfree(fb_helper_connector);
-
return 0;
}
+EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
struct drm_connector *connector)
{
- int err;
-
- if (!fb_helper)
- return 0;
-
- mutex_lock(&fb_helper->lock);
- err = __drm_fb_helper_remove_one_connector(fb_helper, connector);
- mutex_unlock(&fb_helper->lock);
-
- return err;
+ return 0;
}
EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
@@ -443,17 +321,6 @@ int drm_fb_helper_blank(int blank, struct fb_info *info)
}
EXPORT_SYMBOL(drm_fb_helper_blank);
-static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
-{
- int i;
-
- for (i = 0; i < helper->connector_count; i++) {
- drm_connector_put(helper->connector_info[i]->connector);
- kfree(helper->connector_info[i]);
- }
- kfree(helper->connector_info);
-}
-
static void drm_fb_helper_resume_worker(struct work_struct *work)
{
struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
@@ -531,23 +398,13 @@ int drm_fb_helper_init(struct drm_device *dev,
return 0;
}
- fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
- if (!fb_helper->connector_info)
- return -ENOMEM;
-
- fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
- fb_helper->connector_count = 0;
-
fb_helper->display = drm_client_display_create(dev);
if (IS_ERR(fb_helper->display))
- goto out_free;
+ return PTR_ERR(fb_helper->display);
dev->fb_helper = fb_helper;
return 0;
-out_free:
- drm_fb_helper_crtc_free(fb_helper);
- return -ENOMEM;
}
EXPORT_SYMBOL(drm_fb_helper_init);
@@ -651,8 +508,6 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
mutex_destroy(&fb_helper->lock);
drm_client_display_free(fb_helper->display);
- drm_fb_helper_crtc_free(fb_helper);
-
}
EXPORT_SYMBOL(drm_fb_helper_fini);
@@ -1474,8 +1329,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
{
int ret = 0;
int crtc_count = 0;
- int i;
+ struct drm_connector_list_iter conn_iter;
struct drm_fb_helper_surface_size sizes;
+ struct drm_connector *connector;
struct drm_mode_set *mode_set;
int gamma_size = 0;
@@ -1490,11 +1346,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
/* first up get a count of crtcs now in use and new min/maxes width/heights */
- drm_fb_helper_for_each_connector(fb_helper, i) {
- struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
- struct drm_cmdline_mode *cmdline_mode;
-
- cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
+ drm_connector_list_iter_begin(fb_helper->dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode;
if (cmdline_mode->bpp_specified) {
switch (cmdline_mode->bpp) {
@@ -1519,6 +1373,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
break;
}
}
+ drm_connector_list_iter_end(&conn_iter);
crtc_count = 0;
drm_client_display_for_each_modeset(mode_set, fb_helper->display) {
@@ -1707,70 +1562,28 @@ static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
uint32_t maxX,
uint32_t maxY)
{
+ struct drm_connector_list_iter conn_iter;
struct drm_connector *connector;
- int i, count = 0;
+ int count = 0;
- drm_fb_helper_for_each_connector(fb_helper, i) {
- connector = fb_helper->connector_info[i]->connector;
+ drm_connector_list_iter_begin(fb_helper->dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
count += connector->funcs->fill_modes(connector, maxX, maxY);
}
+ drm_connector_list_iter_end(&conn_iter);
return count;
}
-static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
-{
- return fb_connector->connector->cmdline_mode.specified;
-}
-
-static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
-{
- bool enable;
-
- if (connector->display_info.non_desktop)
- return false;
-
- if (strict)
- enable = connector->status == connector_status_connected;
- else
- enable = connector->status != connector_status_disconnected;
-
- return enable;
-}
-
-static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
- bool *enabled)
-{
- bool any_enabled = false;
- struct drm_connector *connector;
- int i = 0;
-
- drm_fb_helper_for_each_connector(fb_helper, i) {
- connector = fb_helper->connector_info[i]->connector;
- enabled[i] = drm_connector_enabled(connector, true);
- DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
- connector->display_info.non_desktop ? "non desktop" : enabled[i] ? "yes" : "no");
-
- any_enabled |= enabled[i];
- }
-
- if (any_enabled)
- return;
-
- drm_fb_helper_for_each_connector(fb_helper, i) {
- connector = fb_helper->connector_info[i]->connector;
- enabled[i] = drm_connector_enabled(connector, false);
- }
-}
-
static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
+ struct drm_connector **connectors,
+ unsigned int connector_count,
struct drm_display_mode **modes,
struct drm_fb_offset *offsets,
bool *enabled, int width, int height)
{
int count, i, j;
bool can_clone = false;
- struct drm_fb_helper_connector *fb_helper_conn;
struct drm_display_mode *dmt_mode, *mode;
/* only contemplate cloning in the single crtc case */
@@ -1778,7 +1591,7 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
return false;
count = 0;
- drm_fb_helper_for_each_connector(fb_helper, i) {
+ for (i = 0; i < connector_count; i++) {
if (enabled[i])
count++;
}
@@ -1789,11 +1602,10 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
/* check the command line or if nothing common pick 1024x768 */
can_clone = true;
- drm_fb_helper_for_each_connector(fb_helper, i) {
+ for (i = 0; i < connector_count; i++) {
if (!enabled[i])
continue;
- fb_helper_conn = fb_helper->connector_info[i];
- modes[i] = drm_connector_pick_cmdline_mode(fb_helper_conn->connector);
+ modes[i] = drm_connector_pick_cmdline_mode(connectors[i]);
if (!modes[i]) {
can_clone = false;
break;
@@ -1815,12 +1627,11 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
can_clone = true;
dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
- drm_fb_helper_for_each_connector(fb_helper, i) {
+ for (i = 0; i < connector_count; i++) {
if (!enabled[i])
continue;
- fb_helper_conn = fb_helper->connector_info[i];
- list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
+ list_for_each_entry(mode, &connectors[i]->modes, head) {
if (drm_mode_equal(mode, dmt_mode))
modes[i] = mode;
}
@@ -1836,30 +1647,31 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
return false;
}
-static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
+static int drm_get_tile_offsets(struct drm_connector **connectors,
+ unsigned int connector_count,
struct drm_display_mode **modes,
struct drm_fb_offset *offsets,
int idx,
int h_idx, int v_idx)
{
- struct drm_fb_helper_connector *fb_helper_conn;
int i;
int hoffset = 0, voffset = 0;
- drm_fb_helper_for_each_connector(fb_helper, i) {
- fb_helper_conn = fb_helper->connector_info[i];
- if (!fb_helper_conn->connector->has_tile)
+ for (i = 0; i < connector_count; i++) {
+ struct drm_connector *connector = connectors[i];
+
+ if (!connector->has_tile)
continue;
if (!modes[i] && (h_idx || v_idx)) {
DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
- fb_helper_conn->connector->base.id);
+ connector->base.id);
continue;
}
- if (fb_helper_conn->connector->tile_h_loc < h_idx)
+ if (connector->tile_h_loc < h_idx)
hoffset += modes[i]->hdisplay;
- if (fb_helper_conn->connector->tile_v_loc < v_idx)
+ if (connector->tile_v_loc < v_idx)
voffset += modes[i]->vdisplay;
}
offsets[idx].x = hoffset;
@@ -1868,20 +1680,20 @@ static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
return 0;
}
-static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
+static bool drm_target_preferred(struct drm_connector **connectors,
+ unsigned int connector_count,
struct drm_display_mode **modes,
struct drm_fb_offset *offsets,
bool *enabled, int width, int height)
{
- struct drm_fb_helper_connector *fb_helper_conn;
- const u64 mask = BIT_ULL(fb_helper->connector_count) - 1;
+ const u64 mask = BIT_ULL(connector_count) - 1;
u64 conn_configured = 0;
int tile_pass = 0;
int i;
retry:
- drm_fb_helper_for_each_connector(fb_helper, i) {
- fb_helper_conn = fb_helper->connector_info[i];
+ for (i = 0; i < connector_count; i++) {
+ struct drm_connector *connector = connectors[i];
if (conn_configured & BIT_ULL(i))
continue;
@@ -1892,17 +1704,17 @@ static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
}
/* first pass over all the untiled connectors */
- if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
+ if (tile_pass == 0 && connector->has_tile)
continue;
if (tile_pass == 1) {
- if (fb_helper_conn->connector->tile_h_loc != 0 ||
- fb_helper_conn->connector->tile_v_loc != 0)
+ if (connector->tile_h_loc != 0 ||
+ connector->tile_v_loc != 0)
continue;
} else {
- if (fb_helper_conn->connector->tile_h_loc != tile_pass - 1 &&
- fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
+ if (connector->tile_h_loc != tile_pass - 1 &&
+ connector->tile_v_loc != tile_pass - 1)
/* if this tile_pass doesn't cover any of the tiles - keep going */
continue;
@@ -1910,24 +1722,23 @@ static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
* find the tile offsets for this pass - need to find
* all tiles left and above
*/
- drm_get_tile_offsets(fb_helper, modes, offsets,
- i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
+ drm_get_tile_offsets(connectors, connector_count, modes, offsets,
+ i, connector->tile_h_loc, connector->tile_v_loc);
}
DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
- fb_helper_conn->connector->base.id);
+ connector->base.id);
/* got for command line mode first */
- modes[i] = drm_connector_pick_cmdline_mode(fb_helper_conn->connector);
+ modes[i] = drm_connector_pick_cmdline_mode(connector);
if (!modes[i]) {
DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
- fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
- modes[i] = drm_connector_has_preferred_mode(fb_helper_conn->connector, width, height);
+ connector->base.id, connector->tile_group ? connector->tile_group->id : 0);
+ modes[i] = drm_connector_has_preferred_mode(connector, width, height);
}
/* No preferred modes, pick one off the list */
- if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
- list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
- break;
- }
+ if (!modes[i])
+ modes[i] = list_first_entry_or_null(&connector->modes, struct drm_display_mode, head);
+
DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
"none");
conn_configured |= BIT_ULL(i);
@@ -1940,12 +1751,13 @@ static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
return true;
}
-static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
+static int drm_pick_crtcs(struct drm_client_display *display,
+ struct drm_connector **connectors,
+ unsigned int connector_count,
struct drm_crtc **best_crtcs,
struct drm_display_mode **modes,
int n, int width, int height)
{
- struct drm_client_display *display = fb_helper->display;
struct drm_device *dev = display->dev;
int o, my_score, best_score, score;
struct drm_connector *connector;
@@ -1953,27 +1765,26 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
struct drm_mode_set *modeset;
struct drm_encoder *encoder;
struct drm_crtc **crtcs;
- struct drm_fb_helper_connector *fb_helper_conn;
- if (n == fb_helper->connector_count)
+ if (n == connector_count)
return 0;
- fb_helper_conn = fb_helper->connector_info[n];
- connector = fb_helper_conn->connector;
+ connector = connectors[n];
best_crtcs[n] = NULL;
- best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
+ best_score = drm_pick_crtcs(display, connectors, connector_count,
+ best_crtcs, modes, n + 1, width, height);
if (modes[n] == NULL)
return best_score;
- crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL);
+ crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
if (!crtcs)
return best_score;
my_score = 1;
if (connector->status == connector_status_connected)
my_score++;
- if (drm_has_cmdline_mode(fb_helper_conn))
+ if (connector->cmdline_mode.specified)
my_score++;
if (drm_connector_has_preferred_mode(connector, width, height))
my_score++;
@@ -1985,7 +1796,7 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
* NULL we fallback to the default drm_atomic_helper_best_encoder()
* helper.
*/
- if (drm_drv_uses_atomic_modeset(fb_helper->dev) &&
+ if (drm_drv_uses_atomic_modeset(dev) &&
!connector_funcs->best_encoder)
encoder = drm_atomic_helper_best_encoder(connector);
else
@@ -2019,12 +1830,12 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
crtcs[n] = crtc;
memcpy(crtcs, best_crtcs, n * sizeof(*crtcs));
- score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
- width, height);
+ score = my_score + drm_pick_crtcs(display, connectors, connector_count,
+ crtcs, modes, n + 1, width, height);
if (score > best_score) {
best_score = score;
memcpy(best_crtcs, crtcs,
- fb_helper->connector_count * sizeof(*crtcs));
+ connector_count * sizeof(*crtcs));
}
}
out:
@@ -2037,11 +1848,12 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
{
struct drm_device *dev = fb_helper->dev;
struct drm_client_display *display;
+ struct drm_connector **connectors;
struct drm_display_mode **modes;
struct drm_fb_offset *offsets;
struct drm_crtc **crtcs;
+ int i, connector_count;
bool *enabled;
- int i;
DRM_DEBUG_KMS("\n");
/* prevent concurrent modification of connector_count by hotplug */
@@ -2061,13 +1873,14 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
}
}
- crtcs = kcalloc(fb_helper->connector_count, sizeof(*crtcs), GFP_KERNEL);
- modes = kcalloc(fb_helper->connector_count,
- sizeof(struct drm_display_mode *), GFP_KERNEL);
- offsets = kcalloc(fb_helper->connector_count,
- sizeof(struct drm_fb_offset), GFP_KERNEL);
- enabled = kcalloc(fb_helper->connector_count,
- sizeof(bool), GFP_KERNEL);
+ connector_count = drm_connector_get_all(dev, &connectors);
+ if (connector_count < 1)
+ return;
+
+ enabled = drm_connector_get_enabled_status(connectors, connector_count);
+ crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
+ modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL);
+ offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL);
if (!crtcs || !modes || !enabled || !offsets) {
DRM_ERROR("Memory allocation failed\n");
goto out;
@@ -2077,27 +1890,26 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
if (IS_ERR(display))
goto out;
- drm_enable_connectors(fb_helper, enabled);
-
- if (!drm_target_cloned(fb_helper, modes, offsets, enabled, width, height) &&
- !drm_target_preferred(fb_helper, modes, offsets, enabled, width, height))
+ if (!drm_target_cloned(fb_helper, connectors, connector_count,
+ modes, offsets, enabled, width, height) &&
+ !drm_target_preferred(connectors, connector_count,
+ modes, offsets, enabled, width, height))
DRM_ERROR("Unable to find initial modes\n");
DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
- drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
+ drm_pick_crtcs(display, connectors, connector_count, crtcs, modes, 0, width, height);
/* need to set the modesets up here for use later */
/* fill out the connector<->crtc mappings into the modesets */
- drm_fb_helper_for_each_connector(fb_helper, i) {
+ for (i = 0; i < connector_count; i++) {
+ struct drm_connector *connector = connectors[i];
struct drm_display_mode *mode = modes[i];
struct drm_crtc *crtc = crtcs[i];
struct drm_fb_offset *offset = &offsets[i];
if (mode && crtc) {
- struct drm_connector *connector =
- fb_helper->connector_info[i]->connector;
struct drm_mode_set *modeset;
modeset = drm_client_display_find_modeset(display, crtc);
@@ -2121,6 +1933,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
fb_helper->display = display;
out:
mutex_unlock(&dev->mode_config.mutex);
+ drm_connector_put_all(connectors, connector_count);
kfree(crtcs);
kfree(modes);
kfree(offsets);
@@ -2136,10 +1949,11 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
*/
static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
{
+ struct drm_connector_list_iter conn_iter;
struct fb_info *info = fb_helper->fbdev;
unsigned int rotation, sw_rotations = 0;
+ struct drm_connector *connector;
struct drm_mode_set *modeset;
- int i;
drm_client_display_for_each_modeset(modeset, fb_helper->display) {
if (!modeset->num_connectors)
@@ -2156,10 +1970,8 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
}
mutex_lock(&fb_helper->dev->mode_config.mutex);
- drm_fb_helper_for_each_connector(fb_helper, i) {
- struct drm_connector *connector =
- fb_helper->connector_info[i]->connector;
-
+ drm_connector_list_iter_begin(fb_helper->dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
/* use first connected connector for the physical dimensions */
if (connector->status == connector_status_connected) {
info->var.width = connector->display_info.width_mm;
@@ -2167,6 +1979,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
break;
}
}
+ drm_connector_list_iter_end(&conn_iter);
mutex_unlock(&fb_helper->dev->mode_config.mutex);
switch (sw_rotations) {
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 408931f7613f..a1e1ab1247c5 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -97,10 +97,6 @@ struct drm_fb_helper_funcs {
struct drm_fb_helper_surface_size *sizes);
};
-struct drm_fb_helper_connector {
- struct drm_connector *connector;
-};
-
/**
* struct drm_fb_helper - main structure to emulate fbdev on top of KMS
* @fb: Scanout framebuffer object
@@ -134,15 +130,6 @@ struct drm_fb_helper {
*/
struct drm_client_display *display;
- int connector_count;
- int connector_info_alloc_count;
- /**
- * @connector_info:
- *
- * Array of per-connector information. Do not iterate directly, but use
- * drm_fb_helper_for_each_connector.
- */
- struct drm_fb_helper_connector **connector_info;
const struct drm_fb_helper_funcs *funcs;
struct fb_info *fbdev;
u32 pseudo_palette[17];
--
2.15.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 15/25] drm/fb-helper: Move modeset config code to drm_client
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 13/25] drm/fb-helper: Remove struct drm_fb_helper_crtc Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 14/25] drm/fb-helper: Remove struct drm_fb_helper_connector Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 16/25] drm: Make ioctls available for in-kernel clients Noralf Trønnes
` (8 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel
Cc: daniel.vetter, intel-gfx, Noralf Trønnes, laurent.pinchart,
mstaudt, dh.herrmann
Call the function drm_client_find_display().
No functional change apart from making width/height arguments optional.
Some function name/signature changes and whitespace adjustments.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_client.c | 399 ++++++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/drm_fb_helper.c | 371 +------------------------------------
include/drm/drm_client.h | 2 +
include/drm/drm_fb_helper.h | 4 -
4 files changed, 403 insertions(+), 373 deletions(-)
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index c85c13568cf9..27818a467b09 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -18,6 +18,10 @@
#include <drm/drm_device.h>
#include <drm/drm_modes.h>
+struct drm_client_display_offset {
+ int x, y;
+};
+
/**
* drm_client_display_create() - Create display structure
* @dev: DRM device
@@ -359,3 +363,398 @@ void drm_client_display_dpms(struct drm_client_display *display, int mode)
drm_client_display_dpms_legacy(display, mode);
}
EXPORT_SYMBOL(drm_client_display_dpms);
+
+static int drm_client_probe_connector_modes(struct drm_device *dev,
+ u32 max_width, u32 max_height)
+{
+ struct drm_connector_list_iter conn_iter;
+ struct drm_connector *connector;
+ int count = 0;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ count += connector->funcs->fill_modes(connector, max_width, max_height);
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ return count;
+}
+
+static bool drm_target_cloned(struct drm_device *dev,
+ struct drm_connector **connectors,
+ unsigned int connector_count,
+ struct drm_display_mode **modes,
+ struct drm_client_display_offset *offsets,
+ bool *enabled, int width, int height)
+{
+ int count, i, j;
+ bool can_clone = false;
+ struct drm_display_mode *dmt_mode, *mode;
+
+ /* only contemplate cloning in the single crtc case */
+ if (dev->mode_config.num_crtc > 1)
+ return false;
+
+ count = 0;
+ for (i = 0; i < connector_count; i++) {
+ if (enabled[i])
+ count++;
+ }
+
+ /* only contemplate cloning if more than one connector is enabled */
+ if (count <= 1)
+ return false;
+
+ /* check the command line or if nothing common pick 1024x768 */
+ can_clone = true;
+ for (i = 0; i < connector_count; i++) {
+ if (!enabled[i])
+ continue;
+ modes[i] = drm_connector_pick_cmdline_mode(connectors[i]);
+ if (!modes[i]) {
+ can_clone = false;
+ break;
+ }
+ for (j = 0; j < i; j++) {
+ if (!enabled[j])
+ continue;
+ if (!drm_mode_equal(modes[j], modes[i]))
+ can_clone = false;
+ }
+ }
+
+ if (can_clone) {
+ DRM_DEBUG_KMS("can clone using command line\n");
+ return true;
+ }
+
+ /* try and find a 1024x768 mode on each connector */
+ can_clone = true;
+ dmt_mode = drm_mode_find_dmt(dev, 1024, 768, 60, false);
+
+ for (i = 0; i < connector_count; i++) {
+ if (!enabled[i])
+ continue;
+
+ list_for_each_entry(mode, &connectors[i]->modes, head) {
+ if (drm_mode_equal(mode, dmt_mode))
+ modes[i] = mode;
+ }
+ if (!modes[i])
+ can_clone = false;
+ }
+
+ if (can_clone) {
+ DRM_DEBUG_KMS("can clone using 1024x768\n");
+ return true;
+ }
+ DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
+
+ return false;
+}
+
+static void drm_get_tile_offsets(struct drm_connector **connectors,
+ unsigned int connector_count,
+ struct drm_display_mode **modes,
+ struct drm_client_display_offset *offsets,
+ int idx,
+ int h_idx, int v_idx)
+{
+ int i;
+ int hoffset = 0, voffset = 0;
+
+ for (i = 0; i < connector_count; i++) {
+ struct drm_connector *connector = connectors[i];
+
+ if (!connector->has_tile)
+ continue;
+
+ if (!modes[i] && (h_idx || v_idx)) {
+ DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
+ connector->base.id);
+ continue;
+ }
+ if (connector->tile_h_loc < h_idx)
+ hoffset += modes[i]->hdisplay;
+
+ if (connector->tile_v_loc < v_idx)
+ voffset += modes[i]->vdisplay;
+ }
+ offsets[idx].x = hoffset;
+ offsets[idx].y = voffset;
+ DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
+}
+
+static bool drm_target_preferred(struct drm_connector **connectors,
+ unsigned int connector_count,
+ struct drm_display_mode **modes,
+ struct drm_client_display_offset *offsets,
+ bool *enabled, int width, int height)
+{
+ const u64 mask = BIT_ULL(connector_count) - 1;
+ u64 conn_configured = 0;
+ int tile_pass = 0;
+ int i;
+
+retry:
+ for (i = 0; i < connector_count; i++) {
+ struct drm_connector *connector = connectors[i];
+
+ if (conn_configured & BIT_ULL(i))
+ continue;
+
+ if (!enabled[i]) {
+ conn_configured |= BIT_ULL(i);
+ continue;
+ }
+
+ /* first pass over all the untiled connectors */
+ if (tile_pass == 0 && connector->has_tile)
+ continue;
+
+ if (tile_pass == 1) {
+ if (connector->tile_h_loc != 0 ||
+ connector->tile_v_loc != 0)
+ continue;
+
+ } else {
+ if (connector->tile_h_loc != tile_pass - 1 &&
+ connector->tile_v_loc != tile_pass - 1)
+ /* if this tile_pass doesn't cover any of the tiles - keep going */
+ continue;
+
+ /*
+ * find the tile offsets for this pass - need to find
+ * all tiles left and above
+ */
+ drm_get_tile_offsets(connectors, connector_count, modes, offsets,
+ i, connector->tile_h_loc, connector->tile_v_loc);
+ }
+ DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", connector->base.id);
+
+ /* got for command line mode first */
+ modes[i] = drm_connector_pick_cmdline_mode(connector);
+ if (!modes[i]) {
+ DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
+ connector->base.id,
+ connector->tile_group ? connector->tile_group->id : 0);
+ modes[i] = drm_connector_has_preferred_mode(connector, width, height);
+ }
+ /* No preferred modes, pick one off the list */
+ if (!modes[i])
+ modes[i] = list_first_entry_or_null(&connector->modes,
+ struct drm_display_mode, head);
+
+ DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : "none");
+ conn_configured |= BIT_ULL(i);
+ }
+
+ if ((conn_configured & mask) != mask) {
+ tile_pass++;
+ goto retry;
+ }
+ return true;
+}
+
+static int drm_pick_crtcs(struct drm_client_display *display,
+ struct drm_connector **connectors,
+ unsigned int connector_count,
+ struct drm_crtc **best_crtcs,
+ struct drm_display_mode **modes,
+ int n, int width, int height)
+{
+ struct drm_device *dev = display->dev;
+ int o, my_score, best_score, score;
+ struct drm_connector *connector;
+ struct drm_mode_set *modeset;
+ struct drm_encoder *encoder;
+ struct drm_crtc **crtcs;
+
+ if (n == connector_count)
+ return 0;
+
+ connector = connectors[n];
+
+ best_crtcs[n] = NULL;
+ best_score = drm_pick_crtcs(display, connectors, connector_count,
+ best_crtcs, modes, n + 1, width, height);
+ if (!modes[n])
+ return best_score;
+
+ crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
+ if (!crtcs)
+ return best_score;
+
+ my_score = 1;
+ if (connector->status == connector_status_connected)
+ my_score++;
+ if (connector->cmdline_mode.specified)
+ my_score++;
+ if (drm_connector_has_preferred_mode(connector, width, height))
+ my_score++;
+
+ /*
+ * If the DRM device implements atomic hooks and ->best_encoder() is
+ * NULL we fallback to the default drm_atomic_helper_best_encoder()
+ * helper.
+ */
+ if (drm_drv_uses_atomic_modeset(dev) &&
+ !connector->helper_private->best_encoder)
+ encoder = drm_atomic_helper_best_encoder(connector);
+ else
+ encoder = connector->helper_private->best_encoder(connector);
+
+ if (!encoder)
+ goto out;
+
+ /*
+ * select a crtc for this connector and then attempt to configure
+ * remaining connectors
+ */
+ drm_client_display_for_each_modeset(modeset, display) {
+ struct drm_crtc *crtc = modeset->crtc;
+
+ if ((encoder->possible_crtcs & drm_crtc_mask(crtc)) == 0)
+ continue;
+
+ for (o = 0; o < n; o++)
+ if (best_crtcs[o] == crtc)
+ break;
+
+ if (o < n) {
+ /* ignore cloning unless only a single crtc */
+ if (dev->mode_config.num_crtc > 1)
+ continue;
+
+ if (!drm_mode_equal(modes[o], modes[n]))
+ continue;
+ }
+
+ crtcs[n] = crtc;
+ memcpy(crtcs, best_crtcs, n * sizeof(*crtcs));
+ score = my_score + drm_pick_crtcs(display, connectors, connector_count,
+ crtcs, modes, n + 1, width, height);
+ if (score > best_score) {
+ best_score = score;
+ memcpy(best_crtcs, crtcs,
+ connector_count * sizeof(*crtcs));
+ }
+ }
+out:
+ kfree(crtcs);
+
+ return best_score;
+}
+
+/**
+ * drm_client_find_display() - Find display
+ * @dev: DRM device
+ * @width: Maximum display mode width (optional)
+ * @height: Maximum display mode height (optional)
+ *
+ * This function returns a display the client can use if available.
+ *
+ * Free resources by calling drm_client_display_free().
+ *
+ * Returns:
+ * A &drm_client_display on success, NULL if no connectors are found
+ * or error pointer on failure.
+ */
+struct drm_client_display *
+drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int height)
+{
+ struct drm_client_display_offset *offsets;
+ struct drm_client_display *display;
+ struct drm_connector **connectors;
+ struct drm_display_mode **modes;
+ struct drm_crtc **crtcs;
+ int i, connector_count;
+ bool *enabled;
+
+ DRM_DEBUG_KMS("\n");
+
+ if (!width)
+ width = dev->mode_config.max_width;
+ if (!height)
+ height = dev->mode_config.max_height;
+
+ mutex_lock(&dev->mode_config.mutex);
+ if (!drm_client_probe_connector_modes(dev, width, height))
+ DRM_DEBUG_KMS("No connectors reported connected with modes\n");
+
+ if (dev->driver->initial_client_display) {
+ display = dev->driver->initial_client_display(dev, width, height);
+ if (display) {
+ mutex_unlock(&dev->mode_config.mutex);
+ return display;
+ }
+ }
+
+ connector_count = drm_connector_get_all(dev, &connectors);
+ if (connector_count < 1)
+ return NULL;
+
+ enabled = drm_connector_get_enabled_status(connectors, connector_count);
+ crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
+ modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL);
+ offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL);
+ if (!crtcs || !modes || !enabled || !offsets) {
+ DRM_ERROR("Memory allocation failed\n");
+ display = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+
+ display = drm_client_display_create(dev);
+ if (IS_ERR(display))
+ goto out;
+
+ if (!drm_target_cloned(dev, connectors, connector_count,
+ modes, offsets, enabled, width, height) &&
+ !drm_target_preferred(connectors, connector_count,
+ modes, offsets, enabled, width, height))
+ DRM_ERROR("Unable to find initial modes\n");
+
+ DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
+
+ drm_pick_crtcs(display, connectors, connector_count, crtcs, modes, 0, width, height);
+
+ /* need to set the modesets up here for use later */
+ /* fill out the connector<->crtc mappings into the modesets */
+
+ for (i = 0; i < connector_count; i++) {
+ struct drm_client_display_offset *offset = &offsets[i];
+ struct drm_connector *connector = connectors[i];
+ struct drm_display_mode *mode = modes[i];
+ struct drm_crtc *crtc = crtcs[i];
+
+ if (mode && crtc) {
+ struct drm_mode_set *modeset;
+
+ modeset = drm_client_display_find_modeset(display, crtc);
+ if (WARN_ON(!modeset)) {
+ drm_client_display_free(display);
+ display = ERR_PTR(-EINVAL);
+ goto out;
+ }
+
+ DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
+ mode->name, modeset->crtc->base.id, offset->x, offset->y);
+
+ modeset->mode = drm_mode_duplicate(dev, mode);
+ drm_connector_get(connector);
+ modeset->connectors[modeset->num_connectors++] = connector;
+ modeset->x = offset->x;
+ modeset->y = offset->y;
+ }
+ }
+out:
+ mutex_unlock(&dev->mode_config.mutex);
+ drm_connector_put_all(connectors, connector_count);
+ kfree(crtcs);
+ kfree(modes);
+ kfree(offsets);
+ kfree(enabled);
+
+ return display;
+}
+EXPORT_SYMBOL(drm_client_find_display);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 6ee61f195321..01d8840930a3 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1558,386 +1558,19 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe
}
EXPORT_SYMBOL(drm_fb_helper_fill_var);
-static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
- uint32_t maxX,
- uint32_t maxY)
-{
- struct drm_connector_list_iter conn_iter;
- struct drm_connector *connector;
- int count = 0;
-
- drm_connector_list_iter_begin(fb_helper->dev, &conn_iter);
- drm_for_each_connector_iter(connector, &conn_iter) {
- count += connector->funcs->fill_modes(connector, maxX, maxY);
- }
- drm_connector_list_iter_end(&conn_iter);
-
- return count;
-}
-
-static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
- struct drm_connector **connectors,
- unsigned int connector_count,
- struct drm_display_mode **modes,
- struct drm_fb_offset *offsets,
- bool *enabled, int width, int height)
-{
- int count, i, j;
- bool can_clone = false;
- struct drm_display_mode *dmt_mode, *mode;
-
- /* only contemplate cloning in the single crtc case */
- if (fb_helper->dev->mode_config.num_crtc > 1)
- return false;
-
- count = 0;
- for (i = 0; i < connector_count; i++) {
- if (enabled[i])
- count++;
- }
-
- /* only contemplate cloning if more than one connector is enabled */
- if (count <= 1)
- return false;
-
- /* check the command line or if nothing common pick 1024x768 */
- can_clone = true;
- for (i = 0; i < connector_count; i++) {
- if (!enabled[i])
- continue;
- modes[i] = drm_connector_pick_cmdline_mode(connectors[i]);
- if (!modes[i]) {
- can_clone = false;
- break;
- }
- for (j = 0; j < i; j++) {
- if (!enabled[j])
- continue;
- if (!drm_mode_equal(modes[j], modes[i]))
- can_clone = false;
- }
- }
-
- if (can_clone) {
- DRM_DEBUG_KMS("can clone using command line\n");
- return true;
- }
-
- /* try and find a 1024x768 mode on each connector */
- can_clone = true;
- dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
-
- for (i = 0; i < connector_count; i++) {
- if (!enabled[i])
- continue;
-
- list_for_each_entry(mode, &connectors[i]->modes, head) {
- if (drm_mode_equal(mode, dmt_mode))
- modes[i] = mode;
- }
- if (!modes[i])
- can_clone = false;
- }
-
- if (can_clone) {
- DRM_DEBUG_KMS("can clone using 1024x768\n");
- return true;
- }
- DRM_INFO("kms: can't enable cloning when we probably wanted to.\n");
- return false;
-}
-
-static int drm_get_tile_offsets(struct drm_connector **connectors,
- unsigned int connector_count,
- struct drm_display_mode **modes,
- struct drm_fb_offset *offsets,
- int idx,
- int h_idx, int v_idx)
-{
- int i;
- int hoffset = 0, voffset = 0;
-
- for (i = 0; i < connector_count; i++) {
- struct drm_connector *connector = connectors[i];
-
- if (!connector->has_tile)
- continue;
-
- if (!modes[i] && (h_idx || v_idx)) {
- DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
- connector->base.id);
- continue;
- }
- if (connector->tile_h_loc < h_idx)
- hoffset += modes[i]->hdisplay;
-
- if (connector->tile_v_loc < v_idx)
- voffset += modes[i]->vdisplay;
- }
- offsets[idx].x = hoffset;
- offsets[idx].y = voffset;
- DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
- return 0;
-}
-
-static bool drm_target_preferred(struct drm_connector **connectors,
- unsigned int connector_count,
- struct drm_display_mode **modes,
- struct drm_fb_offset *offsets,
- bool *enabled, int width, int height)
-{
- const u64 mask = BIT_ULL(connector_count) - 1;
- u64 conn_configured = 0;
- int tile_pass = 0;
- int i;
-
-retry:
- for (i = 0; i < connector_count; i++) {
- struct drm_connector *connector = connectors[i];
-
- if (conn_configured & BIT_ULL(i))
- continue;
-
- if (enabled[i] == false) {
- conn_configured |= BIT_ULL(i);
- continue;
- }
-
- /* first pass over all the untiled connectors */
- if (tile_pass == 0 && connector->has_tile)
- continue;
-
- if (tile_pass == 1) {
- if (connector->tile_h_loc != 0 ||
- connector->tile_v_loc != 0)
- continue;
-
- } else {
- if (connector->tile_h_loc != tile_pass - 1 &&
- connector->tile_v_loc != tile_pass - 1)
- /* if this tile_pass doesn't cover any of the tiles - keep going */
- continue;
-
- /*
- * find the tile offsets for this pass - need to find
- * all tiles left and above
- */
- drm_get_tile_offsets(connectors, connector_count, modes, offsets,
- i, connector->tile_h_loc, connector->tile_v_loc);
- }
- DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
- connector->base.id);
-
- /* got for command line mode first */
- modes[i] = drm_connector_pick_cmdline_mode(connector);
- if (!modes[i]) {
- DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
- connector->base.id, connector->tile_group ? connector->tile_group->id : 0);
- modes[i] = drm_connector_has_preferred_mode(connector, width, height);
- }
- /* No preferred modes, pick one off the list */
- if (!modes[i])
- modes[i] = list_first_entry_or_null(&connector->modes, struct drm_display_mode, head);
-
- DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
- "none");
- conn_configured |= BIT_ULL(i);
- }
-
- if ((conn_configured & mask) != mask) {
- tile_pass++;
- goto retry;
- }
- return true;
-}
-
-static int drm_pick_crtcs(struct drm_client_display *display,
- struct drm_connector **connectors,
- unsigned int connector_count,
- struct drm_crtc **best_crtcs,
- struct drm_display_mode **modes,
- int n, int width, int height)
-{
- struct drm_device *dev = display->dev;
- int o, my_score, best_score, score;
- struct drm_connector *connector;
- const struct drm_connector_helper_funcs *connector_funcs;
- struct drm_mode_set *modeset;
- struct drm_encoder *encoder;
- struct drm_crtc **crtcs;
-
- if (n == connector_count)
- return 0;
-
- connector = connectors[n];
-
- best_crtcs[n] = NULL;
- best_score = drm_pick_crtcs(display, connectors, connector_count,
- best_crtcs, modes, n + 1, width, height);
- if (modes[n] == NULL)
- return best_score;
-
- crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
- if (!crtcs)
- return best_score;
-
- my_score = 1;
- if (connector->status == connector_status_connected)
- my_score++;
- if (connector->cmdline_mode.specified)
- my_score++;
- if (drm_connector_has_preferred_mode(connector, width, height))
- my_score++;
-
- connector_funcs = connector->helper_private;
-
- /*
- * If the DRM device implements atomic hooks and ->best_encoder() is
- * NULL we fallback to the default drm_atomic_helper_best_encoder()
- * helper.
- */
- if (drm_drv_uses_atomic_modeset(dev) &&
- !connector_funcs->best_encoder)
- encoder = drm_atomic_helper_best_encoder(connector);
- else
- encoder = connector_funcs->best_encoder(connector);
-
- if (!encoder)
- goto out;
-
- /*
- * select a crtc for this connector and then attempt to configure
- * remaining connectors
- */
- drm_client_display_for_each_modeset(modeset, display) {
- struct drm_crtc *crtc = modeset->crtc;
-
- if ((encoder->possible_crtcs & drm_crtc_mask(crtc)) == 0)
- continue;
-
- for (o = 0; o < n; o++)
- if (best_crtcs[o] == crtc)
- break;
-
- if (o < n) {
- /* ignore cloning unless only a single crtc */
- if (dev->mode_config.num_crtc > 1)
- continue;
-
- if (!drm_mode_equal(modes[o], modes[n]))
- continue;
- }
-
- crtcs[n] = crtc;
- memcpy(crtcs, best_crtcs, n * sizeof(*crtcs));
- score = my_score + drm_pick_crtcs(display, connectors, connector_count,
- crtcs, modes, n + 1, width, height);
- if (score > best_score) {
- best_score = score;
- memcpy(best_crtcs, crtcs,
- connector_count * sizeof(*crtcs));
- }
- }
-out:
- kfree(crtcs);
- return best_score;
-}
-
static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
u32 width, u32 height)
{
- struct drm_device *dev = fb_helper->dev;
struct drm_client_display *display;
- struct drm_connector **connectors;
- struct drm_display_mode **modes;
- struct drm_fb_offset *offsets;
- struct drm_crtc **crtcs;
- int i, connector_count;
- bool *enabled;
- DRM_DEBUG_KMS("\n");
- /* prevent concurrent modification of connector_count by hotplug */
lockdep_assert_held(&fb_helper->lock);
- mutex_lock(&dev->mode_config.mutex);
- if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)
- DRM_DEBUG_KMS("No connectors reported connected with modes\n");
-
- if (dev->driver->initial_client_display) {
- display = dev->driver->initial_client_display(dev, width, height);
- if (display) {
- drm_client_display_free(fb_helper->display);
- fb_helper->display = display;
- mutex_unlock(&dev->mode_config.mutex);
- return;
- }
- }
-
- connector_count = drm_connector_get_all(dev, &connectors);
- if (connector_count < 1)
+ display = drm_client_find_display(fb_helper->dev, width, height);
+ if (IS_ERR_OR_NULL(display))
return;
- enabled = drm_connector_get_enabled_status(connectors, connector_count);
- crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
- modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL);
- offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL);
- if (!crtcs || !modes || !enabled || !offsets) {
- DRM_ERROR("Memory allocation failed\n");
- goto out;
- }
-
- display = drm_client_display_create(dev);
- if (IS_ERR(display))
- goto out;
-
- if (!drm_target_cloned(fb_helper, connectors, connector_count,
- modes, offsets, enabled, width, height) &&
- !drm_target_preferred(connectors, connector_count,
- modes, offsets, enabled, width, height))
- DRM_ERROR("Unable to find initial modes\n");
-
- DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
-
- drm_pick_crtcs(display, connectors, connector_count, crtcs, modes, 0, width, height);
-
- /* need to set the modesets up here for use later */
- /* fill out the connector<->crtc mappings into the modesets */
-
- for (i = 0; i < connector_count; i++) {
- struct drm_connector *connector = connectors[i];
- struct drm_display_mode *mode = modes[i];
- struct drm_crtc *crtc = crtcs[i];
- struct drm_fb_offset *offset = &offsets[i];
-
- if (mode && crtc) {
- struct drm_mode_set *modeset;
-
- modeset = drm_client_display_find_modeset(display, crtc);
- if (WARN_ON(!modeset)) {
- drm_client_display_free(display);
- goto out;
- }
-
- DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
- mode->name, modeset->crtc->base.id, offset->x, offset->y);
-
- modeset->mode = drm_mode_duplicate(dev, mode);
- drm_connector_get(connector);
- modeset->connectors[modeset->num_connectors++] = connector;
- modeset->x = offset->x;
- modeset->y = offset->y;
- }
- }
-
drm_client_display_free(fb_helper->display);
fb_helper->display = display;
-out:
- mutex_unlock(&dev->mode_config.mutex);
- drm_connector_put_all(connectors, connector_count);
- kfree(crtcs);
- kfree(modes);
- kfree(offsets);
- kfree(enabled);
}
/*
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index ed028f5877d0..27d2a46cd94a 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -48,5 +48,7 @@ bool drm_client_display_panel_rotation(struct drm_connector *connector,
unsigned int *rotation);
int drm_client_display_restore(struct drm_client_display *display);
void drm_client_display_dpms(struct drm_client_display *display, int mode);
+struct drm_client_display *
+drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int height);
#endif
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index a1e1ab1247c5..5f66f253a97b 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -41,10 +41,6 @@ enum mode_set_atomic {
ENTER_ATOMIC_MODE_SET,
};
-struct drm_fb_offset {
- int x, y;
-};
-
/**
* struct drm_fb_helper_surface_size - describes fbdev size and scanout surface size
* @fb_width: fbdev width
--
2.15.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 16/25] drm: Make ioctls available for in-kernel clients
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
` (2 preceding siblings ...)
2018-04-12 16:12 ` [RFC v4 15/25] drm/fb-helper: Move modeset config code to drm_client Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 17/25] drm/client: Bail out if there's a DRM master Noralf Trønnes
` (7 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel
Cc: daniel.vetter, intel-gfx, Noralf Trønnes, laurent.pinchart,
mstaudt, dh.herrmann
Make ioctl wrappers for functions that will be used by the in-kernel API.
The following functions are touched:
- drm_mode_create_dumb_ioctl()
- drm_mode_destroy_dumb_ioctl()
- drm_mode_addfb2()
- drm_mode_rmfb()
- drm_prime_handle_to_fd_ioctl()
drm_mode_addfb2() also gets the ability to override the debug name.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_crtc_internal.h | 18 ++++++++++---
drivers/gpu/drm/drm_dumb_buffers.c | 33 ++++++++++++++++--------
drivers/gpu/drm/drm_framebuffer.c | 50 ++++++++++++++++++++++++-------------
drivers/gpu/drm/drm_internal.h | 3 +++
drivers/gpu/drm/drm_ioc32.c | 2 +-
drivers/gpu/drm/drm_ioctl.c | 4 +--
drivers/gpu/drm/drm_prime.c | 13 +++++++---
7 files changed, 84 insertions(+), 39 deletions(-)
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 3c2b82865ad2..8f8886ac0e4d 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -62,6 +62,12 @@ int drm_mode_getresources(struct drm_device *dev,
/* drm_dumb_buffers.c */
+int drm_mode_create_dumb(struct drm_device *dev,
+ struct drm_mode_create_dumb *args,
+ struct drm_file *file_priv);
+int drm_mode_destroy_dumb(struct drm_device *dev, u32 handle,
+ struct drm_file *file_priv);
+
/* IOCTLs */
int drm_mode_create_dumb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv);
@@ -163,14 +169,18 @@ int drm_framebuffer_check_src_coords(uint32_t src_x, uint32_t src_y,
const struct drm_framebuffer *fb);
void drm_fb_release(struct drm_file *file_priv);
+int drm_mode_addfb2(struct drm_device *dev, struct drm_mode_fb_cmd2 *r,
+ struct drm_file *file_priv, const char *comm);
+int drm_mode_rmfb(struct drm_device *dev, u32 fb_id,
+ struct drm_file *file_priv);
/* IOCTL */
int drm_mode_addfb(struct drm_device *dev,
void *data, struct drm_file *file_priv);
-int drm_mode_addfb2(struct drm_device *dev,
- void *data, struct drm_file *file_priv);
-int drm_mode_rmfb(struct drm_device *dev,
- void *data, struct drm_file *file_priv);
+int drm_mode_addfb2_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+int drm_mode_rmfb_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
int drm_mode_getfb(struct drm_device *dev,
void *data, struct drm_file *file_priv);
int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c
index 39ac15ce4702..eed9687b8698 100644
--- a/drivers/gpu/drm/drm_dumb_buffers.c
+++ b/drivers/gpu/drm/drm_dumb_buffers.c
@@ -53,10 +53,10 @@
* a hardware-specific ioctl to allocate suitable buffer objects.
*/
-int drm_mode_create_dumb_ioctl(struct drm_device *dev,
- void *data, struct drm_file *file_priv)
+int drm_mode_create_dumb(struct drm_device *dev,
+ struct drm_mode_create_dumb *args,
+ struct drm_file *file_priv)
{
- struct drm_mode_create_dumb *args = data;
u32 cpp, stride, size;
if (!dev->driver->dumb_create)
@@ -91,6 +91,12 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev,
return dev->driver->dumb_create(file_priv, dev, args);
}
+int drm_mode_create_dumb_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ return drm_mode_create_dumb(dev, data, file_priv);
+}
+
/**
* drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
* @dev: DRM device
@@ -122,17 +128,22 @@ int drm_mode_mmap_dumb_ioctl(struct drm_device *dev,
&args->offset);
}
+int drm_mode_destroy_dumb(struct drm_device *dev, u32 handle,
+ struct drm_file *file_priv)
+{
+ if (!dev->driver->dumb_create)
+ return -ENOSYS;
+
+ if (dev->driver->dumb_destroy)
+ return dev->driver->dumb_destroy(file_priv, dev, handle);
+ else
+ return drm_gem_dumb_destroy(file_priv, dev, handle);
+}
+
int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
struct drm_mode_destroy_dumb *args = data;
- if (!dev->driver->dumb_create)
- return -ENOSYS;
-
- if (dev->driver->dumb_destroy)
- return dev->driver->dumb_destroy(file_priv, dev, args->handle);
- else
- return drm_gem_dumb_destroy(file_priv, dev, args->handle);
+ return drm_mode_destroy_dumb(dev, args->handle, file_priv);
}
-
diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c
index 8c4d32adcc17..16769e4db5c0 100644
--- a/drivers/gpu/drm/drm_framebuffer.c
+++ b/drivers/gpu/drm/drm_framebuffer.c
@@ -125,7 +125,7 @@ int drm_mode_addfb(struct drm_device *dev,
dev->driver->driver_features & DRIVER_PREFER_XBGR_30BPP)
r.pixel_format = DRM_FORMAT_XBGR2101010;
- ret = drm_mode_addfb2(dev, &r, file_priv);
+ ret = drm_mode_addfb2_ioctl(dev, &r, file_priv);
if (ret)
return ret;
@@ -310,23 +310,23 @@ drm_internal_framebuffer_create(struct drm_device *dev,
/**
* drm_mode_addfb2 - add an FB to the graphics configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
+ * @dev: drm device
+ * @r: pointer to request structure
+ * @file_priv: drm file
+ * @comm: optionally override the allocator name used for debug output
*
* Add a new FB to the specified CRTC, given a user request with format. This is
* the 2nd version of the addfb ioctl, which supports multi-planar framebuffers
* and uses fourcc codes as pixel format specifiers.
*
- * Called by the user via ioctl.
+ * Called by the user via ioctl, or by an in-kernel client.
*
* Returns:
* Zero on success, negative errno on failure.
*/
-int drm_mode_addfb2(struct drm_device *dev,
- void *data, struct drm_file *file_priv)
+int drm_mode_addfb2(struct drm_device *dev, struct drm_mode_fb_cmd2 *r,
+ struct drm_file *file_priv, const char *comm)
{
- struct drm_mode_fb_cmd2 *r = data;
struct drm_framebuffer *fb;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
@@ -336,6 +336,9 @@ int drm_mode_addfb2(struct drm_device *dev,
if (IS_ERR(fb))
return PTR_ERR(fb);
+ if (comm)
+ strscpy(fb->comm, comm, TASK_COMM_LEN);
+
DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
r->fb_id = fb->base.id;
@@ -347,6 +350,12 @@ int drm_mode_addfb2(struct drm_device *dev,
return 0;
}
+int drm_mode_addfb2_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ return drm_mode_addfb2(dev, data, file_priv, NULL);
+}
+
struct drm_mode_rmfb_work {
struct work_struct work;
struct list_head fbs;
@@ -367,29 +376,28 @@ static void drm_mode_rmfb_work_fn(struct work_struct *w)
/**
* drm_mode_rmfb - remove an FB from the configuration
- * @dev: drm device for the ioctl
- * @data: data pointer for the ioctl
- * @file_priv: drm file for the ioctl call
+ * @dev: drm device
+ * @fb_id: id of framebuffer to remove
+ * @file_priv: drm file
*
- * Remove the FB specified by the user.
+ * Remove the specified FB.
*
- * Called by the user via ioctl.
+ * Called by the user via ioctl, or by an in-kernel client.
*
* Returns:
* Zero on success, negative errno on failure.
*/
-int drm_mode_rmfb(struct drm_device *dev,
- void *data, struct drm_file *file_priv)
+int drm_mode_rmfb(struct drm_device *dev, u32 fb_id,
+ struct drm_file *file_priv)
{
struct drm_framebuffer *fb = NULL;
struct drm_framebuffer *fbl = NULL;
- uint32_t *id = data;
int found = 0;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
- fb = drm_framebuffer_lookup(dev, file_priv, *id);
+ fb = drm_framebuffer_lookup(dev, file_priv, fb_id);
if (!fb)
return -ENOENT;
@@ -435,6 +443,14 @@ int drm_mode_rmfb(struct drm_device *dev,
return -ENOENT;
}
+int drm_mode_rmfb_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv)
+{
+ uint32_t *fb_id = data;
+
+ return drm_mode_rmfb(dev, *fb_id, file_priv);
+}
+
/**
* drm_mode_getfb - get FB info
* @dev: drm device for the ioctl
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 40179c5fc6b8..3f5d7706bcc9 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -37,6 +37,9 @@ void drm_pci_agp_destroy(struct drm_device *dev);
int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master);
/* drm_prime.c */
+int drm_prime_handle_to_fd(struct drm_device *dev,
+ struct drm_prime_handle *args,
+ struct drm_file *file_priv);
int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c
index f8e96e648acf..576d00b7dad5 100644
--- a/drivers/gpu/drm/drm_ioc32.c
+++ b/drivers/gpu/drm/drm_ioc32.c
@@ -884,7 +884,7 @@ static int compat_drm_mode_addfb2(struct file *file, unsigned int cmd,
sizeof(req64.modifier)))
return -EFAULT;
- err = drm_ioctl_kernel(file, drm_mode_addfb2, &req64,
+ err = drm_ioctl_kernel(file, drm_mode_addfb2_ioctl, &req64,
DRM_CONTROL_ALLOW|DRM_UNLOCKED);
if (err)
return err;
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index af782911c505..c69fda5d3875 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -635,8 +635,8 @@ static const struct drm_ioctl_desc drm_ioctls[] = {
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
- DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index caf675e3e692..e6052ab2bec4 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -866,11 +866,10 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev,
}
EXPORT_SYMBOL(drm_gem_prime_fd_to_handle);
-int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+int drm_prime_handle_to_fd(struct drm_device *dev,
+ struct drm_prime_handle *args,
+ struct drm_file *file_priv)
{
- struct drm_prime_handle *args = data;
-
if (!drm_core_check_feature(dev, DRIVER_PRIME))
return -EINVAL;
@@ -885,6 +884,12 @@ int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data,
args->handle, args->flags, &args->fd);
}
+int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+{
+ return drm_prime_handle_to_fd(dev, data, file_priv);
+}
+
int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
--
2.15.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 17/25] drm/client: Bail out if there's a DRM master
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
` (3 preceding siblings ...)
2018-04-12 16:12 ` [RFC v4 16/25] drm: Make ioctls available for in-kernel clients Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 18/25] drm/client: Make the display modes available to clients Noralf Trønnes
` (6 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel; +Cc: daniel.vetter, intel-gfx, laurent.pinchart, mstaudt
If there's a DRM master, return -EBUSY.
Block userspace from becoming master by taking the master lock while
the client is setting the mode.
Suggested-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_auth.c | 33 +++++++++++++++++++++++++++++++++
drivers/gpu/drm/drm_client.c | 34 +++++++++++++++++++++++++++++-----
drivers/gpu/drm/drm_fb_helper.c | 8 ++++----
drivers/gpu/drm/drm_internal.h | 2 ++
include/drm/drm_client.h | 4 ++--
5 files changed, 70 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c
index d9c0f7573905..d656d0d93da3 100644
--- a/drivers/gpu/drm/drm_auth.c
+++ b/drivers/gpu/drm/drm_auth.c
@@ -366,3 +366,36 @@ void drm_master_put(struct drm_master **master)
*master = NULL;
}
EXPORT_SYMBOL(drm_master_put);
+
+/**
+ * drm_master_block - Block DRM master operations
+ * @dev: DRM device
+ *
+ * This function checks if there is a master on @dev. If there is no master it
+ * blocks anyone from becoming master. In-kernel clients can use this to know
+ * when they can act as master. Use drm_master_unblock() to unblock.
+ *
+ * Returns:
+ * True if there is no master, false otherwise.
+ */
+bool drm_master_block(struct drm_device *dev)
+{
+ mutex_lock(&dev->master_mutex);
+ if (dev->master) {
+ mutex_unlock(&dev->master_mutex);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * drm_master_unblock - Unblock DRM master operations
+ * @dev: DRM device
+ *
+ * Unblock and allow userspace to become master.
+ */
+void drm_master_unblock(struct drm_device *dev)
+{
+ mutex_unlock(&dev->master_mutex);
+}
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index 27818a467b09..764c556630b8 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -18,6 +18,8 @@
#include <drm/drm_device.h>
#include <drm/drm_modes.h>
+#include "drm_internal.h"
+
struct drm_client_display_offset {
int x, y;
};
@@ -313,18 +315,30 @@ static int drm_client_display_restore_legacy(struct drm_client_display *display)
/**
* drm_client_display_restore() - Restore client display
* @display: Client display
+ * @force: If true, restore even if there's a DRM master
*
* Restore client display using the current modeset configuration.
*
* Return:
* Zero on succes or negative error code on failure.
*/
-int drm_client_display_restore(struct drm_client_display *display)
+int drm_client_display_restore(struct drm_client_display *display, bool force)
{
- if (drm_drv_uses_atomic_modeset(display->dev))
- return drm_client_display_restore_atomic(display, true);
+ struct drm_device *dev = display->dev;
+ int ret;
+
+ if (!force && !drm_master_block(dev))
+ return -EBUSY;
+
+ if (drm_drv_uses_atomic_modeset(dev))
+ ret = drm_client_display_restore_atomic(display, true);
else
- return drm_client_display_restore_legacy(display);
+ ret = drm_client_display_restore_legacy(display);
+
+ if (!force)
+ drm_master_unblock(dev);
+
+ return ret;
}
EXPORT_SYMBOL(drm_client_display_restore);
@@ -354,13 +368,23 @@ static void drm_client_display_dpms_legacy(struct drm_client_display *display, i
* drm_client_display_dpms() - Set display DPMS mode
* @display: Client display
* @mode: DPMS mode
+ *
+ * Returns:
+ * Zero on success, -EBUSY if there's a DRM master.
*/
-void drm_client_display_dpms(struct drm_client_display *display, int mode)
+int drm_client_display_dpms(struct drm_client_display *display, int mode)
{
+ if (!drm_master_block(display->dev))
+ return -EBUSY;
+
if (drm_drv_uses_atomic_modeset(display->dev))
drm_client_display_restore_atomic(display, mode == DRM_MODE_DPMS_ON);
else
drm_client_display_dpms_legacy(display, mode);
+
+ drm_master_unblock(display->dev);
+
+ return 0;
}
EXPORT_SYMBOL(drm_client_display_dpms);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 01d8840930a3..98e5bc92c9f2 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -181,7 +181,7 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
return 0;
mutex_lock(&fb_helper->lock);
- ret = drm_client_display_restore(fb_helper->display);
+ ret = drm_client_display_restore(fb_helper->display, false);
do_delayed = fb_helper->delayed_hotplug;
if (do_delayed)
@@ -243,7 +243,7 @@ static bool drm_fb_helper_force_kernel_mode(void)
continue;
mutex_lock(&helper->lock);
- ret = drm_client_display_restore(helper->display);
+ ret = drm_client_display_restore(helper->display, true);
if (ret)
error = true;
mutex_unlock(&helper->lock);
@@ -1254,7 +1254,7 @@ static int pan_display_atomic(struct fb_var_screeninfo *var,
pan_set(fb_helper, var->xoffset, var->yoffset);
- ret = drm_client_display_restore(fb_helper->display);
+ ret = drm_client_display_restore(fb_helper->display, false);
if (!ret) {
info->var.xoffset = var->xoffset;
info->var.yoffset = var->yoffset;
@@ -1423,7 +1423,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
/* First time: disable all crtc's.. */
if (!fb_helper->deferred_setup && !READ_ONCE(fb_helper->dev->master))
- drm_client_display_restore(fb_helper->display);
+ drm_client_display_restore(fb_helper->display, false);
return -EAGAIN;
}
diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h
index 3f5d7706bcc9..f38dcaf139d7 100644
--- a/drivers/gpu/drm/drm_internal.h
+++ b/drivers/gpu/drm/drm_internal.h
@@ -92,6 +92,8 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int drm_master_open(struct drm_file *file_priv);
void drm_master_release(struct drm_file *file_priv);
+bool drm_master_block(struct drm_device *dev);
+void drm_master_unblock(struct drm_device *dev);
/* drm_sysfs.c */
extern struct class *drm_class;
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 27d2a46cd94a..3befd879a0b0 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -46,8 +46,8 @@ drm_client_display_find_modeset(struct drm_client_display *display, struct drm_c
bool drm_client_display_panel_rotation(struct drm_connector *connector,
struct drm_plane *plane,
unsigned int *rotation);
-int drm_client_display_restore(struct drm_client_display *display);
-void drm_client_display_dpms(struct drm_client_display *display, int mode);
+int drm_client_display_restore(struct drm_client_display *display, bool force);
+int drm_client_display_dpms(struct drm_client_display *display, int mode);
struct drm_client_display *
drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int height);
--
2.15.1
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 18/25] drm/client: Make the display modes available to clients
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
` (4 preceding siblings ...)
2018-04-12 16:12 ` [RFC v4 17/25] drm/client: Bail out if there's a DRM master Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 19/25] drm/client: Finish the in-kernel client API Noralf Trønnes
` (5 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel
Cc: daniel.vetter, intel-gfx, Noralf Trønnes, laurent.pinchart,
mstaudt, dh.herrmann
Give clients easy access to the display modes.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_client.c | 159 +++++++++++++++++++++++++++++++++----------
include/drm/drm_client.h | 25 +++++++
2 files changed, 148 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index 764c556630b8..bce1630a0db2 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -8,6 +8,7 @@
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
*/
+#include <linux/list.h>
#include <linux/slab.h>
#include <drm/drm_atomic.h>
@@ -54,6 +55,7 @@ struct drm_client_display *drm_client_display_create(struct drm_device *dev)
}
display->dev = dev;
+ INIT_LIST_HEAD(&display->modes);
display->modeset_count = num_crtc;
drm_for_each_crtc(crtc, dev)
@@ -84,12 +86,16 @@ EXPORT_SYMBOL(drm_client_display_create);
*/
void drm_client_display_free(struct drm_client_display *display)
{
+ struct drm_display_mode *mode, *tmp;
struct drm_mode_set *modeset;
unsigned int i;
if (!display)
return;
+ list_for_each_entry_safe(mode, tmp, &display->modes, head)
+ drm_mode_destroy(display->dev, mode);
+
drm_client_display_for_each_modeset(modeset, display) {
if (modeset->mode)
drm_mode_destroy(display->dev, modeset->mode);
@@ -670,22 +676,70 @@ static int drm_pick_crtcs(struct drm_client_display *display,
return best_score;
}
-/**
- * drm_client_find_display() - Find display
- * @dev: DRM device
- * @width: Maximum display mode width (optional)
- * @height: Maximum display mode height (optional)
- *
- * This function returns a display the client can use if available.
- *
- * Free resources by calling drm_client_display_free().
- *
- * Returns:
- * A &drm_client_display on success, NULL if no connectors are found
- * or error pointer on failure.
- */
-struct drm_client_display *
-drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int height)
+/* Give the client a static list of display modes */
+static int drm_client_display_copy_modes(struct drm_client_display *display)
+{
+ int hdisplay = 0, vdisplay = 0, vrefresh;
+ struct drm_device *dev = display->dev;
+ struct drm_display_mode *mode, *copy;
+ struct drm_connector *connector;
+ struct drm_mode_set *modeset;
+ unsigned int count = 0;
+
+ drm_client_display_for_each_modeset(modeset, display) {
+ if (!modeset->num_connectors || !modeset->mode)
+ continue;
+
+ connector = modeset->connectors[0];
+ mode = modeset->mode;
+ count++;
+
+ if (modeset->num_connectors == 2) {
+ /* Cloned output */
+ copy = drm_mode_duplicate(dev, modeset->mode);
+ if (!copy)
+ return -ENOMEM;
+ list_add_tail(©->head, &display->modes);
+ display->mode = copy;
+
+ return 0;
+ }
+
+ if (!modeset->y)
+ hdisplay += modeset->mode->hdisplay;
+ if (!modeset->x)
+ vdisplay += modeset->mode->vdisplay;
+ vrefresh = modeset->mode->vrefresh;
+ }
+
+ if (!count)
+ return 0;
+
+ if (count == 1) {
+ struct drm_display_mode *iter;
+
+ list_for_each_entry(iter, &connector->modes, head) {
+ copy = drm_mode_duplicate(dev, iter);
+ if (!copy)
+ return -ENOMEM;
+ list_add_tail(©->head, &display->modes);
+ if (!display->mode && drm_mode_equal(iter, mode))
+ display->mode = copy;
+ }
+ } else {
+ /* Combined tile mode. Only the default one for now */
+ copy = drm_cvt_mode(dev, hdisplay, vdisplay, vrefresh, false, false, false);
+ if (!copy)
+ return -ENOMEM;
+ list_add_tail(©->head, &display->modes);
+ display->mode = copy;
+ }
+
+ return 0;
+}
+
+static struct drm_client_display *
+drm_client_find_display_default(struct drm_device *dev, unsigned int width, unsigned int height)
{
struct drm_client_display_offset *offsets;
struct drm_client_display *display;
@@ -695,25 +749,6 @@ drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int
int i, connector_count;
bool *enabled;
- DRM_DEBUG_KMS("\n");
-
- if (!width)
- width = dev->mode_config.max_width;
- if (!height)
- height = dev->mode_config.max_height;
-
- mutex_lock(&dev->mode_config.mutex);
- if (!drm_client_probe_connector_modes(dev, width, height))
- DRM_DEBUG_KMS("No connectors reported connected with modes\n");
-
- if (dev->driver->initial_client_display) {
- display = dev->driver->initial_client_display(dev, width, height);
- if (display) {
- mutex_unlock(&dev->mode_config.mutex);
- return display;
- }
- }
-
connector_count = drm_connector_get_all(dev, &connectors);
if (connector_count < 1)
return NULL;
@@ -772,7 +807,6 @@ drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int
}
}
out:
- mutex_unlock(&dev->mode_config.mutex);
drm_connector_put_all(connectors, connector_count);
kfree(crtcs);
kfree(modes);
@@ -781,4 +815,57 @@ drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int
return display;
}
+
+/**
+ * drm_client_find_display() - Find display
+ * @dev: DRM device
+ * @width: Maximum display mode width (optional)
+ * @height: Maximum display mode height (optional)
+ *
+ * This function returns a display the client can use if one is found.
+ *
+ * Free resources by calling drm_client_display_free().
+ *
+ * Returns:
+ * A &drm_client_display on success, NULL if no connectors are found
+ * or error pointer on failure.
+ */
+struct drm_client_display *
+drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int height)
+{
+ struct drm_client_display *display = NULL;
+ int ret;
+
+ DRM_DEBUG_KMS("\n");
+
+ if (!width)
+ width = dev->mode_config.max_width;
+ if (!height)
+ height = dev->mode_config.max_height;
+
+ mutex_lock(&dev->mode_config.mutex);
+
+ if (!drm_client_probe_connector_modes(dev, width, height))
+ DRM_DEBUG_KMS("No connectors reported connected with modes\n");
+
+ if (dev->driver->initial_client_display)
+ display = dev->driver->initial_client_display(dev, width, height);
+
+ if (!display)
+ display = drm_client_find_display_default(dev, width, height);
+
+ if (IS_ERR_OR_NULL(display))
+ goto out_unlock;
+
+ ret = drm_client_display_copy_modes(display);
+ if (ret) {
+ drm_client_display_free(display);
+ display = ERR_PTR(ret);
+ }
+
+out_unlock:
+ mutex_unlock(&dev->mode_config.mutex);
+
+ return display;
+}
EXPORT_SYMBOL(drm_client_find_display);
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 3befd879a0b0..524f793d6e7b 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -3,9 +3,12 @@
#ifndef _DRM_CLIENT_H_
#define _DRM_CLIENT_H_
+#include <linux/types.h>
+
struct drm_connector;
struct drm_crtc;
struct drm_device;
+struct drm_display_mode;
struct drm_mode_set;
struct drm_plane;
@@ -33,6 +36,20 @@ struct drm_client_display {
* Number of modesets
*/
unsigned int modeset_count;
+
+ /**
+ * @modes:
+ *
+ * Display modes available on this display.
+ */
+ struct list_head modes;
+
+ /**
+ * @mode:
+ *
+ * The current display mode.
+ */
+ struct drm_display_mode *mode;
};
struct drm_client_display *drm_client_display_create(struct drm_device *dev);
@@ -51,4 +68,12 @@ int drm_client_display_dpms(struct drm_client_display *display, int mode);
struct drm_client_display *
drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int height);
+/**
+ * drm_client_display_for_each_mode - Iterate over the available display modes
+ * @mode: A @drm_display_mode loop cursor
+ * @display: Client display
+ */
+#define drm_client_display_for_each_mode(mode, display) \
+ list_for_each_entry(mode, &display->modes, head)
+
#endif
--
2.15.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 19/25] drm/client: Finish the in-kernel client API
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
` (5 preceding siblings ...)
2018-04-12 16:12 ` [RFC v4 18/25] drm/client: Make the display modes available to clients Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 20/25] drm/prime: Don't pin module on export for in-kernel clients Noralf Trønnes
` (4 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel; +Cc: daniel.vetter, intel-gfx, laurent.pinchart, mstaudt
The modesetting code is already present, this adds the rest of the API.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_client.c | 573 +++++++++++++++++++++++++++++++++++++
drivers/gpu/drm/drm_debugfs.c | 7 +
drivers/gpu/drm/drm_drv.c | 11 +
drivers/gpu/drm/drm_file.c | 3 +
drivers/gpu/drm/drm_probe_helper.c | 3 +
drivers/gpu/drm/drm_sysfs.c | 20 ++
include/drm/drm_client.h | 103 +++++++
include/drm/drm_device.h | 4 +
8 files changed, 724 insertions(+)
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index bce1630a0db2..760f1795f812 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -8,7 +8,9 @@
* Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
*/
+#include <linux/dma-buf.h>
#include <linux/list.h>
+#include <linux/mutex.h>
#include <linux/slab.h>
#include <drm/drm_atomic.h>
@@ -17,14 +19,280 @@
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
+#include <drm/drm_file.h>
#include <drm/drm_modes.h>
+#include "drm_crtc_internal.h"
#include "drm_internal.h"
struct drm_client_display_offset {
int x, y;
};
+static int drm_client_alloc_file(struct drm_client_dev *client)
+{
+ struct drm_device *dev = client->dev;
+ struct drm_file *file;
+
+ file = drm_file_alloc(dev->primary);
+ if (IS_ERR(file))
+ return PTR_ERR(file);
+
+ drm_dev_get(dev);
+
+ mutex_lock(&dev->filelist_mutex);
+ list_add(&file->lhead, &dev->filelist_internal);
+ mutex_unlock(&dev->filelist_mutex);
+
+ client->file = file;
+
+ return 0;
+}
+
+static void drm_client_free_file(struct drm_client_dev *client)
+{
+ struct drm_device *dev = client->dev;
+
+ mutex_lock(&dev->filelist_mutex);
+ list_del(&client->file->lhead);
+ mutex_unlock(&dev->filelist_mutex);
+
+ drm_file_free(client->file);
+ drm_dev_put(dev);
+}
+
+struct drm_client_dev *
+drm_client_new(struct drm_device *dev, const struct drm_client_funcs *funcs)
+{
+ struct drm_client_dev *client;
+ int ret;
+
+ if (WARN_ON(!funcs->name))
+ return ERR_PTR(-EINVAL);
+
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
+ if (!client)
+ return ERR_PTR(-ENOMEM);
+
+ client->dev = dev;
+ client->funcs = funcs;
+
+ ret = drm_client_alloc_file(client);
+ if (ret) {
+ kfree(client);
+ return ERR_PTR(ret);
+ }
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_add(&client->list, &dev->clientlist);
+ mutex_unlock(&dev->clientlist_mutex);
+
+ return client;
+}
+EXPORT_SYMBOL(drm_client_new);
+
+struct drm_client_dev *
+drm_client_new_from_id(unsigned int dev_id, const struct drm_client_funcs *funcs)
+{
+ struct drm_client_dev *client;
+ struct drm_minor *minor;
+
+ minor = drm_minor_acquire(dev_id);
+ if (IS_ERR(minor))
+ return ERR_CAST(minor);
+
+ client = drm_client_new(minor->dev, funcs);
+
+ drm_minor_release(minor);
+
+ return client;
+}
+EXPORT_SYMBOL(drm_client_new_from_id);
+
+/**
+ * drm_client_free - Free DRM client resources
+ * @client: DRM client
+ *
+ * This is called automatically on client removal unless the client returns
+ * non-zero in the &drm_client_funcs->remove callback. The fbdev client does
+ * this when it can't close &drm_file because userspace has an open fd.
+ *
+ * Note:
+ * If the client can't release it's resources on remove, it needs to hold a
+ * reference on the driver module to prevent the code from going away.
+ */
+void drm_client_free(struct drm_client_dev *client)
+{
+ DRM_DEV_DEBUG_KMS(client->dev->dev, "%s\n", client->funcs->name);
+ drm_client_free_file(client);
+ kfree(client);
+}
+EXPORT_SYMBOL(drm_client_free);
+
+static void drm_client_remove_locked(struct drm_client_dev *client)
+{
+ list_del(&client->list);
+
+ if (!client->funcs->remove || !client->funcs->remove(client))
+ drm_client_free(client);
+}
+
+static void drm_client_remove_safe(struct drm_device *dev,
+ struct drm_client_dev *client)
+{
+ struct drm_client_dev *iter;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(iter, &dev->clientlist, list) {
+ if (iter == client) {
+ drm_client_remove_locked(client);
+ break;
+ }
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+
+/**
+ * drm_client_remove - Remove client
+ * @client: Client
+ *
+ * Remove a client.
+ */
+void drm_client_remove(struct drm_client_dev *client)
+{
+ struct drm_device *dev;
+
+ if (!client)
+ return;
+
+ dev = client->dev;
+ drm_dev_get(dev);
+ drm_client_remove_safe(dev, client);
+ drm_dev_put(dev);
+}
+EXPORT_SYMBOL(drm_client_remove);
+
+struct drm_client_remove_defer {
+ struct list_head list;
+ struct drm_device *dev;
+ struct drm_client_dev *client;
+};
+
+static LIST_HEAD(drm_client_remove_defer_list);
+static DEFINE_MUTEX(drm_client_remove_defer_list_lock);
+
+static void drm_client_remove_defer_work_fn(struct work_struct *work)
+{
+ struct drm_client_remove_defer *defer, *tmp;
+
+ mutex_lock(&drm_client_remove_defer_list_lock);
+ list_for_each_entry_safe(defer, tmp, &drm_client_remove_defer_list, list) {
+ drm_client_remove_safe(defer->dev, defer->client);
+ drm_dev_put(defer->dev);
+ list_del(&defer->list);
+ kfree(defer);
+ }
+ mutex_unlock(&drm_client_remove_defer_list_lock);
+}
+
+static DECLARE_WORK(drm_client_remove_defer_work, drm_client_remove_defer_work_fn);
+
+/**
+ * drm_client_remove_defer - Deferred client removal
+ * @client: Client
+ *
+ * Defer client removal to a worker. This makes it possible for a client running
+ * in a worker to remove itself.
+ *
+ * Returns:
+ * Zero on success, or -ENOMEM on allocation failure.
+ */
+int drm_client_remove_defer(struct drm_client_dev *client)
+{
+ struct drm_client_remove_defer *defer;
+
+ if (!client)
+ return 0;
+
+ defer = kzalloc(sizeof(*defer), GFP_KERNEL);
+ if (!defer)
+ return -ENOMEM;
+
+ defer->dev = client->dev;
+ defer->client = client;
+ drm_dev_get(client->dev);
+
+ mutex_lock(&drm_client_remove_defer_list_lock);
+ list_add(&defer->list, &drm_client_remove_defer_list);
+ mutex_unlock(&drm_client_remove_defer_list_lock);
+
+ schedule_work(&drm_client_remove_defer_work);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_client_remove_defer);
+
+void drm_client_init(void)
+{
+}
+
+void drm_client_exit(void)
+{
+ flush_work(&drm_client_remove_defer_work);
+}
+
+void drm_client_dev_unregister(struct drm_device *dev)
+{
+ struct drm_client_dev *client, *tmp;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry_safe(client, tmp, &dev->clientlist, list)
+ drm_client_remove_locked(client);
+ mutex_unlock(&dev->clientlist_mutex);
+}
+
+void drm_client_dev_hotplug(struct drm_device *dev)
+{
+ struct drm_client_dev *client;
+ int ret;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list) {
+ if (!client->funcs->hotplug)
+ continue;
+
+ ret = client->funcs->hotplug(client);
+ DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->funcs->name, ret);
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+EXPORT_SYMBOL(drm_client_dev_hotplug);
+
+void drm_client_dev_lastclose(struct drm_device *dev)
+{
+ struct drm_client_dev *client;
+ int ret;
+
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list) {
+ if (!client->funcs->lastclose)
+ continue;
+
+ ret = client->funcs->lastclose(client);
+ DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->funcs->name, ret);
+ }
+ mutex_unlock(&dev->clientlist_mutex);
+}
+
/**
* drm_client_display_create() - Create display structure
* @dev: DRM device
@@ -348,6 +616,90 @@ int drm_client_display_restore(struct drm_client_display *display, bool force)
}
EXPORT_SYMBOL(drm_client_display_restore);
+/**
+ * drm_client_display_commit_mode - Commit a mode/fb to the CRTC(s)
+ * @display: Client display
+ * @fb: Framebuffer (if NULL the current fb is used)
+ * @mode: Display mode (if NULL the current mode is used)
+ *
+ * Returns:
+ * Zero on success, negative error code on failure.
+ */
+int drm_client_display_commit(struct drm_client_display *display,
+ struct drm_framebuffer *fb, struct drm_display_mode *mode)
+{
+ struct drm_display_mode *use_mode = NULL;
+ struct drm_mode_set *modeset;
+ unsigned int count = 0;
+
+ if (mode) {
+ struct drm_display_mode *iter;
+
+ drm_client_display_for_each_mode(iter, display) {
+ if (!use_mode && drm_mode_equal(iter, mode))
+ use_mode = iter;
+ count++;
+ }
+
+ if (!use_mode)
+ return -EINVAL;
+
+ /*
+ * Don't actually set the mode in the single mode case since it
+ * might be a tiled display which consists of multiple modes.
+ * Just keep the current mode.
+ */
+ if (count == 1)
+ use_mode = NULL;
+ }
+
+ count = 0;
+ drm_client_display_for_each_modeset(modeset, display) {
+ if (!modeset->num_connectors)
+ continue;
+
+ if (fb)
+ modeset->fb = fb;
+
+ if (use_mode) {
+ if (WARN_ON(++count > 1))
+ return -EINVAL;
+
+ if (modeset->mode)
+ drm_mode_destroy(display->dev, modeset->mode);
+ modeset->mode = drm_mode_duplicate(display->dev, use_mode);
+ if (!modeset->mode)
+ return -ENOMEM;
+ }
+ }
+
+ return drm_client_display_restore(display, false);
+}
+EXPORT_SYMBOL(drm_client_display_commit);
+
+struct drm_framebuffer *drm_client_display_current_fb(struct drm_client_display *display)
+{
+ struct drm_mode_set *modeset;
+
+ drm_client_display_for_each_modeset(modeset, display) {
+ struct drm_crtc *crtc = modeset->crtc;
+ struct drm_framebuffer *fb = NULL;
+
+ drm_modeset_lock(&crtc->primary->mutex, NULL);
+ if (crtc->primary->state && crtc->primary->state->fb)
+ fb = crtc->primary->state->fb;
+ else if (!crtc->primary->state && crtc->primary->fb)
+ fb = crtc->primary->fb;
+ drm_modeset_unlock(&crtc->primary->mutex);
+
+ if (fb)
+ return fb;
+ }
+
+ return NULL;
+}
+EXPORT_SYMBOL(drm_client_display_current_fb);
+
static void drm_client_display_dpms_legacy(struct drm_client_display *display, int dpms_mode)
{
struct drm_device *dev = display->dev;
@@ -869,3 +1221,224 @@ drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int
return display;
}
EXPORT_SYMBOL(drm_client_find_display);
+
+static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
+{
+ if (!buffer)
+ return;
+
+ if (buffer->vaddr)
+ dma_buf_vunmap(buffer->dma_buf, buffer->vaddr);
+
+ if (buffer->dma_buf)
+ dma_buf_put(buffer->dma_buf);
+
+ drm_mode_destroy_dumb(buffer->client->dev, buffer->handle, buffer->client->file);
+ kfree(buffer);
+}
+
+/* For testing __close_fd() */
+#include <linux/fdtable.h>
+
+static struct drm_client_buffer *
+drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+{
+ struct drm_mode_create_dumb dumb_args = { };
+ struct drm_prime_handle prime_args = { };
+ struct drm_client_buffer *buffer;
+ struct dma_buf *dma_buf;
+ void *vaddr;
+ int ret;
+
+ buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
+ if (!buffer)
+ return ERR_PTR(-ENOMEM);
+
+ buffer->client = client;
+ buffer->width = width;
+ buffer->height = height;
+ buffer->format = format;
+
+ dumb_args.width = buffer->width;
+ dumb_args.height = buffer->height;
+ dumb_args.bpp = drm_format_plane_cpp(format, 0) * 8;
+ ret = drm_mode_create_dumb(client->dev, &dumb_args, client->file);
+ if (ret)
+ goto err_free;
+
+ buffer->handle = dumb_args.handle;
+ buffer->pitch = dumb_args.pitch;
+ buffer->size = dumb_args.size;
+
+ prime_args.handle = dumb_args.handle;
+ ret = drm_prime_handle_to_fd(client->dev, &prime_args, client->file);
+ if (ret)
+ goto err_delete;
+
+ dma_buf = dma_buf_get(prime_args.fd);
+ if (IS_ERR(dma_buf)) {
+ ret = PTR_ERR(dma_buf);
+ goto err_delete;
+ }
+
+ /*
+ * If called from a worker the dmabuf fd isn't closed and the ref
+ * doesn't drop to zero on free.
+ * If I use __close_fd() it's all fine, but that function is not exported.
+ *
+ * How do I get rid of this fd when in a worker/kernel thread?
+ * The fd isn't used beyond this function.
+ */
+// WARN_ON(__close_fd(current->files, prime_args.fd));
+
+ pr_info("%s: PF_KTHREAD=%u\n", __func__, !!(current->flags & PF_KTHREAD));
+
+ buffer->dma_buf = dma_buf;
+
+ vaddr = dma_buf_vmap(dma_buf);
+ if (!vaddr) {
+ ret = -ENOMEM;
+ goto err_delete;
+ }
+
+ buffer->vaddr = vaddr;
+
+ return buffer;
+
+err_delete:
+ drm_client_buffer_delete(buffer);
+err_free:
+ kfree(buffer);
+
+ return ERR_PTR(ret);
+}
+
+static int drm_client_buffer_rmfb(struct drm_client_buffer *buffer)
+{
+ int ret;
+
+ if (!buffer || !buffer->fb)
+ return 0;
+
+ ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file);
+ if (ret)
+ DRM_DEV_ERROR(buffer->client->dev->dev,
+ "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret);
+
+ buffer->fb = NULL;
+
+ return 0;
+}
+
+static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
+ struct drm_display_mode *mode)
+{
+ struct drm_client_dev *client = buffer->client;
+ struct drm_mode_fb_cmd2 fb_req = { };
+ int ret;
+
+ if (mode->hdisplay > buffer->width || mode->vdisplay > buffer->height)
+ return -EINVAL;
+
+ fb_req.width = mode->hdisplay;
+ fb_req.height = mode->vdisplay;
+ fb_req.pixel_format = buffer->format;
+ fb_req.handles[0] = buffer->handle;
+ fb_req.pitches[0] = buffer->pitch;
+
+ ret = drm_mode_addfb2(client->dev, &fb_req, client->file, client->funcs->name);
+ if (ret)
+ return ret;
+
+ buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id);
+ if (WARN_ON(!buffer->fb))
+ return -ENOENT;
+
+ /* drop the reference we picked up in framebuffer lookup */
+ drm_framebuffer_put(buffer->fb);
+
+ return 0;
+}
+
+/**
+ * drm_client_framebuffer_create - Create a client framebuffer
+ * @client: DRM client
+ * @mode: Display mode to create a buffer for
+ * @format: Buffer format
+ *
+ * This function creates a &drm_client_buffer which consists of a
+ * &drm_framebuffer backed by a dumb buffer. The dumb buffer is &dma_buf
+ * exported to aquire a virtual address which is stored in
+ * &drm_client_buffer->vaddr.
+ * Call drm_client_framebuffer_delete() to free the buffer.
+ *
+ * Returns:
+ * Pointer to a client buffer or an error pointer on failure.
+ */
+struct drm_client_buffer *
+drm_client_framebuffer_create(struct drm_client_dev *client,
+ struct drm_display_mode *mode, u32 format)
+{
+ struct drm_client_buffer *buffer;
+ int ret;
+
+ buffer = drm_client_buffer_create(client, mode->hdisplay,
+ mode->vdisplay, format);
+ if (IS_ERR(buffer))
+ return buffer;
+
+ ret = drm_client_buffer_addfb(buffer, mode);
+ if (ret) {
+ drm_client_buffer_delete(buffer);
+ return ERR_PTR(ret);
+ }
+
+ return buffer;
+}
+EXPORT_SYMBOL(drm_client_framebuffer_create);
+
+void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
+{
+ drm_client_buffer_rmfb(buffer);
+ drm_client_buffer_delete(buffer);
+}
+EXPORT_SYMBOL(drm_client_framebuffer_delete);
+
+int drm_client_framebuffer_flush(struct drm_client_buffer *buffer,
+ struct drm_clip_rect *rect)
+{
+ if (!buffer->fb || !buffer->fb->funcs->dirty)
+ return 0;
+
+ return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file,
+ 0, 0, rect, rect ? 1 : 0);
+}
+EXPORT_SYMBOL(drm_client_framebuffer_flush);
+
+#ifdef CONFIG_DEBUG_FS
+static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
+{
+ struct drm_info_node *node = m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct drm_printer p = drm_seq_file_printer(m);
+ struct drm_client_dev *client;
+
+ mutex_lock(&dev->clientlist_mutex);
+ list_for_each_entry(client, &dev->clientlist, list)
+ drm_printf(&p, "%s\n", client->funcs->name);
+ mutex_unlock(&dev->clientlist_mutex);
+
+ return 0;
+}
+
+static const struct drm_info_list drm_client_debugfs_list[] = {
+ { "internal_clients", drm_client_debugfs_internal_clients, 0 },
+};
+
+int drm_client_debugfs_init(struct drm_minor *minor)
+{
+ return drm_debugfs_create_files(drm_client_debugfs_list,
+ ARRAY_SIZE(drm_client_debugfs_list),
+ minor->debugfs_root, minor);
+}
+#endif
diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index b2482818fee8..50a20bfc07ea 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/export.h>
+#include <drm/drm_client.h>
#include <drm/drm_debugfs.h>
#include <drm/drm_edid.h>
#include <drm/drm_atomic.h>
@@ -164,6 +165,12 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id,
DRM_ERROR("Failed to create framebuffer debugfs file\n");
return ret;
}
+
+ ret = drm_client_debugfs_init(minor);
+ if (ret) {
+ DRM_ERROR("Failed to create client debugfs file\n");
+ return ret;
+ }
}
if (dev->driver->debugfs_init) {
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 32a83b41ab61..6f21bafb29be 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -34,6 +34,7 @@
#include <linux/slab.h>
#include <linux/srcu.h>
+#include <drm/drm_client.h>
#include <drm/drm_drv.h>
#include <drm/drmP.h>
@@ -507,6 +508,8 @@ int drm_dev_init(struct drm_device *dev,
dev->driver = driver;
INIT_LIST_HEAD(&dev->filelist);
+ INIT_LIST_HEAD(&dev->filelist_internal);
+ INIT_LIST_HEAD(&dev->clientlist);
INIT_LIST_HEAD(&dev->ctxlist);
INIT_LIST_HEAD(&dev->vmalist);
INIT_LIST_HEAD(&dev->maplist);
@@ -516,6 +519,7 @@ int drm_dev_init(struct drm_device *dev,
spin_lock_init(&dev->event_lock);
mutex_init(&dev->struct_mutex);
mutex_init(&dev->filelist_mutex);
+ mutex_init(&dev->clientlist_mutex);
mutex_init(&dev->ctxlist_mutex);
mutex_init(&dev->master_mutex);
@@ -572,6 +576,7 @@ int drm_dev_init(struct drm_device *dev,
err_free:
mutex_destroy(&dev->master_mutex);
mutex_destroy(&dev->ctxlist_mutex);
+ mutex_destroy(&dev->clientlist_mutex);
mutex_destroy(&dev->filelist_mutex);
mutex_destroy(&dev->struct_mutex);
return ret;
@@ -607,6 +612,7 @@ void drm_dev_fini(struct drm_device *dev)
mutex_destroy(&dev->master_mutex);
mutex_destroy(&dev->ctxlist_mutex);
+ mutex_destroy(&dev->clientlist_mutex);
mutex_destroy(&dev->filelist_mutex);
mutex_destroy(&dev->struct_mutex);
kfree(dev->unique);
@@ -862,6 +868,8 @@ void drm_dev_unregister(struct drm_device *dev)
{
struct drm_map_list *r_list, *list_temp;
+ drm_client_dev_unregister(dev);
+
if (drm_core_check_feature(dev, DRIVER_LEGACY))
drm_lastclose(dev);
@@ -968,6 +976,7 @@ static const struct file_operations drm_stub_fops = {
static void drm_core_exit(void)
{
+ drm_client_exit();
unregister_chrdev(DRM_MAJOR, "drm");
debugfs_remove(drm_debugfs_root);
drm_sysfs_destroy();
@@ -1001,6 +1010,8 @@ static int __init drm_core_init(void)
if (ret < 0)
goto error;
+ drm_client_init();
+
drm_core_init_complete = true;
DRM_DEBUG("Initialized\n");
diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c
index 55505378df47..bcc688e58776 100644
--- a/drivers/gpu/drm/drm_file.c
+++ b/drivers/gpu/drm/drm_file.c
@@ -35,6 +35,7 @@
#include <linux/slab.h>
#include <linux/module.h>
+#include <drm/drm_client.h>
#include <drm/drm_file.h>
#include <drm/drmP.h>
@@ -443,6 +444,8 @@ void drm_lastclose(struct drm_device * dev)
if (drm_core_check_feature(dev, DRIVER_LEGACY))
drm_legacy_dev_reinit(dev);
+
+ drm_client_dev_lastclose(dev);
}
/**
diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 527743394150..26be57e28a9d 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -33,6 +33,7 @@
#include <linux/moduleparam.h>
#include <drm/drmP.h>
+#include <drm/drm_client.h>
#include <drm/drm_crtc.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_crtc_helper.h>
@@ -563,6 +564,8 @@ void drm_kms_helper_hotplug_event(struct drm_device *dev)
drm_sysfs_hotplug_event(dev);
if (dev->mode_config.funcs->output_poll_changed)
dev->mode_config.funcs->output_poll_changed(dev);
+
+ drm_client_dev_hotplug(dev);
}
EXPORT_SYMBOL(drm_kms_helper_hotplug_event);
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 1c5b5ce1fd7f..1fc066c41861 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -18,6 +18,7 @@
#include <linux/err.h>
#include <linux/export.h>
+#include <drm/drm_client.h>
#include <drm/drm_sysfs.h>
#include <drm/drmP.h>
#include "drm_internal.h"
@@ -320,6 +321,24 @@ void drm_sysfs_hotplug_event(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_sysfs_hotplug_event);
+static ssize_t remove_internal_clients_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct drm_minor *minor = dev_get_drvdata(dev);
+
+ drm_client_dev_unregister(minor->dev);
+
+ return len;
+}
+static DEVICE_ATTR_WO(remove_internal_clients);
+
+static struct attribute *minor_attrs[] = {
+ &dev_attr_remove_internal_clients.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(minor);
+
static void drm_sysfs_release(struct device *dev)
{
kfree(dev);
@@ -347,6 +366,7 @@ struct device *drm_sysfs_minor_alloc(struct drm_minor *minor)
kdev->class = drm_class;
kdev->type = &drm_sysfs_device_minor;
kdev->parent = minor->dev->dev;
+ kdev->groups = minor_groups;
kdev->release = drm_sysfs_release;
dev_set_drvdata(kdev, minor);
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 524f793d6e7b..6fd2fcaae826 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -5,13 +5,91 @@
#include <linux/types.h>
+struct drm_clip_rect;
struct drm_connector;
struct drm_crtc;
struct drm_device;
struct drm_display_mode;
+struct drm_framebuffer;
+struct drm_minor;
struct drm_mode_set;
struct drm_plane;
+struct drm_client_dev;
+
+/**
+ * struct drm_client_funcs - DRM client callbacks
+ */
+struct drm_client_funcs {
+ /**
+ * @name:
+ *
+ * Name of the client. Mandatory.
+ */
+ const char *name;
+
+ /**
+ * @remove:
+ *
+ * Called when a &drm_device is unregistered or the client is
+ * unregistered. If zero is returned drm_client_free() is called
+ * automatically. If the client can't drop it's resources it should
+ * return non-zero and call drm_client_free() later.
+ *
+ * This callback is optional.
+ */
+ int (*remove)(struct drm_client_dev *client);
+
+ /**
+ * @lastclose:
+ *
+ * Called on drm_lastclose(). The first client instance in the list
+ * that returns zero gets the privilege to restore and no more clients
+ * are called.
+ *
+ * This callback is optional.
+ */
+ int (*lastclose)(struct drm_client_dev *client);
+
+ /**
+ * @hotplug:
+ *
+ * Called on drm_kms_helper_hotplug_event().
+ *
+ * This callback is optional.
+ */
+ int (*hotplug)(struct drm_client_dev *client);
+};
+
+/**
+ * struct drm_client_dev - DRM client instance
+ */
+struct drm_client_dev {
+ struct list_head list;
+ struct drm_device *dev;
+ const struct drm_client_funcs *funcs;
+ struct drm_file *file;
+ unsigned int file_ref_count;
+ void *private;
+};
+
+struct drm_client_dev *
+drm_client_new(struct drm_device *dev, const struct drm_client_funcs *funcs);
+struct drm_client_dev *
+drm_client_new_from_id(unsigned int dev_id, const struct drm_client_funcs *funcs);
+void drm_client_remove(struct drm_client_dev *client);
+int drm_client_remove_defer(struct drm_client_dev *client);
+void drm_client_free(struct drm_client_dev *client);
+
+void drm_client_dev_unregister(struct drm_device *dev);
+void drm_client_dev_hotplug(struct drm_device *dev);
+void drm_client_dev_lastclose(struct drm_device *dev);
+
+void drm_client_init(void);
+void drm_client_exit(void);
+
+int drm_client_debugfs_init(struct drm_minor *minor);
+
/**
* struct drm_client_display - DRM client display
*/
@@ -76,4 +154,29 @@ drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int
#define drm_client_display_for_each_mode(mode, display) \
list_for_each_entry(mode, &display->modes, head)
+int drm_client_display_commit(struct drm_client_display *display,
+ struct drm_framebuffer *fb, struct drm_display_mode *mode);
+struct drm_framebuffer *drm_client_display_current_fb(struct drm_client_display *display);
+
+struct drm_client_buffer {
+ struct drm_client_dev *client;
+ u32 width;
+ u32 height;
+ u32 format;
+ u32 handle;
+ u32 pitch;
+ u64 size;
+ struct dma_buf *dma_buf;
+ void *vaddr;
+ struct drm_framebuffer *fb;
+};
+
+struct drm_client_buffer *
+drm_client_framebuffer_create(struct drm_client_dev *client,
+ struct drm_display_mode *mode, u32 format);
+void drm_client_framebuffer_delete(struct drm_client_buffer *buffer);
+
+int drm_client_framebuffer_flush(struct drm_client_buffer *buffer,
+ struct drm_clip_rect *rect);
+
#endif
diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h
index 3a0eac2885b7..17edadf8b691 100644
--- a/include/drm/drm_device.h
+++ b/include/drm/drm_device.h
@@ -74,6 +74,10 @@ struct drm_device {
struct mutex filelist_mutex;
struct list_head filelist;
+ struct list_head filelist_internal;
+
+ struct mutex clientlist_mutex;
+ struct list_head clientlist;
/** \name Memory management */
/*@{ */
--
2.15.1
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 20/25] drm/prime: Don't pin module on export for in-kernel clients
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
` (6 preceding siblings ...)
2018-04-12 16:12 ` [RFC v4 19/25] drm/client: Finish the in-kernel client API Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 21/25] drm/fb-helper: Add drm_fb_helper_fb_open/release() Noralf Trønnes
` (3 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel; +Cc: daniel.vetter, intel-gfx, laurent.pinchart, mstaudt
Avoid pinning the module when exporting a GEM object as a dmabuf. This
makes it possible to unload drivers that has in-kernel clients using it.
The client is removed on drm_dev_unregister() so no need to pin the driver.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_prime.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index e6052ab2bec4..f9dbe3b9db20 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -567,6 +567,30 @@ struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
.flags = flags,
.priv = obj,
};
+ bool is_internal = false;
+ struct drm_file *file;
+
+ mutex_lock(&dev->filelist_mutex);
+ list_for_each_entry(file, &dev->filelist_internal, lhead) {
+ struct drm_gem_object *iter;
+ int id;
+
+ spin_lock(&file->table_lock);
+ idr_for_each_entry(&file->object_idr, iter, id) {
+ if (iter == obj) {
+ is_internal = true;
+ break;
+ }
+ }
+ spin_unlock(&file->table_lock);
+
+ if (is_internal)
+ break;
+ }
+ mutex_unlock(&dev->filelist_mutex);
+
+ if (is_internal)
+ exp_info.owner = NULL;
if (dev->driver->gem_prime_res_obj)
exp_info.resv = dev->driver->gem_prime_res_obj(obj);
--
2.15.1
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 21/25] drm/fb-helper: Add drm_fb_helper_fb_open/release()
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
` (7 preceding siblings ...)
2018-04-12 16:12 ` [RFC v4 20/25] drm/prime: Don't pin module on export for in-kernel clients Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 22/25] drm/fb-helper: Add generic fbdev emulation Noralf Trønnes
` (2 subsequent siblings)
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel
Cc: daniel.vetter, intel-gfx, Noralf Trønnes, laurent.pinchart,
mstaudt, dh.herrmann
These helpers keep track of fbdev users and drm_driver.last_close will
only restore fbdev when actually in use. Additionally the display is
turned off when the last user is closing. fbcon is a user in this context.
If struct fb_ops is defined in a library, fb_open() takes a ref on the
library (fb_ops.owner) instead of the driver module. Fix that by ensuring
that the driver module is pinned.
The functions are not added to the DRM_FB_HELPER_DEFAULT_OPS() macro,
because some of its users do set fb_open/release themselves.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_fb_helper.c | 54 ++++++++++++++++++++++++++++++++++++++++-
include/drm/drm_fb_helper.h | 29 ++++++++++++++++++++++
2 files changed, 82 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 98e5bc92c9f2..b1124c08b1ed 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -177,7 +177,7 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
if (!drm_fbdev_emulation || !fb_helper)
return -ENODEV;
- if (READ_ONCE(fb_helper->deferred_setup))
+ if (READ_ONCE(fb_helper->deferred_setup) || !READ_ONCE(fb_helper->open_count))
return 0;
mutex_lock(&fb_helper->lock);
@@ -368,6 +368,7 @@ void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
INIT_WORK(&helper->dirty_work, drm_fb_helper_dirty_work);
helper->dirty_clip.x1 = helper->dirty_clip.y1 = ~0;
mutex_init(&helper->lock);
+ helper->open_count = 1;
helper->funcs = funcs;
helper->dev = dev;
}
@@ -620,6 +621,53 @@ int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper)
}
EXPORT_SYMBOL(drm_fb_helper_defio_init);
+/**
+ * drm_fb_helper_fb_open - implementation for &fb_ops.fb_open
+ * @info: fbdev registered by the helper
+ * @user: 1=userspace, 0=fbcon
+ *
+ * Increase fbdev use count.
+ * If &fb_ops is wrapped in a library, pin the driver module.
+ */
+int drm_fb_helper_fb_open(struct fb_info *info, int user)
+{
+ struct drm_fb_helper *fb_helper = info->par;
+ struct drm_device *dev = fb_helper->dev;
+
+ if (info->fbops->owner != dev->driver->fops->owner) {
+ if (!try_module_get(dev->driver->fops->owner))
+ return -ENODEV;
+ }
+
+ fb_helper->open_count++;
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_fb_helper_fb_open);
+
+/**
+ * drm_fb_helper_fb_release - implementation for &fb_ops.fb_release
+ * @info: fbdev registered by the helper
+ * @user: 1=userspace, 0=fbcon
+ *
+ * Decrease fbdev use count and turn off if there are no users left.
+ * If &fb_ops is wrapped in a library, unpin the driver module.
+ */
+int drm_fb_helper_fb_release(struct fb_info *info, int user)
+{
+ struct drm_fb_helper *fb_helper = info->par;
+ struct drm_device *dev = fb_helper->dev;
+
+ if (!(--fb_helper->open_count))
+ drm_fb_helper_dpms(info, DRM_MODE_DPMS_OFF);
+
+ if (info->fbops->owner != dev->driver->fops->owner)
+ module_put(dev->driver->fops->owner);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_fb_helper_fb_release);
+
/**
* drm_fb_helper_sys_read - wrapper around fb_sys_read
* @info: fb_info struct pointer
@@ -1436,6 +1484,10 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
if (ret < 0)
return ret;
+ /* Block restore without users if we do track it */
+ if (fb_helper->fbdev->fbops->fb_open == drm_fb_helper_fb_open)
+ fb_helper->open_count = 0;
+
strcpy(fb_helper->fb->comm, "[fbcon]");
return 0;
}
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 5f66f253a97b..330983975d5e 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -184,6 +184,22 @@ struct drm_fb_helper {
* See also: @deferred_setup
*/
int preferred_bpp;
+
+ /**
+ * @open_count:
+ *
+ * Keeps track of fbdev use to know when to not restore fbdev and to
+ * disable the pipeline when the last user is gone.
+ *
+ * Drivers that use drm_fb_helper_fb_open() as their \.fb_open
+ * callback will get an initial value of 0 and get restore based on
+ * actual use. Others will get an initial value of 1 which means that
+ * fbdev will always be restored. Drivers that call
+ * drm_fb_helper_fb_open() in their \.fb_open, thus needs to set the
+ * initial value to 0 themselves in their &drm_fb_helper_funcs->fb_probe
+ * callback.
+ */
+ unsigned int open_count;
};
/**
@@ -230,6 +246,9 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
struct list_head *pagelist);
int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper);
+int drm_fb_helper_fb_open(struct fb_info *info, int user);
+int drm_fb_helper_fb_release(struct fb_info *info, int user);
+
ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
size_t count, loff_t *ppos);
ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
@@ -376,6 +395,16 @@ static inline int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper)
return -ENODEV;
}
+static inline int drm_fb_helper_fb_open(struct fb_info *info, int user)
+{
+ return -ENODEV;
+}
+
+static inline int drm_fb_helper_fb_release(struct fb_info *info, int user)
+{
+ return -ENODEV;
+}
+
static inline ssize_t drm_fb_helper_sys_read(struct fb_info *info,
char __user *buf, size_t count,
loff_t *ppos)
--
2.15.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 22/25] drm/fb-helper: Add generic fbdev emulation
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
` (8 preceding siblings ...)
2018-04-12 16:12 ` [RFC v4 21/25] drm/fb-helper: Add drm_fb_helper_fb_open/release() Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 23/25] drm: Add DRM device registered notifier Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 24/25] drm/client: Hack: Add bootsplash Noralf Trønnes
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel
Cc: daniel.vetter, intel-gfx, Noralf Trønnes, laurent.pinchart,
mstaudt, dh.herrmann
This adds generic fbdev emulation for drivers that supports
dumb buffers which they can export.
All the driver has to do is call drm_fbdev_generic_setup().
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_fb_helper.c | 255 ++++++++++++++++++++++++++++++++++++++++
include/drm/drm_fb_helper.h | 20 ++++
2 files changed, 275 insertions(+)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index b1124c08b1ed..1954de5b13e0 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -30,6 +30,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/console.h>
+#include <linux/dma-buf.h>
#include <linux/kernel.h>
#include <linux/sysrq.h>
#include <linux/slab.h>
@@ -1995,6 +1996,260 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
+static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct drm_fb_helper *fb_helper = info->par;
+
+ return dma_buf_mmap(fb_helper->buffer->dma_buf, vma, 0);
+}
+
+/*
+ * fb_ops.fb_destroy is called by the last put_fb_info() call at the end of
+ * unregister_framebuffer() or fb_release().
+ */
+static void drm_fbdev_fb_destroy(struct fb_info *info)
+{
+ struct drm_fb_helper *fb_helper = info->par;
+ struct fb_ops *fbops = NULL;
+
+ DRM_DEBUG("\n");
+
+ if (fb_helper->fbdev->fbdefio)
+ fbops = fb_helper->fbdev->fbops;
+
+ drm_fb_helper_fini(fb_helper);
+ drm_client_framebuffer_delete(fb_helper->buffer);
+ drm_client_free(fb_helper->client);
+ kfree(fb_helper);
+ kfree(fbops);
+}
+
+static struct fb_ops drm_fbdev_fb_ops = {
+ /*
+ * No need to set owner, this module is already pinned by the driver.
+ * A reference is taken on the driver module in drm_fb_helper_fb_open()
+ * to prevent the driver going away with open fd's.
+ */
+ DRM_FB_HELPER_DEFAULT_OPS,
+ .fb_open = drm_fb_helper_fb_open,
+ .fb_release = drm_fb_helper_fb_release,
+ .fb_destroy = drm_fbdev_fb_destroy,
+ .fb_mmap = drm_fbdev_fb_mmap,
+ .fb_read = drm_fb_helper_sys_read,
+ .fb_write = drm_fb_helper_sys_write,
+ .fb_fillrect = drm_fb_helper_sys_fillrect,
+ .fb_copyarea = drm_fb_helper_sys_copyarea,
+ .fb_imageblit = drm_fb_helper_sys_imageblit,
+};
+
+static struct fb_deferred_io drm_fbdev_defio = {
+ .delay = HZ / 20,
+ .deferred_io = drm_fb_helper_deferred_io,
+};
+
+/* Hack to test tinydrm before converting to vmalloc buffers */
+static int drm_fbdev_cma_deferred_io_mmap(struct fb_info *info,
+ struct vm_area_struct *vma)
+{
+ fb_deferred_io_mmap(info, vma);
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ return 0;
+}
+
+static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct drm_client_dev *client = fb_helper->client;
+ struct drm_display_mode sizes_mode = {
+ .hdisplay = sizes->surface_width,
+ .vdisplay = sizes->surface_height,
+ };
+ struct drm_client_buffer *buffer;
+ struct drm_framebuffer *fb;
+ struct fb_info *fbi;
+ u32 format;
+ int ret;
+
+ DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
+ sizes->surface_width, sizes->surface_height,
+ sizes->surface_bpp);
+
+ format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
+ buffer = drm_client_framebuffer_create(client, &sizes_mode, format);
+ if (IS_ERR(buffer))
+ return PTR_ERR(buffer);
+
+ fb_helper->buffer = buffer;
+ fb_helper->fb = buffer->fb;
+ fb = buffer->fb;
+
+ fbi = drm_fb_helper_alloc_fbi(fb_helper);
+ if (IS_ERR(fbi)) {
+ ret = PTR_ERR(fbi);
+ goto err_free_buffer;
+ }
+
+ fbi->par = fb_helper;
+ fbi->fbops = &drm_fbdev_fb_ops;
+ fbi->screen_size = fb->height * fb->pitches[0];
+ fbi->fix.smem_len = fbi->screen_size;
+ fbi->screen_buffer = buffer->vaddr;
+ strcpy(fbi->fix.id, "DRM emulated");
+
+ drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth);
+ drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height);
+
+ /*
+ * Drivers that set the dirty callback:
+ * - Doesn't use defio:
+ * i915, virtio, rockchip
+ * - defio with vmalloc buffer blitted on the real one:
+ * vmwgfx
+ * - defio is disabled because it doesn't work with shmem:
+ * udl
+ * - defio with special dirty callback for fbdev, uses vmalloc for fbdev:
+ * qxl
+ * - defio with cma buffer, will move to vmalloc buffers:
+ * tinydrm
+ *
+ * TODO:
+ * Maybe add vmalloc shadow buffer support.
+ */
+
+ if (fb->funcs->dirty) {
+ struct fb_ops *fbops;
+
+ /*
+ * fb_deferred_io_cleanup() clears &fbops->fb_mmap so a per
+ * instance version is necessary.
+ */
+ fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
+ if (!fbops) {
+ ret = -ENOMEM;
+ goto err_fb_info_destroy;
+ }
+
+ *fbops = *fbi->fbops;
+ fbi->fbops = fbops;
+
+ fbi->fbdefio = &drm_fbdev_defio;
+
+ /* Hack so I can test with tinydrm */
+ fbi->fix.smem_start = page_to_phys(virt_to_page(buffer->vaddr));
+
+ fb_deferred_io_init(fbi);
+
+ /* Hack so I can test with tinydrm */
+ fbi->fbops->fb_mmap = drm_fbdev_cma_deferred_io_mmap;
+ }
+
+ return 0;
+
+err_fb_info_destroy:
+ drm_fb_helper_fini(fb_helper);
+err_free_buffer:
+ drm_client_framebuffer_delete(buffer);
+
+ return ret;
+}
+
+static const struct drm_fb_helper_funcs drm_fb_helper_generic_funcs = {
+ .fb_probe = drm_fb_helper_generic_probe,
+};
+
+static int drm_fbdev_client_remove(struct drm_client_dev *client)
+{
+ struct drm_fb_helper *fb_helper = client->private;
+
+ if (!fb_helper->fbdev) {
+ kfree(fb_helper);
+ return 0;
+ }
+
+ unregister_framebuffer(fb_helper->fbdev);
+
+ /*
+ * If userspace is closed the client is now freed by
+ * drm_fbdev_fb_destroy(), otherwise it will be freed on the last close.
+ * Return 1 to tell that freeing is taken care of.
+ */
+
+ return 1;
+}
+
+static int drm_fbdev_client_lastclose(struct drm_client_dev *client)
+{
+ struct drm_fb_helper *fb_helper = client->private;
+
+ drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
+
+ return 0;
+}
+
+static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
+{
+ struct drm_fb_helper *fb_helper = client->private;
+
+ if (fb_helper->fbdev)
+ return 0;
+
+ return drm_fb_helper_fbdev_setup(client->dev, fb_helper,
+ &drm_fb_helper_generic_funcs,
+ fb_helper->preferred_bpp, 0);
+}
+
+static const struct drm_client_funcs drm_fbdev_client_funcs = {
+ .name = "fbdev",
+ .remove = drm_fbdev_client_remove,
+ .lastclose = drm_fbdev_client_lastclose,
+ .hotplug = drm_fbdev_client_hotplug,
+};
+
+/**
+ * drm_fb_helper_generic_fbdev_setup() - Setup generic fbdev emulation
+ * @dev: DRM device
+ * @preferred_bpp: Preferred bits per pixel for the device.
+ * @dev->mode_config.preferred_depth is used if this is zero.
+ *
+ * This function sets up generic fbdev emulation for drivers that supports
+ * dumb buffers which can be exported.
+ *
+ * Restore, hotplug events and teardown are all taken care of. Drivers that does
+ * suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves.
+ * Simple drivers might use drm_mode_config_helper_suspend().
+ *
+ * Returns:
+ * Zero on success or negative error code on failure.
+ */
+int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
+{
+ struct drm_fb_helper *fb_helper;
+ struct drm_client_dev *client;
+
+ if (!drm_fbdev_emulation)
+ return 0;
+
+ fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
+ if (!fb_helper)
+ return -ENOMEM;
+
+ client = drm_client_new(dev, &drm_fbdev_client_funcs);
+ if (IS_ERR(client)) {
+ kfree(fb_helper);
+ return PTR_ERR(client);
+ }
+
+ client->private = fb_helper;
+ fb_helper->client = client;
+ fb_helper->preferred_bpp = preferred_bpp;
+
+ drm_fbdev_client_hotplug(client);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_fbdev_generic_setup);
+
/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
* but the module doesn't depend on any fb console symbols. At least
* attempt to load fbcon to avoid leaving the system without a usable console.
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 330983975d5e..711da1747836 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -126,6 +126,20 @@ struct drm_fb_helper {
*/
struct drm_client_display *display;
+ /**
+ * @client:
+ *
+ * DRM client used by the generic fbdev emulation.
+ */
+ struct drm_client_dev *client;
+
+ /**
+ * @buffer:
+ *
+ * Framebuffer used by the generic fbdev emulation.
+ */
+ struct drm_client_buffer *buffer;
+
const struct drm_fb_helper_funcs *funcs;
struct fb_info *fbdev;
u32 pseudo_palette[17];
@@ -219,6 +233,7 @@ struct drm_fb_helper {
.fb_ioctl = drm_fb_helper_ioctl
#ifdef CONFIG_DRM_FBDEV_EMULATION
+int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp);
void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
const struct drm_fb_helper_funcs *funcs);
int drm_fb_helper_init(struct drm_device *dev,
@@ -297,6 +312,11 @@ void drm_fb_helper_fbdev_teardown(struct drm_device *dev);
void drm_fb_helper_lastclose(struct drm_device *dev);
void drm_fb_helper_output_poll_changed(struct drm_device *dev);
#else
+static inline int
+drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
+{
+}
+
static inline void drm_fb_helper_prepare(struct drm_device *dev,
struct drm_fb_helper *helper,
const struct drm_fb_helper_funcs *funcs)
--
2.15.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 23/25] drm: Add DRM device registered notifier
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
` (9 preceding siblings ...)
2018-04-12 16:12 ` [RFC v4 22/25] drm/fb-helper: Add generic fbdev emulation Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 24/25] drm/client: Hack: Add bootsplash Noralf Trønnes
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel
Cc: daniel.vetter, intel-gfx, Noralf Trønnes, laurent.pinchart,
mstaudt, dh.herrmann
Add a notifier that fires when a new DRM device is registered.
This can be used by the bootsplash client to connect to all devices.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_drv.c | 32 ++++++++++++++++++++++++++++++++
include/drm/drm_drv.h | 4 ++++
2 files changed, 36 insertions(+)
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 6f21bafb29be..e42ce320ad07 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -31,6 +31,7 @@
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/mount.h>
+#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/srcu.h>
@@ -79,6 +80,8 @@ static struct dentry *drm_debugfs_root;
DEFINE_STATIC_SRCU(drm_unplug_srcu);
+static BLOCKING_NOTIFIER_HEAD(drm_dev_notifier);
+
/*
* DRM Minors
* A DRM device can provide several char-dev interfaces on the DRM-Major. Each
@@ -837,6 +840,8 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags)
dev->dev ? dev_name(dev->dev) : "virtual device",
dev->primary->index);
+ blocking_notifier_call_chain(&drm_dev_notifier, 0, dev);
+
goto out_unlock;
err_minors:
@@ -894,6 +899,33 @@ void drm_dev_unregister(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_dev_unregister);
+/**
+ * drm_dev_register_notifier - Register a notifier for new DRM devices
+ * @nb: Notifier block
+ *
+ * Register a notifier that fires when a new &drm_device is registered.
+ *
+ * Note:
+ * Users of this function has to be linked into drm.ko. This is done to make
+ * life simple avoiding tricky race situations.
+ */
+void drm_dev_register_notifier(struct notifier_block *nb)
+{
+ /* Currently this can't fail, but catch it in case this changes */
+ WARN_ON(blocking_notifier_chain_register(&drm_dev_notifier, nb));
+}
+
+/**
+ * drm_dev_unregister_notifier - Unregister DRM device notifier
+ * @nb: Notifier block
+ *
+ * This is a no-op if the notifier isn't registered.
+ */
+void drm_dev_unregister_notifier(struct notifier_block *nb)
+{
+ blocking_notifier_chain_unregister(&drm_dev_notifier, nb);
+}
+
/**
* drm_dev_set_unique - Set the unique name of a DRM device
* @dev: device of which to set the unique name
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index 13356e6fd40c..5e6c6ed0d59d 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -40,6 +40,7 @@ struct drm_minor;
struct dma_buf_attachment;
struct drm_display_mode;
struct drm_mode_create_dumb;
+struct notifier_block;
struct drm_printer;
/* driver capabilities and requirements mask */
@@ -641,6 +642,9 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
int drm_dev_register(struct drm_device *dev, unsigned long flags);
void drm_dev_unregister(struct drm_device *dev);
+void drm_dev_register_notifier(struct notifier_block *nb);
+void drm_dev_unregister_notifier(struct notifier_block *nb);
+
void drm_dev_get(struct drm_device *dev);
void drm_dev_put(struct drm_device *dev);
void drm_dev_unref(struct drm_device *dev);
--
2.15.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [RFC v4 24/25] drm/client: Hack: Add bootsplash
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
` (10 preceding siblings ...)
2018-04-12 16:12 ` [RFC v4 23/25] drm: Add DRM device registered notifier Noralf Trønnes
@ 2018-04-12 16:12 ` Noralf Trønnes
11 siblings, 0 replies; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-12 16:12 UTC (permalink / raw)
To: dri-devel
Cc: daniel.vetter, intel-gfx, Noralf Trønnes, laurent.pinchart,
mstaudt, dh.herrmann
A hack to test the client API.
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/Kconfig | 2 +
drivers/gpu/drm/Makefile | 1 +
drivers/gpu/drm/client/Kconfig | 9 ++
drivers/gpu/drm/client/drm_bootsplash.c | 248 ++++++++++++++++++++++++++++++++
drivers/gpu/drm/client/internal.h | 19 +++
drivers/gpu/drm/drm_client.c | 4 +
6 files changed, 283 insertions(+)
create mode 100644 drivers/gpu/drm/client/Kconfig
create mode 100644 drivers/gpu/drm/client/drm_bootsplash.c
create mode 100644 drivers/gpu/drm/client/internal.h
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 757825ac60df..1328202ce17d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -154,6 +154,8 @@ config DRM_SCHED
tristate
depends on DRM
+source "drivers/gpu/drm/client/Kconfig"
+
source "drivers/gpu/drm/i2c/Kconfig"
source "drivers/gpu/drm/arm/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index d25afa136d8f..388527093f80 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -30,6 +30,7 @@ drm-$(CONFIG_OF) += drm_of.o
drm-$(CONFIG_AGP) += drm_agpsupport.o
drm-$(CONFIG_DEBUG_FS) += drm_debugfs.o drm_debugfs_crc.o
drm-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
+drm-$(CONFIG_DRM_CLIENT_BOOTSPLASH) += client/drm_bootsplash.o
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
drm_plane_helper.o drm_dp_mst_topology.o drm_atomic_helper.o \
diff --git a/drivers/gpu/drm/client/Kconfig b/drivers/gpu/drm/client/Kconfig
new file mode 100644
index 000000000000..6b01f2e51fb3
--- /dev/null
+++ b/drivers/gpu/drm/client/Kconfig
@@ -0,0 +1,9 @@
+menu "DRM Clients"
+ depends on DRM
+
+config DRM_CLIENT_BOOTSPLASH
+ bool "DRM Bootsplash"
+ help
+ DRM Bootsplash
+
+endmenu
diff --git a/drivers/gpu/drm/client/drm_bootsplash.c b/drivers/gpu/drm/client/drm_bootsplash.c
new file mode 100644
index 000000000000..bec3105f9b02
--- /dev/null
+++ b/drivers/gpu/drm/client/drm_bootsplash.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/keyboard.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <drm/drm_client.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_modes.h>
+#include <drm/drm_print.h>
+
+// drm_lastclose()
+#include <drm/drmP.h>
+#include "drm_internal.h"
+
+static bool drm_bootsplash_enabled = true;
+module_param_named(bootsplash_enabled, drm_bootsplash_enabled, bool, 0600);
+MODULE_PARM_DESC(bootsplash_enabled, "Enable bootsplash client [default=true]");
+
+struct drm_bootsplash {
+ struct drm_client_dev *client;
+ struct drm_client_display *display;
+ struct drm_client_buffer *buffer[2];
+ struct work_struct worker;
+ bool stop;
+};
+
+static bool drm_bootsplash_key_pressed;
+
+static int drm_bootsplash_keyboard_notifier_call(struct notifier_block *blk,
+ unsigned long code, void *_param)
+{
+ /* Any key is good */
+ drm_bootsplash_key_pressed = true;
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block drm_bootsplash_keyboard_notifier_block = {
+ .notifier_call = drm_bootsplash_keyboard_notifier_call,
+};
+
+static u32 drm_bootsplash_color_table[3] = {
+ 0x00ff0000, 0x0000ff00, 0x000000ff,
+};
+
+/* Draw a box with changing colors */
+static void
+drm_bootsplash_draw(struct drm_client_buffer *buffer, unsigned int sequence)
+{
+ unsigned int x, y;
+ u32 *pix;
+
+ pix = buffer->vaddr;
+ pix += ((buffer->height / 2) - 50) * buffer->width;
+ pix += (buffer->width / 2) - 50;
+
+ for (y = 0; y < 100; y++) {
+ for (x = 0; x < 100; x++)
+ *pix++ = drm_bootsplash_color_table[sequence];
+ pix += buffer->width - 100;
+ }
+}
+
+static void drm_bootsplash_worker(struct work_struct *work)
+{
+ struct drm_bootsplash *splash = container_of(work, struct drm_bootsplash, worker);
+ struct drm_device *dev = splash->client->dev;
+ unsigned int i = 0, sequence = 0;
+ struct drm_framebuffer *fb;
+ int ret = 0;
+
+ while (!splash->stop && !drm_bootsplash_key_pressed) {
+ /* Did someone take over, like another in-kernel client, except fbdev? */
+ fb = drm_client_display_current_fb(splash->display);
+ if (splash->buffer[i]->fb != fb &&
+ !(dev->fb_helper && dev->fb_helper->fb == fb))
+ break;
+
+ i = !i;
+ drm_bootsplash_draw(splash->buffer[i], sequence++);
+ if (sequence == 3)
+ sequence = 0;
+
+ ret = drm_client_display_commit(splash->display, splash->buffer[i]->fb, NULL);
+ /* Is userspace in charge or is the device unplugged? */
+ if (ret == -EBUSY || ret == -ENODEV)
+ break;
+
+ msleep(500);
+ }
+
+ /* Restore fbdev (or other) on key press. */
+ /* TODO: Check if it's OK to call drm_lastclose here. */
+ if (drm_bootsplash_key_pressed)
+ drm_lastclose(dev);
+
+ for (i = 0; i < 2; i++)
+ drm_client_framebuffer_delete(splash->buffer[i]);
+ drm_client_display_free(splash->display);
+ drm_client_remove_defer(splash->client);
+ DRM_DEV_DEBUG_KMS(dev->dev, "Bootsplash has stopped (key=%u stop=%u ret=%d).\n",
+ drm_bootsplash_key_pressed, splash->stop, ret);
+}
+
+static int drm_bootsplash_setup(struct drm_bootsplash *splash)
+{
+ struct drm_client_dev *client = splash->client;
+ struct drm_client_buffer *buffer[2];
+ struct drm_client_display *display;
+ int ret, i;
+
+ display = drm_client_find_display(client->dev, 0, 0);
+ if (IS_ERR(display))
+ return PTR_ERR(display);
+ if (!display)
+ return -ENOENT;
+
+ if (WARN_ON(!display->mode))
+ return -ENOENT;
+
+ for (i = 0; i < 2; i++) {
+ buffer[i] = drm_client_framebuffer_create(client, display->mode,
+ DRM_FORMAT_XRGB8888);
+ if (IS_ERR(buffer[i])) {
+ ret = PTR_ERR(buffer[i]);
+ goto err_free_buffer;
+ }
+ }
+
+ ret = drm_client_display_commit(display, buffer[0]->fb, display->mode);
+ if (ret)
+ goto err_free_buffer;
+
+ splash->display = display;
+ splash->buffer[0] = buffer[0];
+ splash->buffer[1] = buffer[1];
+
+ schedule_work(&splash->worker);
+
+ return 0;
+
+err_free_buffer:
+ for (i--; i >= 0; i--)
+ drm_client_framebuffer_delete(buffer[i]);
+ drm_client_display_free(display);
+
+ return ret;
+}
+
+static int drm_bootsplash_client_hotplug(struct drm_client_dev *client)
+{
+ struct drm_bootsplash *splash = client->private;
+ int ret = 0;
+
+ if (splash->display)
+ return 0;
+
+ ret = drm_bootsplash_setup(splash);
+ if (ret) {
+ DRM_DEV_DEBUG_KMS(client->dev->dev, "ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int drm_bootsplash_client_remove(struct drm_client_dev *client)
+{
+ struct drm_bootsplash *splash = client->private;
+
+ /* Don't hook up to any new devices showing up */
+ drm_bootsplash_enabled = false;
+
+ splash->stop = true;
+ flush_work(&splash->worker);
+ kfree(splash);
+
+ return 0;
+}
+
+static const struct drm_client_funcs drm_bootsplash_client_funcs = {
+ .name = "bootsplash",
+ .remove = drm_bootsplash_client_remove,
+ .hotplug = drm_bootsplash_client_hotplug,
+};
+
+static int drm_bootsplash_dev_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct drm_device *dev = data;
+ struct drm_client_dev *client;
+ struct drm_bootsplash *splash;
+
+ if (!drm_bootsplash_enabled)
+ return 0;
+
+ splash = kzalloc(sizeof(*splash), GFP_KERNEL);
+ if (!splash)
+ return 0;
+
+ client = drm_client_new(dev, &drm_bootsplash_client_funcs);
+ if (IS_ERR(client)) {
+ DRM_DEV_ERROR(dev->dev, "Failed to create client, ret=%ld\n", PTR_ERR(client));
+ kfree(splash);
+ return 0;
+ }
+
+ INIT_WORK(&splash->worker, drm_bootsplash_worker);
+
+ splash->client = client;
+ client->private = splash;
+
+ /*
+ * vc4 isn't done with it's setup when drm_dev_register() is called.
+ * It should have shouldn't it?
+ * So to keep it from crashing defer setup to hotplug...
+ */
+ if (client->dev->mode_config.max_width)
+ drm_bootsplash_client_hotplug(client);
+
+ return 0;
+}
+
+static struct notifier_block drm_bootsplash_dev_notifier = {
+ .notifier_call = drm_bootsplash_dev_notify,
+};
+
+void drm_bootsplash_register(void)
+{
+ register_keyboard_notifier(&drm_bootsplash_keyboard_notifier_block);
+
+ if (!drm_bootsplash_enabled)
+ return;
+
+ drm_dev_register_notifier(&drm_bootsplash_dev_notifier);
+}
+
+void drm_bootsplash_unregister(void)
+{
+ drm_dev_unregister_notifier(&drm_bootsplash_dev_notifier);
+ unregister_keyboard_notifier(&drm_bootsplash_keyboard_notifier_block);
+}
diff --git a/drivers/gpu/drm/client/internal.h b/drivers/gpu/drm/client/internal.h
new file mode 100644
index 000000000000..22e2120c493b
--- /dev/null
+++ b/drivers/gpu/drm/client/internal.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef _DRM_CLIENT_INTERNAL_H_
+#define _DRM_CLIENT_INTERNAL_H_
+
+#ifdef CONFIG_DRM_CLIENT_BOOTSPLASH
+void drm_bootsplash_register(void);
+void drm_bootsplash_unregister(void);
+#else
+static inline void drm_bootsplash_register(void)
+{
+}
+
+static inline void drm_bootsplash_unregister(void)
+{
+}
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index 760f1795f812..cd8c084c8801 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -24,6 +24,7 @@
#include "drm_crtc_internal.h"
#include "drm_internal.h"
+#include "client/internal.h"
struct drm_client_display_offset {
int x, y;
@@ -234,10 +235,13 @@ EXPORT_SYMBOL(drm_client_remove_defer);
void drm_client_init(void)
{
+ drm_bootsplash_register();
}
void drm_client_exit(void)
{
+ drm_bootsplash_unregister();
+
flush_work(&drm_client_remove_defer_work);
}
--
2.15.1
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [RFC v4 22/25] drm/fb-helper: Add generic fbdev emulation
2018-04-14 11:53 ` [RFC v4 22/25] drm/fb-helper: " Noralf Trønnes
@ 2018-04-16 8:52 ` Daniel Vetter
0 siblings, 0 replies; 15+ messages in thread
From: Daniel Vetter @ 2018-04-16 8:52 UTC (permalink / raw)
To: Noralf Trønnes; +Cc: intel-gfx, dri-devel
On Sat, Apr 14, 2018 at 01:53:15PM +0200, Noralf Trønnes wrote:
> This adds generic fbdev emulation for drivers that supports
> dumb buffers which they can export.
>
> All the driver has to do is call drm_fbdev_generic_setup().
>
> Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
> ---
> drivers/gpu/drm/drm_fb_helper.c | 255 ++++++++++++++++++++++++++++++++++++++++
> include/drm/drm_fb_helper.h | 20 ++++
> 2 files changed, 275 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
> index b1124c08b1ed..1954de5b13e0 100644
> --- a/drivers/gpu/drm/drm_fb_helper.c
> +++ b/drivers/gpu/drm/drm_fb_helper.c
> @@ -30,6 +30,7 @@
> #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>
> #include <linux/console.h>
> +#include <linux/dma-buf.h>
> #include <linux/kernel.h>
> #include <linux/sysrq.h>
> #include <linux/slab.h>
> @@ -1995,6 +1996,260 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev)
> }
> EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
>
> +static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
> +{
> + struct drm_fb_helper *fb_helper = info->par;
> +
> + return dma_buf_mmap(fb_helper->buffer->dma_buf, vma, 0);
> +}
> +
> +/*
> + * fb_ops.fb_destroy is called by the last put_fb_info() call at the end of
> + * unregister_framebuffer() or fb_release().
> + */
> +static void drm_fbdev_fb_destroy(struct fb_info *info)
> +{
> + struct drm_fb_helper *fb_helper = info->par;
> + struct fb_ops *fbops = NULL;
> +
> + DRM_DEBUG("\n");
> +
> + if (fb_helper->fbdev->fbdefio)
> + fbops = fb_helper->fbdev->fbops;
> +
> + drm_fb_helper_fini(fb_helper);
> + drm_client_framebuffer_delete(fb_helper->buffer);
> + drm_client_free(fb_helper->client);
> + kfree(fb_helper);
> + kfree(fbops);
> +}
> +
> +static struct fb_ops drm_fbdev_fb_ops = {
> + /*
> + * No need to set owner, this module is already pinned by the driver.
> + * A reference is taken on the driver module in drm_fb_helper_fb_open()
> + * to prevent the driver going away with open fd's.
> + */
> + DRM_FB_HELPER_DEFAULT_OPS,
> + .fb_open = drm_fb_helper_fb_open,
> + .fb_release = drm_fb_helper_fb_release,
> + .fb_destroy = drm_fbdev_fb_destroy,
> + .fb_mmap = drm_fbdev_fb_mmap,
> + .fb_read = drm_fb_helper_sys_read,
> + .fb_write = drm_fb_helper_sys_write,
> + .fb_fillrect = drm_fb_helper_sys_fillrect,
> + .fb_copyarea = drm_fb_helper_sys_copyarea,
> + .fb_imageblit = drm_fb_helper_sys_imageblit,
> +};
> +
> +static struct fb_deferred_io drm_fbdev_defio = {
> + .delay = HZ / 20,
> + .deferred_io = drm_fb_helper_deferred_io,
> +};
> +
> +/* Hack to test tinydrm before converting to vmalloc buffers */
> +static int drm_fbdev_cma_deferred_io_mmap(struct fb_info *info,
> + struct vm_area_struct *vma)
> +{
> + fb_deferred_io_mmap(info, vma);
> + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
> +
> + return 0;
> +}
> +
> +static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
> + struct drm_fb_helper_surface_size *sizes)
> +{
> + struct drm_client_dev *client = fb_helper->client;
> + struct drm_display_mode sizes_mode = {
> + .hdisplay = sizes->surface_width,
> + .vdisplay = sizes->surface_height,
> + };
> + struct drm_client_buffer *buffer;
> + struct drm_framebuffer *fb;
> + struct fb_info *fbi;
> + u32 format;
> + int ret;
> +
> + DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
> + sizes->surface_width, sizes->surface_height,
> + sizes->surface_bpp);
> +
> + format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
> + buffer = drm_client_framebuffer_create(client, &sizes_mode, format);
> + if (IS_ERR(buffer))
> + return PTR_ERR(buffer);
> +
> + fb_helper->buffer = buffer;
> + fb_helper->fb = buffer->fb;
> + fb = buffer->fb;
> +
> + fbi = drm_fb_helper_alloc_fbi(fb_helper);
> + if (IS_ERR(fbi)) {
> + ret = PTR_ERR(fbi);
> + goto err_free_buffer;
> + }
> +
> + fbi->par = fb_helper;
> + fbi->fbops = &drm_fbdev_fb_ops;
> + fbi->screen_size = fb->height * fb->pitches[0];
> + fbi->fix.smem_len = fbi->screen_size;
> + fbi->screen_buffer = buffer->vaddr;
> + strcpy(fbi->fix.id, "DRM emulated");
> +
> + drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth);
> + drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height);
> +
> + /*
> + * Drivers that set the dirty callback:
> + * - Doesn't use defio:
> + * i915, virtio, rockchip
> + * - defio with vmalloc buffer blitted on the real one:
> + * vmwgfx
> + * - defio is disabled because it doesn't work with shmem:
> + * udl
> + * - defio with special dirty callback for fbdev, uses vmalloc for fbdev:
> + * qxl
> + * - defio with cma buffer, will move to vmalloc buffers:
> + * tinydrm
> + *
> + * TODO:
> + * Maybe add vmalloc shadow buffer support.
> + */
> +
> + if (fb->funcs->dirty) {
> + struct fb_ops *fbops;
> +
> + /*
> + * fb_deferred_io_cleanup() clears &fbops->fb_mmap so a per
> + * instance version is necessary.
> + */
> + fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
> + if (!fbops) {
> + ret = -ENOMEM;
> + goto err_fb_info_destroy;
> + }
> +
> + *fbops = *fbi->fbops;
> + fbi->fbops = fbops;
> +
> + fbi->fbdefio = &drm_fbdev_defio;
> +
> + /* Hack so I can test with tinydrm */
> + fbi->fix.smem_start = page_to_phys(virt_to_page(buffer->vaddr));
> +
> + fb_deferred_io_init(fbi);
> +
> + /* Hack so I can test with tinydrm */
> + fbi->fbops->fb_mmap = drm_fbdev_cma_deferred_io_mmap;
> + }
> +
> + return 0;
> +
> +err_fb_info_destroy:
> + drm_fb_helper_fini(fb_helper);
> +err_free_buffer:
> + drm_client_framebuffer_delete(buffer);
> +
> + return ret;
> +}
I'd split this patch into 2:
- First one just adds the generic_probe callback. We could then start
rolling that one out to lots of drivers, which would give all this code
lots of testing.
- Second part is the generic client stuff below. Again then with follow-up
patches to roll it out.
This way we could achieve a slightly more gradual transition of drivers.
And the first step should only be replacing the fb_probe callback.
-Daniel
> +
> +static const struct drm_fb_helper_funcs drm_fb_helper_generic_funcs = {
> + .fb_probe = drm_fb_helper_generic_probe,
> +};
> +
> +static int drm_fbdev_client_remove(struct drm_client_dev *client)
> +{
> + struct drm_fb_helper *fb_helper = client->private;
> +
> + if (!fb_helper->fbdev) {
> + kfree(fb_helper);
> + return 0;
> + }
> +
> + unregister_framebuffer(fb_helper->fbdev);
> +
> + /*
> + * If userspace is closed the client is now freed by
> + * drm_fbdev_fb_destroy(), otherwise it will be freed on the last close.
> + * Return 1 to tell that freeing is taken care of.
> + */
> +
> + return 1;
> +}
> +
> +static int drm_fbdev_client_lastclose(struct drm_client_dev *client)
> +{
> + struct drm_fb_helper *fb_helper = client->private;
> +
> + drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
> +
> + return 0;
> +}
> +
> +static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
> +{
> + struct drm_fb_helper *fb_helper = client->private;
> +
> + if (fb_helper->fbdev)
> + return 0;
> +
> + return drm_fb_helper_fbdev_setup(client->dev, fb_helper,
> + &drm_fb_helper_generic_funcs,
> + fb_helper->preferred_bpp, 0);
> +}
> +
> +static const struct drm_client_funcs drm_fbdev_client_funcs = {
> + .name = "fbdev",
> + .remove = drm_fbdev_client_remove,
> + .lastclose = drm_fbdev_client_lastclose,
> + .hotplug = drm_fbdev_client_hotplug,
> +};
> +
> +/**
> + * drm_fb_helper_generic_fbdev_setup() - Setup generic fbdev emulation
> + * @dev: DRM device
> + * @preferred_bpp: Preferred bits per pixel for the device.
> + * @dev->mode_config.preferred_depth is used if this is zero.
> + *
> + * This function sets up generic fbdev emulation for drivers that supports
> + * dumb buffers which can be exported.
> + *
> + * Restore, hotplug events and teardown are all taken care of. Drivers that does
> + * suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves.
> + * Simple drivers might use drm_mode_config_helper_suspend().
> + *
> + * Returns:
> + * Zero on success or negative error code on failure.
> + */
> +int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
> +{
> + struct drm_fb_helper *fb_helper;
> + struct drm_client_dev *client;
> +
> + if (!drm_fbdev_emulation)
> + return 0;
> +
> + fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
> + if (!fb_helper)
> + return -ENOMEM;
> +
> + client = drm_client_new(dev, &drm_fbdev_client_funcs);
> + if (IS_ERR(client)) {
> + kfree(fb_helper);
> + return PTR_ERR(client);
> + }
> +
> + client->private = fb_helper;
> + fb_helper->client = client;
> + fb_helper->preferred_bpp = preferred_bpp;
> +
> + drm_fbdev_client_hotplug(client);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(drm_fbdev_generic_setup);
> +
> /* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
> * but the module doesn't depend on any fb console symbols. At least
> * attempt to load fbcon to avoid leaving the system without a usable console.
> diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
> index 330983975d5e..711da1747836 100644
> --- a/include/drm/drm_fb_helper.h
> +++ b/include/drm/drm_fb_helper.h
> @@ -126,6 +126,20 @@ struct drm_fb_helper {
> */
> struct drm_client_display *display;
>
> + /**
> + * @client:
> + *
> + * DRM client used by the generic fbdev emulation.
> + */
> + struct drm_client_dev *client;
> +
> + /**
> + * @buffer:
> + *
> + * Framebuffer used by the generic fbdev emulation.
> + */
> + struct drm_client_buffer *buffer;
> +
> const struct drm_fb_helper_funcs *funcs;
> struct fb_info *fbdev;
> u32 pseudo_palette[17];
> @@ -219,6 +233,7 @@ struct drm_fb_helper {
> .fb_ioctl = drm_fb_helper_ioctl
>
> #ifdef CONFIG_DRM_FBDEV_EMULATION
> +int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp);
> void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
> const struct drm_fb_helper_funcs *funcs);
> int drm_fb_helper_init(struct drm_device *dev,
> @@ -297,6 +312,11 @@ void drm_fb_helper_fbdev_teardown(struct drm_device *dev);
> void drm_fb_helper_lastclose(struct drm_device *dev);
> void drm_fb_helper_output_poll_changed(struct drm_device *dev);
> #else
> +static inline int
> +drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
> +{
> +}
> +
> static inline void drm_fb_helper_prepare(struct drm_device *dev,
> struct drm_fb_helper *helper,
> const struct drm_fb_helper_funcs *funcs)
> --
> 2.15.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
--
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/intel-gfx
^ permalink raw reply [flat|nested] 15+ messages in thread
* [RFC v4 22/25] drm/fb-helper: Add generic fbdev emulation
2018-04-14 11:52 [RFC v4 00/25] drm: Add generic fbdev emulation Noralf Trønnes
@ 2018-04-14 11:53 ` Noralf Trønnes
2018-04-16 8:52 ` Daniel Vetter
0 siblings, 1 reply; 15+ messages in thread
From: Noralf Trønnes @ 2018-04-14 11:53 UTC (permalink / raw)
To: dri-devel; +Cc: intel-gfx
This adds generic fbdev emulation for drivers that supports
dumb buffers which they can export.
All the driver has to do is call drm_fbdev_generic_setup().
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
---
drivers/gpu/drm/drm_fb_helper.c | 255 ++++++++++++++++++++++++++++++++++++++++
include/drm/drm_fb_helper.h | 20 ++++
2 files changed, 275 insertions(+)
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index b1124c08b1ed..1954de5b13e0 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -30,6 +30,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/console.h>
+#include <linux/dma-buf.h>
#include <linux/kernel.h>
#include <linux/sysrq.h>
#include <linux/slab.h>
@@ -1995,6 +1996,260 @@ void drm_fb_helper_output_poll_changed(struct drm_device *dev)
}
EXPORT_SYMBOL(drm_fb_helper_output_poll_changed);
+static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ struct drm_fb_helper *fb_helper = info->par;
+
+ return dma_buf_mmap(fb_helper->buffer->dma_buf, vma, 0);
+}
+
+/*
+ * fb_ops.fb_destroy is called by the last put_fb_info() call at the end of
+ * unregister_framebuffer() or fb_release().
+ */
+static void drm_fbdev_fb_destroy(struct fb_info *info)
+{
+ struct drm_fb_helper *fb_helper = info->par;
+ struct fb_ops *fbops = NULL;
+
+ DRM_DEBUG("\n");
+
+ if (fb_helper->fbdev->fbdefio)
+ fbops = fb_helper->fbdev->fbops;
+
+ drm_fb_helper_fini(fb_helper);
+ drm_client_framebuffer_delete(fb_helper->buffer);
+ drm_client_free(fb_helper->client);
+ kfree(fb_helper);
+ kfree(fbops);
+}
+
+static struct fb_ops drm_fbdev_fb_ops = {
+ /*
+ * No need to set owner, this module is already pinned by the driver.
+ * A reference is taken on the driver module in drm_fb_helper_fb_open()
+ * to prevent the driver going away with open fd's.
+ */
+ DRM_FB_HELPER_DEFAULT_OPS,
+ .fb_open = drm_fb_helper_fb_open,
+ .fb_release = drm_fb_helper_fb_release,
+ .fb_destroy = drm_fbdev_fb_destroy,
+ .fb_mmap = drm_fbdev_fb_mmap,
+ .fb_read = drm_fb_helper_sys_read,
+ .fb_write = drm_fb_helper_sys_write,
+ .fb_fillrect = drm_fb_helper_sys_fillrect,
+ .fb_copyarea = drm_fb_helper_sys_copyarea,
+ .fb_imageblit = drm_fb_helper_sys_imageblit,
+};
+
+static struct fb_deferred_io drm_fbdev_defio = {
+ .delay = HZ / 20,
+ .deferred_io = drm_fb_helper_deferred_io,
+};
+
+/* Hack to test tinydrm before converting to vmalloc buffers */
+static int drm_fbdev_cma_deferred_io_mmap(struct fb_info *info,
+ struct vm_area_struct *vma)
+{
+ fb_deferred_io_mmap(info, vma);
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+ return 0;
+}
+
+static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct drm_client_dev *client = fb_helper->client;
+ struct drm_display_mode sizes_mode = {
+ .hdisplay = sizes->surface_width,
+ .vdisplay = sizes->surface_height,
+ };
+ struct drm_client_buffer *buffer;
+ struct drm_framebuffer *fb;
+ struct fb_info *fbi;
+ u32 format;
+ int ret;
+
+ DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d)\n",
+ sizes->surface_width, sizes->surface_height,
+ sizes->surface_bpp);
+
+ format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
+ buffer = drm_client_framebuffer_create(client, &sizes_mode, format);
+ if (IS_ERR(buffer))
+ return PTR_ERR(buffer);
+
+ fb_helper->buffer = buffer;
+ fb_helper->fb = buffer->fb;
+ fb = buffer->fb;
+
+ fbi = drm_fb_helper_alloc_fbi(fb_helper);
+ if (IS_ERR(fbi)) {
+ ret = PTR_ERR(fbi);
+ goto err_free_buffer;
+ }
+
+ fbi->par = fb_helper;
+ fbi->fbops = &drm_fbdev_fb_ops;
+ fbi->screen_size = fb->height * fb->pitches[0];
+ fbi->fix.smem_len = fbi->screen_size;
+ fbi->screen_buffer = buffer->vaddr;
+ strcpy(fbi->fix.id, "DRM emulated");
+
+ drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->format->depth);
+ drm_fb_helper_fill_var(fbi, fb_helper, sizes->fb_width, sizes->fb_height);
+
+ /*
+ * Drivers that set the dirty callback:
+ * - Doesn't use defio:
+ * i915, virtio, rockchip
+ * - defio with vmalloc buffer blitted on the real one:
+ * vmwgfx
+ * - defio is disabled because it doesn't work with shmem:
+ * udl
+ * - defio with special dirty callback for fbdev, uses vmalloc for fbdev:
+ * qxl
+ * - defio with cma buffer, will move to vmalloc buffers:
+ * tinydrm
+ *
+ * TODO:
+ * Maybe add vmalloc shadow buffer support.
+ */
+
+ if (fb->funcs->dirty) {
+ struct fb_ops *fbops;
+
+ /*
+ * fb_deferred_io_cleanup() clears &fbops->fb_mmap so a per
+ * instance version is necessary.
+ */
+ fbops = kzalloc(sizeof(*fbops), GFP_KERNEL);
+ if (!fbops) {
+ ret = -ENOMEM;
+ goto err_fb_info_destroy;
+ }
+
+ *fbops = *fbi->fbops;
+ fbi->fbops = fbops;
+
+ fbi->fbdefio = &drm_fbdev_defio;
+
+ /* Hack so I can test with tinydrm */
+ fbi->fix.smem_start = page_to_phys(virt_to_page(buffer->vaddr));
+
+ fb_deferred_io_init(fbi);
+
+ /* Hack so I can test with tinydrm */
+ fbi->fbops->fb_mmap = drm_fbdev_cma_deferred_io_mmap;
+ }
+
+ return 0;
+
+err_fb_info_destroy:
+ drm_fb_helper_fini(fb_helper);
+err_free_buffer:
+ drm_client_framebuffer_delete(buffer);
+
+ return ret;
+}
+
+static const struct drm_fb_helper_funcs drm_fb_helper_generic_funcs = {
+ .fb_probe = drm_fb_helper_generic_probe,
+};
+
+static int drm_fbdev_client_remove(struct drm_client_dev *client)
+{
+ struct drm_fb_helper *fb_helper = client->private;
+
+ if (!fb_helper->fbdev) {
+ kfree(fb_helper);
+ return 0;
+ }
+
+ unregister_framebuffer(fb_helper->fbdev);
+
+ /*
+ * If userspace is closed the client is now freed by
+ * drm_fbdev_fb_destroy(), otherwise it will be freed on the last close.
+ * Return 1 to tell that freeing is taken care of.
+ */
+
+ return 1;
+}
+
+static int drm_fbdev_client_lastclose(struct drm_client_dev *client)
+{
+ struct drm_fb_helper *fb_helper = client->private;
+
+ drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
+
+ return 0;
+}
+
+static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
+{
+ struct drm_fb_helper *fb_helper = client->private;
+
+ if (fb_helper->fbdev)
+ return 0;
+
+ return drm_fb_helper_fbdev_setup(client->dev, fb_helper,
+ &drm_fb_helper_generic_funcs,
+ fb_helper->preferred_bpp, 0);
+}
+
+static const struct drm_client_funcs drm_fbdev_client_funcs = {
+ .name = "fbdev",
+ .remove = drm_fbdev_client_remove,
+ .lastclose = drm_fbdev_client_lastclose,
+ .hotplug = drm_fbdev_client_hotplug,
+};
+
+/**
+ * drm_fb_helper_generic_fbdev_setup() - Setup generic fbdev emulation
+ * @dev: DRM device
+ * @preferred_bpp: Preferred bits per pixel for the device.
+ * @dev->mode_config.preferred_depth is used if this is zero.
+ *
+ * This function sets up generic fbdev emulation for drivers that supports
+ * dumb buffers which can be exported.
+ *
+ * Restore, hotplug events and teardown are all taken care of. Drivers that does
+ * suspend/resume need to call drm_fb_helper_set_suspend_unlocked() themselves.
+ * Simple drivers might use drm_mode_config_helper_suspend().
+ *
+ * Returns:
+ * Zero on success or negative error code on failure.
+ */
+int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
+{
+ struct drm_fb_helper *fb_helper;
+ struct drm_client_dev *client;
+
+ if (!drm_fbdev_emulation)
+ return 0;
+
+ fb_helper = kzalloc(sizeof(*fb_helper), GFP_KERNEL);
+ if (!fb_helper)
+ return -ENOMEM;
+
+ client = drm_client_new(dev, &drm_fbdev_client_funcs);
+ if (IS_ERR(client)) {
+ kfree(fb_helper);
+ return PTR_ERR(client);
+ }
+
+ client->private = fb_helper;
+ fb_helper->client = client;
+ fb_helper->preferred_bpp = preferred_bpp;
+
+ drm_fbdev_client_hotplug(client);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_fbdev_generic_setup);
+
/* The Kconfig DRM_KMS_HELPER selects FRAMEBUFFER_CONSOLE (if !EXPERT)
* but the module doesn't depend on any fb console symbols. At least
* attempt to load fbcon to avoid leaving the system without a usable console.
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 330983975d5e..711da1747836 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -126,6 +126,20 @@ struct drm_fb_helper {
*/
struct drm_client_display *display;
+ /**
+ * @client:
+ *
+ * DRM client used by the generic fbdev emulation.
+ */
+ struct drm_client_dev *client;
+
+ /**
+ * @buffer:
+ *
+ * Framebuffer used by the generic fbdev emulation.
+ */
+ struct drm_client_buffer *buffer;
+
const struct drm_fb_helper_funcs *funcs;
struct fb_info *fbdev;
u32 pseudo_palette[17];
@@ -219,6 +233,7 @@ struct drm_fb_helper {
.fb_ioctl = drm_fb_helper_ioctl
#ifdef CONFIG_DRM_FBDEV_EMULATION
+int drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp);
void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
const struct drm_fb_helper_funcs *funcs);
int drm_fb_helper_init(struct drm_device *dev,
@@ -297,6 +312,11 @@ void drm_fb_helper_fbdev_teardown(struct drm_device *dev);
void drm_fb_helper_lastclose(struct drm_device *dev);
void drm_fb_helper_output_poll_changed(struct drm_device *dev);
#else
+static inline int
+drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
+{
+}
+
static inline void drm_fb_helper_prepare(struct drm_device *dev,
struct drm_fb_helper *helper,
const struct drm_fb_helper_funcs *funcs)
--
2.15.1
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel
^ permalink raw reply related [flat|nested] 15+ messages in thread
end of thread, other threads:[~2018-04-16 8:52 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-04-12 16:12 [RFC v4 12/25] drm/i915: Add drm_driver->initial_client_display callback Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 13/25] drm/fb-helper: Remove struct drm_fb_helper_crtc Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 14/25] drm/fb-helper: Remove struct drm_fb_helper_connector Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 15/25] drm/fb-helper: Move modeset config code to drm_client Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 16/25] drm: Make ioctls available for in-kernel clients Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 17/25] drm/client: Bail out if there's a DRM master Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 18/25] drm/client: Make the display modes available to clients Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 19/25] drm/client: Finish the in-kernel client API Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 20/25] drm/prime: Don't pin module on export for in-kernel clients Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 21/25] drm/fb-helper: Add drm_fb_helper_fb_open/release() Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 22/25] drm/fb-helper: Add generic fbdev emulation Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 23/25] drm: Add DRM device registered notifier Noralf Trønnes
2018-04-12 16:12 ` [RFC v4 24/25] drm/client: Hack: Add bootsplash Noralf Trønnes
2018-04-14 11:52 [RFC v4 00/25] drm: Add generic fbdev emulation Noralf Trønnes
2018-04-14 11:53 ` [RFC v4 22/25] drm/fb-helper: " Noralf Trønnes
2018-04-16 8:52 ` Daniel Vetter
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).