diff options
Diffstat (limited to 'drivers/gpu/drm/radeon/atom.c')
-rw-r--r-- | drivers/gpu/drm/radeon/atom.c | 105 |
1 files changed, 81 insertions, 24 deletions
diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c index d75788feac6c..1d569830ed99 100644 --- a/drivers/gpu/drm/radeon/atom.c +++ b/drivers/gpu/drm/radeon/atom.c | |||
@@ -24,6 +24,7 @@ | |||
24 | 24 | ||
25 | #include <linux/module.h> | 25 | #include <linux/module.h> |
26 | #include <linux/sched.h> | 26 | #include <linux/sched.h> |
27 | #include <linux/slab.h> | ||
27 | #include <asm/unaligned.h> | 28 | #include <asm/unaligned.h> |
28 | 29 | ||
29 | #define ATOM_DEBUG | 30 | #define ATOM_DEBUG |
@@ -52,15 +53,17 @@ | |||
52 | 53 | ||
53 | typedef struct { | 54 | typedef struct { |
54 | struct atom_context *ctx; | 55 | struct atom_context *ctx; |
55 | |||
56 | uint32_t *ps, *ws; | 56 | uint32_t *ps, *ws; |
57 | int ps_shift; | 57 | int ps_shift; |
58 | uint16_t start; | 58 | uint16_t start; |
59 | unsigned last_jump; | ||
60 | unsigned long last_jump_jiffies; | ||
61 | bool abort; | ||
59 | } atom_exec_context; | 62 | } atom_exec_context; |
60 | 63 | ||
61 | int atom_debug = 0; | 64 | int atom_debug = 0; |
62 | static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params); | 65 | 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); | 66 | int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params); |
64 | 67 | ||
65 | static uint32_t atom_arg_mask[8] = | 68 | static uint32_t atom_arg_mask[8] = |
66 | { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000, | 69 | { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000, |
@@ -604,12 +607,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) | 607 | static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg) |
605 | { | 608 | { |
606 | int idx = U8((*ptr)++); | 609 | int idx = U8((*ptr)++); |
610 | int r = 0; | ||
611 | |||
607 | if (idx < ATOM_TABLE_NAMES_CNT) | 612 | if (idx < ATOM_TABLE_NAMES_CNT) |
608 | SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]); | 613 | SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]); |
609 | else | 614 | else |
610 | SDEBUG(" table: %d\n", idx); | 615 | SDEBUG(" table: %d\n", idx); |
611 | if (U16(ctx->ctx->cmd_table + 4 + 2 * idx)) | 616 | if (U16(ctx->ctx->cmd_table + 4 + 2 * idx)) |
612 | atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift); | 617 | r = atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift); |
618 | if (r) { | ||
619 | ctx->abort = true; | ||
620 | } | ||
613 | } | 621 | } |
614 | 622 | ||
615 | static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg) | 623 | 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) | |||
673 | static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) | 681 | static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) |
674 | { | 682 | { |
675 | int execute = 0, target = U16(*ptr); | 683 | int execute = 0, target = U16(*ptr); |
684 | unsigned long cjiffies; | ||
685 | |||
676 | (*ptr) += 2; | 686 | (*ptr) += 2; |
677 | switch (arg) { | 687 | switch (arg) { |
678 | case ATOM_COND_ABOVE: | 688 | case ATOM_COND_ABOVE: |
@@ -700,8 +710,25 @@ static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg) | |||
700 | if (arg != ATOM_COND_ALWAYS) | 710 | if (arg != ATOM_COND_ALWAYS) |
701 | SDEBUG(" taken: %s\n", execute ? "yes" : "no"); | 711 | SDEBUG(" taken: %s\n", execute ? "yes" : "no"); |
702 | SDEBUG(" target: 0x%04X\n", target); | 712 | SDEBUG(" target: 0x%04X\n", target); |
703 | if (execute) | 713 | if (execute) { |
714 | if (ctx->last_jump == (ctx->start + target)) { | ||
715 | cjiffies = jiffies; | ||
716 | if (time_after(cjiffies, ctx->last_jump_jiffies)) { | ||
717 | cjiffies -= ctx->last_jump_jiffies; | ||
718 | if ((jiffies_to_msecs(cjiffies) > 1000)) { | ||
719 | DRM_ERROR("atombios stuck in loop for more than 1sec aborting\n"); | ||
720 | ctx->abort = true; | ||
721 | } | ||
722 | } else { | ||
723 | /* jiffies wrap around we will just wait a little longer */ | ||
724 | ctx->last_jump_jiffies = jiffies; | ||
725 | } | ||
726 | } else { | ||
727 | ctx->last_jump = ctx->start + target; | ||
728 | ctx->last_jump_jiffies = jiffies; | ||
729 | } | ||
704 | *ptr = ctx->start + target; | 730 | *ptr = ctx->start + target; |
731 | } | ||
705 | } | 732 | } |
706 | 733 | ||
707 | static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg) | 734 | 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) | |||
881 | uint8_t attr = U8((*ptr)++), shift; | 908 | uint8_t attr = U8((*ptr)++), shift; |
882 | uint32_t saved, dst; | 909 | uint32_t saved, dst; |
883 | int dptr = *ptr; | 910 | int dptr = *ptr; |
911 | uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3]; | ||
884 | SDEBUG(" dst: "); | 912 | SDEBUG(" dst: "); |
885 | dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); | 913 | dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); |
914 | /* op needs to full dst value */ | ||
915 | dst = saved; | ||
886 | shift = atom_get_src(ctx, attr, ptr); | 916 | shift = atom_get_src(ctx, attr, ptr); |
887 | SDEBUG(" shift: %d\n", shift); | 917 | SDEBUG(" shift: %d\n", shift); |
888 | dst <<= shift; | 918 | dst <<= shift; |
919 | dst &= atom_arg_mask[dst_align]; | ||
920 | dst >>= atom_arg_shift[dst_align]; | ||
889 | SDEBUG(" dst: "); | 921 | SDEBUG(" dst: "); |
890 | atom_put_dst(ctx, arg, attr, &dptr, dst, saved); | 922 | atom_put_dst(ctx, arg, attr, &dptr, dst, saved); |
891 | } | 923 | } |
@@ -895,11 +927,16 @@ static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg) | |||
895 | uint8_t attr = U8((*ptr)++), shift; | 927 | uint8_t attr = U8((*ptr)++), shift; |
896 | uint32_t saved, dst; | 928 | uint32_t saved, dst; |
897 | int dptr = *ptr; | 929 | int dptr = *ptr; |
930 | uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3]; | ||
898 | SDEBUG(" dst: "); | 931 | SDEBUG(" dst: "); |
899 | dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); | 932 | dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1); |
933 | /* op needs to full dst value */ | ||
934 | dst = saved; | ||
900 | shift = atom_get_src(ctx, attr, ptr); | 935 | shift = atom_get_src(ctx, attr, ptr); |
901 | SDEBUG(" shift: %d\n", shift); | 936 | SDEBUG(" shift: %d\n", shift); |
902 | dst >>= shift; | 937 | dst >>= shift; |
938 | dst &= atom_arg_mask[dst_align]; | ||
939 | dst >>= atom_arg_shift[dst_align]; | ||
903 | SDEBUG(" dst: "); | 940 | SDEBUG(" dst: "); |
904 | atom_put_dst(ctx, arg, attr, &dptr, dst, saved); | 941 | atom_put_dst(ctx, arg, attr, &dptr, dst, saved); |
905 | } | 942 | } |
@@ -1104,15 +1141,16 @@ static struct { | |||
1104 | atom_op_shr, ATOM_ARG_MC}, { | 1141 | atom_op_shr, ATOM_ARG_MC}, { |
1105 | atom_op_debug, 0},}; | 1142 | atom_op_debug, 0},}; |
1106 | 1143 | ||
1107 | static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params) | 1144 | static int atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params) |
1108 | { | 1145 | { |
1109 | int base = CU16(ctx->cmd_table + 4 + 2 * index); | 1146 | int base = CU16(ctx->cmd_table + 4 + 2 * index); |
1110 | int len, ws, ps, ptr; | 1147 | int len, ws, ps, ptr; |
1111 | unsigned char op; | 1148 | unsigned char op; |
1112 | atom_exec_context ectx; | 1149 | atom_exec_context ectx; |
1150 | int ret = 0; | ||
1113 | 1151 | ||
1114 | if (!base) | 1152 | if (!base) |
1115 | return; | 1153 | return -EINVAL; |
1116 | 1154 | ||
1117 | len = CU16(base + ATOM_CT_SIZE_PTR); | 1155 | len = CU16(base + ATOM_CT_SIZE_PTR); |
1118 | ws = CU8(base + ATOM_CT_WS_PTR); | 1156 | ws = CU8(base + ATOM_CT_WS_PTR); |
@@ -1125,6 +1163,8 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3 | |||
1125 | ectx.ps_shift = ps / 4; | 1163 | ectx.ps_shift = ps / 4; |
1126 | ectx.start = base; | 1164 | ectx.start = base; |
1127 | ectx.ps = params; | 1165 | ectx.ps = params; |
1166 | ectx.abort = false; | ||
1167 | ectx.last_jump = 0; | ||
1128 | if (ws) | 1168 | if (ws) |
1129 | ectx.ws = kzalloc(4 * ws, GFP_KERNEL); | 1169 | ectx.ws = kzalloc(4 * ws, GFP_KERNEL); |
1130 | else | 1170 | else |
@@ -1137,6 +1177,12 @@ 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); | 1177 | SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1); |
1138 | else | 1178 | else |
1139 | SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1); | 1179 | SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1); |
1180 | if (ectx.abort) { | ||
1181 | DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n", | ||
1182 | base, len, ws, ps, ptr - 1); | ||
1183 | ret = -EINVAL; | ||
1184 | goto free; | ||
1185 | } | ||
1140 | 1186 | ||
1141 | if (op < ATOM_OP_CNT && op > 0) | 1187 | if (op < ATOM_OP_CNT && op > 0) |
1142 | opcode_table[op].func(&ectx, &ptr, | 1188 | opcode_table[op].func(&ectx, &ptr, |
@@ -1150,12 +1196,16 @@ static void atom_execute_table_locked(struct atom_context *ctx, int index, uint3 | |||
1150 | debug_depth--; | 1196 | debug_depth--; |
1151 | SDEBUG("<<\n"); | 1197 | SDEBUG("<<\n"); |
1152 | 1198 | ||
1199 | free: | ||
1153 | if (ws) | 1200 | if (ws) |
1154 | kfree(ectx.ws); | 1201 | kfree(ectx.ws); |
1202 | return ret; | ||
1155 | } | 1203 | } |
1156 | 1204 | ||
1157 | void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) | 1205 | int atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) |
1158 | { | 1206 | { |
1207 | int r; | ||
1208 | |||
1159 | mutex_lock(&ctx->mutex); | 1209 | mutex_lock(&ctx->mutex); |
1160 | /* reset reg block */ | 1210 | /* reset reg block */ |
1161 | ctx->reg_block = 0; | 1211 | ctx->reg_block = 0; |
@@ -1163,8 +1213,9 @@ void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params) | |||
1163 | ctx->fb_base = 0; | 1213 | ctx->fb_base = 0; |
1164 | /* reset io mode */ | 1214 | /* reset io mode */ |
1165 | ctx->io_mode = ATOM_IO_MM; | 1215 | ctx->io_mode = ATOM_IO_MM; |
1166 | atom_execute_table_locked(ctx, index, params); | 1216 | r = atom_execute_table_locked(ctx, index, params); |
1167 | mutex_unlock(&ctx->mutex); | 1217 | mutex_unlock(&ctx->mutex); |
1218 | return r; | ||
1168 | } | 1219 | } |
1169 | 1220 | ||
1170 | static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 }; | 1221 | 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) | |||
1248 | 1299 | ||
1249 | if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT)) | 1300 | if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT)) |
1250 | return 1; | 1301 | return 1; |
1251 | atom_execute_table(ctx, ATOM_CMD_INIT, ps); | 1302 | return atom_execute_table(ctx, ATOM_CMD_INIT, ps); |
1252 | |||
1253 | return 0; | ||
1254 | } | 1303 | } |
1255 | 1304 | ||
1256 | void atom_destroy(struct atom_context *ctx) | 1305 | void atom_destroy(struct atom_context *ctx) |
@@ -1260,12 +1309,16 @@ void atom_destroy(struct atom_context *ctx) | |||
1260 | kfree(ctx); | 1309 | kfree(ctx); |
1261 | } | 1310 | } |
1262 | 1311 | ||
1263 | void atom_parse_data_header(struct atom_context *ctx, int index, | 1312 | bool atom_parse_data_header(struct atom_context *ctx, int index, |
1264 | uint16_t * size, uint8_t * frev, uint8_t * crev, | 1313 | uint16_t * size, uint8_t * frev, uint8_t * crev, |
1265 | uint16_t * data_start) | 1314 | uint16_t * data_start) |
1266 | { | 1315 | { |
1267 | int offset = index * 2 + 4; | 1316 | int offset = index * 2 + 4; |
1268 | int idx = CU16(ctx->data_table + offset); | 1317 | int idx = CU16(ctx->data_table + offset); |
1318 | u16 *mdt = (u16 *)(ctx->bios + ctx->data_table + 4); | ||
1319 | |||
1320 | if (!mdt[index]) | ||
1321 | return false; | ||
1269 | 1322 | ||
1270 | if (size) | 1323 | if (size) |
1271 | *size = CU16(idx); | 1324 | *size = CU16(idx); |
@@ -1274,38 +1327,42 @@ void atom_parse_data_header(struct atom_context *ctx, int index, | |||
1274 | if (crev) | 1327 | if (crev) |
1275 | *crev = CU8(idx + 3); | 1328 | *crev = CU8(idx + 3); |
1276 | *data_start = idx; | 1329 | *data_start = idx; |
1277 | return; | 1330 | return true; |
1278 | } | 1331 | } |
1279 | 1332 | ||
1280 | void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev, | 1333 | bool atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev, |
1281 | uint8_t * crev) | 1334 | uint8_t * crev) |
1282 | { | 1335 | { |
1283 | int offset = index * 2 + 4; | 1336 | int offset = index * 2 + 4; |
1284 | int idx = CU16(ctx->cmd_table + offset); | 1337 | int idx = CU16(ctx->cmd_table + offset); |
1338 | u16 *mct = (u16 *)(ctx->bios + ctx->cmd_table + 4); | ||
1339 | |||
1340 | if (!mct[index]) | ||
1341 | return false; | ||
1285 | 1342 | ||
1286 | if (frev) | 1343 | if (frev) |
1287 | *frev = CU8(idx + 2); | 1344 | *frev = CU8(idx + 2); |
1288 | if (crev) | 1345 | if (crev) |
1289 | *crev = CU8(idx + 3); | 1346 | *crev = CU8(idx + 3); |
1290 | return; | 1347 | return true; |
1291 | } | 1348 | } |
1292 | 1349 | ||
1293 | int atom_allocate_fb_scratch(struct atom_context *ctx) | 1350 | int atom_allocate_fb_scratch(struct atom_context *ctx) |
1294 | { | 1351 | { |
1295 | int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware); | 1352 | int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware); |
1296 | uint16_t data_offset; | 1353 | uint16_t data_offset; |
1297 | int usage_bytes; | 1354 | int usage_bytes = 0; |
1298 | struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage; | 1355 | struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage; |
1299 | 1356 | ||
1300 | atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset); | 1357 | if (atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset)) { |
1358 | firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset); | ||
1301 | 1359 | ||
1302 | firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset); | 1360 | DRM_DEBUG("atom firmware requested %08x %dkb\n", |
1361 | firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware, | ||
1362 | firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb); | ||
1303 | 1363 | ||
1304 | DRM_DEBUG("atom firmware requested %08x %dkb\n", | 1364 | usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024; |
1305 | firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware, | 1365 | } |
1306 | firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb); | ||
1307 | |||
1308 | usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024; | ||
1309 | if (usage_bytes == 0) | 1366 | if (usage_bytes == 0) |
1310 | usage_bytes = 20 * 1024; | 1367 | usage_bytes = 20 * 1024; |
1311 | /* allocate some scratch memory */ | 1368 | /* allocate some scratch memory */ |