|
@@ -49,6 +49,7 @@ MODULE_PARM_DESC(fbdev_emulation,
|
|
"Enable legacy fbdev emulation [default=true]");
|
|
"Enable legacy fbdev emulation [default=true]");
|
|
|
|
|
|
static LIST_HEAD(kernel_fb_helper_list);
|
|
static LIST_HEAD(kernel_fb_helper_list);
|
|
|
|
+static DEFINE_MUTEX(kernel_fb_helper_lock);
|
|
|
|
|
|
/**
|
|
/**
|
|
* DOC: fbdev helpers
|
|
* DOC: fbdev helpers
|
|
@@ -97,6 +98,10 @@ static LIST_HEAD(kernel_fb_helper_list);
|
|
* mmap page writes.
|
|
* mmap page writes.
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
+#define drm_fb_helper_for_each_connector(fbh, i__) \
|
|
|
|
+ for (({ lockdep_assert_held(&(fbh)->dev->mode_config.mutex); }), \
|
|
|
|
+ i__ = 0; i__ < (fbh)->connector_count; i__++)
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
|
|
* drm_fb_helper_single_add_all_connectors() - add all connectors to fbdev
|
|
* emulation helper
|
|
* emulation helper
|
|
@@ -130,7 +135,7 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
return 0;
|
|
return 0;
|
|
fail:
|
|
fail:
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
struct drm_fb_helper_connector *fb_helper_connector =
|
|
struct drm_fb_helper_connector *fb_helper_connector =
|
|
fb_helper->connector_info[i];
|
|
fb_helper->connector_info[i];
|
|
|
|
|
|
@@ -565,7 +570,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
|
|
continue;
|
|
continue;
|
|
|
|
|
|
/* Walk the connectors & encoders on this fb turning them on/off */
|
|
/* Walk the connectors & encoders on this fb turning them on/off */
|
|
- for (j = 0; j < fb_helper->connector_count; j++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, j) {
|
|
connector = fb_helper->connector_info[j]->connector;
|
|
connector = fb_helper->connector_info[j]->connector;
|
|
connector->funcs->dpms(connector, dpms_mode);
|
|
connector->funcs->dpms(connector, dpms_mode);
|
|
drm_object_property_set_value(&connector->base,
|
|
drm_object_property_set_value(&connector->base,
|
|
@@ -851,12 +856,14 @@ void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
|
|
if (!drm_fbdev_emulation)
|
|
if (!drm_fbdev_emulation)
|
|
return;
|
|
return;
|
|
|
|
|
|
|
|
+ mutex_lock(&kernel_fb_helper_lock);
|
|
if (!list_empty(&fb_helper->kernel_fb_list)) {
|
|
if (!list_empty(&fb_helper->kernel_fb_list)) {
|
|
list_del(&fb_helper->kernel_fb_list);
|
|
list_del(&fb_helper->kernel_fb_list);
|
|
if (list_empty(&kernel_fb_helper_list)) {
|
|
if (list_empty(&kernel_fb_helper_list)) {
|
|
unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
|
|
unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ mutex_unlock(&kernel_fb_helper_lock);
|
|
|
|
|
|
drm_fb_helper_crtc_free(fb_helper);
|
|
drm_fb_helper_crtc_free(fb_helper);
|
|
|
|
|
|
@@ -1469,7 +1476,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
|
|
int ret = 0;
|
|
int ret = 0;
|
|
int crtc_count = 0;
|
|
int crtc_count = 0;
|
|
int i;
|
|
int i;
|
|
- struct fb_info *info;
|
|
|
|
struct drm_fb_helper_surface_size sizes;
|
|
struct drm_fb_helper_surface_size sizes;
|
|
int gamma_size = 0;
|
|
int gamma_size = 0;
|
|
|
|
|
|
@@ -1485,7 +1491,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
|
|
sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
|
|
sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
|
|
|
|
|
|
/* first up get a count of crtcs now in use and new min/maxes width/heights */
|
|
/* first up get a count of crtcs now in use and new min/maxes width/heights */
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
|
|
struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
|
|
struct drm_cmdline_mode *cmdline_mode;
|
|
struct drm_cmdline_mode *cmdline_mode;
|
|
|
|
|
|
@@ -1572,8 +1578,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
return ret;
|
|
return ret;
|
|
|
|
|
|
- info = fb_helper->fbdev;
|
|
|
|
-
|
|
|
|
/*
|
|
/*
|
|
* Set the fb pointer - usually drm_setup_crtcs does this for hotplug
|
|
* Set the fb pointer - usually drm_setup_crtcs does this for hotplug
|
|
* events, but at init time drm_setup_crtcs needs to be called before
|
|
* events, but at init time drm_setup_crtcs needs to be called before
|
|
@@ -1585,20 +1589,6 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
|
|
if (fb_helper->crtc_info[i].mode_set.num_connectors)
|
|
if (fb_helper->crtc_info[i].mode_set.num_connectors)
|
|
fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
|
|
fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
|
|
|
|
|
|
-
|
|
|
|
- info->var.pixclock = 0;
|
|
|
|
- if (register_framebuffer(info) < 0)
|
|
|
|
- return -EINVAL;
|
|
|
|
-
|
|
|
|
- dev_info(fb_helper->dev->dev, "fb%d: %s frame buffer device\n",
|
|
|
|
- info->node, info->fix.id);
|
|
|
|
-
|
|
|
|
- if (list_empty(&kernel_fb_helper_list)) {
|
|
|
|
- register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
|
|
|
|
-
|
|
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1730,7 +1720,7 @@ static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
|
|
int count = 0;
|
|
int count = 0;
|
|
int i;
|
|
int i;
|
|
|
|
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
connector = fb_helper->connector_info[i]->connector;
|
|
connector = fb_helper->connector_info[i]->connector;
|
|
count += connector->funcs->fill_modes(connector, maxX, maxY);
|
|
count += connector->funcs->fill_modes(connector, maxX, maxY);
|
|
}
|
|
}
|
|
@@ -1830,7 +1820,7 @@ static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
|
|
struct drm_connector *connector;
|
|
struct drm_connector *connector;
|
|
int i = 0;
|
|
int i = 0;
|
|
|
|
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
connector = fb_helper->connector_info[i]->connector;
|
|
connector = fb_helper->connector_info[i]->connector;
|
|
enabled[i] = drm_connector_enabled(connector, true);
|
|
enabled[i] = drm_connector_enabled(connector, true);
|
|
DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
|
|
DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
|
|
@@ -1841,7 +1831,7 @@ static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
|
|
if (any_enabled)
|
|
if (any_enabled)
|
|
return;
|
|
return;
|
|
|
|
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
connector = fb_helper->connector_info[i]->connector;
|
|
connector = fb_helper->connector_info[i]->connector;
|
|
enabled[i] = drm_connector_enabled(connector, false);
|
|
enabled[i] = drm_connector_enabled(connector, false);
|
|
}
|
|
}
|
|
@@ -1862,7 +1852,7 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
|
|
return false;
|
|
return false;
|
|
|
|
|
|
count = 0;
|
|
count = 0;
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
if (enabled[i])
|
|
if (enabled[i])
|
|
count++;
|
|
count++;
|
|
}
|
|
}
|
|
@@ -1873,7 +1863,7 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
|
|
|
|
|
|
/* check the command line or if nothing common pick 1024x768 */
|
|
/* check the command line or if nothing common pick 1024x768 */
|
|
can_clone = true;
|
|
can_clone = true;
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
if (!enabled[i])
|
|
if (!enabled[i])
|
|
continue;
|
|
continue;
|
|
fb_helper_conn = fb_helper->connector_info[i];
|
|
fb_helper_conn = fb_helper->connector_info[i];
|
|
@@ -1899,8 +1889,7 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
|
|
can_clone = true;
|
|
can_clone = true;
|
|
dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
|
|
dmt_mode = drm_mode_find_dmt(fb_helper->dev, 1024, 768, 60, false);
|
|
|
|
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
-
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
if (!enabled[i])
|
|
if (!enabled[i])
|
|
continue;
|
|
continue;
|
|
|
|
|
|
@@ -1931,7 +1920,7 @@ static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
|
|
int i;
|
|
int i;
|
|
int hoffset = 0, voffset = 0;
|
|
int hoffset = 0, voffset = 0;
|
|
|
|
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
fb_helper_conn = fb_helper->connector_info[i];
|
|
fb_helper_conn = fb_helper->connector_info[i];
|
|
if (!fb_helper_conn->connector->has_tile)
|
|
if (!fb_helper_conn->connector->has_tile)
|
|
continue;
|
|
continue;
|
|
@@ -1965,7 +1954,7 @@ static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
|
|
int i;
|
|
int i;
|
|
|
|
|
|
retry:
|
|
retry:
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
fb_helper_conn = fb_helper->connector_info[i];
|
|
fb_helper_conn = fb_helper->connector_info[i];
|
|
|
|
|
|
if (conn_configured & BIT_ULL(i))
|
|
if (conn_configured & BIT_ULL(i))
|
|
@@ -2113,20 +2102,22 @@ out:
|
|
return best_score;
|
|
return best_score;
|
|
}
|
|
}
|
|
|
|
|
|
-static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
|
|
|
|
|
|
+static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
|
|
|
|
+ u32 width, u32 height)
|
|
{
|
|
{
|
|
struct drm_device *dev = fb_helper->dev;
|
|
struct drm_device *dev = fb_helper->dev;
|
|
struct drm_fb_helper_crtc **crtcs;
|
|
struct drm_fb_helper_crtc **crtcs;
|
|
struct drm_display_mode **modes;
|
|
struct drm_display_mode **modes;
|
|
struct drm_fb_offset *offsets;
|
|
struct drm_fb_offset *offsets;
|
|
bool *enabled;
|
|
bool *enabled;
|
|
- int width, height;
|
|
|
|
int i;
|
|
int i;
|
|
|
|
|
|
DRM_DEBUG_KMS("\n");
|
|
DRM_DEBUG_KMS("\n");
|
|
|
|
+ if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)
|
|
|
|
+ DRM_DEBUG_KMS("No connectors reported connected with modes\n");
|
|
|
|
|
|
- width = dev->mode_config.max_width;
|
|
|
|
- height = dev->mode_config.max_height;
|
|
|
|
|
|
+ /* prevent concurrent modification of connector_count by hotplug */
|
|
|
|
+ lockdep_assert_held(&fb_helper->dev->mode_config.mutex);
|
|
|
|
|
|
crtcs = kcalloc(fb_helper->connector_count,
|
|
crtcs = kcalloc(fb_helper->connector_count,
|
|
sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
|
|
sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
|
|
@@ -2141,7 +2132,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
|
|
goto out;
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
drm_enable_connectors(fb_helper, enabled);
|
|
drm_enable_connectors(fb_helper, enabled);
|
|
|
|
|
|
if (!(fb_helper->funcs->initial_config &&
|
|
if (!(fb_helper->funcs->initial_config &&
|
|
@@ -2170,7 +2160,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
|
|
drm_fb_helper_modeset_release(fb_helper,
|
|
drm_fb_helper_modeset_release(fb_helper,
|
|
&fb_helper->crtc_info[i].mode_set);
|
|
&fb_helper->crtc_info[i].mode_set);
|
|
|
|
|
|
- for (i = 0; i < fb_helper->connector_count; i++) {
|
|
|
|
|
|
+ drm_fb_helper_for_each_connector(fb_helper, i) {
|
|
struct drm_display_mode *mode = modes[i];
|
|
struct drm_display_mode *mode = modes[i];
|
|
struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
|
|
struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
|
|
struct drm_fb_offset *offset = &offsets[i];
|
|
struct drm_fb_offset *offset = &offsets[i];
|
|
@@ -2247,25 +2237,38 @@ out:
|
|
int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
|
|
int drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
|
|
{
|
|
{
|
|
struct drm_device *dev = fb_helper->dev;
|
|
struct drm_device *dev = fb_helper->dev;
|
|
- int count = 0;
|
|
|
|
|
|
+ struct fb_info *info;
|
|
|
|
+ int ret;
|
|
|
|
|
|
if (!drm_fbdev_emulation)
|
|
if (!drm_fbdev_emulation)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
mutex_lock(&dev->mode_config.mutex);
|
|
mutex_lock(&dev->mode_config.mutex);
|
|
- count = drm_fb_helper_probe_connector_modes(fb_helper,
|
|
|
|
- dev->mode_config.max_width,
|
|
|
|
- dev->mode_config.max_height);
|
|
|
|
|
|
+ drm_setup_crtcs(fb_helper,
|
|
|
|
+ dev->mode_config.max_width,
|
|
|
|
+ dev->mode_config.max_height);
|
|
|
|
+ ret = drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
mutex_unlock(&dev->mode_config.mutex);
|
|
- /*
|
|
|
|
- * we shouldn't end up with no modes here.
|
|
|
|
- */
|
|
|
|
- if (count == 0)
|
|
|
|
- dev_info(fb_helper->dev->dev, "No connectors reported connected with modes\n");
|
|
|
|
|
|
+ if (ret)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ info = fb_helper->fbdev;
|
|
|
|
+ info->var.pixclock = 0;
|
|
|
|
+ ret = register_framebuffer(info);
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ return ret;
|
|
|
|
+
|
|
|
|
+ dev_info(dev->dev, "fb%d: %s frame buffer device\n",
|
|
|
|
+ info->node, info->fix.id);
|
|
|
|
+
|
|
|
|
+ mutex_lock(&kernel_fb_helper_lock);
|
|
|
|
+ if (list_empty(&kernel_fb_helper_list))
|
|
|
|
+ register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
|
|
|
|
|
|
- drm_setup_crtcs(fb_helper);
|
|
|
|
|
|
+ list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
|
|
|
|
+ mutex_unlock(&kernel_fb_helper_lock);
|
|
|
|
|
|
- return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
|
|
|
|
|
|
+ return 0;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(drm_fb_helper_initial_config);
|
|
EXPORT_SYMBOL(drm_fb_helper_initial_config);
|
|
|
|
|
|
@@ -2293,28 +2296,22 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
|
|
int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
|
|
int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
|
|
{
|
|
{
|
|
struct drm_device *dev = fb_helper->dev;
|
|
struct drm_device *dev = fb_helper->dev;
|
|
- u32 max_width, max_height;
|
|
|
|
|
|
|
|
if (!drm_fbdev_emulation)
|
|
if (!drm_fbdev_emulation)
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
- mutex_lock(&fb_helper->dev->mode_config.mutex);
|
|
|
|
|
|
+ mutex_lock(&dev->mode_config.mutex);
|
|
if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
|
|
if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
|
|
fb_helper->delayed_hotplug = true;
|
|
fb_helper->delayed_hotplug = true;
|
|
- mutex_unlock(&fb_helper->dev->mode_config.mutex);
|
|
|
|
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
DRM_DEBUG_KMS("\n");
|
|
DRM_DEBUG_KMS("\n");
|
|
|
|
|
|
- max_width = fb_helper->fb->width;
|
|
|
|
- max_height = fb_helper->fb->height;
|
|
|
|
|
|
+ drm_setup_crtcs(fb_helper, fb_helper->fb->width, fb_helper->fb->height);
|
|
|
|
|
|
- drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height);
|
|
|
|
- mutex_unlock(&fb_helper->dev->mode_config.mutex);
|
|
|
|
|
|
+ mutex_unlock(&dev->mode_config.mutex);
|
|
|
|
|
|
- drm_modeset_lock_all(dev);
|
|
|
|
- drm_setup_crtcs(fb_helper);
|
|
|
|
- drm_modeset_unlock_all(dev);
|
|
|
|
drm_fb_helper_set_par(fb_helper->fbdev);
|
|
drm_fb_helper_set_par(fb_helper->fbdev);
|
|
|
|
|
|
return 0;
|
|
return 0;
|