Skip to content

Commit

Permalink
atyfb: fix HP OmniBook 500 reboot hang
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ville Syrjala authored and Linus Torvalds committed Jul 1, 2009
1 parent 50efacf commit eafad22
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 9 deletions.
2 changes: 2 additions & 0 deletions drivers/video/aty/atyfb.h
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,8 @@ struct atyfb_par {
int mtrr_reg;
#endif
u32 mem_cntl;
struct crtc saved_crtc;
union aty_pll saved_pll;
};

/*
Expand Down
89 changes: 80 additions & 9 deletions drivers/video/aty/atyfb_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <linux/backlight.h>
#include <linux/reboot.h>
#include <linux/dmi.h>

#include <asm/io.h>
#include <linux/uaccess.h>
Expand Down Expand Up @@ -249,8 +251,6 @@ static int aty_init(struct fb_info *info);
static int store_video_par(char *videopar, unsigned char m64_num);
#endif

static struct crtc saved_crtc;
static union aty_pll saved_pll;
static void aty_get_crtc(const struct atyfb_par *par, struct crtc *crtc);

static void aty_set_crtc(const struct atyfb_par *par, const struct crtc *crtc);
Expand All @@ -261,6 +261,8 @@ static void set_off_pitch(struct atyfb_par *par, const struct fb_info *info);
static int read_aty_sense(const struct atyfb_par *par);
#endif

static DEFINE_MUTEX(reboot_lock);
static struct fb_info *reboot_info;

/*
* Interface used by the world
Expand Down Expand Up @@ -2390,9 +2392,9 @@ static int __devinit aty_init(struct fb_info *info)
#endif /* CONFIG_FB_ATY_CT */

/* save previous video mode */
aty_get_crtc(par, &saved_crtc);
aty_get_crtc(par, &par->saved_crtc);
if(par->pll_ops->get_pll)
par->pll_ops->get_pll(info, &saved_pll);
par->pll_ops->get_pll(info, &par->saved_pll);

par->mem_cntl = aty_ld_le32(MEM_CNTL, par);
gtb_memsize = M64_HAS(GTB_DSP);
Expand Down Expand Up @@ -2667,8 +2669,8 @@ static int __devinit aty_init(struct fb_info *info)

aty_init_exit:
/* restore video mode */
aty_set_crtc(par, &saved_crtc);
par->pll_ops->set_pll(info, &saved_pll);
aty_set_crtc(par, &par->saved_crtc);
par->pll_ops->set_pll(info, &par->saved_pll);

#ifdef CONFIG_MTRR
if (par->mtrr_reg >= 0) {
Expand Down Expand Up @@ -3502,6 +3504,11 @@ static int __devinit atyfb_pci_probe(struct pci_dev *pdev, const struct pci_devi
par->mmap_map[1].prot_flag = _PAGE_E;
#endif /* __sparc__ */

mutex_lock(&reboot_lock);
if (!reboot_info)
reboot_info = info;
mutex_unlock(&reboot_lock);

return 0;

err_release_io:
Expand Down Expand Up @@ -3614,8 +3621,8 @@ static void __devexit atyfb_remove(struct fb_info *info)
struct atyfb_par *par = (struct atyfb_par *) info->par;

/* restore video mode */
aty_set_crtc(par, &saved_crtc);
par->pll_ops->set_pll(info, &saved_pll);
aty_set_crtc(par, &par->saved_crtc);
par->pll_ops->set_pll(info, &par->saved_pll);

unregister_framebuffer(info);

Expand Down Expand Up @@ -3661,6 +3668,11 @@ static void __devexit atyfb_pci_remove(struct pci_dev *pdev)
{
struct fb_info *info = pci_get_drvdata(pdev);

mutex_lock(&reboot_lock);
if (reboot_info == info)
reboot_info = NULL;
mutex_unlock(&reboot_lock);

atyfb_remove(info);
}

Expand Down Expand Up @@ -3808,6 +3820,56 @@ static int __init atyfb_setup(char *options)
}
#endif /* MODULE */

static int atyfb_reboot_notify(struct notifier_block *nb,
unsigned long code, void *unused)
{
struct atyfb_par *par;

if (code != SYS_RESTART)
return NOTIFY_DONE;

mutex_lock(&reboot_lock);

if (!reboot_info)
goto out;

if (!lock_fb_info(reboot_info))
goto out;

par = reboot_info->par;

/*
* HP OmniBook 500's BIOS doesn't like the state of the
* hardware after atyfb has been used. Restore the hardware
* to the original state to allow successful reboots.
*/
aty_set_crtc(par, &par->saved_crtc);
par->pll_ops->set_pll(reboot_info, &par->saved_pll);

unlock_fb_info(reboot_info);
out:
mutex_unlock(&reboot_lock);

return NOTIFY_DONE;
}

static struct notifier_block atyfb_reboot_notifier = {
.notifier_call = atyfb_reboot_notify,
};

static const struct dmi_system_id atyfb_reboot_ids[] = {
{
.ident = "HP OmniBook 500",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
DMI_MATCH(DMI_PRODUCT_NAME, "HP OmniBook PC"),
DMI_MATCH(DMI_PRODUCT_VERSION, "HP OmniBook 500 FA"),
},
},

{ }
};

static int __init atyfb_init(void)
{
int err1 = 1, err2 = 1;
Expand All @@ -3826,11 +3888,20 @@ static int __init atyfb_init(void)
err2 = atyfb_atari_probe();
#endif

return (err1 && err2) ? -ENODEV : 0;
if (err1 && err2)
return -ENODEV;

if (dmi_check_system(atyfb_reboot_ids))
register_reboot_notifier(&atyfb_reboot_notifier);

return 0;
}

static void __exit atyfb_exit(void)
{
if (dmi_check_system(atyfb_reboot_ids))
unregister_reboot_notifier(&atyfb_reboot_notifier);

#ifdef CONFIG_PCI
pci_unregister_driver(&atyfb_driver);
#endif
Expand Down

0 comments on commit eafad22

Please sign in to comment.