@@ -300,6 +300,13 @@ it to use drm_mode_hsync() instead.
Contact: Sean Paul
+drm_fb_helper cleanup tasks
+---------------------------
+
+- The max connector argument for drm_fb_helper_init() and
+ drm_fb_helper_fbdev_setup() isn't used anymore and can be removed.
+
+
Core refactorings
=================
@@ -392,6 +392,99 @@ void drm_client_framebuffer_delete(struct drm_client_buffer *buffer)
}
EXPORT_SYMBOL(drm_client_framebuffer_delete);
+/**
+ * drm_client_modesets_create() - Create modeset array
+ * @dev: DRM device
+ *
+ * This function creates a &drm_mode_set array, one entry per CRTC.
+ *
+ * Returns:
+ * A &drm_mode_set array or an error pointer on allocation failure.
+ */
+struct drm_mode_set *drm_client_modesets_create(struct drm_device *dev)
+{
+ unsigned int num_crtc = dev->mode_config.num_crtc;
+ struct drm_mode_set *modeset, *modesets;
+ unsigned int max_connector_count = 1;
+ struct drm_crtc *crtc;
+ unsigned int i = 0;
+
+ /* Add terminating zero entry to enable index less iteration */
+ modesets = kcalloc(num_crtc + 1, sizeof(*modesets), GFP_KERNEL);
+ if (!modesets)
+ return ERR_PTR(-ENOMEM);
+
+ drm_for_each_crtc(crtc, dev)
+ modesets[i++].crtc = crtc;
+
+ /* Cloning is only supported in the single crtc case. */
+ if (num_crtc == 1)
+ max_connector_count = DRM_CLIENT_MAX_CLONED_CONNECTORS;
+
+ drm_client_for_each_modeset(modeset, modesets) {
+ modeset->connectors = kcalloc(max_connector_count,
+ sizeof(*modeset->connectors), GFP_KERNEL);
+ if (!modeset->connectors)
+ goto err_free;
+ }
+
+ return modesets;
+
+err_free:
+ drm_client_modesets_release(modesets);
+
+ return ERR_PTR(-ENOMEM);
+}
+EXPORT_SYMBOL(drm_client_modesets_create);
+
+/**
+ * drm_client_modesets_release() - Free modesets
+ * @modesets: Modeset array (can be NULL or error pointer)
+ *
+ * This function destroys any attached display modes, puts connectors and frees
+ * the modeset array.
+ */
+void drm_client_modesets_release(struct drm_mode_set *modesets)
+{
+ struct drm_mode_set *modeset;
+ unsigned int i;
+
+ if (IS_ERR_OR_NULL(modesets))
+ return;
+
+ drm_client_for_each_modeset(modeset, modesets) {
+ drm_mode_destroy(modeset->crtc->dev, modeset->mode);
+
+ for (i = 0; i < modeset->num_connectors; i++)
+ drm_connector_put(modeset->connectors[i]);
+ kfree(modeset->connectors);
+ }
+ kfree(modesets);
+}
+EXPORT_SYMBOL(drm_client_modesets_release);
+
+/**
+ * drm_client_find_modeset() - Find modeset matching a CRTC
+ * @modesets: Modeset array
+ * @crtc: CRTC
+ *
+ * This function looks up the modeset connected to @crtc.
+ *
+ * Returns:
+ * A &drm_mode_set or NULL.
+ */
+struct drm_mode_set *drm_client_find_modeset(struct drm_mode_set *modesets, struct drm_crtc *crtc)
+{
+ struct drm_mode_set *modeset;
+
+ drm_client_for_each_modeset(modeset, modesets)
+ if (modeset->crtc == crtc)
+ return modeset;
+
+ return NULL;
+}
+EXPORT_SYMBOL(drm_client_find_modeset);
+
#ifdef CONFIG_DEBUG_FS
static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data)
{
@@ -316,13 +316,10 @@ int drm_fb_helper_debug_enter(struct fb_info *info)
{
struct drm_fb_helper *helper = info->par;
const struct drm_crtc_helper_funcs *funcs;
- int i;
+ struct drm_mode_set *mode_set;
list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
- for (i = 0; i < helper->crtc_count; i++) {
- struct drm_mode_set *mode_set =
- &helper->crtc_info[i].mode_set;
-
+ drm_client_for_each_modeset(mode_set, helper->modesets) {
if (!mode_set->crtc->enabled)
continue;
@@ -354,12 +351,10 @@ int drm_fb_helper_debug_leave(struct fb_info *info)
struct drm_fb_helper *helper = info->par;
struct drm_crtc *crtc;
const struct drm_crtc_helper_funcs *funcs;
+ struct drm_mode_set *mode_set;
struct drm_framebuffer *fb;
- int i;
-
- for (i = 0; i < helper->crtc_count; i++) {
- struct drm_mode_set *mode_set = &helper->crtc_info[i].mode_set;
+ drm_client_for_each_modeset(mode_set, helper->modesets) {
crtc = mode_set->crtc;
if (drm_drv_uses_atomic_modeset(crtc->dev))
continue;
@@ -436,8 +431,9 @@ static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool activ
struct drm_plane_state *plane_state;
struct drm_plane *plane;
struct drm_atomic_state *state;
- int i, ret;
struct drm_modeset_acquire_ctx ctx;
+ struct drm_mode_set *mode_set;
+ int ret;
drm_modeset_acquire_init(&ctx, 0);
@@ -467,8 +463,7 @@ static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool activ
goto out_state;
}
- for (i = 0; i < fb_helper->crtc_count; i++) {
- struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
+ drm_client_for_each_modeset(mode_set, fb_helper->modesets) {
struct drm_plane *primary = mode_set->crtc->primary;
unsigned int rotation;
@@ -517,8 +512,9 @@ static int restore_fbdev_mode_atomic(struct drm_fb_helper *fb_helper, bool activ
static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper)
{
struct drm_device *dev = fb_helper->dev;
+ struct drm_mode_set *mode_set;
struct drm_plane *plane;
- int i, ret = 0;
+ int ret = 0;
drm_modeset_lock_all(fb_helper->dev);
drm_for_each_plane(plane, dev) {
@@ -531,8 +527,7 @@ static int restore_fbdev_mode_legacy(struct drm_fb_helper *fb_helper)
DRM_MODE_ROTATE_0);
}
- for (i = 0; i < fb_helper->crtc_count; i++) {
- struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
+ drm_client_for_each_modeset(mode_set, fb_helper->modesets) {
struct drm_crtc *crtc = mode_set->crtc;
if (crtc->funcs->cursor_set2) {
@@ -687,12 +682,10 @@ static void dpms_legacy(struct drm_fb_helper *fb_helper, int dpms_mode)
struct drm_device *dev = fb_helper->dev;
struct drm_connector *connector;
struct drm_mode_set *modeset;
- int i, j;
+ int j;
drm_modeset_lock_all(dev);
- for (i = 0; i < fb_helper->crtc_count; i++) {
- modeset = &fb_helper->crtc_info[i].mode_set;
-
+ drm_client_for_each_modeset(modeset, fb_helper->modesets) {
if (!modeset->crtc->enabled)
continue;
@@ -762,43 +755,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;
-
- 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);
-
- 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)
{
struct drm_fb_helper *helper = container_of(work, struct drm_fb_helper,
@@ -877,7 +833,7 @@ EXPORT_SYMBOL(drm_fb_helper_prepare);
* drm_fb_helper_init - initialize a &struct drm_fb_helper
* @dev: drm device
* @fb_helper: driver-allocated fbdev helper structure to initialize
- * @max_conn_count: max connector count
+ * @max_conn_count: max connector count (not used)
*
* This allocates the structures for the fbdev helper with the given limits.
* Note that this won't yet touch the hardware (through the driver interfaces)
@@ -893,53 +849,28 @@ 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)
+ fb_helper->modesets = drm_client_modesets_create(dev);
+ if (IS_ERR(fb_helper->modesets))
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);
- return -ENOMEM;
- }
+ if (!fb_helper->connector_info)
+ goto out_free;
+
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++;
- }
-
dev->fb_helper = fb_helper;
return 0;
out_free:
- drm_fb_helper_crtc_free(fb_helper);
+ drm_client_modesets_release(fb_helper->modesets);
+
return -ENOMEM;
}
EXPORT_SYMBOL(drm_fb_helper_init);
@@ -1014,6 +945,7 @@ 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;
@@ -1043,8 +975,13 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
mutex_unlock(&kernel_fb_helper_lock);
mutex_destroy(&fb_helper->lock);
- drm_fb_helper_crtc_free(fb_helper);
+ drm_client_modesets_release(fb_helper->modesets);
+ 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);
@@ -1389,13 +1326,14 @@ static int setcmap_pseudo_palette(struct fb_cmap *cmap, struct fb_info *info)
static int setcmap_legacy(struct fb_cmap *cmap, struct fb_info *info)
{
struct drm_fb_helper *fb_helper = info->par;
+ struct drm_mode_set *modeset;
struct drm_crtc *crtc;
u16 *r, *g, *b;
- int i, ret = 0;
+ int ret = 0;
drm_modeset_lock_all(fb_helper->dev);
- for (i = 0; i < fb_helper->crtc_count; i++) {
- crtc = fb_helper->crtc_info[i].mode_set.crtc;
+ drm_client_for_each_modeset(modeset, fb_helper->modesets) {
+ crtc = modeset->crtc;
if (!crtc->funcs->gamma_set || !crtc->gamma_size)
return -EINVAL;
@@ -1471,10 +1409,11 @@ static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info)
struct drm_modeset_acquire_ctx ctx;
struct drm_crtc_state *crtc_state;
struct drm_atomic_state *state;
+ struct drm_mode_set *modeset;
struct drm_crtc *crtc;
u16 *r, *g, *b;
- int i, ret = 0;
bool replaced;
+ int ret = 0;
drm_modeset_acquire_init(&ctx, 0);
@@ -1486,8 +1425,8 @@ static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info)
state->acquire_ctx = &ctx;
retry:
- for (i = 0; i < fb_helper->crtc_count; i++) {
- crtc = fb_helper->crtc_info[i].mode_set.crtc;
+ drm_client_for_each_modeset(modeset, fb_helper->modesets) {
+ crtc = modeset->crtc;
if (!gamma_lut)
gamma_lut = setcmap_new_gamma_lut(crtc, cmap);
@@ -1515,8 +1454,8 @@ static int setcmap_atomic(struct fb_cmap *cmap, struct fb_info *info)
if (ret)
goto out_state;
- for (i = 0; i < fb_helper->crtc_count; i++) {
- crtc = fb_helper->crtc_info[i].mode_set.crtc;
+ drm_client_for_each_modeset(modeset, fb_helper->modesets) {
+ crtc = modeset->crtc;
r = crtc->gamma_store;
g = r + crtc->gamma_size;
@@ -1592,7 +1531,6 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
struct drm_fb_helper *fb_helper = info->par;
- struct drm_mode_set *mode_set;
struct drm_crtc *crtc;
int ret = 0;
@@ -1620,8 +1558,7 @@ int drm_fb_helper_ioctl(struct fb_info *info, unsigned int cmd,
* make. If we're not smart enough here, one should
* just consider switch the userspace to KMS.
*/
- mode_set = &fb_helper->crtc_info[0].mode_set;
- crtc = mode_set->crtc;
+ crtc = fb_helper->modesets[0].crtc;
/*
* Only wait for a vblank event if the CRTC is
@@ -1817,13 +1754,9 @@ EXPORT_SYMBOL(drm_fb_helper_set_par);
static void pan_set(struct drm_fb_helper *fb_helper, int x, int y)
{
- int i;
-
- for (i = 0; i < fb_helper->crtc_count; i++) {
- struct drm_mode_set *mode_set;
-
- mode_set = &fb_helper->crtc_info[i].mode_set;
+ struct drm_mode_set *mode_set;
+ drm_client_for_each_modeset(mode_set, fb_helper->modesets) {
mode_set->x = x;
mode_set->y = y;
}
@@ -1853,12 +1786,9 @@ static int pan_display_legacy(struct fb_var_screeninfo *var,
struct drm_fb_helper *fb_helper = info->par;
struct drm_mode_set *modeset;
int ret = 0;
- int i;
drm_modeset_lock_all(fb_helper->dev);
- for (i = 0; i < fb_helper->crtc_count; i++) {
- modeset = &fb_helper->crtc_info[i].mode_set;
-
+ drm_client_for_each_modeset(modeset, fb_helper->modesets) {
modeset->x = var->xoffset;
modeset->y = var->yoffset;
@@ -1917,6 +1847,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
int crtc_count = 0;
int i;
struct drm_fb_helper_surface_size sizes;
+ struct drm_mode_set *mode_set;
int best_depth = 0;
memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
@@ -1967,13 +1898,12 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
* supports RGBA5551 (16 bpp, depth 15) but not RGB565 (16 bpp, depth
* 16) we need to scale down the depth of the sizes we request.
*/
- for (i = 0; i < fb_helper->crtc_count; i++) {
- struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
+ drm_client_for_each_modeset(mode_set, fb_helper->modesets) {
struct drm_crtc *crtc = mode_set->crtc;
struct drm_plane *plane = crtc->primary;
int j;
- DRM_DEBUG("test CRTC %d primary plane\n", i);
+ DRM_DEBUG("test CRTC %u primary plane\n", drm_crtc_index(crtc));
for (j = 0; j < plane->format_count; j++) {
const struct drm_format_info *fmt;
@@ -2013,9 +1943,8 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
/* first up get a count of crtcs now in use and new min/maxes width/heights */
crtc_count = 0;
- for (i = 0; i < fb_helper->crtc_count; i++) {
+ drm_client_for_each_modeset(mode_set, fb_helper->modesets) {
struct drm_display_mode *desired_mode;
- struct drm_mode_set *mode_set;
int x, y, j;
/* in case of tile group, are we the last tile vert or horiz?
* If no tile group you are always the last one both vertically
@@ -2023,7 +1952,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
*/
bool lastv = true, lasth = true;
- mode_set = &fb_helper->crtc_info[i].mode_set;
desired_mode = mode_set->mode;
if (!desired_mode)
@@ -2282,7 +2210,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;
@@ -2471,15 +2399,17 @@ static bool connector_has_possible_crtc(struct drm_connector *connector,
}
static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
- struct drm_fb_helper_crtc **best_crtcs,
+ struct drm_mode_set *modesets,
+ struct drm_crtc **best_crtcs,
struct drm_display_mode **modes,
int n, int width, int height)
{
- int c, o;
struct drm_connector *connector;
int my_score, best_score, score;
- struct drm_fb_helper_crtc **crtcs, *crtc;
+ 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)
return 0;
@@ -2488,12 +2418,11 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
connector = fb_helper_conn->connector;
best_crtcs[n] = NULL;
- best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
+ best_score = drm_pick_crtcs(fb_helper, modesets, best_crtcs, modes, n + 1, width, height);
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;
@@ -2509,11 +2438,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_for_each_modeset(modeset, modesets) {
+ crtc = modeset->crtc;
- if (!connector_has_possible_crtc(connector,
- crtc->mode_set.crtc))
+ if (!connector_has_possible_crtc(connector, crtc))
continue;
for (o = 0; o < n; o++)
@@ -2522,7 +2450,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 (fb_helper->dev->mode_config.num_crtc > 1)
continue;
if (!drm_mode_equal(modes[o], modes[n]))
@@ -2530,14 +2458,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 *));
- score = my_score + drm_pick_crtcs(fb_helper, crtcs, modes, n + 1,
+ memcpy(crtcs, best_crtcs, n * sizeof(*crtcs));
+ score = my_score + drm_pick_crtcs(fb_helper, modesets, 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));
}
}
@@ -2545,21 +2472,9 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
return best_score;
}
-static struct drm_fb_helper_crtc *
-drm_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 config */
static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper,
- struct drm_fb_helper_crtc **crtcs,
+ struct drm_crtc **crtcs,
struct drm_display_mode **modes,
struct drm_fb_offset *offsets,
bool *enabled, int width, int height)
@@ -2591,7 +2506,7 @@ static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper,
struct drm_fb_helper_connector *fb_conn;
struct drm_connector *connector;
struct drm_encoder *encoder;
- struct drm_fb_helper_crtc *new_crtc;
+ struct drm_crtc *new_crtc;
fb_conn = fb_helper->connector_info[i];
connector = fb_conn->connector;
@@ -2634,7 +2549,7 @@ static bool drm_fb_helper_firmware_config(struct drm_fb_helper *fb_helper,
num_connectors_enabled++;
- new_crtc = drm_fb_helper_crtc(fb_helper, connector->state->crtc);
+ new_crtc = connector->state->crtc;
/*
* Make sure we're not trying to drive multiple connectors
@@ -2737,9 +2652,10 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
u32 width, u32 height)
{
struct drm_device *dev = fb_helper->dev;
- struct drm_fb_helper_crtc **crtcs;
struct drm_display_mode **modes;
struct drm_fb_offset *offsets;
+ struct drm_mode_set *modesets;
+ struct drm_crtc **crtcs;
bool *enabled;
int i;
@@ -2747,15 +2663,15 @@ 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);
- crtcs = kcalloc(fb_helper->connector_count,
- sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
+ modesets = drm_client_modesets_create(dev);
+ 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);
- if (!crtcs || !modes || !enabled || !offsets) {
+ if (IS_ERR(modesets) || !crtcs || !modes || !enabled || !offsets) {
DRM_ERROR("Memory allocation failed\n");
goto out;
}
@@ -2780,28 +2696,26 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
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, modesets, 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 */
- 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) {
- struct drm_mode_set *modeset = &fb_crtc->mode_set;
+ if (mode && crtc) {
+ struct drm_mode_set *modeset = drm_client_find_modeset(modesets, crtc);
struct drm_connector *connector =
fb_helper->connector_info[i]->connector;
DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
- mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
+ mode->name, crtc->base.id, offset->x, offset->y);
+
+ if (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS ||
+ (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1)))
+ break;
modeset->mode = drm_mode_duplicate(dev, mode);
drm_connector_get(connector);
@@ -2810,11 +2724,14 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
modeset->y = offset->y;
}
}
+
+ swap(fb_helper->modesets, modesets);
out:
kfree(crtcs);
kfree(modes);
kfree(offsets);
kfree(enabled);
+ drm_client_modesets_release(modesets);
}
/*
@@ -2828,11 +2745,10 @@ static void drm_setup_crtcs_fb(struct drm_fb_helper *fb_helper)
{
struct fb_info *info = fb_helper->fbdev;
unsigned int rotation, sw_rotations = 0;
+ struct drm_mode_set *modeset;
int i;
- for (i = 0; i < fb_helper->crtc_count; i++) {
- struct drm_mode_set *modeset = &fb_helper->crtc_info[i].mode_set;
-
+ drm_client_for_each_modeset(modeset, fb_helper->modesets) {
if (!modeset->num_connectors)
continue;
@@ -3058,8 +2974,7 @@ EXPORT_SYMBOL(drm_fb_helper_hotplug_event);
* @funcs: fbdev helper functions
* @preferred_bpp: Preferred bits per pixel for the device.
* @dev->mode_config.preferred_depth is used if this is zero.
- * @max_conn_count: Maximum number of connectors.
- * @dev->mode_config.num_connector is used if this is zero.
+ * @max_conn_count: Maximum number of connectors (not used)
*
* This function sets up fbdev emulation and registers fbdev for access by
* userspace. If all connectors are disconnected, setup is deferred to the next
@@ -3087,16 +3002,9 @@ int drm_fb_helper_fbdev_setup(struct drm_device *dev,
if (!preferred_bpp)
preferred_bpp = 32;
- if (!max_conn_count)
- max_conn_count = dev->mode_config.num_connector;
- if (!max_conn_count) {
- DRM_DEV_ERROR(dev->dev, "fbdev: No connectors\n");
- return -EINVAL;
- }
-
drm_fb_helper_prepare(dev, fb_helper, funcs);
- ret = drm_fb_helper_init(dev, fb_helper, max_conn_count);
+ ret = drm_fb_helper_init(dev, fb_helper, 0);
if (ret < 0) {
DRM_DEV_ERROR(dev->dev, "fbdev: Failed to initialize (ret=%d)\n", ret);
return ret;
@@ -3413,7 +3321,7 @@ static int drm_fbdev_client_hotplug(struct drm_client_dev *client)
drm_fb_helper_prepare(dev, fb_helper, &drm_fb_helper_generic_funcs);
- ret = drm_fb_helper_init(dev, fb_helper, dev->mode_config.num_connector);
+ ret = drm_fb_helper_init(dev, fb_helper, 0);
if (ret)
goto err;
@@ -5,6 +5,8 @@
#include <linux/types.h>
+#include <drm/drm_crtc.h>
+
struct drm_client_dev;
struct drm_device;
struct drm_file;
@@ -13,6 +15,8 @@ struct drm_gem_object;
struct drm_minor;
struct module;
+#define DRM_CLIENT_MAX_CLONED_CONNECTORS 8
+
/**
* struct drm_client_funcs - DRM client callbacks
*/
@@ -135,6 +139,18 @@ struct drm_client_buffer *
drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format);
void drm_client_framebuffer_delete(struct drm_client_buffer *buffer);
+struct drm_mode_set *drm_client_modesets_create(struct drm_device *dev);
+void drm_client_modesets_release(struct drm_mode_set *modesets);
+struct drm_mode_set *drm_client_find_modeset(struct drm_mode_set *modesets, struct drm_crtc *crtc);
+
+/**
+ * drm_client_for_each_modeset() - Iterate over modesets
+ * @modeset: &drm_mode_set loop cursor
+ * @modesets: Modeset array to iterate over
+ */
+#define drm_client_for_each_modeset(modeset, modesets) \
+ for (modeset = modesets; modeset->crtc; modeset++)
+
int drm_client_debugfs_init(struct drm_minor *minor);
#endif
@@ -46,10 +46,6 @@ struct drm_fb_offset {
int x, y;
};
-struct drm_fb_helper_crtc {
- struct drm_mode_set mode_set;
-};
-
/**
* struct drm_fb_helper_surface_size - describes fbdev size and scanout surface size
* @fb_width: fbdev width
@@ -110,8 +106,6 @@ struct drm_fb_helper_connector {
* struct drm_fb_helper - main structure to emulate fbdev on top of KMS
* @fb: Scanout framebuffer object
* @dev: DRM device
- * @crtc_count: number of possible CRTCs
- * @crtc_info: per-CRTC helper state (mode, x/y offset, etc)
* @connector_count: number of connected connectors
* @connector_info_alloc_count: size of connector_info
* @funcs: driver callbacks for fb helper
@@ -145,8 +139,12 @@ struct drm_fb_helper {
struct drm_framebuffer *fb;
struct drm_device *dev;
- int crtc_count;
- struct drm_fb_helper_crtc *crtc_info;
+
+ /**
+ * @modesets: CRTC configurations
+ */
+ struct drm_mode_set *modesets;
+
int connector_count;
int connector_info_alloc_count;
/**
It now only contains the modeset so use that directly instead. The modeset code will be moved to drm_client, so add code there. The modeset connector array size is hardcoded for the cloned case to avoid having to pass in a value from the driver. A value of 8 is chosen to err on the safe side. This means that the max connector argument for drm_fb_helper_init() and drm_fb_helper_fbdev_setup() isn't used anymore, a todo entry for this is added. Signed-off-by: Noralf Trønnes <noralf@tronnes.org> --- Documentation/gpu/todo.rst | 7 + drivers/gpu/drm/drm_client.c | 93 ++++++++++++ drivers/gpu/drm/drm_fb_helper.c | 262 +++++++++++--------------------- include/drm/drm_client.h | 16 ++ include/drm/drm_fb_helper.h | 14 +- 5 files changed, 207 insertions(+), 185 deletions(-)