Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit efc08a75 authored by Ville Syrjala's avatar Ville Syrjala Committed by Linus Torvalds
Browse files

[PATCH] atyfb: Improve power management



Some register were only set in aty_init() which is not called on resume.  I
added the aty_resume_chip() function to reset those registers on resume.

Susped-to-ram now works on a HP Omnibook 6000 with acpi_sleep=s3_bios.

Signed-off-by: default avatarVille Syrjala <syrjala@sci.fi>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 4ec3fd71
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -185,6 +185,7 @@ struct atyfb_par {
	int mtrr_aper;
	int mtrr_aper;
	int mtrr_reg;
	int mtrr_reg;
#endif
#endif
	u32 mem_cntl;
};
};


    /*
    /*
@@ -314,6 +315,7 @@ struct aty_pll_ops {
	void (*set_pll)   (const struct fb_info * info, const union aty_pll * pll);
	void (*set_pll)   (const struct fb_info * info, const union aty_pll * pll);
	void (*get_pll)   (const struct fb_info *info, union aty_pll * pll);
	void (*get_pll)   (const struct fb_info *info, union aty_pll * pll);
	int (*init_pll)   (const struct fb_info * info, union aty_pll * pll);
	int (*init_pll)   (const struct fb_info * info, union aty_pll * pll);
	void (*resume_pll)(const struct fb_info *info, union aty_pll *pll);
};
};


extern const struct aty_pll_ops aty_pll_ati18818_1; /* ATI 18818 */
extern const struct aty_pll_ops aty_pll_ati18818_1; /* ATI 18818 */
+40 −21
Original line number Original line Diff line number Diff line
@@ -242,6 +242,7 @@ static int atyfb_sync(struct fb_info *info);
     */
     */


static int aty_init(struct fb_info *info);
static int aty_init(struct fb_info *info);
static void aty_resume_chip(struct fb_info *info);
#ifdef CONFIG_ATARI
#ifdef CONFIG_ATARI
static int store_video_par(char *videopar, unsigned char m64_num);
static int store_video_par(char *videopar, unsigned char m64_num);
#endif
#endif
@@ -1971,6 +1972,7 @@ static void atyfb_palette(int enter)


#if defined(CONFIG_PM) && defined(CONFIG_PCI)
#if defined(CONFIG_PM) && defined(CONFIG_PCI)


#ifdef CONFIG_PPC_PMAC
/* Power management routines. Those are used for PowerBook sleep.
/* Power management routines. Those are used for PowerBook sleep.
 */
 */
static int aty_power_mgmt(int sleep, struct atyfb_par *par)
static int aty_power_mgmt(int sleep, struct atyfb_par *par)
@@ -2027,21 +2029,13 @@ static int aty_power_mgmt(int sleep, struct atyfb_par *par)


	return timeout ? 0 : -EIO;
	return timeout ? 0 : -EIO;
}
}
#endif


static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
{
{
	struct fb_info *info = pci_get_drvdata(pdev);
	struct fb_info *info = pci_get_drvdata(pdev);
	struct atyfb_par *par = (struct atyfb_par *) info->par;
	struct atyfb_par *par = (struct atyfb_par *) info->par;


#ifndef CONFIG_PPC_PMAC
	/* HACK ALERT ! Once I find a proper way to say to each driver
	 * individually what will happen with it's PCI slot, I'll change
	 * that. On laptops, the AGP slot is just unclocked, so D2 is
	 * expected, while on desktops, the card is powered off
	 */
	return 0;
#endif /* CONFIG_PPC_PMAC */

	if (state.event == pdev->dev.power.power_state.event)
	if (state.event == pdev->dev.power.power_state.event)
		return 0;
		return 0;


@@ -2059,6 +2053,7 @@ static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
	par->asleep = 1;
	par->asleep = 1;
	par->lock_blank = 1;
	par->lock_blank = 1;


#ifdef CONFIG_PPC_PMAC
	/* Set chip to "suspend" mode */
	/* Set chip to "suspend" mode */
	if (aty_power_mgmt(1, par)) {
	if (aty_power_mgmt(1, par)) {
		par->asleep = 0;
		par->asleep = 0;
@@ -2068,6 +2063,9 @@ static int atyfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
		release_console_sem();
		release_console_sem();
		return -EIO;
		return -EIO;
	}
	}
#else
	pci_set_power_state(pdev, pci_choose_state(pdev, state));
#endif


	release_console_sem();
	release_console_sem();


@@ -2086,8 +2084,15 @@ static int atyfb_pci_resume(struct pci_dev *pdev)


	acquire_console_sem();
	acquire_console_sem();


#ifdef CONFIG_PPC_PMAC
	if (pdev->dev.power.power_state.event == 2)
	if (pdev->dev.power.power_state.event == 2)
		aty_power_mgmt(0, par);
		aty_power_mgmt(0, par);
#else
	pci_set_power_state(pdev, PCI_D0);
#endif

	aty_resume_chip(info);

	par->asleep = 0;
	par->asleep = 0;


	/* Restore display */
	/* Restore display */
@@ -2339,7 +2344,6 @@ static int __devinit aty_init(struct fb_info *info)
	const char *ramname = NULL, *xtal;
	const char *ramname = NULL, *xtal;
	int gtb_memsize, has_var = 0;
	int gtb_memsize, has_var = 0;
	struct fb_var_screeninfo var;
	struct fb_var_screeninfo var;
	u32 i;


	init_waitqueue_head(&par->vblank.wait);
	init_waitqueue_head(&par->vblank.wait);
	spin_lock_init(&par->int_lock);
	spin_lock_init(&par->int_lock);
@@ -2470,10 +2474,10 @@ static int __devinit aty_init(struct fb_info *info)
	if(par->pll_ops->get_pll)
	if(par->pll_ops->get_pll)
		par->pll_ops->get_pll(info, &saved_pll);
		par->pll_ops->get_pll(info, &saved_pll);


	i = aty_ld_le32(MEM_CNTL, par);
	par->mem_cntl = aty_ld_le32(MEM_CNTL, par);
	gtb_memsize = M64_HAS(GTB_DSP);
	gtb_memsize = M64_HAS(GTB_DSP);
	if (gtb_memsize)
	if (gtb_memsize)
		switch (i & 0xF) {	/* 0xF used instead of MEM_SIZE_ALIAS */
		switch (par->mem_cntl & 0xF) {	/* 0xF used instead of MEM_SIZE_ALIAS */
		case MEM_SIZE_512K:
		case MEM_SIZE_512K:
			info->fix.smem_len = 0x80000;
			info->fix.smem_len = 0x80000;
			break;
			break;
@@ -2495,7 +2499,7 @@ static int __devinit aty_init(struct fb_info *info)
		default:
		default:
			info->fix.smem_len = 0x80000;
			info->fix.smem_len = 0x80000;
	} else
	} else
		switch (i & MEM_SIZE_ALIAS) {
		switch (par->mem_cntl & MEM_SIZE_ALIAS) {
		case MEM_SIZE_512K:
		case MEM_SIZE_512K:
			info->fix.smem_len = 0x80000;
			info->fix.smem_len = 0x80000;
			break;
			break;
@@ -2525,20 +2529,20 @@ static int __devinit aty_init(struct fb_info *info)


	if (vram) {
	if (vram) {
		info->fix.smem_len = vram * 1024;
		info->fix.smem_len = vram * 1024;
		i = i & ~(gtb_memsize ? 0xF : MEM_SIZE_ALIAS);
		par->mem_cntl &= ~(gtb_memsize ? 0xF : MEM_SIZE_ALIAS);
		if (info->fix.smem_len <= 0x80000)
		if (info->fix.smem_len <= 0x80000)
			i |= MEM_SIZE_512K;
			par->mem_cntl |= MEM_SIZE_512K;
		else if (info->fix.smem_len <= 0x100000)
		else if (info->fix.smem_len <= 0x100000)
			i |= MEM_SIZE_1M;
			par->mem_cntl |= MEM_SIZE_1M;
		else if (info->fix.smem_len <= 0x200000)
		else if (info->fix.smem_len <= 0x200000)
			i |= gtb_memsize ? MEM_SIZE_2M_GTB : MEM_SIZE_2M;
			par->mem_cntl |= gtb_memsize ? MEM_SIZE_2M_GTB : MEM_SIZE_2M;
		else if (info->fix.smem_len <= 0x400000)
		else if (info->fix.smem_len <= 0x400000)
			i |= gtb_memsize ? MEM_SIZE_4M_GTB : MEM_SIZE_4M;
			par->mem_cntl |= gtb_memsize ? MEM_SIZE_4M_GTB : MEM_SIZE_4M;
		else if (info->fix.smem_len <= 0x600000)
		else if (info->fix.smem_len <= 0x600000)
			i |= gtb_memsize ? MEM_SIZE_6M_GTB : MEM_SIZE_6M;
			par->mem_cntl |= gtb_memsize ? MEM_SIZE_6M_GTB : MEM_SIZE_6M;
		else
		else
			i |= gtb_memsize ? MEM_SIZE_8M_GTB : MEM_SIZE_8M;
			par->mem_cntl |= gtb_memsize ? MEM_SIZE_8M_GTB : MEM_SIZE_8M;
		aty_st_le32(MEM_CNTL, i, par);
		aty_st_le32(MEM_CNTL, par->mem_cntl, par);
	}
	}


	/*
	/*
@@ -2584,6 +2588,8 @@ static int __devinit aty_init(struct fb_info *info)
#endif
#endif
	if(par->pll_ops->init_pll)
	if(par->pll_ops->init_pll)
		par->pll_ops->init_pll(info, &par->pll);
		par->pll_ops->init_pll(info, &par->pll);
	if (par->pll_ops->resume_pll)
		par->pll_ops->resume_pll(info, &par->pll);


	/*
	/*
	 *  Last page of 8 MB (4 MB on ISA) aperture is MMIO,
	 *  Last page of 8 MB (4 MB on ISA) aperture is MMIO,
@@ -2755,6 +2761,19 @@ static int __devinit aty_init(struct fb_info *info)
	return -1;
	return -1;
}
}


static void aty_resume_chip(struct fb_info *info)
{
	struct atyfb_par *par = info->par;

	aty_st_le32(MEM_CNTL, par->mem_cntl, par);

	if (par->pll_ops->resume_pll)
		par->pll_ops->resume_pll(info, &par->pll);

	if (par->aux_start)
		aty_st_le32(BUS_CNTL, aty_ld_le32(BUS_CNTL, par) | BUS_APER_REG_DIS, par);
}

#ifdef CONFIG_ATARI
#ifdef CONFIG_ATARI
static int __devinit store_video_par(char *video_str, unsigned char m64_num)
static int __devinit store_video_par(char *video_str, unsigned char m64_num)
{
{
+26 −13
Original line number Original line Diff line number Diff line
@@ -398,8 +398,8 @@ static int __devinit aty_init_pll_ct(const struct fb_info *info,
				     union aty_pll *pll)
				     union aty_pll *pll)
{
{
	struct atyfb_par *par = (struct atyfb_par *) info->par;
	struct atyfb_par *par = (struct atyfb_par *) info->par;
	u8 mpost_div, xpost_div, sclk_post_div_real, sclk_fb_div, spll_cntl2;
	u8 mpost_div, xpost_div, sclk_post_div_real;
	u32 q, i, memcntl, trp;
	u32 q, memcntl, trp;
	u32 dsp_config, dsp_on_off, vga_dsp_config, vga_dsp_on_off;
	u32 dsp_config, dsp_on_off, vga_dsp_config, vga_dsp_on_off;
#ifdef DEBUG
#ifdef DEBUG
	int pllmclk, pllsclk;
	int pllmclk, pllsclk;
@@ -575,14 +575,30 @@ static int __devinit aty_init_pll_ct(const struct fb_info *info,
			mpost_div += (q <  32*8);
			mpost_div += (q <  32*8);
		}
		}
		sclk_post_div_real = postdividers[mpost_div];
		sclk_post_div_real = postdividers[mpost_div];
		sclk_fb_div = q * sclk_post_div_real / 8;
		pll->ct.sclk_fb_div = q * sclk_post_div_real / 8;
		spll_cntl2 = mpost_div << 4;
		pll->ct.spll_cntl2 = mpost_div << 4;
#ifdef DEBUG
#ifdef DEBUG
		pllsclk = (1000000 * 2 * sclk_fb_div) /
		pllsclk = (1000000 * 2 * pll->ct.sclk_fb_div) /
			(par->ref_clk_per * pll->ct.pll_ref_div);
			(par->ref_clk_per * pll->ct.pll_ref_div);
		printk("atyfb(%s): use sclk, pllsclk=%d MHz, sclk=mclk=%d MHz\n",
		printk("atyfb(%s): use sclk, pllsclk=%d MHz, sclk=mclk=%d MHz\n",
			__FUNCTION__, pllsclk, pllsclk / sclk_post_div_real);
			__FUNCTION__, pllsclk, pllsclk / sclk_post_div_real);
#endif
#endif
	}

	/* Disable the extra precision pixel clock controls since we do not use them. */
	pll->ct.ext_vpll_cntl = aty_ld_pll_ct(EXT_VPLL_CNTL, par);
	pll->ct.ext_vpll_cntl &= ~(EXT_VPLL_EN | EXT_VPLL_VGA_EN | EXT_VPLL_INSYNC);

	return 0;
}

static void aty_resume_pll_ct(const struct fb_info *info,
			      union aty_pll *pll)
{
	struct atyfb_par *par = info->par;

	if (par->mclk_per != par->xclk_per) {
		int i;
		/*
		/*
		* This disables the sclk, crashes the computer as reported:
		* This disables the sclk, crashes the computer as reported:
		* aty_st_pll_ct(SPLL_CNTL2, 3, info);
		* aty_st_pll_ct(SPLL_CNTL2, 3, info);
@@ -590,8 +606,8 @@ static int __devinit aty_init_pll_ct(const struct fb_info *info,
		* So it seems the sclk must be enabled before it is used;
		* So it seems the sclk must be enabled before it is used;
		* so PLL_GEN_CNTL must be programmed *after* the sclk.
		* so PLL_GEN_CNTL must be programmed *after* the sclk.
		*/
		*/
		aty_st_pll_ct(SCLK_FB_DIV, sclk_fb_div, par);
		aty_st_pll_ct(SCLK_FB_DIV, pll->ct.sclk_fb_div, par);
		aty_st_pll_ct(SPLL_CNTL2, spll_cntl2, par);
		aty_st_pll_ct(SPLL_CNTL2, pll->ct.spll_cntl2, par);
		/*
		/*
		 * The sclk has been started. However, I believe the first clock
		 * The sclk has been started. However, I believe the first clock
		 * ticks it generates are not very stable. Hope this primitive loop
		 * ticks it generates are not very stable. Hope this primitive loop
@@ -605,11 +621,7 @@ static int __devinit aty_init_pll_ct(const struct fb_info *info,
	aty_st_pll_ct(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par);
	aty_st_pll_ct(PLL_GEN_CNTL, pll->ct.pll_gen_cntl, par);
	aty_st_pll_ct(MCLK_FB_DIV, pll->ct.mclk_fb_div, par);
	aty_st_pll_ct(MCLK_FB_DIV, pll->ct.mclk_fb_div, par);
	aty_st_pll_ct(PLL_EXT_CNTL, pll->ct.pll_ext_cntl, par);
	aty_st_pll_ct(PLL_EXT_CNTL, pll->ct.pll_ext_cntl, par);
	/* Disable the extra precision pixel clock controls since we do not use them. */
	aty_st_pll_ct(EXT_VPLL_CNTL, pll->ct.ext_vpll_cntl, par);
	aty_st_pll_ct(EXT_VPLL_CNTL, aty_ld_pll_ct(EXT_VPLL_CNTL, par) &
		~(EXT_VPLL_EN | EXT_VPLL_VGA_EN | EXT_VPLL_INSYNC), par);

	return 0;
}
}


static int dummy(void)
static int dummy(void)
@@ -626,5 +638,6 @@ const struct aty_pll_ops aty_pll_ct = {
	.pll_to_var	= aty_pll_to_var_ct,
	.pll_to_var	= aty_pll_to_var_ct,
	.set_pll	= aty_set_pll_ct,
	.set_pll	= aty_set_pll_ct,
	.get_pll	= aty_get_pll_ct,
	.get_pll	= aty_get_pll_ct,
	.init_pll       = aty_init_pll_ct
	.init_pll	= aty_init_pll_ct,
	.resume_pll	= aty_resume_pll_ct,
};
};