770 lines
25 KiB
C
770 lines
25 KiB
C
|
/*
|
||
|
* Copyright © 2007 Alex Deucher
|
||
|
* Copyright © 2007 Dave Airlie
|
||
|
* Copyright © 2007 Michel Dänzer
|
||
|
* Copyright © 2007 Jerome Glisse
|
||
|
*
|
||
|
* All Rights Reserved.
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||
|
* a copy of this software and associated documentation files (the
|
||
|
* "Software"), to deal in the Software without restriction, including
|
||
|
* without limitation on the rights to use, copy, modify, merge,
|
||
|
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||
|
* and to permit persons to whom the Software is furnished to do so,
|
||
|
* subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice (including the
|
||
|
* next paragraph) shall be included in all copies or substantial
|
||
|
* portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
|
* NON-INFRINGEMENT. IN NO EVENT SHALL ATI, VA LINUX SYSTEMS AND/OR
|
||
|
* THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||
|
* DEALINGS IN THE SOFTWARE.
|
||
|
*/
|
||
|
#include "drmP.h"
|
||
|
#include "drm.h"
|
||
|
#include "drm_crtc.h"
|
||
|
#include "radeon_ms.h"
|
||
|
|
||
|
static void radeon_pll1_init(struct drm_radeon_private *dev_priv,
|
||
|
struct radeon_state *state);
|
||
|
static void radeon_pll1_restore(struct drm_radeon_private *dev_priv,
|
||
|
struct radeon_state *state);
|
||
|
static void radeon_pll1_save(struct drm_radeon_private *dev_priv,
|
||
|
struct radeon_state *state);
|
||
|
static void radeon_ms_crtc_load_lut(struct drm_crtc *crtc);
|
||
|
|
||
|
/**
|
||
|
* radeon_ms_crtc1_init - initialize CRTC state
|
||
|
* @dev_priv: radeon private structure
|
||
|
* @state: state structure to initialize to default value
|
||
|
*
|
||
|
* Initialize CRTC state to default values
|
||
|
*/
|
||
|
static void radeon_ms_crtc1_init(struct drm_radeon_private *dev_priv,
|
||
|
struct radeon_state *state)
|
||
|
{
|
||
|
state->surface_cntl = SURFACE_CNTL__SURF_TRANSLATION_DIS;
|
||
|
state->surface0_info = 0;
|
||
|
state->surface0_lower_bound = 0;
|
||
|
state->surface0_upper_bound = 0;
|
||
|
state->surface1_info = 0;
|
||
|
state->surface1_lower_bound = 0;
|
||
|
state->surface1_upper_bound = 0;
|
||
|
state->surface2_info = 0;
|
||
|
state->surface2_lower_bound = 0;
|
||
|
state->surface2_upper_bound = 0;
|
||
|
state->surface3_info = 0;
|
||
|
state->surface3_lower_bound = 0;
|
||
|
state->surface3_upper_bound = 0;
|
||
|
state->surface4_info = 0;
|
||
|
state->surface4_lower_bound = 0;
|
||
|
state->surface4_upper_bound = 0;
|
||
|
state->surface5_info = 0;
|
||
|
state->surface5_lower_bound = 0;
|
||
|
state->surface5_upper_bound = 0;
|
||
|
state->surface6_info = 0;
|
||
|
state->surface6_lower_bound = 0;
|
||
|
state->surface6_upper_bound = 0;
|
||
|
state->surface7_info = 0;
|
||
|
state->surface7_lower_bound = 0;
|
||
|
state->surface7_upper_bound = 0;
|
||
|
state->crtc_gen_cntl = CRTC_GEN_CNTL__CRTC_EXT_DISP_EN |
|
||
|
CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B;
|
||
|
state->crtc_ext_cntl = CRTC_EXT_CNTL__VGA_ATI_LINEAR |
|
||
|
CRTC_EXT_CNTL__VGA_XCRT_CNT_EN |
|
||
|
CRTC_EXT_CNTL__CRT_ON;
|
||
|
state->crtc_h_total_disp = 0;
|
||
|
state->crtc_h_sync_strt_wid = 0;
|
||
|
state->crtc_v_total_disp = 0;
|
||
|
state->crtc_v_sync_strt_wid = 0;
|
||
|
state->crtc_offset = 0;
|
||
|
state->crtc_pitch = 0;
|
||
|
state->crtc_more_cntl = 0;
|
||
|
state->crtc_tile_x0_y0 = 0;
|
||
|
state->crtc_offset_cntl = 0;
|
||
|
switch (dev_priv->family) {
|
||
|
case CHIP_R100:
|
||
|
case CHIP_R200:
|
||
|
case CHIP_RV200:
|
||
|
case CHIP_RV250:
|
||
|
case CHIP_RV280:
|
||
|
case CHIP_RS300:
|
||
|
break;
|
||
|
case CHIP_R300:
|
||
|
case CHIP_R350:
|
||
|
case CHIP_R360:
|
||
|
case CHIP_RV350:
|
||
|
case CHIP_RV370:
|
||
|
case CHIP_RV380:
|
||
|
case CHIP_RS400:
|
||
|
case CHIP_RV410:
|
||
|
case CHIP_R420:
|
||
|
case CHIP_R430:
|
||
|
case CHIP_R480:
|
||
|
state->crtc_offset_cntl |= REG_S(CRTC_OFFSET_CNTL,
|
||
|
CRTC_MICRO_TILE_BUFFER_MODE,
|
||
|
CRTC_MICRO_TILE_BUFFER_MODE__DIS);
|
||
|
break;
|
||
|
default:
|
||
|
DRM_ERROR("Unknown radeon family, aborting\n");
|
||
|
return;
|
||
|
}
|
||
|
radeon_pll1_init(dev_priv, state);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* radeon_pll1_init - initialize PLL1 state
|
||
|
* @dev_priv: radeon private structure
|
||
|
* @state: state structure to initialize to default value
|
||
|
*
|
||
|
* Initialize PLL1 state to default values
|
||
|
*/
|
||
|
static void radeon_pll1_init(struct drm_radeon_private *dev_priv,
|
||
|
struct radeon_state *state)
|
||
|
{
|
||
|
state->clock_cntl_index = 0;
|
||
|
state->ppll_cntl = PPLL_R(PPLL_CNTL);
|
||
|
state->ppll_cntl |= PPLL_CNTL__PPLL_ATOMIC_UPDATE_EN |
|
||
|
PPLL_CNTL__PPLL_ATOMIC_UPDATE_SYNC |
|
||
|
PPLL_CNTL__PPLL_VGA_ATOMIC_UPDATE_EN;
|
||
|
state->ppll_cntl &= ~PPLL_CNTL__PPLL_TST_EN;
|
||
|
state->ppll_cntl &= ~PPLL_CNTL__PPLL_TCPOFF;
|
||
|
state->ppll_cntl &= ~PPLL_CNTL__PPLL_TVCOMAX;
|
||
|
state->ppll_cntl &= ~PPLL_CNTL__PPLL_DISABLE_AUTO_RESET;
|
||
|
state->ppll_ref_div = 0;
|
||
|
state->ppll_ref_div = REG_S(PPLL_REF_DIV, PPLL_REF_DIV, 12) |
|
||
|
REG_S(PPLL_REF_DIV, PPLL_REF_DIV_SRC, PPLL_REF_DIV_SRC__XTALIN);
|
||
|
state->ppll_div_0 = 0;
|
||
|
state->ppll_div_1 = 0;
|
||
|
state->ppll_div_2 = 0;
|
||
|
state->ppll_div_3 = 0;
|
||
|
state->vclk_ecp_cntl = 0;
|
||
|
state->htotal_cntl = 0;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* radeon_ms_crtc1_restore - restore CRTC state
|
||
|
* @dev_priv: radeon private structure
|
||
|
* @state: CRTC state to restore
|
||
|
*/
|
||
|
void radeon_ms_crtc1_restore(struct drm_device *dev, struct radeon_state *state)
|
||
|
{
|
||
|
struct drm_radeon_private *dev_priv = dev->dev_private;
|
||
|
|
||
|
/* We prevent the CRTC from hitting the memory controller until
|
||
|
* fully programmed
|
||
|
*/
|
||
|
MMIO_W(CRTC_GEN_CNTL, ~CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B &
|
||
|
state->crtc_gen_cntl);
|
||
|
MMIO_W(CRTC_EXT_CNTL, CRTC_EXT_CNTL__CRTC_VSYNC_DIS |
|
||
|
CRTC_EXT_CNTL__CRTC_HSYNC_DIS |
|
||
|
CRTC_EXT_CNTL__CRTC_DISPLAY_DIS |
|
||
|
state->crtc_ext_cntl);
|
||
|
MMIO_W(SURFACE_CNTL, state->surface_cntl);
|
||
|
MMIO_W(SURFACE0_INFO, state->surface0_info);
|
||
|
MMIO_W(SURFACE0_LOWER_BOUND, state->surface0_lower_bound);
|
||
|
MMIO_W(SURFACE0_UPPER_BOUND, state->surface0_upper_bound);
|
||
|
MMIO_W(SURFACE1_INFO, state->surface1_info);
|
||
|
MMIO_W(SURFACE1_LOWER_BOUND, state->surface1_lower_bound);
|
||
|
MMIO_W(SURFACE1_UPPER_BOUND, state->surface1_upper_bound);
|
||
|
MMIO_W(SURFACE2_INFO, state->surface2_info);
|
||
|
MMIO_W(SURFACE2_LOWER_BOUND, state->surface2_lower_bound);
|
||
|
MMIO_W(SURFACE2_UPPER_BOUND, state->surface2_upper_bound);
|
||
|
MMIO_W(SURFACE3_INFO, state->surface3_info);
|
||
|
MMIO_W(SURFACE3_LOWER_BOUND, state->surface3_lower_bound);
|
||
|
MMIO_W(SURFACE3_UPPER_BOUND, state->surface3_upper_bound);
|
||
|
MMIO_W(SURFACE4_INFO, state->surface4_info);
|
||
|
MMIO_W(SURFACE4_LOWER_BOUND, state->surface4_lower_bound);
|
||
|
MMIO_W(SURFACE4_UPPER_BOUND, state->surface4_upper_bound);
|
||
|
MMIO_W(SURFACE5_INFO, state->surface5_info);
|
||
|
MMIO_W(SURFACE5_LOWER_BOUND, state->surface5_lower_bound);
|
||
|
MMIO_W(SURFACE5_UPPER_BOUND, state->surface5_upper_bound);
|
||
|
MMIO_W(SURFACE6_INFO, state->surface6_info);
|
||
|
MMIO_W(SURFACE6_LOWER_BOUND, state->surface6_lower_bound);
|
||
|
MMIO_W(SURFACE6_UPPER_BOUND, state->surface6_upper_bound);
|
||
|
MMIO_W(SURFACE7_INFO, state->surface7_info);
|
||
|
MMIO_W(SURFACE7_LOWER_BOUND, state->surface7_lower_bound);
|
||
|
MMIO_W(SURFACE7_UPPER_BOUND, state->surface7_upper_bound);
|
||
|
MMIO_W(CRTC_H_TOTAL_DISP, state->crtc_h_total_disp);
|
||
|
MMIO_W(CRTC_H_SYNC_STRT_WID, state->crtc_h_sync_strt_wid);
|
||
|
MMIO_W(CRTC_V_TOTAL_DISP, state->crtc_v_total_disp);
|
||
|
MMIO_W(CRTC_V_SYNC_STRT_WID, state->crtc_v_sync_strt_wid);
|
||
|
MMIO_W(FP_H_SYNC_STRT_WID, state->fp_h_sync_strt_wid);
|
||
|
MMIO_W(FP_V_SYNC_STRT_WID, state->fp_v_sync_strt_wid);
|
||
|
MMIO_W(FP_CRTC_H_TOTAL_DISP, state->fp_crtc_h_total_disp);
|
||
|
MMIO_W(FP_CRTC_V_TOTAL_DISP, state->fp_crtc_v_total_disp);
|
||
|
MMIO_W(CRTC_TILE_X0_Y0, state->crtc_tile_x0_y0);
|
||
|
MMIO_W(CRTC_OFFSET_CNTL, state->crtc_offset_cntl);
|
||
|
MMIO_W(CRTC_OFFSET, state->crtc_offset);
|
||
|
MMIO_W(CRTC_PITCH, state->crtc_pitch);
|
||
|
radeon_pll1_restore(dev_priv, state);
|
||
|
MMIO_W(CRTC_MORE_CNTL, state->crtc_more_cntl);
|
||
|
MMIO_W(CRTC_GEN_CNTL, state->crtc_gen_cntl);
|
||
|
MMIO_W(CRTC_EXT_CNTL, state->crtc_ext_cntl);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* radeon_pll1_restore - restore PLL1 state
|
||
|
* @dev_priv: radeon private structure
|
||
|
* @state: PLL1 state to restore
|
||
|
*/
|
||
|
static void radeon_pll1_restore(struct drm_radeon_private *dev_priv,
|
||
|
struct radeon_state *state)
|
||
|
{
|
||
|
uint32_t tmp;
|
||
|
|
||
|
/* switch to gpu clock while programing new clock */
|
||
|
MMIO_W(CLOCK_CNTL_INDEX, state->clock_cntl_index);
|
||
|
tmp = state->vclk_ecp_cntl;
|
||
|
tmp = REG_S(VCLK_ECP_CNTL, VCLK_SRC_SEL, VCLK_SRC_SEL__CPUCLK);
|
||
|
PPLL_W(VCLK_ECP_CNTL, tmp);
|
||
|
/* reset PLL and update atomicly */
|
||
|
state->ppll_cntl |= PPLL_CNTL__PPLL_ATOMIC_UPDATE_EN |
|
||
|
PPLL_CNTL__PPLL_ATOMIC_UPDATE_SYNC;
|
||
|
|
||
|
PPLL_W(PPLL_CNTL, state->ppll_cntl | PPLL_CNTL__PPLL_RESET);
|
||
|
PPLL_W(PPLL_REF_DIV, state->ppll_ref_div);
|
||
|
PPLL_W(PPLL_DIV_0, state->ppll_div_0);
|
||
|
PPLL_W(PPLL_DIV_1, state->ppll_div_1);
|
||
|
PPLL_W(PPLL_DIV_2, state->ppll_div_2);
|
||
|
PPLL_W(PPLL_DIV_3, state->ppll_div_3);
|
||
|
PPLL_W(HTOTAL_CNTL, state->htotal_cntl);
|
||
|
|
||
|
/* update */
|
||
|
PPLL_W(PPLL_REF_DIV, state->ppll_ref_div |
|
||
|
PPLL_REF_DIV__PPLL_ATOMIC_UPDATE_W);
|
||
|
for (tmp = 0; tmp < 100; tmp++) {
|
||
|
if (!(PPLL_REF_DIV__PPLL_ATOMIC_UPDATE_R &
|
||
|
PPLL_R(PPLL_REF_DIV))) {
|
||
|
break;
|
||
|
}
|
||
|
DRM_UDELAY(10);
|
||
|
}
|
||
|
state->ppll_cntl &= ~PPLL_CNTL__PPLL_RESET;
|
||
|
PPLL_W(PPLL_CNTL, state->ppll_cntl);
|
||
|
PPLL_W(VCLK_ECP_CNTL, state->vclk_ecp_cntl);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* radeon_ms_crtc1_save - save CRTC state
|
||
|
* @dev_priv: radeon private structure
|
||
|
* @state: state where saving current CRTC state
|
||
|
*/
|
||
|
void radeon_ms_crtc1_save(struct drm_device *dev, struct radeon_state *state)
|
||
|
{
|
||
|
struct drm_radeon_private *dev_priv = dev->dev_private;
|
||
|
|
||
|
state->surface_cntl = MMIO_R(SURFACE_CNTL);
|
||
|
state->surface0_info = MMIO_R(SURFACE0_INFO);
|
||
|
state->surface0_lower_bound = MMIO_R(SURFACE0_LOWER_BOUND);
|
||
|
state->surface0_upper_bound = MMIO_R(SURFACE0_UPPER_BOUND);
|
||
|
state->surface1_info = MMIO_R(SURFACE1_INFO);
|
||
|
state->surface1_lower_bound = MMIO_R(SURFACE1_LOWER_BOUND);
|
||
|
state->surface1_upper_bound = MMIO_R(SURFACE1_UPPER_BOUND);
|
||
|
state->surface2_info = MMIO_R(SURFACE2_INFO);
|
||
|
state->surface2_lower_bound = MMIO_R(SURFACE2_LOWER_BOUND);
|
||
|
state->surface2_upper_bound = MMIO_R(SURFACE2_UPPER_BOUND);
|
||
|
state->surface3_info = MMIO_R(SURFACE3_INFO);
|
||
|
state->surface3_lower_bound = MMIO_R(SURFACE3_LOWER_BOUND);
|
||
|
state->surface3_upper_bound = MMIO_R(SURFACE3_UPPER_BOUND);
|
||
|
state->surface4_info = MMIO_R(SURFACE4_INFO);
|
||
|
state->surface4_lower_bound = MMIO_R(SURFACE4_LOWER_BOUND);
|
||
|
state->surface4_upper_bound = MMIO_R(SURFACE4_UPPER_BOUND);
|
||
|
state->surface5_info = MMIO_R(SURFACE5_INFO);
|
||
|
state->surface5_lower_bound = MMIO_R(SURFACE5_LOWER_BOUND);
|
||
|
state->surface5_upper_bound = MMIO_R(SURFACE5_UPPER_BOUND);
|
||
|
state->surface6_info = MMIO_R(SURFACE6_INFO);
|
||
|
state->surface6_lower_bound = MMIO_R(SURFACE6_LOWER_BOUND);
|
||
|
state->surface6_upper_bound = MMIO_R(SURFACE6_UPPER_BOUND);
|
||
|
state->surface7_info = MMIO_R(SURFACE7_INFO);
|
||
|
state->surface7_lower_bound = MMIO_R(SURFACE7_LOWER_BOUND);
|
||
|
state->surface7_upper_bound = MMIO_R(SURFACE7_UPPER_BOUND);
|
||
|
state->crtc_gen_cntl = MMIO_R(CRTC_GEN_CNTL);
|
||
|
state->crtc_ext_cntl = MMIO_R(CRTC_EXT_CNTL);
|
||
|
state->crtc_h_total_disp = MMIO_R(CRTC_H_TOTAL_DISP);
|
||
|
state->crtc_h_sync_strt_wid = MMIO_R(CRTC_H_SYNC_STRT_WID);
|
||
|
state->crtc_v_total_disp = MMIO_R(CRTC_V_TOTAL_DISP);
|
||
|
state->crtc_v_sync_strt_wid = MMIO_R(CRTC_V_SYNC_STRT_WID);
|
||
|
state->fp_h_sync_strt_wid = MMIO_R(FP_H_SYNC_STRT_WID);
|
||
|
state->fp_v_sync_strt_wid = MMIO_R(FP_V_SYNC_STRT_WID);
|
||
|
state->fp_crtc_h_total_disp = MMIO_R(FP_CRTC_H_TOTAL_DISP);
|
||
|
state->fp_crtc_v_total_disp = MMIO_R(FP_CRTC_V_TOTAL_DISP);
|
||
|
state->crtc_offset = MMIO_R(CRTC_OFFSET);
|
||
|
state->crtc_offset_cntl = MMIO_R(CRTC_OFFSET_CNTL);
|
||
|
state->crtc_pitch = MMIO_R(CRTC_PITCH);
|
||
|
state->crtc_more_cntl = MMIO_R(CRTC_MORE_CNTL);
|
||
|
state->crtc_tile_x0_y0 = MMIO_R(CRTC_TILE_X0_Y0);
|
||
|
radeon_pll1_save(dev_priv,state);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* radeon_pll1_save - save PLL1 state
|
||
|
* @dev_priv: radeon private structure
|
||
|
* @state: state where saving current PLL1 state
|
||
|
*/
|
||
|
static void radeon_pll1_save(struct drm_radeon_private *dev_priv,
|
||
|
struct radeon_state *state)
|
||
|
{
|
||
|
state->clock_cntl_index = MMIO_R(CLOCK_CNTL_INDEX);
|
||
|
state->ppll_cntl = PPLL_R(PPLL_CNTL);
|
||
|
state->ppll_ref_div = PPLL_R(PPLL_REF_DIV);
|
||
|
state->ppll_div_0 = PPLL_R(PPLL_DIV_0);
|
||
|
state->ppll_div_1 = PPLL_R(PPLL_DIV_1);
|
||
|
state->ppll_div_2 = PPLL_R(PPLL_DIV_2);
|
||
|
state->ppll_div_3 = PPLL_R(PPLL_DIV_3);
|
||
|
state->vclk_ecp_cntl = PPLL_R(VCLK_ECP_CNTL);
|
||
|
state->htotal_cntl = PPLL_R(HTOTAL_CNTL);
|
||
|
}
|
||
|
|
||
|
static void radeon_ms_crtc1_dpms(struct drm_crtc *crtc, int mode)
|
||
|
{
|
||
|
struct drm_radeon_private *dev_priv = crtc->dev->dev_private;
|
||
|
struct radeon_state *state = &dev_priv->driver_state;
|
||
|
|
||
|
state->crtc_gen_cntl &= ~CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B;
|
||
|
state->crtc_ext_cntl &= ~CRTC_EXT_CNTL__CRTC_DISPLAY_DIS;
|
||
|
state->crtc_ext_cntl &= ~CRTC_EXT_CNTL__CRTC_HSYNC_DIS;
|
||
|
state->crtc_ext_cntl &= ~CRTC_EXT_CNTL__CRTC_VSYNC_DIS;
|
||
|
switch(mode) {
|
||
|
case DPMSModeOn:
|
||
|
break;
|
||
|
case DPMSModeStandby:
|
||
|
state->crtc_ext_cntl |=
|
||
|
CRTC_EXT_CNTL__CRTC_DISPLAY_DIS |
|
||
|
CRTC_EXT_CNTL__CRTC_HSYNC_DIS;
|
||
|
break;
|
||
|
case DPMSModeSuspend:
|
||
|
state->crtc_ext_cntl |=
|
||
|
CRTC_EXT_CNTL__CRTC_DISPLAY_DIS |
|
||
|
CRTC_EXT_CNTL__CRTC_VSYNC_DIS;
|
||
|
break;
|
||
|
case DPMSModeOff:
|
||
|
state->crtc_ext_cntl |=
|
||
|
CRTC_EXT_CNTL__CRTC_DISPLAY_DIS |
|
||
|
CRTC_EXT_CNTL__CRTC_HSYNC_DIS |
|
||
|
CRTC_EXT_CNTL__CRTC_VSYNC_DIS;
|
||
|
state->crtc_gen_cntl |=
|
||
|
CRTC_GEN_CNTL__CRTC_DISP_REQ_EN_B;
|
||
|
break;
|
||
|
}
|
||
|
MMIO_W(CRTC_GEN_CNTL, state->crtc_gen_cntl);
|
||
|
MMIO_W(CRTC_EXT_CNTL, state->crtc_ext_cntl);
|
||
|
|
||
|
dev_priv->crtc1_dpms = mode;
|
||
|
/* FIXME: once adding crtc2 remove this */
|
||
|
dev_priv->crtc2_dpms = mode;
|
||
|
radeon_ms_gpu_dpms(crtc->dev);
|
||
|
|
||
|
if (mode != DPMSModeOff) {
|
||
|
radeon_ms_crtc_load_lut(crtc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool radeon_ms_crtc_mode_fixup(struct drm_crtc *crtc,
|
||
|
struct drm_display_mode *mode,
|
||
|
struct drm_display_mode *adjusted_mode)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void radeon_ms_crtc_mode_prepare(struct drm_crtc *crtc)
|
||
|
{
|
||
|
crtc->funcs->dpms(crtc, DPMSModeOff);
|
||
|
}
|
||
|
|
||
|
/* Compute n/d with rounding */
|
||
|
static int radeon_div(int n, int d)
|
||
|
{
|
||
|
return (n + (d / 2)) / d;
|
||
|
}
|
||
|
|
||
|
/* compute PLL registers values for requested video mode */
|
||
|
static void radeon_pll1_compute(struct drm_crtc *crtc,
|
||
|
struct drm_display_mode *mode)
|
||
|
{
|
||
|
struct {
|
||
|
int divider;
|
||
|
int divider_id;
|
||
|
} *post_div, post_divs[] = {
|
||
|
/* From RAGE 128 VR/RAGE 128 GL Register
|
||
|
* Reference Manual (Technical Reference
|
||
|
* Manual P/N RRG-G04100-C Rev. 0.04), page
|
||
|
* 3-17 (PLL_DIV_[3:0]).
|
||
|
*/
|
||
|
{ 1, 0 }, /* VCLK_SRC */
|
||
|
{ 2, 1 }, /* VCLK_SRC/2 */
|
||
|
{ 4, 2 }, /* VCLK_SRC/4 */
|
||
|
{ 8, 3 }, /* VCLK_SRC/8 */
|
||
|
{ 3, 4 }, /* VCLK_SRC/3 */
|
||
|
{ 16, 5 }, /* VCLK_SRC/16 */
|
||
|
{ 6, 6 }, /* VCLK_SRC/6 */
|
||
|
{ 12, 7 }, /* VCLK_SRC/12 */
|
||
|
{ 0, 0 }
|
||
|
};
|
||
|
struct drm_radeon_private *dev_priv = crtc->dev->dev_private;
|
||
|
struct radeon_state *state = &dev_priv->driver_state;
|
||
|
unsigned long freq = mode->clock / 10;
|
||
|
int pll_output_freq;
|
||
|
int min_rcenter_dist;
|
||
|
int rcenter_dist;
|
||
|
int post_divider;
|
||
|
int post_divider_id;
|
||
|
int ref_div;
|
||
|
int fb_div;
|
||
|
int vco_freq;
|
||
|
int vco_gain;
|
||
|
|
||
|
/* clamp frequency into pll [min; max] frequency range */
|
||
|
if (freq > dev_priv->properties->pll_max_pll_freq) {
|
||
|
freq = dev_priv->properties->pll_max_pll_freq;
|
||
|
}
|
||
|
if (freq < dev_priv->properties->pll_min_pll_freq) {
|
||
|
freq = dev_priv->properties->pll_min_pll_freq;
|
||
|
}
|
||
|
/* select divider so that pll output frequency is the nearest to
|
||
|
* the center of [350; 125]Mhz range */
|
||
|
min_rcenter_dist = 350 * 100;
|
||
|
post_divider = post_divs[0].divider;
|
||
|
post_divider_id = post_divs[0].divider_id;
|
||
|
for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
|
||
|
if (post_div->divider == 0)
|
||
|
break;
|
||
|
/* pll output frequency (before post divider) */
|
||
|
pll_output_freq = post_div->divider * freq;
|
||
|
/* compute distance to [350; 125] range center*/
|
||
|
rcenter_dist = abs(pll_output_freq - 11250);
|
||
|
if (rcenter_dist < min_rcenter_dist) {
|
||
|
min_rcenter_dist = rcenter_dist;
|
||
|
post_divider = post_div->divider;
|
||
|
post_divider_id = post_div->divider_id;
|
||
|
}
|
||
|
}
|
||
|
pll_output_freq = post_divider * freq;
|
||
|
/* select first feedback */
|
||
|
state->clock_cntl_index = REG_S(CLOCK_CNTL_INDEX, PPLL_DIV_SEL, 0);
|
||
|
/* set ref div so that ref_freq/ref_div is in middle of [2; 3.3]Mhz */
|
||
|
ref_div = dev_priv->properties->pll_reference_freq / 265;
|
||
|
state->ppll_ref_div = REG_S(PPLL_REF_DIV, PPLL_REF_DIV, ref_div) |
|
||
|
REG_S(PPLL_REF_DIV, PPLL_REF_DIV_ACC, ref_div);
|
||
|
fb_div = radeon_div(pll_output_freq * ref_div,
|
||
|
dev_priv->properties->pll_reference_freq);
|
||
|
state->ppll_div_0 = REG_S(PPLL_DIV_0, PPLL_FB0_DIV, fb_div) |
|
||
|
REG_S(PPLL_DIV_0, PPLL_POST0_DIV, post_divider_id);
|
||
|
/* configure vco gain */
|
||
|
state->ppll_cntl = PPLL_R(PPLL_CNTL);
|
||
|
vco_gain = REG_G(PPLL_CNTL, PPLL_PVG, state->ppll_cntl);
|
||
|
vco_freq = (fb_div * dev_priv->properties->pll_reference_freq) /
|
||
|
ref_div;
|
||
|
/* This is horribly crude: the VCO frequency range is divided into
|
||
|
* 3 parts, each part having a fixed PLL gain value.
|
||
|
*/
|
||
|
if (vco_freq >= 30000) {
|
||
|
/* [300..max] MHz : 7 */
|
||
|
vco_gain = 7;
|
||
|
} else if (vco_freq >= 18000) {
|
||
|
/* [180..300) MHz : 4 */
|
||
|
vco_gain = 4;
|
||
|
} else {
|
||
|
/* [0..180) MHz : 1 */
|
||
|
vco_gain = 1;
|
||
|
}
|
||
|
state->ppll_cntl |= REG_S(PPLL_CNTL, PPLL_PVG, vco_gain);
|
||
|
state->vclk_ecp_cntl |= REG_S(VCLK_ECP_CNTL, VCLK_SRC_SEL,
|
||
|
VCLK_SRC_SEL__PPLLCLK);
|
||
|
state->htotal_cntl = 0;
|
||
|
}
|
||
|
|
||
|
static void radeon_ms_crtc1_mode_set(struct drm_crtc *crtc,
|
||
|
struct drm_display_mode *mode,
|
||
|
struct drm_display_mode *adjusted_mode,
|
||
|
int x, int y)
|
||
|
{
|
||
|
struct drm_device *dev = crtc->dev;
|
||
|
struct drm_radeon_private *dev_priv = dev->dev_private;
|
||
|
struct radeon_state *state = &dev_priv->driver_state;
|
||
|
int format, hsync_wid, vsync_wid, pitch;
|
||
|
|
||
|
DRM_INFO("[radeon_ms] set modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x\n",
|
||
|
mode->mode_id, mode->name, mode->vrefresh, mode->clock,
|
||
|
mode->hdisplay, mode->hsync_start,
|
||
|
mode->hsync_end, mode->htotal,
|
||
|
mode->vdisplay, mode->vsync_start,
|
||
|
mode->vsync_end, mode->vtotal, mode->type);
|
||
|
DRM_INFO("[radeon_ms] set modeline %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x (adjusted)\n",
|
||
|
adjusted_mode->mode_id, adjusted_mode->name, adjusted_mode->vrefresh, adjusted_mode->clock,
|
||
|
adjusted_mode->hdisplay, adjusted_mode->hsync_start,
|
||
|
adjusted_mode->hsync_end, adjusted_mode->htotal,
|
||
|
adjusted_mode->vdisplay, adjusted_mode->vsync_start,
|
||
|
adjusted_mode->vsync_end, adjusted_mode->vtotal, adjusted_mode->type);
|
||
|
|
||
|
/* only support RGB555,RGB565,ARGB8888 should satisfy all users */
|
||
|
switch (crtc->fb->bits_per_pixel) {
|
||
|
case 16:
|
||
|
if (crtc->fb->depth == 15) {
|
||
|
format = 3;
|
||
|
} else {
|
||
|
format = 4;
|
||
|
}
|
||
|
break;
|
||
|
case 32:
|
||
|
format = 6;
|
||
|
break;
|
||
|
default:
|
||
|
DRM_ERROR("Unknown color depth\n");
|
||
|
return;
|
||
|
}
|
||
|
radeon_pll1_compute(crtc, adjusted_mode);
|
||
|
|
||
|
state->crtc_offset = REG_S(CRTC_OFFSET, CRTC_OFFSET, crtc->fb->offset);
|
||
|
state->crtc_gen_cntl = CRTC_GEN_CNTL__CRTC_EXT_DISP_EN |
|
||
|
CRTC_GEN_CNTL__CRTC_EN |
|
||
|
REG_S(CRTC_GEN_CNTL, CRTC_PIX_WIDTH, format);
|
||
|
if (adjusted_mode->flags & V_DBLSCAN) {
|
||
|
state->crtc_gen_cntl |= CRTC_GEN_CNTL__CRTC_DBL_SCAN_EN;
|
||
|
}
|
||
|
if (adjusted_mode->flags & V_CSYNC) {
|
||
|
state->crtc_gen_cntl |= CRTC_GEN_CNTL__CRTC_C_SYNC_EN;
|
||
|
}
|
||
|
if (adjusted_mode->flags & V_INTERLACE) {
|
||
|
state->crtc_gen_cntl |= CRTC_GEN_CNTL__CRTC_INTERLACE_EN;
|
||
|
}
|
||
|
state->crtc_more_cntl = 0;
|
||
|
state->crtc_h_total_disp =
|
||
|
REG_S(CRTC_H_TOTAL_DISP,
|
||
|
CRTC_H_TOTAL,
|
||
|
(adjusted_mode->crtc_htotal/8) - 1) |
|
||
|
REG_S(CRTC_H_TOTAL_DISP,
|
||
|
CRTC_H_DISP,
|
||
|
(adjusted_mode->crtc_hdisplay/8) - 1);
|
||
|
hsync_wid = (adjusted_mode->crtc_hsync_end -
|
||
|
adjusted_mode->crtc_hsync_start) / 8;
|
||
|
if (!hsync_wid) {
|
||
|
hsync_wid = 1;
|
||
|
}
|
||
|
if (hsync_wid > 0x3f) {
|
||
|
hsync_wid = 0x3f;
|
||
|
}
|
||
|
state->crtc_h_sync_strt_wid =
|
||
|
REG_S(CRTC_H_SYNC_STRT_WID,
|
||
|
CRTC_H_SYNC_WID, hsync_wid) |
|
||
|
REG_S(CRTC_H_SYNC_STRT_WID,
|
||
|
CRTC_H_SYNC_STRT_PIX,
|
||
|
adjusted_mode->crtc_hsync_start) |
|
||
|
REG_S(CRTC_H_SYNC_STRT_WID,
|
||
|
CRTC_H_SYNC_STRT_CHAR,
|
||
|
adjusted_mode->crtc_hsync_start/8);
|
||
|
if (adjusted_mode->flags & V_NHSYNC) {
|
||
|
state->crtc_h_sync_strt_wid |=
|
||
|
CRTC_H_SYNC_STRT_WID__CRTC_H_SYNC_POL;
|
||
|
}
|
||
|
|
||
|
state->crtc_v_total_disp =
|
||
|
REG_S(CRTC_V_TOTAL_DISP, CRTC_V_TOTAL,
|
||
|
adjusted_mode->crtc_vtotal - 1) |
|
||
|
REG_S(CRTC_V_TOTAL_DISP, CRTC_V_DISP,
|
||
|
adjusted_mode->crtc_vdisplay - 1);
|
||
|
vsync_wid = adjusted_mode->crtc_vsync_end -
|
||
|
adjusted_mode->crtc_vsync_start;
|
||
|
if (!vsync_wid) {
|
||
|
vsync_wid = 1;
|
||
|
}
|
||
|
if (vsync_wid > 0x1f) {
|
||
|
vsync_wid = 0x1f;
|
||
|
}
|
||
|
state->crtc_v_sync_strt_wid =
|
||
|
REG_S(CRTC_V_SYNC_STRT_WID,
|
||
|
CRTC_V_SYNC_WID,
|
||
|
vsync_wid) |
|
||
|
REG_S(CRTC_V_SYNC_STRT_WID,
|
||
|
CRTC_V_SYNC_STRT,
|
||
|
adjusted_mode->crtc_vsync_start);
|
||
|
if (adjusted_mode->flags & V_NVSYNC) {
|
||
|
state->crtc_v_sync_strt_wid |=
|
||
|
CRTC_V_SYNC_STRT_WID__CRTC_V_SYNC_POL;
|
||
|
}
|
||
|
|
||
|
pitch = (crtc->fb->width * crtc->fb->bits_per_pixel +
|
||
|
((crtc->fb->bits_per_pixel * 8)- 1)) /
|
||
|
(crtc->fb->bits_per_pixel * 8);
|
||
|
state->crtc_pitch = REG_S(CRTC_PITCH, CRTC_PITCH, pitch) |
|
||
|
REG_S(CRTC_PITCH, CRTC_PITCH_RIGHT, pitch);
|
||
|
|
||
|
state->fp_h_sync_strt_wid = state->crtc_h_sync_strt_wid;
|
||
|
state->fp_v_sync_strt_wid = state->crtc_v_sync_strt_wid;
|
||
|
state->fp_crtc_h_total_disp = state->crtc_h_total_disp;
|
||
|
state->fp_crtc_v_total_disp = state->crtc_v_total_disp;
|
||
|
|
||
|
radeon_ms_crtc1_restore(dev, state);
|
||
|
}
|
||
|
|
||
|
static void radeon_ms_crtc_mode_commit(struct drm_crtc *crtc)
|
||
|
{
|
||
|
crtc->funcs->dpms(crtc, DPMSModeOn);
|
||
|
}
|
||
|
|
||
|
static void radeon_ms_crtc_gamma_set(struct drm_crtc *crtc, u16 r,
|
||
|
u16 g, u16 b, int regno)
|
||
|
{
|
||
|
struct drm_radeon_private *dev_priv = crtc->dev->dev_private;
|
||
|
struct radeon_ms_crtc *radeon_ms_crtc = crtc->driver_private;
|
||
|
struct radeon_state *state = &dev_priv->driver_state;
|
||
|
uint32_t color;
|
||
|
|
||
|
switch(radeon_ms_crtc->crtc) {
|
||
|
case 1:
|
||
|
state->dac_cntl2 &= ~DAC_CNTL2__PALETTE_ACCESS_CNTL;
|
||
|
break;
|
||
|
case 2:
|
||
|
state->dac_cntl2 |= DAC_CNTL2__PALETTE_ACCESS_CNTL;
|
||
|
break;
|
||
|
}
|
||
|
MMIO_W(DAC_CNTL2, state->dac_cntl2);
|
||
|
if (crtc->fb->bits_per_pixel == 16 && crtc->fb->depth == 16) {
|
||
|
if (regno >= 64) {
|
||
|
return;
|
||
|
}
|
||
|
MMIO_W(PALETTE_INDEX,
|
||
|
REG_S(PALETTE_INDEX, PALETTE_W_INDEX,
|
||
|
regno * 4));
|
||
|
color = 0;
|
||
|
color = REG_S(PALETTE_DATA, PALETTE_DATA_R, r >> 8) |
|
||
|
REG_S(PALETTE_DATA, PALETTE_DATA_G, g >> 8) |
|
||
|
REG_S(PALETTE_DATA, PALETTE_DATA_B, b >> 8);
|
||
|
MMIO_W(PALETTE_DATA, color);
|
||
|
MMIO_W(PALETTE_INDEX,
|
||
|
REG_S(PALETTE_INDEX, PALETTE_W_INDEX, regno * 4));
|
||
|
color = 0;
|
||
|
color = REG_S(PALETTE_30_DATA, PALETTE_DATA_R, r >> 6) |
|
||
|
REG_S(PALETTE_30_DATA, PALETTE_DATA_G, g >> 6) |
|
||
|
REG_S(PALETTE_30_DATA, PALETTE_DATA_B, b >> 6);
|
||
|
MMIO_W(PALETTE_30_DATA, color);
|
||
|
radeon_ms_crtc->lut_r[regno * 4] = r;
|
||
|
radeon_ms_crtc->lut_g[regno * 4] = g;
|
||
|
radeon_ms_crtc->lut_b[regno * 4] = b;
|
||
|
if (regno < 32) {
|
||
|
MMIO_W(PALETTE_INDEX,
|
||
|
REG_S(PALETTE_INDEX, PALETTE_W_INDEX,
|
||
|
regno * 8));
|
||
|
color = 0;
|
||
|
color = REG_S(PALETTE_DATA, PALETTE_DATA_R, r >> 8) |
|
||
|
REG_S(PALETTE_DATA, PALETTE_DATA_G, g >> 8) |
|
||
|
REG_S(PALETTE_DATA, PALETTE_DATA_B, b >> 8);
|
||
|
MMIO_W(PALETTE_DATA, color);
|
||
|
MMIO_W(PALETTE_INDEX,
|
||
|
REG_S(PALETTE_INDEX, PALETTE_W_INDEX,
|
||
|
regno * 8));
|
||
|
color = 0;
|
||
|
color = REG_S(PALETTE_30_DATA, PALETTE_DATA_R,r >> 6) |
|
||
|
REG_S(PALETTE_30_DATA, PALETTE_DATA_G,g >> 6) |
|
||
|
REG_S(PALETTE_30_DATA, PALETTE_DATA_B,b >> 6);
|
||
|
MMIO_W(PALETTE_30_DATA, color);
|
||
|
radeon_ms_crtc->lut_r[regno * 8] = r;
|
||
|
radeon_ms_crtc->lut_g[regno * 8] = g;
|
||
|
radeon_ms_crtc->lut_b[regno * 8] = b;
|
||
|
}
|
||
|
} else {
|
||
|
if (regno >= 256) {
|
||
|
return;
|
||
|
}
|
||
|
radeon_ms_crtc->lut_r[regno] = r;
|
||
|
radeon_ms_crtc->lut_g[regno] = g;
|
||
|
radeon_ms_crtc->lut_b[regno] = b;
|
||
|
MMIO_W(PALETTE_INDEX,
|
||
|
REG_S(PALETTE_INDEX, PALETTE_W_INDEX, regno));
|
||
|
color = 0;
|
||
|
color = REG_S(PALETTE_DATA, PALETTE_DATA_R, r >> 8) |
|
||
|
REG_S(PALETTE_DATA, PALETTE_DATA_G, g >> 8) |
|
||
|
REG_S(PALETTE_DATA, PALETTE_DATA_B, b >> 8);
|
||
|
MMIO_W(PALETTE_DATA, color);
|
||
|
MMIO_W(PALETTE_INDEX,
|
||
|
REG_S(PALETTE_INDEX, PALETTE_W_INDEX, regno));
|
||
|
color = 0;
|
||
|
color = REG_S(PALETTE_30_DATA, PALETTE_DATA_R, r >> 6) |
|
||
|
REG_S(PALETTE_30_DATA, PALETTE_DATA_G, g >> 6) |
|
||
|
REG_S(PALETTE_30_DATA, PALETTE_DATA_B, b >> 6);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void radeon_ms_crtc_load_lut(struct drm_crtc *crtc)
|
||
|
{
|
||
|
struct radeon_ms_crtc *radeon_ms_crtc = crtc->driver_private;
|
||
|
int i;
|
||
|
|
||
|
if (!crtc->enabled)
|
||
|
return;
|
||
|
|
||
|
for (i = 0; i < 256; i++) {
|
||
|
radeon_ms_crtc_gamma_set(crtc,
|
||
|
radeon_ms_crtc->lut_r[i],
|
||
|
radeon_ms_crtc->lut_g[i],
|
||
|
radeon_ms_crtc->lut_b[i],
|
||
|
i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool radeon_ms_crtc_lock(struct drm_crtc *crtc)
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void radeon_ms_crtc_unlock(struct drm_crtc *crtc)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static const struct drm_crtc_funcs radeon_ms_crtc1_funcs= {
|
||
|
.dpms = radeon_ms_crtc1_dpms,
|
||
|
.save = NULL, /* XXX */
|
||
|
.restore = NULL, /* XXX */
|
||
|
.lock = radeon_ms_crtc_lock,
|
||
|
.unlock = radeon_ms_crtc_unlock,
|
||
|
.prepare = radeon_ms_crtc_mode_prepare,
|
||
|
.commit = radeon_ms_crtc_mode_commit,
|
||
|
.mode_fixup = radeon_ms_crtc_mode_fixup,
|
||
|
.mode_set = radeon_ms_crtc1_mode_set,
|
||
|
.gamma_set = radeon_ms_crtc_gamma_set,
|
||
|
.cleanup = NULL, /* XXX */
|
||
|
};
|
||
|
|
||
|
int radeon_ms_crtc_create(struct drm_device *dev, int crtc)
|
||
|
{
|
||
|
struct drm_radeon_private *dev_priv = dev->dev_private;
|
||
|
struct drm_crtc *drm_crtc;
|
||
|
struct radeon_ms_crtc *radeon_ms_crtc;
|
||
|
int i;
|
||
|
|
||
|
switch (crtc) {
|
||
|
case 1:
|
||
|
radeon_ms_crtc1_init(dev_priv, &dev_priv->driver_state);
|
||
|
drm_crtc = drm_crtc_create(dev, &radeon_ms_crtc1_funcs);
|
||
|
break;
|
||
|
case 2:
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
if (drm_crtc == NULL) {
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
radeon_ms_crtc = drm_alloc(sizeof(struct radeon_ms_crtc), DRM_MEM_DRIVER);
|
||
|
if (radeon_ms_crtc == NULL) {
|
||
|
kfree(drm_crtc);
|
||
|
return -ENOMEM;
|
||
|
}
|
||
|
|
||
|
radeon_ms_crtc->crtc = crtc;
|
||
|
for (i = 0; i < 256; i++) {
|
||
|
radeon_ms_crtc->lut_r[i] = i << 8;
|
||
|
radeon_ms_crtc->lut_g[i] = i << 8;
|
||
|
radeon_ms_crtc->lut_b[i] = i << 8;
|
||
|
}
|
||
|
drm_crtc->driver_private = radeon_ms_crtc;
|
||
|
return 0;
|
||
|
}
|