viafb: General power management infrastructure
Multiple devices need S/R hooks (framebuffer, GPIO, camera). Add infrastructure and convert existing framebuffer code to the new model. This patch should create no functional change. Based on earlier work by Jonathan Corbet. Signed-off-by: Daniel Drake <dsd@laptop.org> Acked-by: Jonathan Corbet <corbet@lwn.net> Signed-off-by: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
This commit is contained in:
Родитель
51f4332bb5
Коммит
751305d9b2
|
@ -15,6 +15,8 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
/*
|
||||
* The default port config.
|
||||
|
@ -563,6 +565,78 @@ static void via_teardown_subdevs(void)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Power management functions
|
||||
*/
|
||||
#ifdef CONFIG_PM
|
||||
static LIST_HEAD(viafb_pm_hooks);
|
||||
static DEFINE_MUTEX(viafb_pm_hooks_lock);
|
||||
|
||||
void viafb_pm_register(struct viafb_pm_hooks *hooks)
|
||||
{
|
||||
INIT_LIST_HEAD(&hooks->list);
|
||||
|
||||
mutex_lock(&viafb_pm_hooks_lock);
|
||||
list_add_tail(&hooks->list, &viafb_pm_hooks);
|
||||
mutex_unlock(&viafb_pm_hooks_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(viafb_pm_register);
|
||||
|
||||
void viafb_pm_unregister(struct viafb_pm_hooks *hooks)
|
||||
{
|
||||
mutex_lock(&viafb_pm_hooks_lock);
|
||||
list_del(&hooks->list);
|
||||
mutex_unlock(&viafb_pm_hooks_lock);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(viafb_pm_unregister);
|
||||
|
||||
static int via_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
{
|
||||
struct viafb_pm_hooks *hooks;
|
||||
|
||||
if (state.event != PM_EVENT_SUSPEND)
|
||||
return 0;
|
||||
/*
|
||||
* "I've occasionally hit a few drivers that caused suspend
|
||||
* failures, and each and every time it was a driver bug, and
|
||||
* the right thing to do was to just ignore the error and suspend
|
||||
* anyway - returning an error code and trying to undo the suspend
|
||||
* is not what anybody ever really wants, even if our model
|
||||
*_allows_ for it."
|
||||
* -- Linus Torvalds, Dec. 7, 2009
|
||||
*/
|
||||
mutex_lock(&viafb_pm_hooks_lock);
|
||||
list_for_each_entry_reverse(hooks, &viafb_pm_hooks, list)
|
||||
hooks->suspend(hooks->private);
|
||||
mutex_unlock(&viafb_pm_hooks_lock);
|
||||
|
||||
pci_save_state(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int via_resume(struct pci_dev *pdev)
|
||||
{
|
||||
struct viafb_pm_hooks *hooks;
|
||||
|
||||
/* Get the bus side powered up */
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
if (pci_enable_device(pdev))
|
||||
return 0;
|
||||
|
||||
pci_set_master(pdev);
|
||||
|
||||
/* Now bring back any subdevs */
|
||||
mutex_lock(&viafb_pm_hooks_lock);
|
||||
list_for_each_entry(hooks, &viafb_pm_hooks, list)
|
||||
hooks->resume(hooks->private);
|
||||
mutex_unlock(&viafb_pm_hooks_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static int __devinit via_pci_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *ent)
|
||||
|
@ -572,6 +646,7 @@ static int __devinit via_pci_probe(struct pci_dev *pdev,
|
|||
ret = pci_enable_device(pdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Global device initialization.
|
||||
*/
|
||||
|
@ -651,8 +726,8 @@ static struct pci_driver via_driver = {
|
|||
.probe = via_pci_probe,
|
||||
.remove = __devexit_p(via_pci_remove),
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = viafb_suspend,
|
||||
.resume = viafb_resume,
|
||||
.suspend = via_suspend,
|
||||
.resume = via_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -1672,31 +1672,19 @@ static int parse_mode(const char *str, u32 *xres, u32 *yres)
|
|||
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int viafb_suspend(struct pci_dev *pdev, pm_message_t state)
|
||||
static int viafb_suspend(void *unused)
|
||||
{
|
||||
if (state.event == PM_EVENT_SUSPEND) {
|
||||
acquire_console_sem();
|
||||
fb_set_suspend(viafbinfo, 1);
|
||||
|
||||
viafb_sync(viafbinfo);
|
||||
|
||||
pci_save_state(pdev);
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, pci_choose_state(pdev, state));
|
||||
release_console_sem();
|
||||
}
|
||||
acquire_console_sem();
|
||||
fb_set_suspend(viafbinfo, 1);
|
||||
viafb_sync(viafbinfo);
|
||||
release_console_sem();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int viafb_resume(struct pci_dev *pdev)
|
||||
static int viafb_resume(void *unused)
|
||||
{
|
||||
acquire_console_sem();
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
if (pci_enable_device(pdev))
|
||||
goto fail;
|
||||
pci_set_master(pdev);
|
||||
if (viaparinfo->shared->vdev->engine_mmio)
|
||||
viafb_reset_engine(viaparinfo);
|
||||
viafb_set_par(viafbinfo);
|
||||
|
@ -1704,11 +1692,15 @@ int viafb_resume(struct pci_dev *pdev)
|
|||
viafb_set_par(viafbinfo1);
|
||||
fb_set_suspend(viafbinfo, 0);
|
||||
|
||||
fail:
|
||||
release_console_sem();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct viafb_pm_hooks viafb_fb_pm_hooks = {
|
||||
.suspend = viafb_suspend,
|
||||
.resume = viafb_resume
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -1899,6 +1891,10 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev)
|
|||
|
||||
viafb_init_proc(viaparinfo->shared);
|
||||
viafb_init_dac(IGA2);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
viafb_pm_register(&viafb_fb_pm_hooks);
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
out_fb_unreg:
|
||||
|
|
|
@ -108,6 +108,4 @@ void via_fb_pci_remove(struct pci_dev *pdev);
|
|||
/* Temporary */
|
||||
int viafb_init(void);
|
||||
void viafb_exit(void);
|
||||
int viafb_suspend(struct pci_dev *pdev, pm_message_t state);
|
||||
int viafb_resume(struct pci_dev *pdev);
|
||||
#endif /* __VIAFBDEV_H__ */
|
||||
|
|
|
@ -59,6 +59,21 @@ struct via_port_cfg {
|
|||
u8 ioport_index;
|
||||
};
|
||||
|
||||
/*
|
||||
* Allow subdevs to register suspend/resume hooks.
|
||||
*/
|
||||
#ifdef CONFIG_PM
|
||||
struct viafb_pm_hooks {
|
||||
struct list_head list;
|
||||
int (*suspend)(void *private);
|
||||
int (*resume)(void *private);
|
||||
void *private;
|
||||
};
|
||||
|
||||
void viafb_pm_register(struct viafb_pm_hooks *hooks);
|
||||
void viafb_pm_unregister(struct viafb_pm_hooks *hooks);
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
/*
|
||||
* This is the global viafb "device" containing stuff needed by
|
||||
* all subdevs.
|
||||
|
|
Загрузка…
Ссылка в новой задаче