diff options
author | Rafael J. Wysocki <rjw@sisk.pl> | 2010-02-07 15:48:24 -0500 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2010-02-10 20:34:33 -0500 |
commit | 84b79f8d2882b0a84330c04839ed4d3cefd2ff77 (patch) | |
tree | 087d75e21a0550bc10f109635b93bf02fa220bfb /drivers/gpu/drm/i915/i915_drv.c | |
parent | a40e8d3139e9eb54bf1d29f91639a6c5e05f652e (diff) |
drm/i915: Fix crash while aborting hibernation
Commit cbda12d77ea590082edb6d30bd342a67ebc459e0 (drm/i915: implement
new pm ops for i915) introduced the problem that if s2disk hibernation
is aborted, the system will crash, because i915_pm_freeze() does
nothing, while it should at least reverse some operations carried out
by i915_suspend().
Fix this issue by splitting the i915 suspend into a freeze part a
suspend part, where the latter is not executed before creating a
hibernation image, and the i915 resume into a "low-level" resume part
and a thaw part, where the former is not executed after the image has
been created.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Tested-by: Alan Jenkins <alan-jenkins@tuffmail.co.uk>
Signed-off-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers/gpu/drm/i915/i915_drv.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.c | 168 |
1 files changed, 101 insertions, 67 deletions
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index ecac882e1d5..79beffcf593 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c | |||
@@ -174,78 +174,100 @@ const static struct pci_device_id pciidlist[] = { | |||
174 | MODULE_DEVICE_TABLE(pci, pciidlist); | 174 | MODULE_DEVICE_TABLE(pci, pciidlist); |
175 | #endif | 175 | #endif |
176 | 176 | ||
177 | static int i915_suspend(struct drm_device *dev, pm_message_t state) | 177 | static int i915_drm_freeze(struct drm_device *dev) |
178 | { | 178 | { |
179 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
180 | |||
181 | if (!dev || !dev_priv) { | ||
182 | DRM_ERROR("dev: %p, dev_priv: %p\n", dev, dev_priv); | ||
183 | DRM_ERROR("DRM not initialized, aborting suspend.\n"); | ||
184 | return -ENODEV; | ||
185 | } | ||
186 | |||
187 | if (state.event == PM_EVENT_PRETHAW) | ||
188 | return 0; | ||
189 | |||
190 | pci_save_state(dev->pdev); | 179 | pci_save_state(dev->pdev); |
191 | 180 | ||
192 | /* If KMS is active, we do the leavevt stuff here */ | 181 | /* If KMS is active, we do the leavevt stuff here */ |
193 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | 182 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { |
194 | if (i915_gem_idle(dev)) | 183 | int error = i915_gem_idle(dev); |
184 | if (error) { | ||
195 | dev_err(&dev->pdev->dev, | 185 | dev_err(&dev->pdev->dev, |
196 | "GEM idle failed, resume may fail\n"); | 186 | "GEM idle failed, resume might fail\n"); |
187 | return error; | ||
188 | } | ||
197 | drm_irq_uninstall(dev); | 189 | drm_irq_uninstall(dev); |
198 | } | 190 | } |
199 | 191 | ||
200 | i915_save_state(dev); | 192 | i915_save_state(dev); |
201 | 193 | ||
194 | return 0; | ||
195 | } | ||
196 | |||
197 | static void i915_drm_suspend(struct drm_device *dev) | ||
198 | { | ||
199 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
200 | |||
202 | intel_opregion_free(dev, 1); | 201 | intel_opregion_free(dev, 1); |
203 | 202 | ||
203 | /* Modeset on resume, not lid events */ | ||
204 | dev_priv->modeset_on_lid = 0; | ||
205 | } | ||
206 | |||
207 | static int i915_suspend(struct drm_device *dev, pm_message_t state) | ||
208 | { | ||
209 | int error; | ||
210 | |||
211 | if (!dev || !dev->dev_private) { | ||
212 | DRM_ERROR("dev: %p\n", dev); | ||
213 | DRM_ERROR("DRM not initialized, aborting suspend.\n"); | ||
214 | return -ENODEV; | ||
215 | } | ||
216 | |||
217 | if (state.event == PM_EVENT_PRETHAW) | ||
218 | return 0; | ||
219 | |||
220 | error = i915_drm_freeze(dev); | ||
221 | if (error) | ||
222 | return error; | ||
223 | |||
224 | i915_drm_suspend(dev); | ||
225 | |||
204 | if (state.event == PM_EVENT_SUSPEND) { | 226 | if (state.event == PM_EVENT_SUSPEND) { |
205 | /* Shut down the device */ | 227 | /* Shut down the device */ |
206 | pci_disable_device(dev->pdev); | 228 | pci_disable_device(dev->pdev); |
207 | pci_set_power_state(dev->pdev, PCI_D3hot); | 229 | pci_set_power_state(dev->pdev, PCI_D3hot); |
208 | } | 230 | } |
209 | 231 | ||
210 | /* Modeset on resume, not lid events */ | ||
211 | dev_priv->modeset_on_lid = 0; | ||
212 | |||
213 | return 0; | 232 | return 0; |
214 | } | 233 | } |
215 | 234 | ||
216 | static int i915_resume(struct drm_device *dev) | 235 | static int i915_drm_thaw(struct drm_device *dev) |
217 | { | 236 | { |
218 | struct drm_i915_private *dev_priv = dev->dev_private; | 237 | struct drm_i915_private *dev_priv = dev->dev_private; |
219 | int ret = 0; | 238 | int error = 0; |
220 | |||
221 | if (pci_enable_device(dev->pdev)) | ||
222 | return -1; | ||
223 | pci_set_master(dev->pdev); | ||
224 | |||
225 | i915_restore_state(dev); | ||
226 | |||
227 | intel_opregion_init(dev, 1); | ||
228 | 239 | ||
229 | /* KMS EnterVT equivalent */ | 240 | /* KMS EnterVT equivalent */ |
230 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | 241 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { |
231 | mutex_lock(&dev->struct_mutex); | 242 | mutex_lock(&dev->struct_mutex); |
232 | dev_priv->mm.suspended = 0; | 243 | dev_priv->mm.suspended = 0; |
233 | 244 | ||
234 | ret = i915_gem_init_ringbuffer(dev); | 245 | error = i915_gem_init_ringbuffer(dev); |
235 | if (ret != 0) | ||
236 | ret = -1; | ||
237 | mutex_unlock(&dev->struct_mutex); | 246 | mutex_unlock(&dev->struct_mutex); |
238 | 247 | ||
239 | drm_irq_install(dev); | 248 | drm_irq_install(dev); |
240 | } | 249 | |
241 | if (drm_core_check_feature(dev, DRIVER_MODESET)) { | ||
242 | /* Resume the modeset for every activated CRTC */ | 250 | /* Resume the modeset for every activated CRTC */ |
243 | drm_helper_resume_force_mode(dev); | 251 | drm_helper_resume_force_mode(dev); |
244 | } | 252 | } |
245 | 253 | ||
246 | dev_priv->modeset_on_lid = 0; | 254 | dev_priv->modeset_on_lid = 0; |
247 | 255 | ||
248 | return ret; | 256 | return error; |
257 | } | ||
258 | |||
259 | static int i915_resume(struct drm_device *dev) | ||
260 | { | ||
261 | if (pci_enable_device(dev->pdev)) | ||
262 | return -EIO; | ||
263 | |||
264 | pci_set_master(dev->pdev); | ||
265 | |||
266 | i915_restore_state(dev); | ||
267 | |||
268 | intel_opregion_init(dev, 1); | ||
269 | |||
270 | return i915_drm_thaw(dev); | ||
249 | } | 271 | } |
250 | 272 | ||
251 | /** | 273 | /** |
@@ -386,57 +408,69 @@ i915_pci_remove(struct pci_dev *pdev) | |||
386 | drm_put_dev(dev); | 408 | drm_put_dev(dev); |
387 | } | 409 | } |
388 | 410 | ||
389 | static int | 411 | static int i915_pm_suspend(struct device *dev) |
390 | i915_pci_suspend(struct pci_dev *pdev, pm_message_t state) | ||
391 | { | 412 | { |
392 | struct drm_device *dev = pci_get_drvdata(pdev); | 413 | struct pci_dev *pdev = to_pci_dev(dev); |
414 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | ||
415 | int error; | ||
393 | 416 | ||
394 | return i915_suspend(dev, state); | 417 | if (!drm_dev || !drm_dev->dev_private) { |
395 | } | 418 | dev_err(dev, "DRM not initialized, aborting suspend.\n"); |
419 | return -ENODEV; | ||
420 | } | ||
396 | 421 | ||
397 | static int | 422 | error = i915_drm_freeze(drm_dev); |
398 | i915_pci_resume(struct pci_dev *pdev) | 423 | if (error) |
399 | { | 424 | return error; |
400 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
401 | 425 | ||
402 | return i915_resume(dev); | 426 | i915_drm_suspend(drm_dev); |
403 | } | ||
404 | 427 | ||
405 | static int | 428 | pci_disable_device(pdev); |
406 | i915_pm_suspend(struct device *dev) | 429 | pci_set_power_state(pdev, PCI_D3hot); |
407 | { | ||
408 | return i915_pci_suspend(to_pci_dev(dev), PMSG_SUSPEND); | ||
409 | } | ||
410 | 430 | ||
411 | static int | 431 | return 0; |
412 | i915_pm_resume(struct device *dev) | ||
413 | { | ||
414 | return i915_pci_resume(to_pci_dev(dev)); | ||
415 | } | 432 | } |
416 | 433 | ||
417 | static int | 434 | static int i915_pm_resume(struct device *dev) |
418 | i915_pm_freeze(struct device *dev) | ||
419 | { | 435 | { |
420 | return i915_pci_suspend(to_pci_dev(dev), PMSG_FREEZE); | 436 | struct pci_dev *pdev = to_pci_dev(dev); |
437 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | ||
438 | |||
439 | return i915_resume(drm_dev); | ||
421 | } | 440 | } |
422 | 441 | ||
423 | static int | 442 | static int i915_pm_freeze(struct device *dev) |
424 | i915_pm_thaw(struct device *dev) | ||
425 | { | 443 | { |
426 | /* thaw during hibernate, do nothing! */ | 444 | struct pci_dev *pdev = to_pci_dev(dev); |
427 | return 0; | 445 | struct drm_device *drm_dev = pci_get_drvdata(pdev); |
446 | |||
447 | if (!drm_dev || !drm_dev->dev_private) { | ||
448 | dev_err(dev, "DRM not initialized, aborting suspend.\n"); | ||
449 | return -ENODEV; | ||
450 | } | ||
451 | |||
452 | return i915_drm_freeze(drm_dev); | ||
428 | } | 453 | } |
429 | 454 | ||
430 | static int | 455 | static int i915_pm_thaw(struct device *dev) |
431 | i915_pm_poweroff(struct device *dev) | ||
432 | { | 456 | { |
433 | return i915_pci_suspend(to_pci_dev(dev), PMSG_HIBERNATE); | 457 | struct pci_dev *pdev = to_pci_dev(dev); |
458 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | ||
459 | |||
460 | return i915_drm_thaw(drm_dev); | ||
434 | } | 461 | } |
435 | 462 | ||
436 | static int | 463 | static int i915_pm_poweroff(struct device *dev) |
437 | i915_pm_restore(struct device *dev) | ||
438 | { | 464 | { |
439 | return i915_pci_resume(to_pci_dev(dev)); | 465 | struct pci_dev *pdev = to_pci_dev(dev); |
466 | struct drm_device *drm_dev = pci_get_drvdata(pdev); | ||
467 | int error; | ||
468 | |||
469 | error = i915_drm_freeze(drm_dev); | ||
470 | if (!error) | ||
471 | i915_drm_suspend(drm_dev); | ||
472 | |||
473 | return error; | ||
440 | } | 474 | } |
441 | 475 | ||
442 | const struct dev_pm_ops i915_pm_ops = { | 476 | const struct dev_pm_ops i915_pm_ops = { |
@@ -445,7 +479,7 @@ const struct dev_pm_ops i915_pm_ops = { | |||
445 | .freeze = i915_pm_freeze, | 479 | .freeze = i915_pm_freeze, |
446 | .thaw = i915_pm_thaw, | 480 | .thaw = i915_pm_thaw, |
447 | .poweroff = i915_pm_poweroff, | 481 | .poweroff = i915_pm_poweroff, |
448 | .restore = i915_pm_restore, | 482 | .restore = i915_pm_resume, |
449 | }; | 483 | }; |
450 | 484 | ||
451 | static struct vm_operations_struct i915_gem_vm_ops = { | 485 | static struct vm_operations_struct i915_gem_vm_ops = { |