diff options
author | Ville Syrjala <syrjala@sci.fi> | 2009-06-30 14:41:40 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-30 21:56:01 -0400 |
commit | eafad22a05fdaca60f06433ffe8810aaa920d539 (patch) | |
tree | df799e81ce5b2e9d63dbece7d9c2f4beff83e618 /drivers/video/aty/atyfb_base.c | |
parent | 50efacf6711e6c75595afd9b92aa15c1e4f7c79d (diff) |
atyfb: fix HP OmniBook 500 reboot hang
Apparently HP OmniBook 500's BIOS doesn't like the way atyfb reprograms
the hardware. The BIOS will simply hang after a reboot. Fix the problem
by restoring the hardware to it's original state on reboot.
Signed-off-by: Ville Syrjala <syrjala@sci.fi>
Cc: Mikulas Patocka <mpatocka@redhat.com>
Cc: Krzysztof Helt <krzysztof.h1@poczta.fm>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video/aty/atyfb_base.c')
-rw-r--r-- | drivers/video/aty/atyfb_base.c | 89 |
1 files changed, 80 insertions, 9 deletions
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c index 1207c208a30b..06782906daf5 100644 --- a/drivers/video/aty/atyfb_base.c +++ b/drivers/video/aty/atyfb_base.c | |||
@@ -66,6 +66,8 @@ | |||
66 | #include <linux/spinlock.h> | 66 | #include <linux/spinlock.h> |
67 | #include <linux/wait.h> | 67 | #include <linux/wait.h> |
68 | #include <linux/backlight.h> | 68 | #include <linux/backlight.h> |
69 | #include <linux/reboot.h> | ||
70 | #include <linux/dmi.h> | ||
69 | 71 | ||
70 | #include <asm/io.h> | 72 | #include <asm/io.h> |
71 | #include <linux/uaccess.h> | 73 | #include <linux/uaccess.h> |
@@ -249,8 +251,6 @@ static int aty_init(struct fb_info *info); | |||
249 | static int store_video_par(char *videopar, unsigned char m64_num); | 251 | static int store_video_par(char *videopar, unsigned char m64_num); |
250 | #endif | 252 | #endif |
251 | 253 | ||
252 | static struct crtc saved_crtc; | ||
253 | static union aty_pll saved_pll; | ||
254 | static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc); | 254 | static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc); |
255 | 255 | ||
256 | static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc); | 256 | static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc); |
@@ -261,6 +261,8 @@ static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info); | |||
261 | static int read_aty_sense(const struct atyfb_par *par); | 261 | static int read_aty_sense(const struct atyfb_par *par); |
262 | #endif | 262 | #endif |
263 | 263 | ||
264 | static DEFINE_MUTEX(reboot_lock); | ||
265 | static struct fb_info *reboot_info; | ||
264 | 266 | ||
265 | /* | 267 | /* |
266 | * Interface used by the world | 268 | * Interface used by the world |
@@ -2390,9 +2392,9 @@ static int __devinit aty_init(struct fb_info *info) | |||
2390 | #endif /* CONFIG_FB_ATY_CT */ | 2392 | #endif /* CONFIG_FB_ATY_CT */ |
2391 | 2393 | ||
2392 | /* save previous video mode */ | 2394 | /* save previous video mode */ |
2393 | aty_get_crtc(par, &saved_crtc); | 2395 | aty_get_crtc(par, &par->saved_crtc); |
2394 | if(par->pll_ops->get_pll) | 2396 | if(par->pll_ops->get_pll) |
2395 | par->pll_ops->get_pll(info, &saved_pll); | 2397 | par->pll_ops->get_pll(info, &par->saved_pll); |
2396 | 2398 | ||
2397 | par->mem_cntl = aty_ld_le32(MEM_CNTL, par); | 2399 | par->mem_cntl = aty_ld_le32(MEM_CNTL, par); |
2398 | gtb_memsize = M64_HAS(GTB_DSP); | 2400 | gtb_memsize = M64_HAS(GTB_DSP); |
@@ -2667,8 +2669,8 @@ static int __devinit aty_init(struct fb_info *info) | |||
2667 | 2669 | ||
2668 | aty_init_exit: | 2670 | aty_init_exit: |
2669 | /* restore video mode */ | 2671 | /* restore video mode */ |
2670 | aty_set_crtc(par, &saved_crtc); | 2672 | aty_set_crtc(par, &par->saved_crtc); |
2671 | par->pll_ops->set_pll(info, &saved_pll); | 2673 | par->pll_ops->set_pll(info, &par->saved_pll); |
2672 | 2674 | ||
2673 | #ifdef CONFIG_MTRR | 2675 | #ifdef CONFIG_MTRR |
2674 | if (par->mtrr_reg >= 0) { | 2676 | if (par->mtrr_reg >= 0) { |
@@ -3502,6 +3504,11 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi | |||
3502 | par->mmap_map[1].prot_flag = _PAGE_E; | 3504 | par->mmap_map[1].prot_flag = _PAGE_E; |
3503 | #endif /* __sparc__ */ | 3505 | #endif /* __sparc__ */ |
3504 | 3506 | ||
3507 | mutex_lock(&reboot_lock); | ||
3508 | if (!reboot_info) | ||
3509 | reboot_info = info; | ||
3510 | mutex_unlock(&reboot_lock); | ||
3511 | |||
3505 | return 0; | 3512 | return 0; |
3506 | 3513 | ||
3507 | err_release_io: | 3514 | err_release_io: |
@@ -3614,8 +3621,8 @@ static void __devexit atyfb_remove(struct fb_info *info) | |||
3614 | struct atyfb_par *par = (struct atyfb_par *) info->par; | 3621 | struct atyfb_par *par = (struct atyfb_par *) info->par; |
3615 | 3622 | ||
3616 | /* restore video mode */ | 3623 | /* restore video mode */ |
3617 | aty_set_crtc(par, &saved_crtc); | 3624 | aty_set_crtc(par, &par->saved_crtc); |
3618 | par->pll_ops->set_pll(info, &saved_pll); | 3625 | par->pll_ops->set_pll(info, &par->saved_pll); |
3619 | 3626 | ||
3620 | unregister_framebuffer(info); | 3627 | unregister_framebuffer(info); |
3621 | 3628 | ||
@@ -3661,6 +3668,11 @@ static void __devexit atyfb_pci_remove(struct pci_dev *pdev) | |||
3661 | { | 3668 | { |
3662 | struct fb_info *info = pci_get_drvdata(pdev); | 3669 | struct fb_info *info = pci_get_drvdata(pdev); |
3663 | 3670 | ||
3671 | mutex_lock(&reboot_lock); | ||
3672 | if (reboot_info == info) | ||
3673 | reboot_info = NULL; | ||
3674 | mutex_unlock(&reboot_lock); | ||
3675 | |||
3664 | atyfb_remove(info); | 3676 | atyfb_remove(info); |
3665 | } | 3677 | } |
3666 | 3678 | ||
@@ -3808,6 +3820,56 @@ static int __init atyfb_setup(char *options) | |||
3808 | } | 3820 | } |
3809 | #endif /* MODULE */ | 3821 | #endif /* MODULE */ |
3810 | 3822 | ||
3823 | static int atyfb_reboot_notify(struct notifier_block *nb, | ||
3824 | unsigned long code, void *unused) | ||
3825 | { | ||
3826 | struct atyfb_par *par; | ||
3827 | |||
3828 | if (code != SYS_RESTART) | ||
3829 | return NOTIFY_DONE; | ||
3830 | |||
3831 | mutex_lock(&reboot_lock); | ||
3832 | |||
3833 | if (!reboot_info) | ||
3834 | goto out; | ||
3835 | |||
3836 | if (!lock_fb_info(reboot_info)) | ||
3837 | goto out; | ||
3838 | |||
3839 | par = reboot_info->par; | ||
3840 | |||
3841 | /* | ||
3842 | * HP OmniBook 500's BIOS doesn't like the state of the | ||
3843 | * hardware after atyfb has been used. Restore the hardware | ||
3844 | * to the original state to allow successful reboots. | ||
3845 | */ | ||
3846 | aty_set_crtc(par, &par->saved_crtc); | ||
3847 | par->pll_ops->set_pll(reboot_info, &par->saved_pll); | ||
3848 | |||
3849 | unlock_fb_info(reboot_info); | ||
3850 | out: | ||
3851 | mutex_unlock(&reboot_lock); | ||
3852 | |||
3853 | return NOTIFY_DONE; | ||
3854 | } | ||
3855 | |||
3856 | static struct notifier_block atyfb_reboot_notifier = { | ||
3857 | .notifier_call = atyfb_reboot_notify, | ||
3858 | }; | ||
3859 | |||
3860 | static const struct dmi_system_id atyfb_reboot_ids[] = { | ||
3861 | { | ||
3862 | .ident = "HP OmniBook 500", | ||
3863 | .matches = { | ||
3864 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), | ||
3865 | DMI_MATCH(DMI_PRODUCT_NAME, "HP OmniBook PC"), | ||
3866 | DMI_MATCH(DMI_PRODUCT_VERSION, "HP OmniBook 500 FA"), | ||
3867 | }, | ||
3868 | }, | ||
3869 | |||
3870 | { } | ||
3871 | }; | ||
3872 | |||
3811 | static int __init atyfb_init(void) | 3873 | static int __init atyfb_init(void) |
3812 | { | 3874 | { |
3813 | int err1 = 1, err2 = 1; | 3875 | int err1 = 1, err2 = 1; |
@@ -3826,11 +3888,20 @@ static int __init atyfb_init(void) | |||
3826 | err2 = atyfb_atari_probe(); | 3888 | err2 = atyfb_atari_probe(); |
3827 | #endif | 3889 | #endif |
3828 | 3890 | ||
3829 | return (err1 && err2) ? -ENODEV : 0; | 3891 | if (err1 && err2) |
3892 | return -ENODEV; | ||
3893 | |||
3894 | if (dmi_check_system(atyfb_reboot_ids)) | ||
3895 | register_reboot_notifier(&atyfb_reboot_notifier); | ||
3896 | |||
3897 | return 0; | ||
3830 | } | 3898 | } |
3831 | 3899 | ||
3832 | static void __exit atyfb_exit(void) | 3900 | static void __exit atyfb_exit(void) |
3833 | { | 3901 | { |
3902 | if (dmi_check_system(atyfb_reboot_ids)) | ||
3903 | unregister_reboot_notifier(&atyfb_reboot_notifier); | ||
3904 | |||
3834 | #ifdef CONFIG_PCI | 3905 | #ifdef CONFIG_PCI |
3835 | pci_unregister_driver(&atyfb_driver); | 3906 | pci_unregister_driver(&atyfb_driver); |
3836 | #endif | 3907 | #endif |