summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/gpu/todo.rst4
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c506
-rw-r--r--include/drm/drm_client.h15
-rw-r--r--include/drm/drm_fb_helper.h80
4 files changed, 197 insertions, 408 deletions
diff --git a/Documentation/gpu/todo.rst b/Documentation/gpu/todo.rst
index 9d4038c50013..ab96ba0600a9 100644
--- a/Documentation/gpu/todo.rst
+++ b/Documentation/gpu/todo.rst
@@ -292,6 +292,10 @@ drm_fb_helper tasks
- The max connector argument for drm_fb_helper_init() and
drm_fb_helper_fbdev_setup() isn't used anymore and can be removed.
+- The helper doesn't keep an array of connectors anymore so these can be
+ removed: drm_fb_helper_single_add_all_connectors(),
+ drm_fb_helper_add_one_connector() and drm_fb_helper_remove_one_connector().
+
Core refactorings
=================
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 7b388674a456..b936db6280ac 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -95,12 +95,6 @@ static DEFINE_MUTEX(kernel_fb_helper_lock);
* Setup fbdev emulation by calling drm_fb_helper_fbdev_setup() and tear it
* down by calling drm_fb_helper_fbdev_teardown().
*
- * Drivers that need to handle connector hotplugging (e.g. dp mst) can't use
- * the setup helper and will need to do the whole four-step setup process with
- * drm_fb_helper_prepare(), drm_fb_helper_init(),
- * drm_fb_helper_single_add_all_connectors(), enable hotplugging and
- * drm_fb_helper_initial_config() to avoid a possible race window.
- *
* At runtime drivers should restore the fbdev console by using
* drm_fb_helper_lastclose() as their &drm_driver.lastclose callback.
* They should also notify the fb helper code from updates to the output
@@ -123,8 +117,7 @@ static DEFINE_MUTEX(kernel_fb_helper_lock);
* encoders and connectors. To finish up the fbdev helper initialization, the
* drm_fb_helper_init() function is called. To probe for all attached displays
* and set up an initial configuration using the detected hardware, drivers
- * should call drm_fb_helper_single_add_all_connectors() followed by
- * drm_fb_helper_initial_config().
+ * should call drm_fb_helper_initial_config().
*
* If &drm_framebuffer_funcs.dirty is set, the
* drm_fb_helper_{cfb,sys}_{write,fillrect,copyarea,imageblit} functions will
@@ -137,165 +130,6 @@ 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;
-}
-EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
-
-/**
- * drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
- * emulation helper
- * @fb_helper: fbdev initialized with drm_fb_helper_init, can be NULL
- *
- * This functions adds all the available connectors for use with the given
- * fb_helper. This is a separate step to allow drivers to freely assign
- * connectors to the fbdev, e.g. if some are reserved for special purposes or
- * not adequate to be used for the fbcon.
- *
- * This function is protected against concurrent connector hotadds/removals
- * using drm_fb_helper_add_one_connector() and
- * drm_fb_helper_remove_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) {
- if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK)
- continue;
-
- 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;
-}
-
-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;
-}
-EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
-
static void drm_fb_helper_restore_lut_atomic(struct drm_crtc *crtc)
{
uint16_t *r_base, *g_base, *b_base;
@@ -645,20 +479,9 @@ int drm_fb_helper_init(struct drm_device *dev,
return ret;
}
- fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
- if (!fb_helper->connector_info)
- goto out_free;
-
- fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
- fb_helper->connector_count = 0;
-
dev->fb_helper = fb_helper;
return 0;
-out_free:
- drm_client_release(&fb_helper->client);
-
- return -ENOMEM;
}
EXPORT_SYMBOL(drm_fb_helper_init);
@@ -733,7 +556,6 @@ EXPORT_SYMBOL(drm_fb_helper_unregister_fbi);
void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
{
struct fb_info *info;
- int i;
if (!fb_helper)
return;
@@ -766,12 +588,6 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
if (!fb_helper->client.funcs)
drm_client_release(&fb_helper->client);
-
- for (i = 0; i < fb_helper->connector_count; i++) {
- drm_connector_put(fb_helper->connector_info[i]->connector);
- kfree(fb_helper->connector_info[i]);
- }
- kfree(fb_helper->connector_info);
}
EXPORT_SYMBOL(drm_fb_helper_fini);
@@ -1650,8 +1466,9 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
struct drm_client_dev *client = &fb_helper->client;
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 best_depth = 0;
@@ -1668,11 +1485,11 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
if (preferred_bpp != sizes.surface_bpp)
sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
- drm_fb_helper_for_each_connector(fb_helper, i) {
- struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
+ drm_connector_list_iter_begin(fb_helper->dev, &conn_iter);
+ drm_client_for_each_connector_iter(connector, &conn_iter) {
struct drm_cmdline_mode *cmdline_mode;
- cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
+ cmdline_mode = &connector->cmdline_mode;
if (cmdline_mode->bpp_specified) {
switch (cmdline_mode->bpp) {
@@ -1697,6 +1514,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
break;
}
}
+ drm_connector_list_iter_end(&conn_iter);
/*
* If we run into a situation where, for example, the primary plane
@@ -1881,26 +1699,12 @@ void drm_fb_helper_fill_info(struct fb_info *info,
}
EXPORT_SYMBOL(drm_fb_helper_fill_info);
-static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
- uint32_t maxX,
- uint32_t maxY)
-{
- struct drm_connector *connector;
- int i, count = 0;
-
- drm_fb_helper_for_each_connector(fb_helper, i) {
- connector = fb_helper->connector_info[i]->connector;
- count += connector->funcs->fill_modes(connector, maxX, maxY);
- }
-
- return count;
-}
-
-struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
+static struct drm_display_mode *
+drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int height)
{
struct drm_display_mode *mode;
- list_for_each_entry(mode, &fb_connector->connector->modes, head) {
+ list_for_each_entry(mode, &connector->modes, head) {
if (mode->hdisplay > width ||
mode->vdisplay > height)
continue;
@@ -1909,20 +1713,15 @@ struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *
}
return NULL;
}
-EXPORT_SYMBOL(drm_has_preferred_mode);
-
-static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_connector)
-{
- return fb_connector->connector->cmdline_mode.specified;
-}
-struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn)
+static struct drm_display_mode *
+drm_connector_pick_cmdline_mode(struct drm_connector *connector)
{
struct drm_cmdline_mode *cmdline_mode;
struct drm_display_mode *mode;
bool prefer_non_interlace;
- cmdline_mode = &fb_helper_conn->connector->cmdline_mode;
+ cmdline_mode = &connector->cmdline_mode;
if (cmdline_mode->specified == false)
return NULL;
@@ -1934,7 +1733,7 @@ struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *f
prefer_non_interlace = !cmdline_mode->interlace;
again:
- list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
+ list_for_each_entry(mode, &connector->modes, head) {
/* check width/height */
if (mode->hdisplay != cmdline_mode->xres ||
mode->vdisplay != cmdline_mode->yres)
@@ -1961,12 +1760,11 @@ again:
}
create_mode:
- mode = drm_mode_create_from_cmdline_mode(fb_helper_conn->connector->dev,
- cmdline_mode);
- list_add(&mode->head, &fb_helper_conn->connector->modes);
+ mode = drm_mode_create_from_cmdline_mode(connector->dev, cmdline_mode);
+ list_add(&mode->head, &connector->modes);
+
return mode;
}
-EXPORT_SYMBOL(drm_pick_cmdline_mode);
static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
{
@@ -1983,15 +1781,16 @@ static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
return enable;
}
-static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
- bool *enabled)
+static void drm_client_connectors_enabled(struct drm_connector **connectors,
+ unsigned int connector_count,
+ 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;
+ for (i = 0; i < connector_count; i++) {
+ connector = connectors[i];
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");
@@ -2002,28 +1801,27 @@ static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
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);
- }
+ for (i = 0; i < connector_count; i++)
+ enabled[i] = drm_connector_enabled(connectors[i], false);
}
-static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
- struct drm_display_mode **modes,
- struct drm_fb_offset *offsets,
- bool *enabled, int width, int height)
+static bool drm_client_target_cloned(struct drm_device *dev,
+ 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 */
- if (fb_helper->dev->mode_config.num_crtc > 1)
+ if (dev->mode_config.num_crtc > 1)
return false;
count = 0;
- drm_fb_helper_for_each_connector(fb_helper, i) {
+ for (i = 0; i < connector_count; i++) {
if (enabled[i])
count++;
}
@@ -2034,11 +1832,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_pick_cmdline_mode(fb_helper_conn);
+ modes[i] = drm_connector_pick_cmdline_mode(connectors[i]);
if (!modes[i]) {
can_clone = false;
break;
@@ -2062,14 +1859,13 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
/* 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);
+ dmt_mode = drm_mode_find_dmt(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_match(mode, dmt_mode,
DRM_MODE_MATCH_TIMINGS |
DRM_MODE_MATCH_CLOCK |
@@ -2089,30 +1885,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,
- struct drm_display_mode **modes,
- struct drm_fb_offset *offsets,
- int idx,
- int h_idx, int v_idx)
+static int drm_client_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;
+ struct drm_connector *connector;
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++) {
+ 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;
@@ -2121,20 +1918,21 @@ 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,
- struct drm_display_mode **modes,
- struct drm_fb_offset *offsets,
- bool *enabled, int width, int height)
+static bool drm_client_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;
+ struct drm_connector *connector;
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++) {
+ connector = connectors[i];
if (conn_configured & BIT_ULL(i))
continue;
@@ -2145,17 +1943,17 @@ retry:
}
/* 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;
@@ -2163,22 +1961,22 @@ retry:
* 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_client_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_pick_cmdline_mode(fb_helper_conn);
+ 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_has_preferred_mode(fb_helper_conn, 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)
+ if (!modes[i] && !list_empty(&connector->modes)) {
+ list_for_each_entry(modes[i], &connector->modes, head)
break;
}
DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
@@ -2207,40 +2005,41 @@ static bool connector_has_possible_crtc(struct drm_connector *connector,
return false;
}
-static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
- struct drm_crtc **best_crtcs,
- struct drm_display_mode **modes,
- int n, int width, int height)
+static int drm_client_pick_crtcs(struct drm_client_dev *client,
+ 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_dev *client = &fb_helper->client;
+ struct drm_device *dev = client->dev;
struct drm_connector *connector;
int my_score, best_score, score;
struct drm_crtc **crtcs, *crtc;
struct drm_mode_set *modeset;
- struct drm_fb_helper_connector *fb_helper_conn;
int o;
- 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_client_pick_crtcs(client, 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_has_preferred_mode(fb_helper_conn, width, height))
+ if (drm_connector_has_preferred_mode(connector, width, height))
my_score++;
/*
@@ -2259,7 +2058,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->dev->mode_config.num_crtc > 1)
+ if (dev->mode_config.num_crtc > 1)
continue;
if (!drm_mode_equal(modes[o], modes[n]))
@@ -2268,12 +2067,11 @@ 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_client_pick_crtcs(client, 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));
+ memcpy(best_crtcs, crtcs, connector_count * sizeof(*crtcs));
}
}
@@ -2282,15 +2080,17 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
}
/* Try to read the BIOS display configuration and use it for the initial config */
-static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper,
- struct drm_crtc **crtcs,
- struct drm_display_mode **modes,
- struct drm_fb_offset *offsets,
- bool *enabled, int width, int height)
-{
- struct drm_device *dev = fb_helper->dev;
- unsigned int count = min(fb_helper->connector_count, BITS_PER_LONG);
+static bool drm_client_firmware_config(struct drm_client_dev *client,
+ struct drm_connector **connectors,
+ unsigned int connector_count,
+ struct drm_crtc **crtcs,
+ struct drm_display_mode **modes,
+ struct drm_fb_offset *offsets,
+ bool *enabled, int width, int height)
+{
+ unsigned int count = min_t(unsigned int, connector_count, BITS_PER_LONG);
unsigned long conn_configured, conn_seq, mask;
+ struct drm_device *dev = client->dev;
int i, j;
bool *save_enabled;
bool fallback = true, ret = true;
@@ -2316,13 +2116,11 @@ static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper,
retry:
conn_seq = conn_configured;
for (i = 0; i < count; i++) {
- struct drm_fb_helper_connector *fb_conn;
struct drm_connector *connector;
struct drm_encoder *encoder;
struct drm_crtc *new_crtc;
- fb_conn = fb_helper->connector_info[i];
- connector = fb_conn->connector;
+ connector = connectors[i];
if (conn_configured & BIT(i))
continue;
@@ -2379,14 +2177,13 @@ retry:
connector->name);
/* go for command line mode first */
- modes[i] = drm_pick_cmdline_mode(fb_conn);
+ modes[i] = drm_connector_pick_cmdline_mode(connector);
/* try for preferred next */
if (!modes[i]) {
DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n",
connector->name, connector->has_tile);
- modes[i] = drm_has_preferred_mode(fb_conn, width,
- height);
+ modes[i] = drm_connector_has_preferred_mode(connector, width, height);
}
/* No preferred mode marked by the EDID? Are there any modes? */
@@ -2461,8 +2258,12 @@ bail:
static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
u32 width, u32 height)
{
+ struct drm_connector *connector, **connectors = NULL;
struct drm_client_dev *client = &fb_helper->client;
+ struct drm_connector_list_iter conn_iter;
struct drm_device *dev = fb_helper->dev;
+ unsigned int total_modes_count = 0;
+ unsigned int connector_count = 0;
struct drm_display_mode **modes;
struct drm_fb_offset *offsets;
struct drm_crtc **crtcs;
@@ -2470,16 +2271,28 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
int i;
DRM_DEBUG_KMS("\n");
- /* prevent concurrent modification of connector_count by hotplug */
- lockdep_assert_held(&fb_helper->lock);
-
- 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);
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+ drm_client_for_each_connector_iter(connector, &conn_iter) {
+ struct drm_connector **tmp;
+
+ tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL);
+ if (!tmp)
+ goto free_connectors;
+
+ connectors = tmp;
+ drm_connector_get(connector);
+ connectors[connector_count++] = connector;
+ }
+ drm_connector_list_iter_end(&conn_iter);
+
+ if (!connector_count)
+ return;
+
+ crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
+ modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL);
+ offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL);
+ enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL);
if (!crtcs || !modes || !enabled || !offsets) {
DRM_ERROR("Memory allocation failed\n");
goto out;
@@ -2488,40 +2301,42 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
mutex_lock(&client->modeset_mutex);
mutex_lock(&fb_helper->dev->mode_config.mutex);
- if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)
+ for (i = 0; i < connector_count; i++)
+ total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height);
+ if (!total_modes_count)
DRM_DEBUG_KMS("No connectors reported connected with modes\n");
- drm_enable_connectors(fb_helper, enabled);
-
- if (!drm_fb_helper_firmware_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_client_connectors_enabled(connectors, connector_count, enabled);
+
+ if (!drm_client_firmware_config(client, connectors, connector_count, crtcs,
+ modes, offsets, enabled, width, height)) {
+ memset(modes, 0, connector_count * sizeof(*modes));
+ memset(crtcs, 0, connector_count * sizeof(*crtcs));
+ memset(offsets, 0, connector_count * sizeof(*offsets));
+
+ if (!drm_client_target_cloned(dev, connectors, connector_count, modes,
+ offsets, enabled, width, height) &&
+ !drm_client_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_client_pick_crtcs(client, connectors, connector_count,
+ crtcs, modes, 0, width, height);
}
mutex_unlock(&fb_helper->dev->mode_config.mutex);
drm_client_modeset_release(client);
- drm_fb_helper_for_each_connector(fb_helper, i) {
+ for (i = 0; i < connector_count; 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 = drm_client_find_modeset(client, crtc);
- struct drm_connector *connector =
- fb_helper->connector_info[i]->connector;
+ struct drm_connector *connector = connectors[i];
DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
mode->name, crtc->base.id, offset->x, offset->y);
@@ -2544,6 +2359,10 @@ out:
kfree(modes);
kfree(offsets);
kfree(enabled);
+free_connectors:
+ for (i = 0; i < connector_count; i++)
+ drm_connector_put(connectors[i]);
+ kfree(connectors);
}
/*
@@ -2556,10 +2375,11 @@ out:
static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
{
struct drm_client_dev *client = &fb_helper->client;
+ 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;
mutex_lock(&client->modeset_mutex);
drm_client_for_each_modeset(modeset, client) {
@@ -2576,10 +2396,8 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
}
mutex_unlock(&client->modeset_mutex);
- 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_client_for_each_connector_iter(connector, &conn_iter) {
/* use first connected connector for the physical dimensions */
if (connector->status == connector_status_connected) {
@@ -2588,7 +2406,7 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
break;
}
}
- mutex_unlock(&fb_helper->dev->mode_config.mutex);
+ drm_connector_list_iter_end(&conn_iter);
switch (sw_rotations) {
case DRM_MODE_ROTATE_0:
@@ -2826,12 +2644,6 @@ int drm_fb_helper_fbdev_setup(struct drm_device *dev,
return ret;
}
- ret = drm_fb_helper_single_add_all_connectors(fb_helper);
- if (ret < 0) {
- DRM_DEV_ERROR(dev->dev, "fbdev: Failed to add connectors (ret=%d)\n", ret);
- goto err_drm_fb_helper_fini;
- }
-
if (!drm_drv_uses_atomic_modeset(dev))
drm_helper_disable_unused_functions(dev);
@@ -3137,10 +2949,6 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
if (ret)
goto err;
- ret = drm_fb_helper_single_add_all_connectors(fb_helper);
- if (ret)
- goto err_cleanup;
-
if (!drm_drv_uses_atomic_modeset(dev))
drm_helper_disable_unused_functions(dev);
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 6cf48419f77f..8d94880bbe25 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -7,6 +7,7 @@
#include <linux/mutex.h>
#include <linux/types.h>
+#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
struct drm_client_dev;
@@ -169,6 +170,20 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode);
for (({ lockdep_assert_held(&(client)->modeset_mutex); }), \
modeset = (client)->modesets; modeset->crtc; modeset++)
+/**
+ * drm_client_for_each_connector_iter - connector_list iterator macro
+ * @connector: &struct drm_connector pointer used as cursor
+ * @iter: &struct drm_connector_list_iter
+ *
+ * This iterates the connectors that are useable for internal clients (excludes
+ * writeback connectors).
+ *
+ * For more info see drm_for_each_connector_iter().
+ */
+#define drm_client_for_each_connector_iter(connector, iter) \
+ drm_for_each_connector_iter(connector, iter) \
+ if (connector->connector_type != DRM_MODE_CONNECTOR_WRITEBACK)
+
int drm_client_debugfs_init(struct drm_minor *minor);
#endif
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 6b334f4d8a22..e14d52ef9638 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -97,16 +97,10 @@ 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
* @dev: DRM device
- * @connector_count: number of connected connectors
- * @connector_info_alloc_count: size of connector_info
* @funcs: driver callbacks for fb helper
* @fbdev: emulated fbdev device info struct
* @pseudo_palette: fake palette of 16 colors
@@ -138,15 +132,6 @@ struct drm_fb_helper {
struct drm_framebuffer *fb;
struct drm_device *dev;
- 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];
@@ -286,18 +271,8 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper);
int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel);
-int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper);
int drm_fb_helper_debug_enter(struct fb_info *info);
int drm_fb_helper_debug_leave(struct fb_info *info);
-struct drm_display_mode *
-drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector,
- int width, int height);
-struct drm_display_mode *
-drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn);
-
-int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector);
-int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
- struct drm_connector *connector);
int drm_fb_helper_fbdev_setup(struct drm_device *dev,
struct drm_fb_helper *fb_helper,
@@ -472,12 +447,6 @@ static inline int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper,
return 0;
}
-static inline int
-drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
-{
- return 0;
-}
-
static inline int drm_fb_helper_debug_enter(struct fb_info *info)
{
return 0;
@@ -488,34 +457,6 @@ static inline int drm_fb_helper_debug_leave(struct fb_info *info)
return 0;
}
-static inline struct drm_display_mode *
-drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector,
- int width, int height)
-{
- return NULL;
-}
-
-static inline struct drm_display_mode *
-drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
- int width, int height)
-{
- return NULL;
-}
-
-static inline int
-drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
- struct drm_connector *connector)
-{
- return 0;
-}
-
-static inline int
-drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
- struct drm_connector *connector)
-{
- return 0;
-}
-
static inline int
drm_fb_helper_fbdev_setup(struct drm_device *dev,
struct drm_fb_helper *fb_helper,
@@ -557,6 +498,27 @@ drm_fbdev_generic_setup(struct drm_device *dev, unsigned int preferred_bpp)
#endif
+/* TODO: There's a todo entry to remove these three */
+static inline int
+drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
+{
+ return 0;
+}
+
+static inline int
+drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper,
+ struct drm_connector *connector)
+{
+ return 0;
+}
+
+static inline int
+drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
+ struct drm_connector *connector)
+{
+ return 0;
+}
+
/**
* drm_fb_helper_remove_conflicting_framebuffers - remove firmware-configured framebuffers
* @a: memory range, users of which are to be removed