diff options
Diffstat (limited to 'drivers/gpu/drm/radeon')
63 files changed, 2376 insertions, 1599 deletions
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index ed38262d998..3c91312dea9 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -50,7 +50,7 @@ $(obj)/r600_cs.o: $(obj)/r600_reg_safe.h radeon-y := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o \ radeon_irq.o r300_cmdbuf.o r600_cp.o # add KMS driver -radeon-y += radeon_device.o radeon_kms.o \ +radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \ radeon_atombios.o radeon_agp.o atombios_crtc.o radeon_combios.o \ atom.o radeon_fence.o radeon_ttm.o radeon_object.o radeon_gart.o \ radeon_legacy_crtc.o radeon_legacy_encoders.o radeon_connectors.o \ diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index d75788feac6..1d569830ed9 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c @@ -24,6 +24,7 @@ #include <linux/module.h> #include <linux/sched.h> +#include <linux/slab.h> #include <asm/unaligned.h> #define ATOM_DEBUG @@ -52,15 +53,17 @@ typedef struct { struct atom_context *ctx; - uint32_t *ps, *ws; int ps_shift; uint16_t start; + unsigned last_jump; + unsigned long last_jump_jiffies; + bool abort; } atom_exec_context; int atom_debug = 0; -static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params); -void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params); +static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params); +int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params); static uint32_t atom_arg_mask[8] = { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000, @@ -604,12 +607,17 @@ static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg) static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg) { int idx = U8((*ptr)++); + int r = 0; + if (idx < ATOM_TABLE_NAMES_CNT) SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]); else SDEBUG(" table: %d\n", idx); if (U16(ctx->ctx->cmd_table + 4 + 2 * idx)) - atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift); + r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift); + if (r) { + ctx->abort = true; + } } static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg) @@ -673,6 +681,8 @@ static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg) static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) { int execute = 0, target = U16(*ptr); + unsigned long cjiffies; + (*ptr) += 2; switch (arg) { case ATOM_COND_ABOVE: @@ -700,8 +710,25 @@ static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) if (arg != ATOM_COND_ALWAYS) SDEBUG(" taken: %s\n", execute ? "yes" : "no"); SDEBUG(" target: 0x%04X\n", target); - if (execute) + if (execute) { + if (ctx->last_jump == (ctx->start + target)) { + cjiffies = jiffies; + if (time_after(cjiffies, ctx->last_jump_jiffies)) { + cjiffies -= ctx->last_jump_jiffies; + if ((jiffies_to_msecs(cjiffies) > 1000)) { + DRM_ERROR("atombios stuck in loop for more than 1sec aborting\n"); + ctx->abort = true; + } + } else { + /* jiffies wrap around we will just wait a little longer */ + ctx->last_jump_jiffies = jiffies; + } + } else { + ctx->last_jump = ctx->start + target; + ctx->last_jump_jiffies = jiffies; + } *ptr = ctx->start + target; + } } static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg) @@ -881,11 +908,16 @@ static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg) uint8_t attr = U8((*ptr)++), shift; uint32_t saved, dst; int dptr = *ptr; + uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3]; SDEBUG(" dst: "); dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + /* op needs to full dst value */ + dst = saved; shift = atom_get_src(ctx, attr, ptr); SDEBUG(" shift: %d\n", shift); dst <<= shift; + dst &= atom_arg_mask[dst_align]; + dst >>= atom_arg_shift[dst_align]; SDEBUG(" dst: "); atom_put_dst(ctx, arg, attr, &dptr, dst, saved); } @@ -895,11 +927,16 @@ static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg) uint8_t attr = U8((*ptr)++), shift; uint32_t saved, dst; int dptr = *ptr; + uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3]; SDEBUG(" dst: "); dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); + /* op needs to full dst value */ + dst = saved; shift = atom_get_src(ctx, attr, ptr); SDEBUG(" shift: %d\n", shift); dst >>= shift; + dst &= atom_arg_mask[dst_align]; + dst >>= atom_arg_shift[dst_align]; SDEBUG(" dst: "); atom_put_dst(ctx, arg, attr, &dptr, dst, saved); } @@ -1104,15 +1141,16 @@ static struct { atom_op_shr, ATOM_ARG_MC}, { atom_op_debug, 0},}; -static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params) +static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params) { int base = CU16(ctx->cmd_table + 4 + 2 * index); int len, ws, ps, ptr; unsigned char op; atom_exec_context ectx; + int ret = 0; if (!base) - return; + return -EINVAL; len = CU16(base + ATOM_CT_SIZE_PTR); ws = CU8(base + ATOM_CT_WS_PTR); @@ -1125,6 +1163,8 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3 ectx.ps_shift = ps / 4; ectx.start = base; ectx.ps = params; + ectx.abort = false; + ectx.last_jump = 0; if (ws) ectx.ws = kzalloc(4 * ws, GFP_KERNEL); else @@ -1137,6 +1177,12 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3 SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1); else SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1); + if (ectx.abort) { + DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n", + base, len, ws, ps, ptr - 1); + ret = -EINVAL; + goto free; + } if (op < ATOM_OP_CNT && op > 0) opcode_table[op].func(&ectx, &ptr, @@ -1150,12 +1196,16 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3 debug_depth--; SDEBUG("<<\n"); +free: if (ws) kfree(ectx.ws); + return ret; } -void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) +int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) { + int r; + mutex_lock(&ctx->mutex); /* reset reg block */ ctx->reg_block = 0; @@ -1163,8 +1213,9 @@ void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) ctx->fb_base = 0; /* reset io mode */ ctx->io_mode = ATOM_IO_MM; - atom_execute_table_locked(ctx, index, params); + r = atom_execute_table_locked(ctx, index, params); mutex_unlock(&ctx->mutex); + return r; } static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 }; @@ -1248,9 +1299,7 @@ int atom_asic_init(struct atom_context *ctx) if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT)) return 1; - atom_execute_table(ctx, ATOM_CMD_INIT, ps); - - return 0; + return atom_execute_table(ctx, ATOM_CMD_INIT, ps); } void atom_destroy(struct atom_context *ctx) @@ -1260,12 +1309,16 @@ void atom_destroy(struct atom_context *ctx) kfree(ctx); } -void atom_parse_data_header(struct atom_context *ctx, int index, +bool atom_parse_data_header(struct atom_context *ctx, int index, uint16_t * size, uint8_t * frev, uint8_t * crev, uint16_t * data_start) { int offset = index * 2 + 4; int idx = CU16(ctx->data_table + offset); + u16 *mdt = (u16 *)(ctx->bios + ctx->data_table + 4); + + if (!mdt[index]) + return false; if (size) *size = CU16(idx); @@ -1274,38 +1327,42 @@ void atom_parse_data_header(struct atom_context *ctx, int index, if (crev) *crev = CU8(idx + 3); *data_start = idx; - return; + return true; } -void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev, +bool atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev, uint8_t * crev) { int offset = index * 2 + 4; int idx = CU16(ctx->cmd_table + offset); + u16 *mct = (u16 *)(ctx->bios + ctx->cmd_table + 4); + + if (!mct[index]) + return false; if (frev) *frev = CU8(idx + 2); if (crev) *crev = CU8(idx + 3); - return; + return true; } int atom_allocate_fb_scratch(struct atom_context *ctx) { int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware); uint16_t data_offset; - int usage_bytes; + int usage_bytes = 0; struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage; - atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset); + if (atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) { + firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset); - firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset); + DRM_DEBUG("atom firmware requested %08x %dkb\n", + firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware, + firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb); - DRM_DEBUG("atom firmware requested %08x %dkb\n", - firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware, - firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb); - - usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024; + usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024; + } if (usage_bytes == 0) usage_bytes = 20 * 1024; /* allocate some scratch memory */ diff --git a/drivers/gpu/drm/radeon/atom.h b/drivers/gpu/drm/radeon/atom.h index bc73781423a..cd1b64ab5ca 100644 --- a/drivers/gpu/drm/radeon/atom.h +++ b/drivers/gpu/drm/radeon/atom.h @@ -140,11 +140,13 @@ struct atom_context { extern int atom_debug; struct atom_context *atom_parse(struct card_info *, void *); -void atom_execute_table(struct atom_context *, int, uint32_t *); +int atom_execute_table(struct atom_context *, int, uint32_t *); int atom_asic_init(struct atom_context *); void atom_destroy(struct atom_context *); -void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start); -void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev, uint8_t *crev); +bool atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, + uint8_t *frev, uint8_t *crev, uint16_t *data_start); +bool atom_parse_cmd_header(struct atom_context *ctx, int index, + uint8_t *frev, uint8_t *crev); int atom_allocate_fb_scratch(struct atom_context *ctx); #include "atom-types.h" #include "atombios.h" diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c index dd9fdf56061..a87990b3ae8 100644 --- a/drivers/gpu/drm/radeon/atombios_crtc.c +++ b/drivers/gpu/drm/radeon/atombios_crtc.c @@ -353,12 +353,55 @@ static void atombios_crtc_set_timing(struct drm_crtc *crtc, atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); } +static void atombios_disable_ss(struct drm_crtc *crtc) +{ + struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct radeon_device *rdev = dev->dev_private; + u32 ss_cntl; + + if (ASIC_IS_DCE4(rdev)) { + switch (radeon_crtc->pll_id) { + case ATOM_PPLL1: + ss_cntl = RREG32(EVERGREEN_P1PLL_SS_CNTL); + ss_cntl &= ~EVERGREEN_PxPLL_SS_EN; + WREG32(EVERGREEN_P1PLL_SS_CNTL, ss_cntl); + break; + case ATOM_PPLL2: + ss_cntl = RREG32(EVERGREEN_P2PLL_SS_CNTL); + ss_cntl &= ~EVERGREEN_PxPLL_SS_EN; + WREG32(EVERGREEN_P2PLL_SS_CNTL, ss_cntl); + break; + case ATOM_DCPLL: + case ATOM_PPLL_INVALID: + return; + } + } else if (ASIC_IS_AVIVO(rdev)) { + switch (radeon_crtc->pll_id) { + case ATOM_PPLL1: + ss_cntl = RREG32(AVIVO_P1PLL_INT_SS_CNTL); + ss_cntl &= ~1; + WREG32(AVIVO_P1PLL_INT_SS_CNTL, ss_cntl); + break; + case ATOM_PPLL2: + ss_cntl = RREG32(AVIVO_P2PLL_INT_SS_CNTL); + ss_cntl &= ~1; + WREG32(AVIVO_P2PLL_INT_SS_CNTL, ss_cntl); + break; + case ATOM_DCPLL: + case ATOM_PPLL_INVALID: + return; + } + } +} + + union atom_enable_ss { ENABLE_LVDS_SS_PARAMETERS legacy; ENABLE_SPREAD_SPECTRUM_ON_PPLL_PS_ALLOCATION v1; }; -static void atombios_set_ss(struct drm_crtc *crtc, int enable) +static void atombios_enable_ss(struct drm_crtc *crtc) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct drm_device *dev = crtc->dev; @@ -387,9 +430,9 @@ static void atombios_set_ss(struct drm_crtc *crtc, int enable) step = dig->ss->step; delay = dig->ss->delay; range = dig->ss->range; - } else if (enable) + } else return; - } else if (enable) + } else return; break; } @@ -406,13 +449,13 @@ static void atombios_set_ss(struct drm_crtc *crtc, int enable) args.v1.ucSpreadSpectrumDelay = delay; args.v1.ucSpreadSpectrumRange = range; args.v1.ucPpll = radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1; - args.v1.ucEnable = enable; + args.v1.ucEnable = ATOM_ENABLE; } else { args.legacy.usSpreadSpectrumPercentage = cpu_to_le16(percentage); args.legacy.ucSpreadSpectrumType = type; args.legacy.ucSpreadSpectrumStepSize_Delay = (step & 3) << 2; args.legacy.ucSpreadSpectrumStepSize_Delay |= (delay & 7) << 4; - args.legacy.ucEnable = enable; + args.legacy.ucEnable = ATOM_ENABLE; } atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); } @@ -478,10 +521,9 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, /* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */ if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1) adjusted_clock = mode->clock * 2; - /* LVDS PLL quirks */ - if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) { - struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; - pll->algo = dig->pll_algo; + if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) { + pll->algo = PLL_ALGO_LEGACY; + pll->flags |= RADEON_PLL_PREFER_CLOSEST_LOWER; } } else { if (encoder->encoder_type != DRM_MODE_ENCODER_DAC) @@ -503,8 +545,9 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, int index; index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll); - atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, - &crev); + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, + &crev)) + return adjusted_clock; memset(&args, 0, sizeof(args)); @@ -542,11 +585,16 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc, } } else if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { /* may want to enable SS on DP/eDP eventually */ - args.v3.sInput.ucDispPllConfig |= - DISPPLL_CONFIG_SS_ENABLE; - if (mode->clock > 165000) + /*args.v3.sInput.ucDispPllConfig |= + DISPPLL_CONFIG_SS_ENABLE;*/ + if (encoder_mode == ATOM_ENCODER_MODE_DP) args.v3.sInput.ucDispPllConfig |= - DISPPLL_CONFIG_DUAL_LINK; + DISPPLL_CONFIG_COHERENT_MODE; + else { + if (mode->clock > 165000) + args.v3.sInput.ucDispPllConfig |= + DISPPLL_CONFIG_DUAL_LINK; + } } atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); @@ -592,8 +640,9 @@ static void atombios_crtc_set_dcpll(struct drm_crtc *crtc) memset(&args, 0, sizeof(args)); index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); - atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, - &crev); + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, + &crev)) + return; switch (frev) { case 1: @@ -667,8 +716,9 @@ static void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode &ref_div, &post_div); index = GetIndexIntoMasterTable(COMMAND, SetPixelClock); - atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, - &crev); + if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, + &crev)) + return; switch (frev) { case 1: @@ -1083,15 +1133,12 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc, /* TODO color tiling */ - /* pick pll */ - radeon_crtc->pll_id = radeon_atom_pick_pll(crtc); - - atombios_set_ss(crtc, 0); + atombios_disable_ss(crtc); /* always set DCPLL */ if (ASIC_IS_DCE4(rdev)) atombios_crtc_set_dcpll(crtc); atombios_crtc_set_pll(crtc, adjusted_mode); - atombios_set_ss(crtc, 1); + atombios_enable_ss(crtc); if (ASIC_IS_DCE4(rdev)) atombios_set_crtc_dtd_timing(crtc, adjusted_mode); @@ -1120,6 +1167,11 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc, static void atombios_crtc_prepare(struct drm_crtc *crtc) { + |