diff options
-rw-r--r-- | drivers/gpu/drm/radeon/atom.c | 59 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/atom.h | 2 |
2 files changed, 48 insertions, 13 deletions
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index d75788feac6c..b7fe660985c4 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c | |||
@@ -52,15 +52,17 @@ | |||
52 | 52 | ||
53 | typedef struct { | 53 | typedef struct { |
54 | struct atom_context *ctx; | 54 | struct atom_context *ctx; |
55 | |||
56 | uint32_t *ps, *ws; | 55 | uint32_t *ps, *ws; |
57 | int ps_shift; | 56 | int ps_shift; |
58 | uint16_t start; | 57 | uint16_t start; |
58 | unsigned last_jump; | ||
59 | unsigned long last_jump_jiffies; | ||
60 | bool abort; | ||
59 | } atom_exec_context; | 61 | } atom_exec_context; |
60 | 62 | ||
61 | int atom_debug = 0; | 63 | int atom_debug = 0; |
62 | static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params); | 64 | static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params); |
63 | void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params); | 65 | int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params); |
64 | 66 | ||
65 | static uint32_t atom_arg_mask[8] = | 67 | static uint32_t atom_arg_mask[8] = |
66 | { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000, | 68 | { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000, |
@@ -604,12 +606,17 @@ static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg) | |||
604 | static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg) | 606 | static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg) |
605 | { | 607 | { |
606 | int idx = U8((*ptr)++); | 608 | int idx = U8((*ptr)++); |
609 | int r = 0; | ||
610 | |||
607 | if (idx < ATOM_TABLE_NAMES_CNT) | 611 | if (idx < ATOM_TABLE_NAMES_CNT) |
608 | SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]); | 612 | SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]); |
609 | else | 613 | else |
610 | SDEBUG(" table: %d\n", idx); | 614 | SDEBUG(" table: %d\n", idx); |
611 | if (U16(ctx->ctx->cmd_table + 4 + 2 * idx)) | 615 | if (U16(ctx->ctx->cmd_table + 4 + 2 * idx)) |
612 | atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift); | 616 | r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift); |
617 | if (r) { | ||
618 | ctx->abort = true; | ||
619 | } | ||
613 | } | 620 | } |
614 | 621 | ||
615 | static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg) | 622 | static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg) |
@@ -673,6 +680,8 @@ static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg) | |||
673 | static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) | 680 | static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) |
674 | { | 681 | { |
675 | int execute = 0, target = U16(*ptr); | 682 | int execute = 0, target = U16(*ptr); |
683 | unsigned long cjiffies; | ||
684 | |||
676 | (*ptr) += 2; | 685 | (*ptr) += 2; |
677 | switch (arg) { | 686 | switch (arg) { |
678 | case ATOM_COND_ABOVE: | 687 | case ATOM_COND_ABOVE: |
@@ -700,8 +709,25 @@ static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) | |||
700 | if (arg != ATOM_COND_ALWAYS) | 709 | if (arg != ATOM_COND_ALWAYS) |
701 | SDEBUG(" taken: %s\n", execute ? "yes" : "no"); | 710 | SDEBUG(" taken: %s\n", execute ? "yes" : "no"); |
702 | SDEBUG(" target: 0x%04X\n", target); | 711 | SDEBUG(" target: 0x%04X\n", target); |
703 | if (execute) | 712 | if (execute) { |
713 | if (ctx->last_jump == (ctx->start + target)) { | ||
714 | cjiffies = jiffies; | ||
715 | if (time_after(cjiffies, ctx->last_jump_jiffies)) { | ||
716 | cjiffies -= ctx->last_jump_jiffies; | ||
717 | if ((jiffies_to_msecs(cjiffies) > 1000)) { | ||
718 | DRM_ERROR("atombios stuck in loop for more than 1sec aborting\n"); | ||
719 | ctx->abort = true; | ||
720 | } | ||
721 | } else { | ||
722 | /* jiffies wrap around we will just wait a little longer */ | ||
723 | ctx->last_jump_jiffies = jiffies; | ||
724 | } | ||
725 | } else { | ||
726 | ctx->last_jump = ctx->start + target; | ||
727 | ctx->last_jump_jiffies = jiffies; | ||
728 | } | ||
704 | *ptr = ctx->start + target; | 729 | *ptr = ctx->start + target; |
730 | } | ||
705 | } | 731 | } |
706 | 732 | ||
707 | static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg) | 733 | static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg) |
@@ -1104,7 +1130,7 @@ static struct { | |||
1104 | atom_op_shr, ATOM_ARG_MC}, { | 1130 | atom_op_shr, ATOM_ARG_MC}, { |
1105 | atom_op_debug, 0},}; | 1131 | atom_op_debug, 0},}; |
1106 | 1132 | ||
1107 | static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params) | 1133 | static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params) |
1108 | { | 1134 | { |
1109 | int base = CU16(ctx->cmd_table + 4 + 2 * index); | 1135 | int base = CU16(ctx->cmd_table + 4 + 2 * index); |
1110 | int len, ws, ps, ptr; | 1136 | int len, ws, ps, ptr; |
@@ -1112,7 +1138,7 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3 | |||
1112 | atom_exec_context ectx; | 1138 | atom_exec_context ectx; |
1113 | 1139 | ||
1114 | if (!base) | 1140 | if (!base) |
1115 | return; | 1141 | return -EINVAL; |
1116 | 1142 | ||
1117 | len = CU16(base + ATOM_CT_SIZE_PTR); | 1143 | len = CU16(base + ATOM_CT_SIZE_PTR); |
1118 | ws = CU8(base + ATOM_CT_WS_PTR); | 1144 | ws = CU8(base + ATOM_CT_WS_PTR); |
@@ -1125,6 +1151,8 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3 | |||
1125 | ectx.ps_shift = ps / 4; | 1151 | ectx.ps_shift = ps / 4; |
1126 | ectx.start = base; | 1152 | ectx.start = base; |
1127 | ectx.ps = params; | 1153 | ectx.ps = params; |
1154 | ectx.abort = false; | ||
1155 | ectx.last_jump = 0; | ||
1128 | if (ws) | 1156 | if (ws) |
1129 | ectx.ws = kzalloc(4 * ws, GFP_KERNEL); | 1157 | ectx.ws = kzalloc(4 * ws, GFP_KERNEL); |
1130 | else | 1158 | else |
@@ -1137,6 +1165,11 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3 | |||
1137 | SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1); | 1165 | SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1); |
1138 | else | 1166 | else |
1139 | SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1); | 1167 | SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1); |
1168 | if (ectx.abort) { | ||
1169 | DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n", | ||
1170 | base, len, ws, ps, ptr - 1); | ||
1171 | return -EINVAL; | ||
1172 | } | ||
1140 | 1173 | ||
1141 | if (op < ATOM_OP_CNT && op > 0) | 1174 | if (op < ATOM_OP_CNT && op > 0) |
1142 | opcode_table[op].func(&ectx, &ptr, | 1175 | opcode_table[op].func(&ectx, &ptr, |
@@ -1152,10 +1185,13 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3 | |||
1152 | 1185 | ||
1153 | if (ws) | 1186 | if (ws) |
1154 | kfree(ectx.ws); | 1187 | kfree(ectx.ws); |
1188 | return 0; | ||
1155 | } | 1189 | } |
1156 | 1190 | ||
1157 | void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) | 1191 | int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) |
1158 | { | 1192 | { |
1193 | int r; | ||
1194 | |||
1159 | mutex_lock(&ctx->mutex); | 1195 | mutex_lock(&ctx->mutex); |
1160 | /* reset reg block */ | 1196 | /* reset reg block */ |
1161 | ctx->reg_block = 0; | 1197 | ctx->reg_block = 0; |
@@ -1163,8 +1199,9 @@ void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) | |||
1163 | ctx->fb_base = 0; | 1199 | ctx->fb_base = 0; |
1164 | /* reset io mode */ | 1200 | /* reset io mode */ |
1165 | ctx->io_mode = ATOM_IO_MM; | 1201 | ctx->io_mode = ATOM_IO_MM; |
1166 | atom_execute_table_locked(ctx, index, params); | 1202 | r = atom_execute_table_locked(ctx, index, params); |
1167 | mutex_unlock(&ctx->mutex); | 1203 | mutex_unlock(&ctx->mutex); |
1204 | return r; | ||
1168 | } | 1205 | } |
1169 | 1206 | ||
1170 | static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 }; | 1207 | static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 }; |
@@ -1248,9 +1285,7 @@ int atom_asic_init(struct atom_context *ctx) | |||
1248 | 1285 | ||
1249 | if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT)) | 1286 | if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT)) |
1250 | return 1; | 1287 | return 1; |
1251 | atom_execute_table(ctx, ATOM_CMD_INIT, ps); | 1288 | return atom_execute_table(ctx, ATOM_CMD_INIT, ps); |
1252 | |||
1253 | return 0; | ||
1254 | } | 1289 | } |
1255 | 1290 | ||
1256 | void atom_destroy(struct atom_context *ctx) | 1291 | void atom_destroy(struct atom_context *ctx) |
diff --git a/drivers/gpu/drm/radeon/atom.h b/drivers/gpu/drm/radeon/atom.h index bc73781423a1..1b2626314804 100644 --- a/drivers/gpu/drm/radeon/atom.h +++ b/drivers/gpu/drm/radeon/atom.h | |||
@@ -140,7 +140,7 @@ struct atom_context { | |||
140 | extern int atom_debug; | 140 | extern int atom_debug; |
141 | 141 | ||
142 | struct atom_context *atom_parse(struct card_info *, void *); | 142 | struct atom_context *atom_parse(struct card_info *, void *); |
143 | void atom_execute_table(struct atom_context *, int, uint32_t *); | 143 | int atom_execute_table(struct atom_context *, int, uint32_t *); |
144 | int atom_asic_init(struct atom_context *); | 144 | int atom_asic_init(struct atom_context *); |
145 | void atom_destroy(struct atom_context *); | 145 | void atom_destroy(struct atom_context *); |
146 | void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start); | 146 | void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start); |