
/* power mangement control taken from Benjamin Herrenschmidt and ATI */
/* reworked for xfree86 by Alex Deucher */

#include "radeon_pm.h"


static void RADEONPMDisableDynamicMode(ScrnInfoPtr pScrn)
{
	RADEONInfoPtr  info   = RADEONPTR(pScrn); 
	unsigned long sclk_cntl;
	uusigned long mclk_cntl;
	unsigned long sclk_more_cntl;
	
	unsigned long vclk_ecp_cntl;
	unsigend long pixclks_cntl;

	/* Mobility chips only */
	if ((info->ChipFamily != CHIP_FAMILY_M6) && 
		(info->ChipFamily != CHIP_FAMILY_M7) && 
		(info->ChipFamily != CHIP_FAMILY_M9))
		return;
	
	/* Force Core Clocks */
	sclk_cntl = INPLL(RADEON_SCLK_CNTL);
	sclk_cntl |= 	SCLK_CNTL_M6__FORCE_CP|
			SCLK_CNTL_M6__FORCE_HDP|
			SCLK_CNTL_M6__FORCE_DISP1|
			SCLK_CNTL_M6__FORCE_DISP2|
			SCLK_CNTL_M6__FORCE_TOP|
			SCLK_CNTL_M6__FORCE_E2|
			SCLK_CNTL_M6__FORCE_SE|
			SCLK_CNTL_M6__FORCE_IDCT|
			SCLK_CNTL_M6__FORCE_VIP|
			SCLK_CNTL_M6__FORCE_RE|
			SCLK_CNTL_M6__FORCE_PB|
			SCLK_CNTL_M6__FORCE_TAM|
			SCLK_CNTL_M6__FORCE_TDM|
			SCLK_CNTL_M6__FORCE_RB|
			SCLK_CNTL_M6__FORCE_TV_SCLK|
			SCLK_CNTL_M6__FORCE_SUBPIC|
			SCLK_CNTL_M6__FORCE_OV0;
    	OUTPLL(RADEON_SCLK_CNTL, sclk_cntl);
	
	
	
	sclk_more_cntl = INPLL(RADEON_SCLK_MORE_CNTL);
	sclk_more_cntl |= 	SCLK_MORE_CNTL__FORCE_DISPREGS|

				SCLK_MORE_CNTL__FORCE_MC_GUI|
				SCLK_MORE_CNTL__FORCE_MC_HOST;	
	OUTPLL(RADEON_SCLK_MORE_CNTL, sclk_more_cntl);
	
	/* Force Display clocks	*/
	vclk_ecp_cntl = INPLL(RADEON_VCLK_ECP_CNTL);
	vclk_ecp_cntl &= ~(	VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb |
			 	VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb);
 
	OUTPLL(RADEON_VCLK_ECP_CNTL, vclk_ecp_cntl);
	
	pixclks_cntl = INPLL(RADEON_PIXCLKS_CNTL);
	pixclks_cntl &= ~(	PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb |
			 	PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
				PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb |
				PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
				PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb|
				PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb|
				PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb);
						
 	OUTPLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);

	/* Force Memory Clocks */
	mclk_cntl  = INPLL(RADEON_MCLK_CNTL);
	mclk_cntl |= 	MCLK_CNTL_M6__FORCE_MCLKA|  
			MCLK_CNTL_M6__FORCE_MCLKB|	
			MCLK_CNTL_M6__FORCE_YCLKA|
			MCLK_CNTL_M6__FORCE_YCLKB;
			
    	OUTPLL(RADEON_MCLK_CNTL, mclk_cntl);
}

static void RADEONPMEnableDynamicMode(ScrnInfoPtr pScrn)
{
    	RADEONInfoPtr  info   = RADEONPTR(pScrn);
	unsigned long clk_pwrmgt_cntl;
	unsigned long sclk_cntl;
	unsigned long sclk_more_cntl;
	unsigned long clk_pin_cntl;
	unsigned long pixclks_cntl;
	unsigned long vclk_ecp_cntl;
	unsigned long mclk_cntl;
	unsigned long mclk_misc;

        /* Mobility chips only */
        if ((info->ChipFamily != CHIP_FAMILY_M6) &&
                (info->ChipFamily != CHIP_FAMILY_M7) &&
                (info->ChipFamily != CHIP_FAMILY_M9))
                return;
	
	/* Set Latencies */
	clk_pwrmgt_cntl = INPLL(RADEON_CLK_PWRMGT_CNTL_M6);
	
	clk_pwrmgt_cntl &= ~(	 CLK_PWRMGT_CNTL_M6__ENGINE_DYNCLK_MODE_MASK|
				 CLK_PWRMGT_CNTL_M6__ACTIVE_HILO_LAT_MASK|
				 CLK_PWRMGT_CNTL_M6__DISP_DYN_STOP_LAT_MASK|
				 CLK_PWRMGT_CNTL_M6__DYN_STOP_MODE_MASK);
	/* Mode 1 */
	clk_pwrmgt_cntl = 	CLK_PWRMGT_CNTL_M6__MC_CH_MODE|
				CLK_PWRMGT_CNTL_M6__ENGINE_DYNCLK_MODE | 
				(1<<CLK_PWRMGT_CNTL_M6__ACTIVE_HILO_LAT__SHIFT) |
				(0<<CLK_PWRMGT_CNTL_M6__DISP_DYN_STOP_LAT__SHIFT)|
				(0<<CLK_PWRMGT_CNTL_M6__DYN_STOP_MODE__SHIFT);

	OUTPLL(RADEON_CLK_PWRMGT_CNTL_M6, clk_pwrmgt_cntl);
						

	clk_pin_cntl = INPLL(RADEON_CLK_PIN_CNTL);
	clk_pin_cntl |= CLK_PIN_CNTL__SCLK_DYN_START_CNTL;
	 
	OUTPLL(RADEON_CLK_PIN_CNTL, clk_pin_cntl);

	/* Enable Dyanmic mode for SCLK */

	sclk_cntl = INPLL(RADEON_SCLK_CNTL);	
	sclk_cntl &= SCLK_CNTL_M6__SCLK_SRC_SEL_MASK;
	sclk_cntl |= SCLK_CNTL_M6__FORCE_VIP;		

	OUTPLL(RADEON_SCLK_CNTL, sclk_cntl);


	sclk_more_cntl = INPLL(RADEON_SCLK_MORE_CNTL);
	sclk_more_cntl &= ~(SCLK_MORE_CNTL__FORCE_DISPREGS);
				                    
	OUTPLL(RADEON_SCLK_MORE_CNTL, sclk_more_cntl);

	
	/* Enable Dynamic mode for PIXCLK & PIX2CLK */

	pixclks_cntl = INPLL(RADEON_PIXCLKS_CNTL);
	
	pixclks_cntl|=  PIXCLKS_CNTL__PIX2CLK_ALWAYS_ONb | 
			PIXCLKS_CNTL__PIX2CLK_DAC_ALWAYS_ONb|
			PIXCLKS_CNTL__PIXCLK_BLEND_ALWAYS_ONb|
			PIXCLKS_CNTL__PIXCLK_GV_ALWAYS_ONb|
			PIXCLKS_CNTL__PIXCLK_DIG_TMDS_ALWAYS_ONb|
			PIXCLKS_CNTL__PIXCLK_LVDS_ALWAYS_ONb|
			PIXCLKS_CNTL__PIXCLK_TMDS_ALWAYS_ONb;

	OUTPLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);
		
		
	vclk_ecp_cntl = INPLL(RADEON_VCLK_ECP_CNTL);
	
	vclk_ecp_cntl|=  VCLK_ECP_CNTL__PIXCLK_ALWAYS_ONb | 
			 VCLK_ECP_CNTL__PIXCLK_DAC_ALWAYS_ONb;

	OUTPLL(RADEON_VCLK_ECP_CNTL, vclk_ecp_cntl);


	/* Enable Dynamic mode for MCLK	*/
	mclk_cntl = INPLL(RADEON_MCLK_CNTL);
	mclk_cntl &= ~(	MCLK_CNTL_M6__FORCE_MCLKA |  
			MCLK_CNTL_M6__FORCE_MCLKB |
			MCLK_CNTL_M6__FORCE_YCLKA |
			MCLK_CNTL_M6__FORCE_YCLKB );
    	OUTPLL(RADEON_MCLK_CNTL, mclk_cntl);

	mclk_misc = INPLL(RADEON_MCLK_MISC);
	mclk_misc |= 	MCLK_MISC__MC_MCLK_MAX_DYN_STOP_LAT|
			MCLK_MISC__IO_MCLK_MAX_DYN_STOP_LAT|
			MCLK_MISC__MC_MCLK_DYN_ENABLE|
			MCLK_MISC__IO_MCLK_DYN_ENABLE;	
	
	OUTPLL(RADEON_MCLK_MISC, mclk_misc);
}
