/* * Copyright 2010 Advanced Micro Devices, Inc. * * 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 * 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 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 NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) 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. * * Authors: Alex Deucher
*/
/** * evergreen_get_allowed_info_register - fetch the register for the info ioctl * * @rdev: radeon_device pointer * @reg: register offset in bytes * @val: register value * * Returns 0 for success or -EINVAL for an invalid register *
*/ int evergreen_get_allowed_info_register(struct radeon_device *rdev,
u32 reg, u32 *val)
{ switch (reg) { case GRBM_STATUS: case GRBM_STATUS_SE0: case GRBM_STATUS_SE1: case SRBM_STATUS: case SRBM_STATUS2: case DMA_STATUS_REG: case UVD_STATUS:
*val = RREG32(reg); return0; default: return -EINVAL;
}
}
int evergreen_set_uvd_clocks(struct radeon_device *rdev, u32 vclk, u32 dclk)
{ /* start off with something large */ unsigned fb_div = 0, vclk_div = 0, dclk_div = 0; int r;
/* bypass vclk and dclk with bclk */
WREG32_P(CG_UPLL_FUNC_CNTL_2,
VCLK_SRC_SEL(1) | DCLK_SRC_SEL(1),
~(VCLK_SRC_SEL_MASK | DCLK_SRC_SEL_MASK));
/* put PLL in bypass mode */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_BYPASS_EN_MASK, ~UPLL_BYPASS_EN_MASK);
if (!vclk || !dclk) { /* keep the Bypass mode, put PLL to sleep */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK); return0;
}
r = radeon_uvd_calc_upll_dividers(rdev, vclk, dclk, 125000, 250000, 16384, 0x03FFFFFF, 0, 128, 5,
&fb_div, &vclk_div, &dclk_div); if (r) return r;
/* set VCO_MODE to 1 */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_VCO_MODE_MASK, ~UPLL_VCO_MODE_MASK);
/* toggle UPLL_SLEEP to 1 then back to 0 */
WREG32_P(CG_UPLL_FUNC_CNTL, UPLL_SLEEP_MASK, ~UPLL_SLEEP_MASK);
WREG32_P(CG_UPLL_FUNC_CNTL, 0, ~UPLL_SLEEP_MASK);
void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev)
{ int readrq;
u16 v;
readrq = pcie_get_readrq(rdev->pdev);
v = ffs(readrq) - 8; /* if bios or OS sets MAX_READ_REQUEST_SIZE to an invalid value, fix it * to avoid hangs or perfomance issues
*/ if ((v == 0) || (v == 6) || (v == 7))
pcie_set_readrq(rdev->pdev, 512);
}
/* LVDS/eDP FMT is set up by atom */ if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT) return;
/* not needed for analog */ if ((radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1) ||
(radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2)) return;
/** * dce4_wait_for_vblank - vblank wait asic callback. * * @rdev: radeon_device pointer * @crtc: crtc to wait for vblank on * * Wait for vblank on the requested crtc (evergreen+).
*/ void dce4_wait_for_vblank(struct radeon_device *rdev, int crtc)
{ unsigned i = 0;
if (crtc >= rdev->num_crtc) return;
if (!(RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[crtc]) & EVERGREEN_CRTC_MASTER_EN)) return;
/* depending on when we hit vblank, we may be close to active; if so, * wait for another frame.
*/ while (dce4_is_in_vblank(rdev, crtc)) { if (i++ % 100 == 0) { if (!dce4_is_counter_moving(rdev, crtc)) break;
}
}
while (!dce4_is_in_vblank(rdev, crtc)) { if (i++ % 100 == 0) { if (!dce4_is_counter_moving(rdev, crtc)) break;
}
}
}
/** * evergreen_page_flip - pageflip callback. * * @rdev: radeon_device pointer * @crtc_id: crtc to cleanup pageflip on * @crtc_base: new address of the crtc (GPU MC address) * @async: asynchronous flip * * Triggers the actual pageflip by updating the primary * surface base address (evergreen+).
*/ void evergreen_page_flip(struct radeon_device *rdev, int crtc_id, u64 crtc_base, bool async)
{ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id]; struct drm_framebuffer *fb = radeon_crtc->base.primary->fb;
/* flip at hsync for async, default is vsync */
WREG32(EVERGREEN_GRPH_FLIP_CONTROL + radeon_crtc->crtc_offset,
async ? EVERGREEN_GRPH_SURFACE_UPDATE_H_RETRACE_EN : 0); /* update pitch */
WREG32(EVERGREEN_GRPH_PITCH + radeon_crtc->crtc_offset,
fb->pitches[0] / fb->format->cpp[0]); /* update the scanout addresses */
WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset,
upper_32_bits(crtc_base));
WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset,
(u32)crtc_base); /* post the write */
RREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + radeon_crtc->crtc_offset);
}
/** * evergreen_page_flip_pending - check if page flip is still pending * * @rdev: radeon_device pointer * @crtc_id: crtc to check * * Returns the current update pending status.
*/ bool evergreen_page_flip_pending(struct radeon_device *rdev, int crtc_id)
{ struct radeon_crtc *radeon_crtc = rdev->mode_info.crtcs[crtc_id];
int sumo_get_temp(struct radeon_device *rdev)
{
u32 temp = RREG32(CG_THERMAL_STATUS) & 0xff; int actual_temp = temp - 49;
return actual_temp * 1000;
}
/** * sumo_pm_init_profile - Initialize power profiles callback. * * @rdev: radeon_device pointer * * Initialize the power states used in profile mode * (sumo, trinity, SI). * Used for profile mode only.
*/ void sumo_pm_init_profile(struct radeon_device *rdev)
{ int idx;
/** * btc_pm_init_profile - Initialize power profiles callback. * * @rdev: radeon_device pointer * * Initialize the power states used in profile mode * (BTC, cayman). * Used for profile mode only.
*/ void btc_pm_init_profile(struct radeon_device *rdev)
{ int idx;
/* default */
rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_ps_idx = rdev->pm.default_power_state_index;
rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_ps_idx = rdev->pm.default_power_state_index;
rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_off_cm_idx = 0;
rdev->pm.profiles[PM_PROFILE_DEFAULT_IDX].dpms_on_cm_idx = 2; /* starting with BTC, there is one state that is used for both * MH and SH. Difference is that we always use the high clock index for * mclk.
*/ if (rdev->flags & RADEON_IS_MOBILITY)
idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_BATTERY, 0); else
idx = radeon_pm_get_type_index(rdev, POWER_STATE_TYPE_PERFORMANCE, 0); /* low sh */
rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_off_cm_idx = 0;
rdev->pm.profiles[PM_PROFILE_LOW_SH_IDX].dpms_on_cm_idx = 0; /* mid sh */
rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_off_cm_idx = 0;
rdev->pm.profiles[PM_PROFILE_MID_SH_IDX].dpms_on_cm_idx = 1; /* high sh */
rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_off_cm_idx = 0;
rdev->pm.profiles[PM_PROFILE_HIGH_SH_IDX].dpms_on_cm_idx = 2; /* low mh */
rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_off_cm_idx = 0;
rdev->pm.profiles[PM_PROFILE_LOW_MH_IDX].dpms_on_cm_idx = 0; /* mid mh */
rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_off_cm_idx = 0;
rdev->pm.profiles[PM_PROFILE_MID_MH_IDX].dpms_on_cm_idx = 1; /* high mh */
rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_ps_idx = idx;
rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_off_cm_idx = 0;
rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx = 2;
}
/** * evergreen_pm_misc - set additional pm hw parameters callback. * * @rdev: radeon_device pointer * * Set non-clock parameters associated with a power state * (voltage, etc.) (evergreen+).
*/ void evergreen_pm_misc(struct radeon_device *rdev)
{ int req_ps_idx = rdev->pm.requested_power_state_index; int req_cm_idx = rdev->pm.requested_clock_mode_index; struct radeon_power_state *ps = &rdev->pm.power_state[req_ps_idx]; struct radeon_voltage *voltage = &ps->clock_info[req_cm_idx].voltage;
if (voltage->type == VOLTAGE_SW) { /* 0xff0x are flags rather then an actual voltage */ if ((voltage->voltage & 0xff00) == 0xff00) return; if (voltage->voltage && (voltage->voltage != rdev->pm.current_vddc)) {
radeon_atom_set_voltage(rdev, voltage->voltage, SET_VOLTAGE_TYPE_ASIC_VDDC);
rdev->pm.current_vddc = voltage->voltage;
DRM_DEBUG("Setting: vddc: %d\n", voltage->voltage);
}
/* starting with BTC, there is one state that is used for both * MH and SH. Difference is that we always use the high clock index for * mclk and vddci.
*/ if ((rdev->pm.pm_method == PM_METHOD_PROFILE) &&
(rdev->family >= CHIP_BARTS) &&
rdev->pm.active_crtc_count &&
((rdev->pm.profile_index == PM_PROFILE_MID_MH_IDX) ||
(rdev->pm.profile_index == PM_PROFILE_LOW_MH_IDX)))
voltage = &rdev->pm.power_state[req_ps_idx].
clock_info[rdev->pm.profiles[PM_PROFILE_HIGH_MH_IDX].dpms_on_cm_idx].voltage;
/* 0xff0x are flags rather then an actual voltage */ if ((voltage->vddci & 0xff00) == 0xff00) return; if (voltage->vddci && (voltage->vddci != rdev->pm.current_vddci)) {
radeon_atom_set_voltage(rdev, voltage->vddci, SET_VOLTAGE_TYPE_ASIC_VDDCI);
rdev->pm.current_vddci = voltage->vddci;
DRM_DEBUG("Setting: vddci: %d\n", voltage->vddci);
}
}
}
/** * evergreen_pm_prepare - pre-power state change callback. * * @rdev: radeon_device pointer * * Prepare for a power state change (evergreen+).
*/ void evergreen_pm_prepare(struct radeon_device *rdev)
{ struct drm_device *ddev = rdev_to_drm(rdev); struct drm_crtc *crtc; struct radeon_crtc *radeon_crtc;
u32 tmp;
if (connector->connector_type == DRM_MODE_CONNECTOR_eDP ||
connector->connector_type == DRM_MODE_CONNECTOR_LVDS) { /* don't try to enable hpd on eDP or LVDS avoid breaking the * aux dp channel on imac and help (but not completely fix) * https://bugzilla.redhat.com/show_bug.cgi?id=726143 * also avoid interrupt storms during dpms.
*/ continue;
}
static u32 evergreen_line_buffer_adjust(struct radeon_device *rdev, struct radeon_crtc *radeon_crtc, struct drm_display_mode *mode, struct drm_display_mode *other_mode)
{
u32 tmp, buffer_alloc, i;
u32 pipe_offset = radeon_crtc->crtc_id * 0x20; /* * Line Buffer Setup * There are 3 line buffers, each one shared by 2 display controllers. * DC_LB_MEMORY_SPLIT controls how that line buffer is shared between * the display controllers. The paritioning is done via one of four * preset allocations specified in bits 2:0: * first display controller * 0 - first half of lb (3840 * 2) * 1 - first 3/4 of lb (5760 * 2) * 2 - whole lb (7680 * 2), other crtc must be disabled * 3 - first 1/4 of lb (1920 * 2) * second display controller * 4 - second half of lb (3840 * 2) * 5 - second 3/4 of lb (5760 * 2) * 6 - whole lb (7680 * 2), other crtc must be disabled * 7 - last 1/4 of lb (1920 * 2)
*/ /* this can get tricky if we have two large displays on a paired group * of crtcs. Ideally for multiple large displays we'd assign them to * non-linked crtcs for maximum line buffer allocation.
*/ if (radeon_crtc->base.enabled && mode) { if (other_mode) {
tmp = 0; /* 1/2 */
buffer_alloc = 1;
} else {
tmp = 2; /* whole */
buffer_alloc = 2;
}
} else {
tmp = 0;
buffer_alloc = 0;
}
/* second controller of the pair uses second half of the lb */ if (radeon_crtc->crtc_id % 2)
tmp += 4;
WREG32(DC_LB_MEMORY_SPLIT + radeon_crtc->crtc_offset, tmp);
if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) {
WREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset,
DMIF_BUFFERS_ALLOCATED(buffer_alloc)); for (i = 0; i < rdev->usec_timeout; i++) { if (RREG32(PIPE0_DMIF_BUFFER_CONTROL + pipe_offset) &
DMIF_BUFFERS_ALLOCATED_COMPLETED) break;
udelay(1);
}
}
struct evergreen_wm_params {
u32 dram_channels; /* number of dram channels */
u32 yclk; /* bandwidth per dram data pin in kHz */
u32 sclk; /* engine clock in kHz */
u32 disp_clk; /* display clock in kHz */
u32 src_width; /* viewport width */
u32 active_time; /* active display time in ns */
u32 blank_time; /* blank time in ns */ bool interlaced; /* mode is interlaced */
fixed20_12 vsc; /* vertical scale ratio */
u32 num_heads; /* number of active crtcs */
u32 bytes_per_pixel; /* bytes per pixel display + overlay */
u32 lb_size; /* line buffer allocated to pipe */
u32 vtaps; /* vertical scaler taps */
};
static u32 evergreen_dram_bandwidth(struct evergreen_wm_params *wm)
{ /* Calculate DRAM Bandwidth and the part allocated to display. */
fixed20_12 dram_efficiency; /* 0.7 */
fixed20_12 yclk, dram_channels, bandwidth;
fixed20_12 a;
static u32 evergreen_dram_bandwidth_for_display(struct evergreen_wm_params *wm)
{ /* Calculate DRAM Bandwidth and the part allocated to display. */
fixed20_12 disp_dram_allocation; /* 0.3 to 0.7 */
fixed20_12 yclk, dram_channels, bandwidth;
fixed20_12 a;
static u32 evergreen_available_bandwidth(struct evergreen_wm_params *wm)
{ /* Calculate the Available bandwidth. Display can use this temporarily but not in average. */
u32 dram_bandwidth = evergreen_dram_bandwidth(wm);
u32 data_return_bandwidth = evergreen_data_return_bandwidth(wm);
u32 dmif_req_bandwidth = evergreen_dmif_request_bandwidth(wm);
/* set for high clocks */
latency_watermark_a = min(evergreen_latency_watermark(&wm_high), (u32)65535); /* set for low clocks */
latency_watermark_b = min(evergreen_latency_watermark(&wm_low), (u32)65535);
/* possibly force display priority to high */ /* should really do this at mode validation time... */ if (!evergreen_average_bandwidth_vs_dram_bandwidth_for_display(&wm_high) ||
!evergreen_average_bandwidth_vs_available_bandwidth(&wm_high) ||
!evergreen_check_latency_hiding(&wm_high) ||
(rdev->disp_priority == 2)) {
DRM_DEBUG_KMS("force priority a to high\n");
priority_a_cnt |= PRIORITY_ALWAYS_ON;
} if (!evergreen_average_bandwidth_vs_dram_bandwidth_for_display(&wm_low) ||
!evergreen_average_bandwidth_vs_available_bandwidth(&wm_low) ||
!evergreen_check_latency_hiding(&wm_low) ||
(rdev->disp_priority == 2)) {
DRM_DEBUG_KMS("force priority b to high\n");
priority_b_cnt |= PRIORITY_ALWAYS_ON;
}
/* write the priority marks */
WREG32(PRIORITY_A_CNT + radeon_crtc->crtc_offset, priority_a_cnt);
WREG32(PRIORITY_B_CNT + radeon_crtc->crtc_offset, priority_b_cnt);
/* save values for DPM */
radeon_crtc->line_time = line_time;
radeon_crtc->wm_high = latency_watermark_a;
radeon_crtc->wm_low = latency_watermark_b;
}
/** * evergreen_bandwidth_update - update display watermarks callback. * * @rdev: radeon_device pointer * * Update the display watermarks based on the requested mode(s) * (evergreen+).
*/ void evergreen_bandwidth_update(struct radeon_device *rdev)
{ struct drm_display_mode *mode0 = NULL; struct drm_display_mode *mode1 = NULL;
u32 num_heads = 0, lb_size; int i;
if (!rdev->mode_info.mode_config_initialized) return;
radeon_update_display_priority(rdev);
for (i = 0; i < rdev->num_crtc; i++) { if (rdev->mode_info.crtcs[i]->base.enabled)
num_heads++;
} for (i = 0; i < rdev->num_crtc; i += 2) {
mode0 = &rdev->mode_info.crtcs[i]->base.mode;
mode1 = &rdev->mode_info.crtcs[i+1]->base.mode;
lb_size = evergreen_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i], mode0, mode1);
evergreen_program_watermarks(rdev, rdev->mode_info.crtcs[i], lb_size, num_heads);
lb_size = evergreen_line_buffer_adjust(rdev, rdev->mode_info.crtcs[i+1], mode1, mode0);
evergreen_program_watermarks(rdev, rdev->mode_info.crtcs[i+1], lb_size, num_heads);
}
}
/** * evergreen_mc_wait_for_idle - wait for MC idle callback. * * @rdev: radeon_device pointer * * Wait for the MC (memory controller) to be idle. * (evergreen+). * Returns 0 if the MC is idle, -1 if not.
*/ int evergreen_mc_wait_for_idle(struct radeon_device *rdev)
{ unsigned i;
u32 tmp;
for (i = 0; i < rdev->usec_timeout; i++) { /* read MC_STATUS */
tmp = RREG32(SRBM_STATUS) & 0x1F00; if (!tmp) return0;
udelay(1);
} return -1;
}
/* * Assumption is that EVERGREEN_CRTC_MASTER_EN enable for requested crtc * We go from crtc to connector and it is not relible since it * should be an opposite direction .If crtc is enable then * find the dig_fe which selects this crtc and insure that it enable. * if such dig_fe is found then find dig_be which selects found dig_be and * insure that it enable and in DP_SST mode. * if UNIPHY_PLL_CONTROL1.enable then we should disconnect timing * from dp symbols clocks .
*/ staticbool evergreen_is_dp_sst_stream_enabled(struct radeon_device *rdev, unsigned crtc_id, unsigned *ret_dig_fe)
{ unsigned i; unsigned dig_fe; unsigned dig_be; unsigned dig_en_be; unsigned uniphy_pll; unsigned digs_fe_selected; unsigned dig_be_mode; unsigned dig_fe_mask; bool is_enabled = false; bool found_crtc = false;
/* loop through all running dig_fe to find selected crtc */ for (i = 0; i < ARRAY_SIZE(ni_dig_offsets); i++) {
dig_fe = RREG32(NI_DIG_FE_CNTL + ni_dig_offsets[i]); if (dig_fe & NI_DIG_FE_CNTL_SYMCLK_FE_ON &&
crtc_id == NI_DIG_FE_CNTL_SOURCE_SELECT(dig_fe)) { /* found running pipe */
found_crtc = true;
dig_fe_mask = 1 << i;
dig_fe = i; break;
}
}
if (found_crtc) { /* loop through all running dig_be to find selected dig_fe */ for (i = 0; i < ARRAY_SIZE(ni_dig_offsets); i++) {
dig_be = RREG32(NI_DIG_BE_CNTL + ni_dig_offsets[i]); /* if dig_fe_selected by dig_be? */
digs_fe_selected = NI_DIG_BE_CNTL_FE_SOURCE_SELECT(dig_be);
dig_be_mode = NI_DIG_FE_CNTL_MODE(dig_be); if (dig_fe_mask & digs_fe_selected && /* if dig_be in sst mode? */
dig_be_mode == NI_DIG_BE_DPSST) {
dig_en_be = RREG32(NI_DIG_BE_EN_CNTL +
ni_dig_offsets[i]);
uniphy_pll = RREG32(NI_DCIO_UNIPHY0_PLL_CONTROL1 +
ni_tx_offsets[i]); /* dig_be enable and tx is running */ if (dig_en_be & NI_DIG_BE_EN_CNTL_ENABLE &&
dig_en_be & NI_DIG_BE_EN_CNTL_SYMBCLK_ON &&
uniphy_pll & NI_DCIO_UNIPHY0_PLL_CONTROL1_ENABLE) {
is_enabled = true;
*ret_dig_fe = dig_fe; break;
}
}
}
}
void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save)
{
u32 crtc_enabled, tmp, frame_count, blackout; int i, j; unsigned dig_fe;
if (!ASIC_IS_NODCE(rdev)) {
save->vga_render_control = RREG32(VGA_RENDER_CONTROL);
save->vga_hdp_control = RREG32(VGA_HDP_CONTROL);
/* disable VGA render */
WREG32(VGA_RENDER_CONTROL, 0);
} /* blank the display controllers */ for (i = 0; i < rdev->num_crtc; i++) {
crtc_enabled = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]) & EVERGREEN_CRTC_MASTER_EN; if (crtc_enabled) {
save->crtc_enabled[i] = true; if (ASIC_IS_DCE6(rdev)) {
tmp = RREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i]); if (!(tmp & EVERGREEN_CRTC_BLANK_DATA_EN)) {
radeon_wait_for_vblank(rdev, i);
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
tmp |= EVERGREEN_CRTC_BLANK_DATA_EN;
WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
}
} else {
tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]); if (!(tmp & EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE)) {
radeon_wait_for_vblank(rdev, i);
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
tmp |= EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE;
WREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i], tmp);
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
}
} /* wait for the next frame */
frame_count = radeon_get_vblank_counter(rdev, i); for (j = 0; j < rdev->usec_timeout; j++) { if (radeon_get_vblank_counter(rdev, i) != frame_count) break;
udelay(1);
} /*we should disable dig if it drives dp sst*/ /*but we are in radeon_device_init and the topology is unknown*/ /*and it is available after radeon_modeset_init*/ /*the following method radeon_atom_encoder_dpms_dig*/ /*does the job if we initialize it properly*/ /*for now we do it this manually*/ /**/ if (ASIC_IS_DCE5(rdev) &&
evergreen_is_dp_sst_stream_enabled(rdev, i, &dig_fe))
evergreen_blank_dp_output(rdev, dig_fe); /*we could remove 6 lines below*/ /* XXX this is a hack to avoid strange behavior with EFI on certain systems */
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]);
tmp &= ~EVERGREEN_CRTC_MASTER_EN;
WREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i], tmp);
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
save->crtc_enabled[i] = false; /* ***** */
} else {
save->crtc_enabled[i] = false;
}
}
radeon_mc_wait_for_idle(rdev);
blackout = RREG32(MC_SHARED_BLACKOUT_CNTL); if ((blackout & BLACKOUT_MODE_MASK) != 1) { /* Block CPU access */
WREG32(BIF_FB_EN, 0); /* blackout the MC */
blackout &= ~BLACKOUT_MODE_MASK;
WREG32(MC_SHARED_BLACKOUT_CNTL, blackout | 1);
} /* wait for the MC to settle */
udelay(100);
/* lock double buffered regs */ for (i = 0; i < rdev->num_crtc; i++) { if (save->crtc_enabled[i]) {
tmp = RREG32(EVERGREEN_GRPH_UPDATE + crtc_offsets[i]); if (!(tmp & EVERGREEN_GRPH_UPDATE_LOCK)) {
tmp |= EVERGREEN_GRPH_UPDATE_LOCK;
WREG32(EVERGREEN_GRPH_UPDATE + crtc_offsets[i], tmp);
}
tmp = RREG32(EVERGREEN_MASTER_UPDATE_LOCK + crtc_offsets[i]); if (!(tmp & 1)) {
tmp |= 1;
WREG32(EVERGREEN_MASTER_UPDATE_LOCK + crtc_offsets[i], tmp);
}
}
}
}
void evergreen_mc_resume(struct radeon_device *rdev, struct evergreen_mc_save *save)
{
u32 tmp, frame_count; int i, j;
/* update crtc base addresses */ for (i = 0; i < rdev->num_crtc; i++) {
WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS_HIGH + crtc_offsets[i],
upper_32_bits(rdev->mc.vram_start));
WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS_HIGH + crtc_offsets[i],
upper_32_bits(rdev->mc.vram_start));
WREG32(EVERGREEN_GRPH_PRIMARY_SURFACE_ADDRESS + crtc_offsets[i],
(u32)rdev->mc.vram_start);
WREG32(EVERGREEN_GRPH_SECONDARY_SURFACE_ADDRESS + crtc_offsets[i],
(u32)rdev->mc.vram_start);
}
if (!ASIC_IS_NODCE(rdev)) {
WREG32(EVERGREEN_VGA_MEMORY_BASE_ADDRESS_HIGH, upper_32_bits(rdev->mc.vram_start));
WREG32(EVERGREEN_VGA_MEMORY_BASE_ADDRESS, (u32)rdev->mc.vram_start);
}
/* unlock regs and wait for update */ for (i = 0; i < rdev->num_crtc; i++) { if (save->crtc_enabled[i]) {
tmp = RREG32(EVERGREEN_MASTER_UPDATE_MODE + crtc_offsets[i]); if ((tmp & 0x7) != 0) {
tmp &= ~0x7;
WREG32(EVERGREEN_MASTER_UPDATE_MODE + crtc_offsets[i], tmp);
}
tmp = RREG32(EVERGREEN_GRPH_UPDATE + crtc_offsets[i]); if (tmp & EVERGREEN_GRPH_UPDATE_LOCK) {
tmp &= ~EVERGREEN_GRPH_UPDATE_LOCK;
WREG32(EVERGREEN_GRPH_UPDATE + crtc_offsets[i], tmp);
}
tmp = RREG32(EVERGREEN_MASTER_UPDATE_LOCK + crtc_offsets[i]); if (tmp & 1) {
tmp &= ~1;
WREG32(EVERGREEN_MASTER_UPDATE_LOCK + crtc_offsets[i], tmp);
} for (j = 0; j < rdev->usec_timeout; j++) {
tmp = RREG32(EVERGREEN_GRPH_UPDATE + crtc_offsets[i]); if ((tmp & EVERGREEN_GRPH_SURFACE_UPDATE_PENDING) == 0) break;
udelay(1);
}
}
}
/* unblackout the MC */
tmp = RREG32(MC_SHARED_BLACKOUT_CNTL);
tmp &= ~BLACKOUT_MODE_MASK;
WREG32(MC_SHARED_BLACKOUT_CNTL, tmp); /* allow CPU access */
WREG32(BIF_FB_EN, FB_READ_EN | FB_WRITE_EN);
for (i = 0; i < rdev->num_crtc; i++) { if (save->crtc_enabled[i]) { if (ASIC_IS_DCE6(rdev)) {
tmp = RREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i]);
tmp &= ~EVERGREEN_CRTC_BLANK_DATA_EN;
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
WREG32(EVERGREEN_CRTC_BLANK_CONTROL + crtc_offsets[i], tmp);
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
} else {
tmp = RREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i]);
tmp &= ~EVERGREEN_CRTC_DISP_READ_REQUEST_DISABLE;
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 1);
WREG32(EVERGREEN_CRTC_CONTROL + crtc_offsets[i], tmp);
WREG32(EVERGREEN_CRTC_UPDATE_LOCK + crtc_offsets[i], 0);
} /* wait for the next frame */
frame_count = radeon_get_vblank_counter(rdev, i); for (j = 0; j < rdev->usec_timeout; j++) { if (radeon_get_vblank_counter(rdev, i) != frame_count) break;
udelay(1);
}
}
} if (!ASIC_IS_NODCE(rdev)) { /* Unlock vga access */
WREG32(VGA_HDP_CONTROL, save->vga_hdp_control);
mdelay(1);
WREG32(VGA_RENDER_CONTROL, save->vga_render_control);
}
}
void evergreen_mc_program(struct radeon_device *rdev)
{ struct evergreen_mc_save save;
u32 tmp; int i, j;
/* Reset cp; if cp is reset, then PA, SH, VGT also need to be reset */
WREG32(GRBM_SOFT_RESET, (SOFT_RESET_CP |
SOFT_RESET_PA |
SOFT_RESET_SH |
SOFT_RESET_VGT |
SOFT_RESET_SPI |
SOFT_RESET_SX));
RREG32(GRBM_SOFT_RESET);
mdelay(15);
WREG32(GRBM_SOFT_RESET, 0);
RREG32(GRBM_SOFT_RESET);
WREG32(GRBM_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i));
WREG32(RLC_GFX_INDEX, INSTANCE_BROADCAST_WRITES | SE_INDEX(i));
rb_disable_bitmap = (RREG32(CC_RB_BACKEND_DISABLE) & 0x00ff0000) >> 16;
tmp <<= 4;
tmp |= rb_disable_bitmap;
}
} /* enabled rb are just the one not disabled :) */
disabled_rb_mask = tmp;
tmp = 0; for (i = 0; i < rdev->config.evergreen.max_backends; i++)
tmp |= (1 << i); /* if all the backends are disabled, fix it up here */ if ((disabled_rb_mask & tmp) == tmp) { for (i = 0; i < rdev->config.evergreen.max_backends; i++)
disabled_rb_mask &= ~(1 << i);
}
for (i = 0; i < rdev->config.evergreen.num_ses; i++) {
u32 simd_disable_bitmap;
switch (rdev->family) { case CHIP_CEDAR: case CHIP_PALM: case CHIP_SUMO: case CHIP_SUMO2: case CHIP_CAICOS: /* no vertex cache */
sq_config &= ~VC_ENABLE; break; default: break;
}
/* set the shader const cache sizes to 0 */ for (i = SQ_ALU_CONST_BUFFER_SIZE_PS_0; i < 0x28200; i += 4)
WREG32(i, 0); for (i = SQ_ALU_CONST_BUFFER_SIZE_HS_0; i < 0x29000; i += 4)
WREG32(i, 0);
/* set mclk/sclk to bypass */
rv770_set_clk_bypass_mode(rdev); /* disable BM */
pci_clear_master(rdev->pdev); /* disable mem access */
evergreen_mc_stop(rdev, &save); if (evergreen_mc_wait_for_idle(rdev)) {
dev_warn(rdev->dev, "Wait for MC idle timed out !\n");
} /* reset */
radeon_pci_config_reset(rdev); /* wait for asic to come out of reset */ for (i = 0; i < rdev->usec_timeout; i++) { if (RREG32(CONFIG_MEMSIZE) != 0xffffffff) break;
udelay(1);
}
}
int evergreen_asic_reset(struct radeon_device *rdev, bool hard)
{
u32 reset_mask;
if (hard) {
evergreen_gpu_pci_config_reset(rdev); return0;
}
if (!reset_mask)
r600_set_bios_scratch_engine_hung(rdev, false);
return0;
}
/** * evergreen_gfx_is_lockup - Check if the GFX engine is locked up * * @rdev: radeon_device pointer * @ring: radeon_ring structure holding ring information * * Check if the GFX engine is locked up. * Returns true if the engine appears to be locked up, false if not.
*/ bool evergreen_gfx_is_lockup(struct radeon_device *rdev, struct radeon_ring *ring)
{
u32 reset_mask = evergreen_gpu_check_soft_reset(rdev);
void sumo_rlc_fini(struct radeon_device *rdev)
{ int r;
/* save restore block */ if (rdev->rlc.save_restore_obj) {
r = radeon_bo_reserve(rdev->rlc.save_restore_obj, false); if (unlikely(r != 0))
dev_warn(rdev->dev, "(%d) reserve RLC sr bo failed\n", r);
radeon_bo_unpin(rdev->rlc.save_restore_obj);
radeon_bo_unreserve(rdev->rlc.save_restore_obj);
/* clear state block */ if (rdev->rlc.clear_state_obj) {
r = radeon_bo_reserve(rdev->rlc.clear_state_obj, false); if (unlikely(r != 0))
dev_warn(rdev->dev, "(%d) reserve RLC c bo failed\n", r);
radeon_bo_unpin(rdev->rlc.clear_state_obj);
radeon_bo_unreserve(rdev->rlc.clear_state_obj);
fw_data = (const __be32 *)rdev->rlc_fw->data; if (rdev->family >= CHIP_ARUBA) { for (i = 0; i < ARUBA_RLC_UCODE_SIZE; i++) {
WREG32(RLC_UCODE_ADDR, i);
WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
}
} elseif (rdev->family >= CHIP_CAYMAN) { for (i = 0; i < CAYMAN_RLC_UCODE_SIZE; i++) {
WREG32(RLC_UCODE_ADDR, i);
WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
}
} else { for (i = 0; i < EVERGREEN_RLC_UCODE_SIZE; i++) {
WREG32(RLC_UCODE_ADDR, i);
WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
}
}
WREG32(RLC_UCODE_ADDR, 0);
evergreen_rlc_start(rdev);
return0;
}
/* Interrupts */
u32 evergreen_get_vblank_counter(struct radeon_device *rdev, int crtc)
{ if (crtc >= rdev->num_crtc) return0; else return RREG32(CRTC_STATUS_FRAME_COUNT + crtc_offsets[crtc]);
}
void evergreen_disable_interrupt_state(struct radeon_device *rdev)
{ int i;
u32 tmp;
if (rdev->family >= CHIP_CAYMAN) {
cayman_cp_int_cntl_setup(rdev, 0,
CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE);
cayman_cp_int_cntl_setup(rdev, 1, 0);
cayman_cp_int_cntl_setup(rdev, 2, 0);
tmp = RREG32(CAYMAN_DMA1_CNTL) & ~TRAP_ENABLE;
WREG32(CAYMAN_DMA1_CNTL, tmp);
} else
WREG32(CP_INT_CNTL, CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE);
tmp = RREG32(DMA_CNTL) & ~TRAP_ENABLE;
WREG32(DMA_CNTL, tmp);
WREG32(GRBM_INT_CNTL, 0);
WREG32(SRBM_INT_CNTL, 0); for (i = 0; i < rdev->num_crtc; i++)
WREG32(INT_MASK + crtc_offsets[i], 0); for (i = 0; i < rdev->num_crtc; i++)
WREG32(GRPH_INT_CONTROL + crtc_offsets[i], 0);
/* only one DAC on DCE5 */ if (!ASIC_IS_DCE5(rdev))
WREG32(DACA_AUTODETECT_INT_CONTROL, 0);
WREG32(DACB_AUTODETECT_INT_CONTROL, 0);
for (i = 0; i < 6; i++)
WREG32_AND(DC_HPDx_INT_CONTROL(i), DC_HPDx_INT_POLARITY);
}
/* Note that the order we write back regs here is important */ int evergreen_irq_set(struct radeon_device *rdev)
{ int i;
u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
u32 cp_int_cntl1 = 0, cp_int_cntl2 = 0;
u32 grbm_int_cntl = 0;
u32 dma_cntl, dma_cntl1 = 0;
u32 thermal_int = 0;
if (!rdev->irq.installed) {
WARN(1, "Can't enable IRQ/MSI because no handler is installed\n"); return -EINVAL;
} /* don't enable anything if the ih is disabled */ if (!rdev->ih.enabled) {
r600_disable_interrupts(rdev); /* force the active interrupt state to all disabled */
evergreen_disable_interrupt_state(rdev); return0;
}
if (rdev->family >= CHIP_CAYMAN)
WREG32(CAYMAN_DMA1_CNTL, dma_cntl1);
WREG32(GRBM_INT_CNTL, grbm_int_cntl);
for (i = 0; i < rdev->num_crtc; i++) {
radeon_irq_kms_set_irq_n_enabled(
rdev, INT_MASK + crtc_offsets[i],
VBLANK_INT_MASK,
rdev->irq.crtc_vblank_int[i] ||
atomic_read(&rdev->irq.pflip[i]), "vblank", i);
}
for (i = 0; i < rdev->num_crtc; i++)
WREG32(GRPH_INT_CONTROL + crtc_offsets[i], GRPH_PFLIP_INT_MASK);
for (i = 0; i < 6; i++) {
radeon_irq_kms_set_irq_n_enabled(
rdev, DC_HPDx_INT_CONTROL(i),
DC_HPDx_INT_EN | DC_HPDx_RX_INT_EN,
rdev->irq.hpd[i], "HPD", i);
}
if (rdev->family == CHIP_ARUBA)
WREG32(TN_CG_THERMAL_INT_CTRL, thermal_int); else
WREG32(CG_THERMAL_INT, thermal_int);
for (i = 0; i < 6; i++) {
radeon_irq_kms_set_irq_n_enabled(
rdev, AFMT_AUDIO_PACKET_CONTROL + crtc_offsets[i],
AFMT_AZ_FORMAT_WTRIG_MASK,
rdev->irq.afmt[i], "HDMI", i);
}
/* posting read */
RREG32(SRBM_STATUS);
return0;
}
/* Note that the order we write back regs here is important */ staticvoid evergreen_irq_ack(struct radeon_device *rdev)
{ int i, j;
u32 *grph_int = rdev->irq.stat_regs.evergreen.grph_int;
u32 *disp_int = rdev->irq.stat_regs.evergreen.disp_int;
u32 *afmt_status = rdev->irq.stat_regs.evergreen.afmt_status;
for (i = 0; i < 6; i++) {
disp_int[i] = RREG32(evergreen_disp_int_status[i]);
afmt_status[i] = RREG32(AFMT_STATUS + crtc_offsets[i]); if (i < rdev->num_crtc)
grph_int[i] = RREG32(GRPH_INT_STATUS + crtc_offsets[i]);
}
/* We write back each interrupt register in pairs of two */ for (i = 0; i < rdev->num_crtc; i += 2) { for (j = i; j < (i + 2); j++) { if (grph_int[j] & GRPH_PFLIP_INT_OCCURRED)
WREG32(GRPH_INT_STATUS + crtc_offsets[j],
GRPH_PFLIP_INT_CLEAR);
}
for (j = i; j < (i + 2); j++) { if (disp_int[j] & LB_D1_VBLANK_INTERRUPT)
WREG32(VBLANK_STATUS + crtc_offsets[j],
VBLANK_ACK); if (disp_int[j] & LB_D1_VLINE_INTERRUPT)
WREG32(VLINE_STATUS + crtc_offsets[j],
VLINE_ACK);
}
}
for (i = 0; i < 6; i++) { if (disp_int[i] & DC_HPD1_INTERRUPT)
WREG32_OR(DC_HPDx_INT_CONTROL(i), DC_HPDx_INT_ACK);
}
for (i = 0; i < 6; i++) { if (disp_int[i] & DC_HPD1_RX_INTERRUPT)
WREG32_OR(DC_HPDx_INT_CONTROL(i), DC_HPDx_RX_INT_ACK);
}
for (i = 0; i < 6; i++) { if (afmt_status[i] & AFMT_AZ_FORMAT_WTRIG)
WREG32_OR(AFMT_AUDIO_PACKET_CONTROL + crtc_offsets[i],
AFMT_AZ_FORMAT_WTRIG_ACK);
}
}
/* wptr/rptr are in bytes! */
rptr += 16;
rptr &= rdev->ih.ptr_mask;
WREG32(IH_RB_RPTR, rptr);
} if (queue_dp)
schedule_work(&rdev->dp_work); if (queue_hotplug)
schedule_delayed_work(&rdev->hotplug_work, 0); if (queue_hdmi)
schedule_work(&rdev->audio_work); if (queue_thermal && rdev->pm.dpm_enabled)
schedule_work(&rdev->pm.dpm.thermal.work);
rdev->ih.rptr = rptr;
atomic_set(&rdev->ih.lock, 0);
/* make sure wptr hasn't changed while processing */
wptr = evergreen_get_ih_wptr(rdev); if (wptr != rptr) goto restart_ih;
return IRQ_HANDLED;
}
staticvoid evergreen_uvd_init(struct radeon_device *rdev)
{ int r;
if (!rdev->has_uvd) return;
r = radeon_uvd_init(rdev); if (r) {
dev_err(rdev->dev, "failed UVD (%d) init.\n", r); /* * At this point rdev->uvd.vcpu_bo is NULL which trickles down * to early fails uvd_v2_2_resume() and thus nothing happens * there. So it is pointless to try to go through that code * hence why we disable uvd here.
*/
rdev->has_uvd = false; return;
}
rdev->ring[R600_RING_TYPE_UVD_INDEX].ring_obj = NULL;
r600_ring_init(rdev, &rdev->ring[R600_RING_TYPE_UVD_INDEX], 4096);
}
staticvoid evergreen_uvd_start(struct radeon_device *rdev)
{ int r;
if (!rdev->has_uvd) return;
r = uvd_v2_2_resume(rdev); if (r) {
dev_err(rdev->dev, "failed UVD resume (%d).\n", r); goto error;
}
r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_UVD_INDEX); if (r) {
dev_err(rdev->dev, "failed initializing UVD fences (%d).\n", r); goto error;
} return;
/* scratch needs to be initialized before MC */
r = r600_vram_scratch_init(rdev); if (r) return r;
evergreen_mc_program(rdev);
if (ASIC_IS_DCE5(rdev) && !rdev->pm.dpm_enabled) {
r = ni_mc_load_microcode(rdev); if (r) {
DRM_ERROR("Failed to load MC firmware!\n"); return r;
}
}
if (rdev->flags & RADEON_IS_AGP) {
evergreen_agp_enable(rdev);
} else {
r = evergreen_pcie_gart_enable(rdev); if (r) return r;
}
evergreen_gpu_init(rdev);
/* allocate rlc buffers */ if (rdev->flags & RADEON_IS_IGP) {
rdev->rlc.reg_list = sumo_rlc_save_restore_register_list;
rdev->rlc.reg_list_size =
(u32)ARRAY_SIZE(sumo_rlc_save_restore_register_list);
rdev->rlc.cs_data = evergreen_cs_data;
r = sumo_rlc_init(rdev); if (r) {
DRM_ERROR("Failed to init rlc BOs!\n"); return r;
}
}
/* allocate wb buffer */
r = radeon_wb_init(rdev); if (r) return r;
r = radeon_fence_driver_start_ring(rdev, RADEON_RING_TYPE_GFX_INDEX); if (r) {
dev_err(rdev->dev, "failed initializing CP fences (%d).\n", r); return r;
}
r = radeon_fence_driver_start_ring(rdev, R600_RING_TYPE_DMA_INDEX); if (r) {
dev_err(rdev->dev, "failed initializing DMA fences (%d).\n", r); return r;
}
evergreen_uvd_start(rdev);
/* Enable IRQ */ if (!rdev->irq.installed) {
r = radeon_irq_kms_init(rdev); if (r) return r;
}
r = r600_irq_init(rdev); if (r) {
DRM_ERROR("radeon: IH init failed (%d).\n", r);
radeon_irq_kms_fini(rdev); return r;
}
evergreen_irq_set(rdev);
ring = &rdev->ring[RADEON_RING_TYPE_GFX_INDEX];
r = radeon_ring_init(rdev, ring, ring->ring_size, RADEON_WB_CP_RPTR_OFFSET,
RADEON_CP_PACKET2); if (r) return r;
ring = &rdev->ring[R600_RING_TYPE_DMA_INDEX];
r = radeon_ring_init(rdev, ring, ring->ring_size, R600_WB_DMA_RPTR_OFFSET,
DMA_PACKET(DMA_PACKET_NOP, 0, 0)); if (r) return r;
r = evergreen_cp_load_microcode(rdev); if (r) return r;
r = evergreen_cp_resume(rdev); if (r) return r;
r = r600_dma_resume(rdev); if (r) return r;
evergreen_uvd_resume(rdev);
r = radeon_ib_pool_init(rdev); if (r) {
dev_err(rdev->dev, "IB initialization failed (%d).\n", r); return r;
}
r = radeon_audio_init(rdev); if (r) {
DRM_ERROR("radeon: audio init failed\n"); return r;
}
return0;
}
int evergreen_resume(struct radeon_device *rdev)
{ int r;
/* reset the asic, the gfx blocks are often in a bad state * after the driver is unloaded or after a resume
*/ if (radeon_asic_reset(rdev))
dev_warn(rdev->dev, "GPU reset failed !\n"); /* Do not reset GPU before posting, on rv770 hw unlike on r500 hw, * posting will perform necessary task to bring back GPU into good * shape.
*/ /* post card */
atom_asic_init(rdev->mode_info.atom_context);
/* init golden registers */
evergreen_init_golden_registers(rdev);
if (rdev->pm.pm_method == PM_METHOD_DPM)
radeon_pm_resume(rdev);
rdev->accel_working = true;
r = evergreen_startup(rdev); if (r) {
DRM_ERROR("evergreen startup failed on resume\n");
rdev->accel_working = false; return r;
}
return r;
}
int evergreen_suspend(struct radeon_device *rdev)
{
radeon_pm_suspend(rdev);
radeon_audio_fini(rdev); if (rdev->has_uvd) {
radeon_uvd_suspend(rdev);
uvd_v1_0_fini(rdev);
}
r700_cp_stop(rdev);
r600_dma_stop(rdev);
evergreen_irq_suspend(rdev);
radeon_wb_disable(rdev);
evergreen_pcie_gart_disable(rdev);
return0;
}
/* Plan is to move initialization in that function and use * helper function so that radeon_device_init pretty much * do nothing more than calling asic specific function. This * should also allow to remove a bunch of callback function * like vram_info.
*/ int evergreen_init(struct radeon_device *rdev)
{ int r;
/* Read BIOS */ if (!radeon_get_bios(rdev)) { if (ASIC_IS_AVIVO(rdev)) return -EINVAL;
} /* Must be an ATOMBIOS */ if (!rdev->is_atom_bios) {
dev_err(rdev->dev, "Expecting atombios for evergreen GPU\n"); return -EINVAL;
}
r = radeon_atombios_init(rdev); if (r) return r; /* reset the asic, the gfx blocks are often in a bad state * after the driver is unloaded or after a resume
*/ if (radeon_asic_reset(rdev))
dev_warn(rdev->dev, "GPU reset failed !\n"); /* Post card if necessary */ if (!radeon_card_posted(rdev)) { if (!rdev->bios) {
dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n"); return -EINVAL;
}
DRM_INFO("GPU not posted. posting now...\n");
atom_asic_init(rdev->mode_info.atom_context);
} /* init golden registers */
evergreen_init_golden_registers(rdev); /* Initialize scratch registers */
r600_scratch_init(rdev); /* Initialize surface registers */
radeon_surface_init(rdev); /* Initialize clocks */
radeon_get_clock_info(rdev_to_drm(rdev)); /* Fence driver */
radeon_fence_driver_init(rdev); /* initialize AGP */ if (rdev->flags & RADEON_IS_AGP) {
r = radeon_agp_init(rdev); if (r)
radeon_agp_disable(rdev);
} /* initialize memory controller */
r = evergreen_mc_init(rdev); if (r) return r; /* Memory manager */
r = radeon_bo_init(rdev); if (r) return r;
if (ASIC_IS_DCE5(rdev)) { if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw || !rdev->mc_fw) {
r = ni_init_microcode(rdev); if (r) {
DRM_ERROR("Failed to load firmware!\n"); return r;
}
}
} else { if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) {
r = r600_init_microcode(rdev); if (r) {
DRM_ERROR("Failed to load firmware!\n"); return r;
}
}
}
/* Initialize power management */
radeon_pm_init(rdev);
rdev->accel_working = true;
r = evergreen_startup(rdev); if (r) {
dev_err(rdev->dev, "disabling GPU acceleration\n");
r700_cp_fini(rdev);
r600_dma_fini(rdev);
r600_irq_fini(rdev); if (rdev->flags & RADEON_IS_IGP)
sumo_rlc_fini(rdev);
radeon_wb_fini(rdev);
radeon_ib_pool_fini(rdev);
radeon_irq_kms_fini(rdev);
evergreen_pcie_gart_fini(rdev);
rdev->accel_working = false;
}
/* Don't start up if the MC ucode is missing on BTC parts. * The default clocks and voltages before the MC ucode * is loaded are not suffient for advanced operations.
*/ if (ASIC_IS_DCE5(rdev)) { if (!rdev->mc_fw && !(rdev->flags & RADEON_IS_IGP)) {
DRM_ERROR("radeon: MC ucode required for NI+.\n"); return -EINVAL;
}
}
} else {
link_width_cntl = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL); /* XXX: only disable it if gen1 bridge vendor == 0x111d or 0x1106 */ if (1)
link_width_cntl |= LC_UPCONFIGURE_DIS; else
link_width_cntl &= ~LC_UPCONFIGURE_DIS;
WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl);
}
}
void evergreen_program_aspm(struct radeon_device *rdev)
{
u32 data, orig;
u32 pcie_lc_cntl, pcie_lc_cntl_old; bool disable_l0s, disable_l1 = false, disable_plloff_in_l1 = false; /* fusion_platform = true * if the system is a fusion system * (APU or DGPU in a fusion system). * todo: check if the system is a fusion platform.
*/ bool fusion_platform = false;
if (radeon_aspm == 0) return;
if (!(rdev->flags & RADEON_IS_PCIE)) return;
switch (rdev->family) { case CHIP_CYPRESS: case CHIP_HEMLOCK: case CHIP_JUNIPER: case CHIP_REDWOOD: case CHIP_CEDAR: case CHIP_SUMO: case CHIP_SUMO2: case CHIP_PALM: case CHIP_ARUBA:
disable_l0s = true; break; default:
disable_l0s = false; break;
}
if (rdev->flags & RADEON_IS_IGP)
fusion_platform = true; /* XXX also dGPUs in a fusion system */
data = orig = RREG32_PIF_PHY0(PB0_PIF_PAIRING); if (fusion_platform)
data &= ~MULTI_PIF; else
data |= MULTI_PIF; if (data != orig)
WREG32_PIF_PHY0(PB0_PIF_PAIRING, data);
data = orig = RREG32_PIF_PHY1(PB1_PIF_PAIRING); if (fusion_platform)
data &= ~MULTI_PIF; else
data |= MULTI_PIF; if (data != orig)
WREG32_PIF_PHY1(PB1_PIF_PAIRING, data);
if (!disable_l1) { if (rdev->family >= CHIP_BARTS)
pcie_lc_cntl |= LC_L1_INACTIVITY(7); else
pcie_lc_cntl |= LC_L1_INACTIVITY(8);
if (!disable_plloff_in_l1) {
data = orig = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0);
data &= ~(PLL_POWER_STATE_IN_OFF_0_MASK | PLL_POWER_STATE_IN_TXS2_0_MASK);
data |= PLL_POWER_STATE_IN_OFF_0(7) | PLL_POWER_STATE_IN_TXS2_0(7); if (data != orig)
WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0, data);
data = orig = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1);
data &= ~(PLL_POWER_STATE_IN_OFF_1_MASK | PLL_POWER_STATE_IN_TXS2_1_MASK);
data |= PLL_POWER_STATE_IN_OFF_1(7) | PLL_POWER_STATE_IN_TXS2_1(7); if (data != orig)
WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1, data);
data = orig = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0);
data &= ~(PLL_POWER_STATE_IN_OFF_0_MASK | PLL_POWER_STATE_IN_TXS2_0_MASK);
data |= PLL_POWER_STATE_IN_OFF_0(7) | PLL_POWER_STATE_IN_TXS2_0(7); if (data != orig)
WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0, data);
data = orig = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1);
data &= ~(PLL_POWER_STATE_IN_OFF_1_MASK | PLL_POWER_STATE_IN_TXS2_1_MASK);
data |= PLL_POWER_STATE_IN_OFF_1(7) | PLL_POWER_STATE_IN_TXS2_1(7); if (data != orig)
WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1, data);
if (rdev->family >= CHIP_BARTS) {
data = orig = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0);
data &= ~PLL_RAMP_UP_TIME_0_MASK;
data |= PLL_RAMP_UP_TIME_0(4); if (data != orig)
WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_0, data);
data = orig = RREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1);
data &= ~PLL_RAMP_UP_TIME_1_MASK;
data |= PLL_RAMP_UP_TIME_1(4); if (data != orig)
WREG32_PIF_PHY0(PB0_PIF_PWRDOWN_1, data);
data = orig = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0);
data &= ~PLL_RAMP_UP_TIME_0_MASK;
data |= PLL_RAMP_UP_TIME_0(4); if (data != orig)
WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_0, data);
data = orig = RREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1);
data &= ~PLL_RAMP_UP_TIME_1_MASK;
data |= PLL_RAMP_UP_TIME_1(4); if (data != orig)
WREG32_PIF_PHY1(PB1_PIF_PWRDOWN_1, data);
}
data = orig = RREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL);
data &= ~LC_DYN_LANES_PWR_STATE_MASK;
data |= LC_DYN_LANES_PWR_STATE(3); if (data != orig)
WREG32_PCIE_PORT(PCIE_LC_LINK_WIDTH_CNTL, data);
if (rdev->family >= CHIP_BARTS) {
data = orig = RREG32_PIF_PHY0(PB0_PIF_CNTL);
data &= ~LS2_EXIT_TIME_MASK;
data |= LS2_EXIT_TIME(1); if (data != orig)
WREG32_PIF_PHY0(PB0_PIF_CNTL, data);
data = orig = RREG32_PIF_PHY1(PB1_PIF_CNTL);
data &= ~LS2_EXIT_TIME_MASK;
data |= LS2_EXIT_TIME(1); if (data != orig)
WREG32_PIF_PHY1(PB1_PIF_CNTL, data);
}
}
}
/* evergreen parts only */ if (rdev->family < CHIP_BARTS)
pcie_lc_cntl |= LC_PMI_TO_L1_DIS;
if (pcie_lc_cntl != pcie_lc_cntl_old)
WREG32_PCIE_PORT(PCIE_LC_CNTL, pcie_lc_cntl);
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.126 Sekunden
(vorverarbeitet am 2026-06-07)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.