diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index 1d30802..042fc29 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c @@ -52,6 +52,17 @@ struct intelfb_par { uint32_t crtc_ids[2]; }; +/* + * Track each CRTCs intelfb mode config for restoration at panic or + * sysrq time. + */ +struct intelfb_console_restore { + struct drm_mode_set mode_sets[I915_NUM_PIPE]; + int num_crtcs; +}; + +static struct intelfb_console_restore console_restore; + static int intelfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, unsigned transp, struct fb_info *info) @@ -412,21 +423,6 @@ int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc) } EXPORT_SYMBOL(intelfb_resize); -static struct drm_mode_set kernelfb_mode; - -static int intelfb_panic(struct notifier_block *n, unsigned long ununsed, - void *panic_str) -{ - DRM_ERROR("panic occurred, switching back to text console\n"); - - intelfb_restore(); - return 0; -} - -static struct notifier_block paniced = { - .notifier_call = intelfb_panic, -}; - static int intelfb_create(struct drm_device *dev, uint32_t fb_width, uint32_t fb_height, uint32_t surface_width, uint32_t surface_height, @@ -625,7 +621,9 @@ out: return ret; } -static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc *crtc) +static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, + struct drm_crtc *crtc, + struct drm_mode_set *set) { struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_framebuffer *intel_fb; @@ -701,10 +699,7 @@ static int intelfb_multi_fb_probe_crtc(struct drm_device *dev, struct drm_crtc * DRM_INFO("fb%d: %s frame buffer device\n", info->node, info->fix.id); - /* Switch back to kernel console on panic */ - kernelfb_mode = *modeset; - atomic_notifier_chain_register(&panic_notifier_list, &paniced); - DRM_DEBUG("registered panic notifier\n"); + *set = *modeset; return 0; } @@ -713,12 +708,15 @@ static int intelfb_multi_fb_probe(struct drm_device *dev) { struct drm_crtc *crtc; - int ret = 0; + int ret = 0, crtc_count = 0; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - ret = intelfb_multi_fb_probe_crtc(dev, crtc); + struct drm_mode_set *set = + &console_restore.mode_sets[crtc_count++]; + ret = intelfb_multi_fb_probe_crtc(dev, crtc, set); if (ret) return ret; + console_restore.num_crtcs++; } return ret; } @@ -812,7 +810,12 @@ static int intelfb_single_fb_probe(struct drm_device *dev) * set configuration. */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + struct intel_crtc *intel_crtc; + + if (!drm_helper_crtc_in_use(crtc) || !crtc->desired_mode) + continue; + + intel_crtc = to_intel_crtc(crtc); modeset = &intel_crtc->mode_set; modeset->fb = &intel_fb->base; @@ -833,7 +836,7 @@ static int intelfb_single_fb_probe(struct drm_device *dev) for (i = conn_count; i < INTELFB_CONN_LIMIT; i++) modeset->connectors[i] = NULL; - par->crtc_ids[crtc_count++] = crtc->base.id; + par->crtc_ids[crtc_count] = crtc->base.id; modeset->num_connectors = conn_count; if (modeset->crtc->desired_mode) { @@ -842,6 +845,10 @@ static int intelfb_single_fb_probe(struct drm_device *dev) modeset->mode = drm_mode_duplicate(dev, modeset->crtc->desired_mode); } + + console_restore.mode_sets[crtc_count] = *modeset; + console_restore.num_crtcs++; + crtc_count++; } par->crtc_count = crtc_count; @@ -855,11 +862,6 @@ static int intelfb_single_fb_probe(struct drm_device *dev) DRM_INFO("fb%d: %s frame buffer device\n", info->node, info->fix.id); - /* Switch back to kernel console on panic */ - kernelfb_mode = *modeset; - atomic_notifier_chain_register(&panic_notifier_list, &paniced); - DRM_DEBUG("registered panic notifier\n"); - return 0; } @@ -870,13 +872,30 @@ static int intelfb_single_fb_probe(struct drm_device *dev) */ void intelfb_restore(void) { - int ret; - if ((ret = drm_crtc_helper_set_config(&kernelfb_mode)) != 0) { - DRM_ERROR("Failed to restore crtc configuration: %d\n", - ret); + int ret, i; + + for (i = 0; i < console_restore.num_crtcs; i++) { + struct drm_mode_set *set = &console_restore.mode_sets[i]; + if ((ret = drm_crtc_helper_set_config(set)) != 0) { + DRM_ERROR("Failed to restore crtc configuration: %d\n", + ret); + } } } +static int intelfb_panic(struct notifier_block *n, unsigned long ununsed, + void *panic_str) +{ + DRM_ERROR("panic occurred, switching back to text console\n"); + + intelfb_restore(); + return 0; +} + +static struct notifier_block paniced = { + .notifier_call = intelfb_panic, +}; + static void intelfb_restore_work_fn(struct work_struct *ignored) { intelfb_restore(); @@ -924,6 +943,7 @@ int intelfb_probe(struct drm_device *dev) ret = intelfb_single_fb_probe(dev); } + atomic_notifier_chain_register(&panic_notifier_list, &paniced); register_sysrq_key('v', &sysrq_intelfb_restore_op); return ret; @@ -946,7 +966,7 @@ int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb) } atomic_notifier_chain_unregister(&panic_notifier_list, &paniced); - memset(&kernelfb_mode, 0, sizeof(struct drm_mode_set)); + memset(&console_restore, 0, sizeof(struct intelfb_console_restore)); return 0; } EXPORT_SYMBOL(intelfb_remove);