Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Ville Syrjala and committed by
Linus Torvalds
eafad22a 50efacf6

+82 -9
+2
drivers/video/aty/atyfb.h
··· 187 187 int mtrr_reg; 188 188 #endif 189 189 u32 mem_cntl; 190 + struct crtc saved_crtc; 191 + union aty_pll saved_pll; 190 192 }; 191 193 192 194 /*
+80 -9
drivers/video/aty/atyfb_base.c
··· 66 66 #include <linux/spinlock.h> 67 67 #include <linux/wait.h> 68 68 #include <linux/backlight.h> 69 + #include <linux/reboot.h> 70 + #include <linux/dmi.h> 69 71 70 72 #include <asm/io.h> 71 73 #include <linux/uaccess.h> ··· 251 249 static int store_video_par(char *videopar, unsigned char m64_num); 252 250 #endif 253 251 254 - static struct crtc saved_crtc; 255 - static union aty_pll saved_pll; 256 252 static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc); 257 253 258 254 static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc); ··· 261 261 static int read_aty_sense(const struct atyfb_par *par); 262 262 #endif 263 263 264 + static DEFINE_MUTEX(reboot_lock); 265 + static struct fb_info *reboot_info; 264 266 265 267 /* 266 268 * Interface used by the world ··· 2392 2390 #endif /* CONFIG_FB_ATY_CT */ 2393 2391 2394 2392 /* save previous video mode */ 2395 - aty_get_crtc(par, &saved_crtc); 2393 + aty_get_crtc(par, &par->saved_crtc); 2396 2394 if(par->pll_ops->get_pll) 2397 - par->pll_ops->get_pll(info, &saved_pll); 2395 + par->pll_ops->get_pll(info, &par->saved_pll); 2398 2396 2399 2397 par->mem_cntl = aty_ld_le32(MEM_CNTL, par); 2400 2398 gtb_memsize = M64_HAS(GTB_DSP); ··· 2669 2667 2670 2668 aty_init_exit: 2671 2669 /* restore video mode */ 2672 - aty_set_crtc(par, &saved_crtc); 2673 - par->pll_ops->set_pll(info, &saved_pll); 2670 + aty_set_crtc(par, &par->saved_crtc); 2671 + par->pll_ops->set_pll(info, &par->saved_pll); 2674 2672 2675 2673 #ifdef CONFIG_MTRR 2676 2674 if (par->mtrr_reg >= 0) { ··· 3504 3502 par->mmap_map[1].prot_flag = _PAGE_E; 3505 3503 #endif /* __sparc__ */ 3506 3504 3505 + mutex_lock(&reboot_lock); 3506 + if (!reboot_info) 3507 + reboot_info = info; 3508 + mutex_unlock(&reboot_lock); 3509 + 3507 3510 return 0; 3508 3511 3509 3512 err_release_io: ··· 3621 3614 struct atyfb_par *par = (struct atyfb_par *) info->par; 3622 3615 3623 3616 /* restore video mode */ 3624 - aty_set_crtc(par, &saved_crtc); 3625 - par->pll_ops->set_pll(info, &saved_pll); 3617 + aty_set_crtc(par, &par->saved_crtc); 3618 + par->pll_ops->set_pll(info, &par->saved_pll); 3626 3619 3627 3620 unregister_framebuffer(info); 3628 3621 ··· 3667 3660 static void __devexit atyfb_pci_remove(struct pci_dev *pdev) 3668 3661 { 3669 3662 struct fb_info *info = pci_get_drvdata(pdev); 3663 + 3664 + mutex_lock(&reboot_lock); 3665 + if (reboot_info == info) 3666 + reboot_info = NULL; 3667 + mutex_unlock(&reboot_lock); 3670 3668 3671 3669 atyfb_remove(info); 3672 3670 } ··· 3820 3808 } 3821 3809 #endif /* MODULE */ 3822 3810 3811 + static int atyfb_reboot_notify(struct notifier_block *nb, 3812 + unsigned long code, void *unused) 3813 + { 3814 + struct atyfb_par *par; 3815 + 3816 + if (code != SYS_RESTART) 3817 + return NOTIFY_DONE; 3818 + 3819 + mutex_lock(&reboot_lock); 3820 + 3821 + if (!reboot_info) 3822 + goto out; 3823 + 3824 + if (!lock_fb_info(reboot_info)) 3825 + goto out; 3826 + 3827 + par = reboot_info->par; 3828 + 3829 + /* 3830 + * HP OmniBook 500's BIOS doesn't like the state of the 3831 + * hardware after atyfb has been used. Restore the hardware 3832 + * to the original state to allow successful reboots. 3833 + */ 3834 + aty_set_crtc(par, &par->saved_crtc); 3835 + par->pll_ops->set_pll(reboot_info, &par->saved_pll); 3836 + 3837 + unlock_fb_info(reboot_info); 3838 + out: 3839 + mutex_unlock(&reboot_lock); 3840 + 3841 + return NOTIFY_DONE; 3842 + } 3843 + 3844 + static struct notifier_block atyfb_reboot_notifier = { 3845 + .notifier_call = atyfb_reboot_notify, 3846 + }; 3847 + 3848 + static const struct dmi_system_id atyfb_reboot_ids[] = { 3849 + { 3850 + .ident = "HP OmniBook 500", 3851 + .matches = { 3852 + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), 3853 + DMI_MATCH(DMI_PRODUCT_NAME, "HP OmniBook PC"), 3854 + DMI_MATCH(DMI_PRODUCT_VERSION, "HP OmniBook 500 FA"), 3855 + }, 3856 + }, 3857 + 3858 + { } 3859 + }; 3860 + 3823 3861 static int __init atyfb_init(void) 3824 3862 { 3825 3863 int err1 = 1, err2 = 1; ··· 3888 3826 err2 = atyfb_atari_probe(); 3889 3827 #endif 3890 3828 3891 - return (err1 && err2) ? -ENODEV : 0; 3829 + if (err1 && err2) 3830 + return -ENODEV; 3831 + 3832 + if (dmi_check_system(atyfb_reboot_ids)) 3833 + register_reboot_notifier(&atyfb_reboot_notifier); 3834 + 3835 + return 0; 3892 3836 } 3893 3837 3894 3838 static void __exit atyfb_exit(void) 3895 3839 { 3840 + if (dmi_check_system(atyfb_reboot_ids)) 3841 + unregister_reboot_notifier(&atyfb_reboot_notifier); 3842 + 3896 3843 #ifdef CONFIG_PCI 3897 3844 pci_unregister_driver(&atyfb_driver); 3898 3845 #endif