1d38ceaf9SAlex Deucher /*
2d38ceaf9SAlex Deucher * Copyright 2008 Advanced Micro Devices, Inc.
3d38ceaf9SAlex Deucher *
4d38ceaf9SAlex Deucher * Permission is hereby granted, free of charge, to any person obtaining a
5d38ceaf9SAlex Deucher * copy of this software and associated documentation files (the "Software"),
6d38ceaf9SAlex Deucher * to deal in the Software without restriction, including without limitation
7d38ceaf9SAlex Deucher * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8d38ceaf9SAlex Deucher * and/or sell copies of the Software, and to permit persons to whom the
9d38ceaf9SAlex Deucher * Software is furnished to do so, subject to the following conditions:
10d38ceaf9SAlex Deucher *
11d38ceaf9SAlex Deucher * The above copyright notice and this permission notice shall be included in
12d38ceaf9SAlex Deucher * all copies or substantial portions of the Software.
13d38ceaf9SAlex Deucher *
14d38ceaf9SAlex Deucher * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15d38ceaf9SAlex Deucher * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16d38ceaf9SAlex Deucher * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17d38ceaf9SAlex Deucher * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
18d38ceaf9SAlex Deucher * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19d38ceaf9SAlex Deucher * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20d38ceaf9SAlex Deucher * OTHER DEALINGS IN THE SOFTWARE.
21d38ceaf9SAlex Deucher *
22d38ceaf9SAlex Deucher * Author: Stanislaw Skowronek
23d38ceaf9SAlex Deucher */
24d38ceaf9SAlex Deucher
25d38ceaf9SAlex Deucher #include <linux/module.h>
26d38ceaf9SAlex Deucher #include <linux/sched.h>
27d38ceaf9SAlex Deucher #include <linux/slab.h>
28b8c75bd9SLucas De Marchi #include <linux/string_helpers.h>
29b8c75bd9SLucas De Marchi
30*5f60d5f6SAl Viro #include <linux/unaligned.h>
31d38ceaf9SAlex Deucher
32e9eafcb5SSam Ravnborg #include <drm/drm_util.h>
33e9eafcb5SSam Ravnborg
34d38ceaf9SAlex Deucher #define ATOM_DEBUG
35d38ceaf9SAlex Deucher
3629b4c589SJiawei Gu #include "atomfirmware.h"
37d38ceaf9SAlex Deucher #include "atom.h"
38d38ceaf9SAlex Deucher #include "atom-names.h"
39d38ceaf9SAlex Deucher #include "atom-bits.h"
40d38ceaf9SAlex Deucher #include "amdgpu.h"
41d38ceaf9SAlex Deucher
42d38ceaf9SAlex Deucher #define ATOM_COND_ABOVE 0
43d38ceaf9SAlex Deucher #define ATOM_COND_ABOVEOREQUAL 1
44d38ceaf9SAlex Deucher #define ATOM_COND_ALWAYS 2
45d38ceaf9SAlex Deucher #define ATOM_COND_BELOW 3
46d38ceaf9SAlex Deucher #define ATOM_COND_BELOWOREQUAL 4
47d38ceaf9SAlex Deucher #define ATOM_COND_EQUAL 5
48d38ceaf9SAlex Deucher #define ATOM_COND_NOTEQUAL 6
49d38ceaf9SAlex Deucher
50d38ceaf9SAlex Deucher #define ATOM_PORT_ATI 0
51d38ceaf9SAlex Deucher #define ATOM_PORT_PCI 1
52d38ceaf9SAlex Deucher #define ATOM_PORT_SYSIO 2
53d38ceaf9SAlex Deucher
54d38ceaf9SAlex Deucher #define ATOM_UNIT_MICROSEC 0
55d38ceaf9SAlex Deucher #define ATOM_UNIT_MILLISEC 1
56d38ceaf9SAlex Deucher
57d38ceaf9SAlex Deucher #define PLL_INDEX 2
58d38ceaf9SAlex Deucher #define PLL_DATA 3
59d38ceaf9SAlex Deucher
609a785c7aSJohn Clements #define ATOM_CMD_TIMEOUT_SEC 20
619a785c7aSJohn Clements
62d38ceaf9SAlex Deucher typedef struct {
63d38ceaf9SAlex Deucher struct atom_context *ctx;
64d38ceaf9SAlex Deucher uint32_t *ps, *ws;
654630d503SAlexander Richards int ps_size, ws_size;
66d38ceaf9SAlex Deucher int ps_shift;
67d38ceaf9SAlex Deucher uint16_t start;
68d38ceaf9SAlex Deucher unsigned last_jump;
69d38ceaf9SAlex Deucher unsigned long last_jump_jiffies;
70d38ceaf9SAlex Deucher bool abort;
71d38ceaf9SAlex Deucher } atom_exec_context;
72d38ceaf9SAlex Deucher
7387fb7833SDeepak R Varma int amdgpu_atom_debug;
744630d503SAlexander Richards static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params, int params_size);
754630d503SAlexander Richards int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *params, int params_size);
76d38ceaf9SAlex Deucher
77d38ceaf9SAlex Deucher static uint32_t atom_arg_mask[8] =
78d38ceaf9SAlex Deucher { 0xFFFFFFFF, 0xFFFF, 0xFFFF00, 0xFFFF0000, 0xFF, 0xFF00, 0xFF0000,
79d38ceaf9SAlex Deucher 0xFF000000 };
80d38ceaf9SAlex Deucher static int atom_arg_shift[8] = { 0, 0, 8, 16, 0, 8, 16, 24 };
81d38ceaf9SAlex Deucher
82d38ceaf9SAlex Deucher static int atom_dst_to_src[8][4] = {
83d38ceaf9SAlex Deucher /* translate destination alignment field to the source alignment encoding */
84d38ceaf9SAlex Deucher {0, 0, 0, 0},
85d38ceaf9SAlex Deucher {1, 2, 3, 0},
86d38ceaf9SAlex Deucher {1, 2, 3, 0},
87d38ceaf9SAlex Deucher {1, 2, 3, 0},
88d38ceaf9SAlex Deucher {4, 5, 6, 7},
89d38ceaf9SAlex Deucher {4, 5, 6, 7},
90d38ceaf9SAlex Deucher {4, 5, 6, 7},
91d38ceaf9SAlex Deucher {4, 5, 6, 7},
92d38ceaf9SAlex Deucher };
93d38ceaf9SAlex Deucher static int atom_def_dst[8] = { 0, 0, 1, 2, 0, 1, 2, 3 };
94d38ceaf9SAlex Deucher
9587fb7833SDeepak R Varma static int debug_depth;
96d38ceaf9SAlex Deucher #ifdef ATOM_DEBUG
debug_print_spaces(int n)97d38ceaf9SAlex Deucher static void debug_print_spaces(int n)
98d38ceaf9SAlex Deucher {
99d38ceaf9SAlex Deucher while (n--)
100d38ceaf9SAlex Deucher printk(" ");
101d38ceaf9SAlex Deucher }
102d38ceaf9SAlex Deucher
103d38ceaf9SAlex Deucher #define DEBUG(...) do if (amdgpu_atom_debug) { printk(KERN_DEBUG __VA_ARGS__); } while (0)
104d38ceaf9SAlex Deucher #define SDEBUG(...) do if (amdgpu_atom_debug) { printk(KERN_DEBUG); debug_print_spaces(debug_depth); printk(__VA_ARGS__); } while (0)
105d38ceaf9SAlex Deucher #else
106d38ceaf9SAlex Deucher #define DEBUG(...) do { } while (0)
107d38ceaf9SAlex Deucher #define SDEBUG(...) do { } while (0)
108d38ceaf9SAlex Deucher #endif
109d38ceaf9SAlex Deucher
atom_iio_execute(struct atom_context * ctx,int base,uint32_t index,uint32_t data)110d38ceaf9SAlex Deucher static uint32_t atom_iio_execute(struct atom_context *ctx, int base,
111d38ceaf9SAlex Deucher uint32_t index, uint32_t data)
112d38ceaf9SAlex Deucher {
113d38ceaf9SAlex Deucher uint32_t temp = 0xCDCDCDCD;
114d38ceaf9SAlex Deucher
115d38ceaf9SAlex Deucher while (1)
116d38ceaf9SAlex Deucher switch (CU8(base)) {
117d38ceaf9SAlex Deucher case ATOM_IIO_NOP:
118d38ceaf9SAlex Deucher base++;
119d38ceaf9SAlex Deucher break;
120d38ceaf9SAlex Deucher case ATOM_IIO_READ:
121e99d2eaaSAlex Deucher temp = ctx->card->reg_read(ctx->card, CU16(base + 1));
122d38ceaf9SAlex Deucher base += 3;
123d38ceaf9SAlex Deucher break;
124d38ceaf9SAlex Deucher case ATOM_IIO_WRITE:
125e99d2eaaSAlex Deucher ctx->card->reg_write(ctx->card, CU16(base + 1), temp);
126d38ceaf9SAlex Deucher base += 3;
127d38ceaf9SAlex Deucher break;
128d38ceaf9SAlex Deucher case ATOM_IIO_CLEAR:
129d38ceaf9SAlex Deucher temp &=
130d38ceaf9SAlex Deucher ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
131d38ceaf9SAlex Deucher CU8(base + 2));
132d38ceaf9SAlex Deucher base += 3;
133d38ceaf9SAlex Deucher break;
134d38ceaf9SAlex Deucher case ATOM_IIO_SET:
135d38ceaf9SAlex Deucher temp |=
136d38ceaf9SAlex Deucher (0xFFFFFFFF >> (32 - CU8(base + 1))) << CU8(base +
137d38ceaf9SAlex Deucher 2);
138d38ceaf9SAlex Deucher base += 3;
139d38ceaf9SAlex Deucher break;
140d38ceaf9SAlex Deucher case ATOM_IIO_MOVE_INDEX:
141d38ceaf9SAlex Deucher temp &=
142d38ceaf9SAlex Deucher ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
143d38ceaf9SAlex Deucher CU8(base + 3));
144d38ceaf9SAlex Deucher temp |=
145d38ceaf9SAlex Deucher ((index >> CU8(base + 2)) &
146d38ceaf9SAlex Deucher (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
147d38ceaf9SAlex Deucher 3);
148d38ceaf9SAlex Deucher base += 4;
149d38ceaf9SAlex Deucher break;
150d38ceaf9SAlex Deucher case ATOM_IIO_MOVE_DATA:
151d38ceaf9SAlex Deucher temp &=
152d38ceaf9SAlex Deucher ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
153d38ceaf9SAlex Deucher CU8(base + 3));
154d38ceaf9SAlex Deucher temp |=
155d38ceaf9SAlex Deucher ((data >> CU8(base + 2)) &
156d38ceaf9SAlex Deucher (0xFFFFFFFF >> (32 - CU8(base + 1)))) << CU8(base +
157d38ceaf9SAlex Deucher 3);
158d38ceaf9SAlex Deucher base += 4;
159d38ceaf9SAlex Deucher break;
160d38ceaf9SAlex Deucher case ATOM_IIO_MOVE_ATTR:
161d38ceaf9SAlex Deucher temp &=
162d38ceaf9SAlex Deucher ~((0xFFFFFFFF >> (32 - CU8(base + 1))) <<
163d38ceaf9SAlex Deucher CU8(base + 3));
164d38ceaf9SAlex Deucher temp |=
165d38ceaf9SAlex Deucher ((ctx->
166d38ceaf9SAlex Deucher io_attr >> CU8(base + 2)) & (0xFFFFFFFF >> (32 -
167d38ceaf9SAlex Deucher CU8
168d38ceaf9SAlex Deucher (base
169d38ceaf9SAlex Deucher +
170d38ceaf9SAlex Deucher 1))))
171d38ceaf9SAlex Deucher << CU8(base + 3);
172d38ceaf9SAlex Deucher base += 4;
173d38ceaf9SAlex Deucher break;
174d38ceaf9SAlex Deucher case ATOM_IIO_END:
175d38ceaf9SAlex Deucher return temp;
176d38ceaf9SAlex Deucher default:
1777ca85295SJoe Perches pr_info("Unknown IIO opcode\n");
178d38ceaf9SAlex Deucher return 0;
179d38ceaf9SAlex Deucher }
180d38ceaf9SAlex Deucher }
181d38ceaf9SAlex Deucher
atom_get_src_int(atom_exec_context * ctx,uint8_t attr,int * ptr,uint32_t * saved,int print)182d38ceaf9SAlex Deucher static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
183d38ceaf9SAlex Deucher int *ptr, uint32_t *saved, int print)
184d38ceaf9SAlex Deucher {
185d38ceaf9SAlex Deucher uint32_t idx, val = 0xCDCDCDCD, align, arg;
186d38ceaf9SAlex Deucher struct atom_context *gctx = ctx->ctx;
187d38ceaf9SAlex Deucher arg = attr & 7;
188d38ceaf9SAlex Deucher align = (attr >> 3) & 7;
189d38ceaf9SAlex Deucher switch (arg) {
190d38ceaf9SAlex Deucher case ATOM_ARG_REG:
191d38ceaf9SAlex Deucher idx = U16(*ptr);
192d38ceaf9SAlex Deucher (*ptr) += 2;
193d38ceaf9SAlex Deucher if (print)
194d38ceaf9SAlex Deucher DEBUG("REG[0x%04X]", idx);
195d38ceaf9SAlex Deucher idx += gctx->reg_block;
196d38ceaf9SAlex Deucher switch (gctx->io_mode) {
197d38ceaf9SAlex Deucher case ATOM_IO_MM:
198d38ceaf9SAlex Deucher val = gctx->card->reg_read(gctx->card, idx);
199d38ceaf9SAlex Deucher break;
200d38ceaf9SAlex Deucher case ATOM_IO_PCI:
2017ca85295SJoe Perches pr_info("PCI registers are not implemented\n");
202d38ceaf9SAlex Deucher return 0;
203d38ceaf9SAlex Deucher case ATOM_IO_SYSIO:
2047ca85295SJoe Perches pr_info("SYSIO registers are not implemented\n");
205d38ceaf9SAlex Deucher return 0;
206d38ceaf9SAlex Deucher default:
207d38ceaf9SAlex Deucher if (!(gctx->io_mode & 0x80)) {
2087ca85295SJoe Perches pr_info("Bad IO mode\n");
209d38ceaf9SAlex Deucher return 0;
210d38ceaf9SAlex Deucher }
211d38ceaf9SAlex Deucher if (!gctx->iio[gctx->io_mode & 0x7F]) {
2127ca85295SJoe Perches pr_info("Undefined indirect IO read method %d\n",
213d38ceaf9SAlex Deucher gctx->io_mode & 0x7F);
214d38ceaf9SAlex Deucher return 0;
215d38ceaf9SAlex Deucher }
216d38ceaf9SAlex Deucher val =
217d38ceaf9SAlex Deucher atom_iio_execute(gctx,
218d38ceaf9SAlex Deucher gctx->iio[gctx->io_mode & 0x7F],
219d38ceaf9SAlex Deucher idx, 0);
220d38ceaf9SAlex Deucher }
221d38ceaf9SAlex Deucher break;
222d38ceaf9SAlex Deucher case ATOM_ARG_PS:
223d38ceaf9SAlex Deucher idx = U8(*ptr);
224d38ceaf9SAlex Deucher (*ptr)++;
225d38ceaf9SAlex Deucher /* get_unaligned_le32 avoids unaligned accesses from atombios
226d38ceaf9SAlex Deucher * tables, noticed on a DEC Alpha. */
2274630d503SAlexander Richards if (idx < ctx->ps_size)
228d38ceaf9SAlex Deucher val = get_unaligned_le32((u32 *)&ctx->ps[idx]);
2294630d503SAlexander Richards else
2304630d503SAlexander Richards pr_info("PS index out of range: %i > %i\n", idx, ctx->ps_size);
231d38ceaf9SAlex Deucher if (print)
232d38ceaf9SAlex Deucher DEBUG("PS[0x%02X,0x%04X]", idx, val);
233d38ceaf9SAlex Deucher break;
234d38ceaf9SAlex Deucher case ATOM_ARG_WS:
235d38ceaf9SAlex Deucher idx = U8(*ptr);
236d38ceaf9SAlex Deucher (*ptr)++;
237d38ceaf9SAlex Deucher if (print)
238d38ceaf9SAlex Deucher DEBUG("WS[0x%02X]", idx);
239d38ceaf9SAlex Deucher switch (idx) {
240d38ceaf9SAlex Deucher case ATOM_WS_QUOTIENT:
241d38ceaf9SAlex Deucher val = gctx->divmul[0];
242d38ceaf9SAlex Deucher break;
243d38ceaf9SAlex Deucher case ATOM_WS_REMAINDER:
244d38ceaf9SAlex Deucher val = gctx->divmul[1];
245d38ceaf9SAlex Deucher break;
246d38ceaf9SAlex Deucher case ATOM_WS_DATAPTR:
247d38ceaf9SAlex Deucher val = gctx->data_block;
248d38ceaf9SAlex Deucher break;
249d38ceaf9SAlex Deucher case ATOM_WS_SHIFT:
250d38ceaf9SAlex Deucher val = gctx->shift;
251d38ceaf9SAlex Deucher break;
252d38ceaf9SAlex Deucher case ATOM_WS_OR_MASK:
253d38ceaf9SAlex Deucher val = 1 << gctx->shift;
254d38ceaf9SAlex Deucher break;
255d38ceaf9SAlex Deucher case ATOM_WS_AND_MASK:
256d38ceaf9SAlex Deucher val = ~(1 << gctx->shift);
257d38ceaf9SAlex Deucher break;
258d38ceaf9SAlex Deucher case ATOM_WS_FB_WINDOW:
259d38ceaf9SAlex Deucher val = gctx->fb_base;
260d38ceaf9SAlex Deucher break;
261d38ceaf9SAlex Deucher case ATOM_WS_ATTRIBUTES:
262d38ceaf9SAlex Deucher val = gctx->io_attr;
263d38ceaf9SAlex Deucher break;
264d38ceaf9SAlex Deucher case ATOM_WS_REGPTR:
265d38ceaf9SAlex Deucher val = gctx->reg_block;
266d38ceaf9SAlex Deucher break;
267d38ceaf9SAlex Deucher default:
2684630d503SAlexander Richards if (idx < ctx->ws_size)
269d38ceaf9SAlex Deucher val = ctx->ws[idx];
2704630d503SAlexander Richards else
2714630d503SAlexander Richards pr_info("WS index out of range: %i > %i\n", idx, ctx->ws_size);
272d38ceaf9SAlex Deucher }
273d38ceaf9SAlex Deucher break;
274d38ceaf9SAlex Deucher case ATOM_ARG_ID:
275d38ceaf9SAlex Deucher idx = U16(*ptr);
276d38ceaf9SAlex Deucher (*ptr) += 2;
277d38ceaf9SAlex Deucher if (print) {
278d38ceaf9SAlex Deucher if (gctx->data_block)
279d38ceaf9SAlex Deucher DEBUG("ID[0x%04X+%04X]", idx, gctx->data_block);
280d38ceaf9SAlex Deucher else
281d38ceaf9SAlex Deucher DEBUG("ID[0x%04X]", idx);
282d38ceaf9SAlex Deucher }
283d38ceaf9SAlex Deucher val = U32(idx + gctx->data_block);
284d38ceaf9SAlex Deucher break;
285d38ceaf9SAlex Deucher case ATOM_ARG_FB:
286d38ceaf9SAlex Deucher idx = U8(*ptr);
287d38ceaf9SAlex Deucher (*ptr)++;
288d38ceaf9SAlex Deucher if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
289d38ceaf9SAlex Deucher DRM_ERROR("ATOM: fb read beyond scratch region: %d vs. %d\n",
290d38ceaf9SAlex Deucher gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
291d38ceaf9SAlex Deucher val = 0;
292d38ceaf9SAlex Deucher } else
293d38ceaf9SAlex Deucher val = gctx->scratch[(gctx->fb_base / 4) + idx];
294d38ceaf9SAlex Deucher if (print)
295d38ceaf9SAlex Deucher DEBUG("FB[0x%02X]", idx);
296d38ceaf9SAlex Deucher break;
297d38ceaf9SAlex Deucher case ATOM_ARG_IMM:
298d38ceaf9SAlex Deucher switch (align) {
299d38ceaf9SAlex Deucher case ATOM_SRC_DWORD:
300d38ceaf9SAlex Deucher val = U32(*ptr);
301d38ceaf9SAlex Deucher (*ptr) += 4;
302d38ceaf9SAlex Deucher if (print)
303d38ceaf9SAlex Deucher DEBUG("IMM 0x%08X\n", val);
30427b500b7SJesse Zhang break;
305d38ceaf9SAlex Deucher case ATOM_SRC_WORD0:
306d38ceaf9SAlex Deucher case ATOM_SRC_WORD8:
307d38ceaf9SAlex Deucher case ATOM_SRC_WORD16:
308d38ceaf9SAlex Deucher val = U16(*ptr);
309d38ceaf9SAlex Deucher (*ptr) += 2;
310d38ceaf9SAlex Deucher if (print)
311d38ceaf9SAlex Deucher DEBUG("IMM 0x%04X\n", val);
31227b500b7SJesse Zhang break;
313d38ceaf9SAlex Deucher case ATOM_SRC_BYTE0:
314d38ceaf9SAlex Deucher case ATOM_SRC_BYTE8:
315d38ceaf9SAlex Deucher case ATOM_SRC_BYTE16:
316d38ceaf9SAlex Deucher case ATOM_SRC_BYTE24:
317d38ceaf9SAlex Deucher val = U8(*ptr);
318d38ceaf9SAlex Deucher (*ptr)++;
319d38ceaf9SAlex Deucher if (print)
320d38ceaf9SAlex Deucher DEBUG("IMM 0x%02X\n", val);
3217cf1ad2fSSrinivasan Shanmugam break;
32227b500b7SJesse Zhang }
32327b500b7SJesse Zhang return val;
324d38ceaf9SAlex Deucher case ATOM_ARG_PLL:
325d38ceaf9SAlex Deucher idx = U8(*ptr);
326d38ceaf9SAlex Deucher (*ptr)++;
327d38ceaf9SAlex Deucher if (print)
328d38ceaf9SAlex Deucher DEBUG("PLL[0x%02X]", idx);
329d38ceaf9SAlex Deucher val = gctx->card->pll_read(gctx->card, idx);
330d38ceaf9SAlex Deucher break;
331d38ceaf9SAlex Deucher case ATOM_ARG_MC:
332d38ceaf9SAlex Deucher idx = U8(*ptr);
333d38ceaf9SAlex Deucher (*ptr)++;
334d38ceaf9SAlex Deucher if (print)
335d38ceaf9SAlex Deucher DEBUG("MC[0x%02X]", idx);
336d38ceaf9SAlex Deucher val = gctx->card->mc_read(gctx->card, idx);
337d38ceaf9SAlex Deucher break;
338d38ceaf9SAlex Deucher }
339d38ceaf9SAlex Deucher if (saved)
340d38ceaf9SAlex Deucher *saved = val;
341d38ceaf9SAlex Deucher val &= atom_arg_mask[align];
342d38ceaf9SAlex Deucher val >>= atom_arg_shift[align];
343d38ceaf9SAlex Deucher if (print)
344d38ceaf9SAlex Deucher switch (align) {
345d38ceaf9SAlex Deucher case ATOM_SRC_DWORD:
346d38ceaf9SAlex Deucher DEBUG(".[31:0] -> 0x%08X\n", val);
347d38ceaf9SAlex Deucher break;
348d38ceaf9SAlex Deucher case ATOM_SRC_WORD0:
349d38ceaf9SAlex Deucher DEBUG(".[15:0] -> 0x%04X\n", val);
350d38ceaf9SAlex Deucher break;
351d38ceaf9SAlex Deucher case ATOM_SRC_WORD8:
352d38ceaf9SAlex Deucher DEBUG(".[23:8] -> 0x%04X\n", val);
353d38ceaf9SAlex Deucher break;
354d38ceaf9SAlex Deucher case ATOM_SRC_WORD16:
355d38ceaf9SAlex Deucher DEBUG(".[31:16] -> 0x%04X\n", val);
356d38ceaf9SAlex Deucher break;
357d38ceaf9SAlex Deucher case ATOM_SRC_BYTE0:
358d38ceaf9SAlex Deucher DEBUG(".[7:0] -> 0x%02X\n", val);
359d38ceaf9SAlex Deucher break;
360d38ceaf9SAlex Deucher case ATOM_SRC_BYTE8:
361d38ceaf9SAlex Deucher DEBUG(".[15:8] -> 0x%02X\n", val);
362d38ceaf9SAlex Deucher break;
363d38ceaf9SAlex Deucher case ATOM_SRC_BYTE16:
364d38ceaf9SAlex Deucher DEBUG(".[23:16] -> 0x%02X\n", val);
365d38ceaf9SAlex Deucher break;
366d38ceaf9SAlex Deucher case ATOM_SRC_BYTE24:
367d38ceaf9SAlex Deucher DEBUG(".[31:24] -> 0x%02X\n", val);
368d38ceaf9SAlex Deucher break;
369d38ceaf9SAlex Deucher }
370d38ceaf9SAlex Deucher return val;
371d38ceaf9SAlex Deucher }
372d38ceaf9SAlex Deucher
atom_skip_src_int(atom_exec_context * ctx,uint8_t attr,int * ptr)373d38ceaf9SAlex Deucher static void atom_skip_src_int(atom_exec_context *ctx, uint8_t attr, int *ptr)
374d38ceaf9SAlex Deucher {
375d38ceaf9SAlex Deucher uint32_t align = (attr >> 3) & 7, arg = attr & 7;
376d38ceaf9SAlex Deucher switch (arg) {
377d38ceaf9SAlex Deucher case ATOM_ARG_REG:
378d38ceaf9SAlex Deucher case ATOM_ARG_ID:
379d38ceaf9SAlex Deucher (*ptr) += 2;
380d38ceaf9SAlex Deucher break;
381d38ceaf9SAlex Deucher case ATOM_ARG_PLL:
382d38ceaf9SAlex Deucher case ATOM_ARG_MC:
383d38ceaf9SAlex Deucher case ATOM_ARG_PS:
384d38ceaf9SAlex Deucher case ATOM_ARG_WS:
385d38ceaf9SAlex Deucher case ATOM_ARG_FB:
386d38ceaf9SAlex Deucher (*ptr)++;
387d38ceaf9SAlex Deucher break;
388d38ceaf9SAlex Deucher case ATOM_ARG_IMM:
389d38ceaf9SAlex Deucher switch (align) {
390d38ceaf9SAlex Deucher case ATOM_SRC_DWORD:
391d38ceaf9SAlex Deucher (*ptr) += 4;
392d38ceaf9SAlex Deucher return;
393d38ceaf9SAlex Deucher case ATOM_SRC_WORD0:
394d38ceaf9SAlex Deucher case ATOM_SRC_WORD8:
395d38ceaf9SAlex Deucher case ATOM_SRC_WORD16:
396d38ceaf9SAlex Deucher (*ptr) += 2;
397d38ceaf9SAlex Deucher return;
398d38ceaf9SAlex Deucher case ATOM_SRC_BYTE0:
399d38ceaf9SAlex Deucher case ATOM_SRC_BYTE8:
400d38ceaf9SAlex Deucher case ATOM_SRC_BYTE16:
401d38ceaf9SAlex Deucher case ATOM_SRC_BYTE24:
402d38ceaf9SAlex Deucher (*ptr)++;
403d38ceaf9SAlex Deucher return;
404d38ceaf9SAlex Deucher }
405d38ceaf9SAlex Deucher }
406d38ceaf9SAlex Deucher }
407d38ceaf9SAlex Deucher
atom_get_src(atom_exec_context * ctx,uint8_t attr,int * ptr)408d38ceaf9SAlex Deucher static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr)
409d38ceaf9SAlex Deucher {
410d38ceaf9SAlex Deucher return atom_get_src_int(ctx, attr, ptr, NULL, 1);
411d38ceaf9SAlex Deucher }
412d38ceaf9SAlex Deucher
atom_get_src_direct(atom_exec_context * ctx,uint8_t align,int * ptr)413d38ceaf9SAlex Deucher static uint32_t atom_get_src_direct(atom_exec_context *ctx, uint8_t align, int *ptr)
414d38ceaf9SAlex Deucher {
415d38ceaf9SAlex Deucher uint32_t val = 0xCDCDCDCD;
416d38ceaf9SAlex Deucher
417d38ceaf9SAlex Deucher switch (align) {
418d38ceaf9SAlex Deucher case ATOM_SRC_DWORD:
419d38ceaf9SAlex Deucher val = U32(*ptr);
420d38ceaf9SAlex Deucher (*ptr) += 4;
421d38ceaf9SAlex Deucher break;
422d38ceaf9SAlex Deucher case ATOM_SRC_WORD0:
423d38ceaf9SAlex Deucher case ATOM_SRC_WORD8:
424d38ceaf9SAlex Deucher case ATOM_SRC_WORD16:
425d38ceaf9SAlex Deucher val = U16(*ptr);
426d38ceaf9SAlex Deucher (*ptr) += 2;
427d38ceaf9SAlex Deucher break;
428d38ceaf9SAlex Deucher case ATOM_SRC_BYTE0:
429d38ceaf9SAlex Deucher case ATOM_SRC_BYTE8:
430d38ceaf9SAlex Deucher case ATOM_SRC_BYTE16:
431d38ceaf9SAlex Deucher case ATOM_SRC_BYTE24:
432d38ceaf9SAlex Deucher val = U8(*ptr);
433d38ceaf9SAlex Deucher (*ptr)++;
434d38ceaf9SAlex Deucher break;
435d38ceaf9SAlex Deucher }
436d38ceaf9SAlex Deucher return val;
437d38ceaf9SAlex Deucher }
438d38ceaf9SAlex Deucher
atom_get_dst(atom_exec_context * ctx,int arg,uint8_t attr,int * ptr,uint32_t * saved,int print)439d38ceaf9SAlex Deucher static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr,
440d38ceaf9SAlex Deucher int *ptr, uint32_t *saved, int print)
441d38ceaf9SAlex Deucher {
442d38ceaf9SAlex Deucher return atom_get_src_int(ctx,
443d38ceaf9SAlex Deucher arg | atom_dst_to_src[(attr >> 3) &
444d38ceaf9SAlex Deucher 7][(attr >> 6) & 3] << 3,
445d38ceaf9SAlex Deucher ptr, saved, print);
446d38ceaf9SAlex Deucher }
447d38ceaf9SAlex Deucher
atom_skip_dst(atom_exec_context * ctx,int arg,uint8_t attr,int * ptr)448d38ceaf9SAlex Deucher static void atom_skip_dst(atom_exec_context *ctx, int arg, uint8_t attr, int *ptr)
449d38ceaf9SAlex Deucher {
450d38ceaf9SAlex Deucher atom_skip_src_int(ctx,
451d38ceaf9SAlex Deucher arg | atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) &
452d38ceaf9SAlex Deucher 3] << 3, ptr);
453d38ceaf9SAlex Deucher }
454d38ceaf9SAlex Deucher
atom_put_dst(atom_exec_context * ctx,int arg,uint8_t attr,int * ptr,uint32_t val,uint32_t saved)455d38ceaf9SAlex Deucher static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
456d38ceaf9SAlex Deucher int *ptr, uint32_t val, uint32_t saved)
457d38ceaf9SAlex Deucher {
458d38ceaf9SAlex Deucher uint32_t align =
459d38ceaf9SAlex Deucher atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3], old_val =
460d38ceaf9SAlex Deucher val, idx;
461d38ceaf9SAlex Deucher struct atom_context *gctx = ctx->ctx;
462d38ceaf9SAlex Deucher old_val &= atom_arg_mask[align] >> atom_arg_shift[align];
463d38ceaf9SAlex Deucher val <<= atom_arg_shift[align];
464d38ceaf9SAlex Deucher val &= atom_arg_mask[align];
465d38ceaf9SAlex Deucher saved &= ~atom_arg_mask[align];
466d38ceaf9SAlex Deucher val |= saved;
467d38ceaf9SAlex Deucher switch (arg) {
468d38ceaf9SAlex Deucher case ATOM_ARG_REG:
469d38ceaf9SAlex Deucher idx = U16(*ptr);
470d38ceaf9SAlex Deucher (*ptr) += 2;
471d38ceaf9SAlex Deucher DEBUG("REG[0x%04X]", idx);
472d38ceaf9SAlex Deucher idx += gctx->reg_block;
473d38ceaf9SAlex Deucher switch (gctx->io_mode) {
474d38ceaf9SAlex Deucher case ATOM_IO_MM:
475d38ceaf9SAlex Deucher if (idx == 0)
476d38ceaf9SAlex Deucher gctx->card->reg_write(gctx->card, idx,
477d38ceaf9SAlex Deucher val << 2);
478d38ceaf9SAlex Deucher else
479d38ceaf9SAlex Deucher gctx->card->reg_write(gctx->card, idx, val);
480d38ceaf9SAlex Deucher break;
481d38ceaf9SAlex Deucher case ATOM_IO_PCI:
4827ca85295SJoe Perches pr_info("PCI registers are not implemented\n");
483d38ceaf9SAlex Deucher return;
484d38ceaf9SAlex Deucher case ATOM_IO_SYSIO:
4857ca85295SJoe Perches pr_info("SYSIO registers are not implemented\n");
486d38ceaf9SAlex Deucher return;
487d38ceaf9SAlex Deucher default:
488d38ceaf9SAlex Deucher if (!(gctx->io_mode & 0x80)) {
4897ca85295SJoe Perches pr_info("Bad IO mode\n");
490d38ceaf9SAlex Deucher return;
491d38ceaf9SAlex Deucher }
492d38ceaf9SAlex Deucher if (!gctx->iio[gctx->io_mode & 0xFF]) {
4937ca85295SJoe Perches pr_info("Undefined indirect IO write method %d\n",
494d38ceaf9SAlex Deucher gctx->io_mode & 0x7F);
495d38ceaf9SAlex Deucher return;
496d38ceaf9SAlex Deucher }
497d38ceaf9SAlex Deucher atom_iio_execute(gctx, gctx->iio[gctx->io_mode & 0xFF],
498d38ceaf9SAlex Deucher idx, val);
499d38ceaf9SAlex Deucher }
500d38ceaf9SAlex Deucher break;
501d38ceaf9SAlex Deucher case ATOM_ARG_PS:
502d38ceaf9SAlex Deucher idx = U8(*ptr);
503d38ceaf9SAlex Deucher (*ptr)++;
504d38ceaf9SAlex Deucher DEBUG("PS[0x%02X]", idx);
5054630d503SAlexander Richards if (idx >= ctx->ps_size) {
5064630d503SAlexander Richards pr_info("PS index out of range: %i > %i\n", idx, ctx->ps_size);
5074630d503SAlexander Richards return;
5084630d503SAlexander Richards }
509d38ceaf9SAlex Deucher ctx->ps[idx] = cpu_to_le32(val);
510d38ceaf9SAlex Deucher break;
511d38ceaf9SAlex Deucher case ATOM_ARG_WS:
512d38ceaf9SAlex Deucher idx = U8(*ptr);
513d38ceaf9SAlex Deucher (*ptr)++;
514d38ceaf9SAlex Deucher DEBUG("WS[0x%02X]", idx);
515d38ceaf9SAlex Deucher switch (idx) {
516d38ceaf9SAlex Deucher case ATOM_WS_QUOTIENT:
517d38ceaf9SAlex Deucher gctx->divmul[0] = val;
518d38ceaf9SAlex Deucher break;
519d38ceaf9SAlex Deucher case ATOM_WS_REMAINDER:
520d38ceaf9SAlex Deucher gctx->divmul[1] = val;
521d38ceaf9SAlex Deucher break;
522d38ceaf9SAlex Deucher case ATOM_WS_DATAPTR:
523d38ceaf9SAlex Deucher gctx->data_block = val;
524d38ceaf9SAlex Deucher break;
525d38ceaf9SAlex Deucher case ATOM_WS_SHIFT:
526d38ceaf9SAlex Deucher gctx->shift = val;
527d38ceaf9SAlex Deucher break;
528d38ceaf9SAlex Deucher case ATOM_WS_OR_MASK:
529d38ceaf9SAlex Deucher case ATOM_WS_AND_MASK:
530d38ceaf9SAlex Deucher break;
531d38ceaf9SAlex Deucher case ATOM_WS_FB_WINDOW:
532d38ceaf9SAlex Deucher gctx->fb_base = val;
533d38ceaf9SAlex Deucher break;
534d38ceaf9SAlex Deucher case ATOM_WS_ATTRIBUTES:
535d38ceaf9SAlex Deucher gctx->io_attr = val;
536d38ceaf9SAlex Deucher break;
537d38ceaf9SAlex Deucher case ATOM_WS_REGPTR:
538d38ceaf9SAlex Deucher gctx->reg_block = val;
539d38ceaf9SAlex Deucher break;
540d38ceaf9SAlex Deucher default:
5414630d503SAlexander Richards if (idx >= ctx->ws_size) {
5424630d503SAlexander Richards pr_info("WS index out of range: %i > %i\n", idx, ctx->ws_size);
5434630d503SAlexander Richards return;
5444630d503SAlexander Richards }
545d38ceaf9SAlex Deucher ctx->ws[idx] = val;
546d38ceaf9SAlex Deucher }
547d38ceaf9SAlex Deucher break;
548d38ceaf9SAlex Deucher case ATOM_ARG_FB:
549d38ceaf9SAlex Deucher idx = U8(*ptr);
550d38ceaf9SAlex Deucher (*ptr)++;
551d38ceaf9SAlex Deucher if ((gctx->fb_base + (idx * 4)) > gctx->scratch_size_bytes) {
552d38ceaf9SAlex Deucher DRM_ERROR("ATOM: fb write beyond scratch region: %d vs. %d\n",
553d38ceaf9SAlex Deucher gctx->fb_base + (idx * 4), gctx->scratch_size_bytes);
554d38ceaf9SAlex Deucher } else
555d38ceaf9SAlex Deucher gctx->scratch[(gctx->fb_base / 4) + idx] = val;
556d38ceaf9SAlex Deucher DEBUG("FB[0x%02X]", idx);
557d38ceaf9SAlex Deucher break;
558d38ceaf9SAlex Deucher case ATOM_ARG_PLL:
559d38ceaf9SAlex Deucher idx = U8(*ptr);
560d38ceaf9SAlex Deucher (*ptr)++;
561d38ceaf9SAlex Deucher DEBUG("PLL[0x%02X]", idx);
562d38ceaf9SAlex Deucher gctx->card->pll_write(gctx->card, idx, val);
563d38ceaf9SAlex Deucher break;
564d38ceaf9SAlex Deucher case ATOM_ARG_MC:
565d38ceaf9SAlex Deucher idx = U8(*ptr);
566d38ceaf9SAlex Deucher (*ptr)++;
567d38ceaf9SAlex Deucher DEBUG("MC[0x%02X]", idx);
568d38ceaf9SAlex Deucher gctx->card->mc_write(gctx->card, idx, val);
569d38ceaf9SAlex Deucher return;
570d38ceaf9SAlex Deucher }
571d38ceaf9SAlex Deucher switch (align) {
572d38ceaf9SAlex Deucher case ATOM_SRC_DWORD:
573d38ceaf9SAlex Deucher DEBUG(".[31:0] <- 0x%08X\n", old_val);
574d38ceaf9SAlex Deucher break;
575d38ceaf9SAlex Deucher case ATOM_SRC_WORD0:
576d38ceaf9SAlex Deucher DEBUG(".[15:0] <- 0x%04X\n", old_val);
577d38ceaf9SAlex Deucher break;
578d38ceaf9SAlex Deucher case ATOM_SRC_WORD8:
579d38ceaf9SAlex Deucher DEBUG(".[23:8] <- 0x%04X\n", old_val);
580d38ceaf9SAlex Deucher break;
581d38ceaf9SAlex Deucher case ATOM_SRC_WORD16:
582d38ceaf9SAlex Deucher DEBUG(".[31:16] <- 0x%04X\n", old_val);
583d38ceaf9SAlex Deucher break;
584d38ceaf9SAlex Deucher case ATOM_SRC_BYTE0:
585d38ceaf9SAlex Deucher DEBUG(".[7:0] <- 0x%02X\n", old_val);
586d38ceaf9SAlex Deucher break;
587d38ceaf9SAlex Deucher case ATOM_SRC_BYTE8:
588d38ceaf9SAlex Deucher DEBUG(".[15:8] <- 0x%02X\n", old_val);
589d38ceaf9SAlex Deucher break;
590d38ceaf9SAlex Deucher case ATOM_SRC_BYTE16:
591d38ceaf9SAlex Deucher DEBUG(".[23:16] <- 0x%02X\n", old_val);
592d38ceaf9SAlex Deucher break;
593d38ceaf9SAlex Deucher case ATOM_SRC_BYTE24:
594d38ceaf9SAlex Deucher DEBUG(".[31:24] <- 0x%02X\n", old_val);
595d38ceaf9SAlex Deucher break;
596d38ceaf9SAlex Deucher }
597d38ceaf9SAlex Deucher }
598d38ceaf9SAlex Deucher
atom_op_add(atom_exec_context * ctx,int * ptr,int arg)599d38ceaf9SAlex Deucher static void atom_op_add(atom_exec_context *ctx, int *ptr, int arg)
600d38ceaf9SAlex Deucher {
601d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
602d38ceaf9SAlex Deucher uint32_t dst, src, saved;
603d38ceaf9SAlex Deucher int dptr = *ptr;
604d38ceaf9SAlex Deucher SDEBUG(" dst: ");
605d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
606d38ceaf9SAlex Deucher SDEBUG(" src: ");
607d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
608d38ceaf9SAlex Deucher dst += src;
609d38ceaf9SAlex Deucher SDEBUG(" dst: ");
610d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
611d38ceaf9SAlex Deucher }
612d38ceaf9SAlex Deucher
atom_op_and(atom_exec_context * ctx,int * ptr,int arg)613d38ceaf9SAlex Deucher static void atom_op_and(atom_exec_context *ctx, int *ptr, int arg)
614d38ceaf9SAlex Deucher {
615d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
616d38ceaf9SAlex Deucher uint32_t dst, src, saved;
617d38ceaf9SAlex Deucher int dptr = *ptr;
618d38ceaf9SAlex Deucher SDEBUG(" dst: ");
619d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
620d38ceaf9SAlex Deucher SDEBUG(" src: ");
621d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
622d38ceaf9SAlex Deucher dst &= src;
623d38ceaf9SAlex Deucher SDEBUG(" dst: ");
624d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
625d38ceaf9SAlex Deucher }
626d38ceaf9SAlex Deucher
atom_op_beep(atom_exec_context * ctx,int * ptr,int arg)627d38ceaf9SAlex Deucher static void atom_op_beep(atom_exec_context *ctx, int *ptr, int arg)
628d38ceaf9SAlex Deucher {
629d38ceaf9SAlex Deucher printk("ATOM BIOS beeped!\n");
630d38ceaf9SAlex Deucher }
631d38ceaf9SAlex Deucher
atom_op_calltable(atom_exec_context * ctx,int * ptr,int arg)632d38ceaf9SAlex Deucher static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg)
633d38ceaf9SAlex Deucher {
634d38ceaf9SAlex Deucher int idx = U8((*ptr)++);
635d38ceaf9SAlex Deucher int r = 0;
636d38ceaf9SAlex Deucher
637d38ceaf9SAlex Deucher if (idx < ATOM_TABLE_NAMES_CNT)
638d38ceaf9SAlex Deucher SDEBUG(" table: %d (%s)\n", idx, atom_table_names[idx]);
639d38ceaf9SAlex Deucher else
640d38ceaf9SAlex Deucher SDEBUG(" table: %d\n", idx);
641d38ceaf9SAlex Deucher if (U16(ctx->ctx->cmd_table + 4 + 2 * idx))
6424630d503SAlexander Richards r = amdgpu_atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift, ctx->ps_size - ctx->ps_shift);
643d38ceaf9SAlex Deucher if (r) {
644d38ceaf9SAlex Deucher ctx->abort = true;
645d38ceaf9SAlex Deucher }
646d38ceaf9SAlex Deucher }
647d38ceaf9SAlex Deucher
atom_op_clear(atom_exec_context * ctx,int * ptr,int arg)648d38ceaf9SAlex Deucher static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg)
649d38ceaf9SAlex Deucher {
650d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
651d38ceaf9SAlex Deucher uint32_t saved;
652d38ceaf9SAlex Deucher int dptr = *ptr;
653d38ceaf9SAlex Deucher attr &= 0x38;
654d38ceaf9SAlex Deucher attr |= atom_def_dst[attr >> 3] << 6;
655d38ceaf9SAlex Deucher atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
656d38ceaf9SAlex Deucher SDEBUG(" dst: ");
657d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, 0, saved);
658d38ceaf9SAlex Deucher }
659d38ceaf9SAlex Deucher
atom_op_compare(atom_exec_context * ctx,int * ptr,int arg)660d38ceaf9SAlex Deucher static void atom_op_compare(atom_exec_context *ctx, int *ptr, int arg)
661d38ceaf9SAlex Deucher {
662d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
663d38ceaf9SAlex Deucher uint32_t dst, src;
664d38ceaf9SAlex Deucher SDEBUG(" src1: ");
665d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
666d38ceaf9SAlex Deucher SDEBUG(" src2: ");
667d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
668d38ceaf9SAlex Deucher ctx->ctx->cs_equal = (dst == src);
669d38ceaf9SAlex Deucher ctx->ctx->cs_above = (dst > src);
670d38ceaf9SAlex Deucher SDEBUG(" result: %s %s\n", ctx->ctx->cs_equal ? "EQ" : "NE",
671d38ceaf9SAlex Deucher ctx->ctx->cs_above ? "GT" : "LE");
672d38ceaf9SAlex Deucher }
673d38ceaf9SAlex Deucher
atom_op_delay(atom_exec_context * ctx,int * ptr,int arg)674d38ceaf9SAlex Deucher static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg)
675d38ceaf9SAlex Deucher {
676d38ceaf9SAlex Deucher unsigned count = U8((*ptr)++);
677d38ceaf9SAlex Deucher SDEBUG(" count: %d\n", count);
678d38ceaf9SAlex Deucher if (arg == ATOM_UNIT_MICROSEC)
679d38ceaf9SAlex Deucher udelay(count);
680d38ceaf9SAlex Deucher else if (!drm_can_sleep())
681d38ceaf9SAlex Deucher mdelay(count);
682d38ceaf9SAlex Deucher else
683d38ceaf9SAlex Deucher msleep(count);
684d38ceaf9SAlex Deucher }
685d38ceaf9SAlex Deucher
atom_op_div(atom_exec_context * ctx,int * ptr,int arg)686d38ceaf9SAlex Deucher static void atom_op_div(atom_exec_context *ctx, int *ptr, int arg)
687d38ceaf9SAlex Deucher {
688d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
689d38ceaf9SAlex Deucher uint32_t dst, src;
690d38ceaf9SAlex Deucher SDEBUG(" src1: ");
691d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
692d38ceaf9SAlex Deucher SDEBUG(" src2: ");
693d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
694d38ceaf9SAlex Deucher if (src != 0) {
695d38ceaf9SAlex Deucher ctx->ctx->divmul[0] = dst / src;
696d38ceaf9SAlex Deucher ctx->ctx->divmul[1] = dst % src;
697d38ceaf9SAlex Deucher } else {
698d38ceaf9SAlex Deucher ctx->ctx->divmul[0] = 0;
699d38ceaf9SAlex Deucher ctx->ctx->divmul[1] = 0;
700d38ceaf9SAlex Deucher }
701d38ceaf9SAlex Deucher }
702d38ceaf9SAlex Deucher
atom_op_div32(atom_exec_context * ctx,int * ptr,int arg)703c2fe16aaSAlex Deucher static void atom_op_div32(atom_exec_context *ctx, int *ptr, int arg)
704c2fe16aaSAlex Deucher {
705c2fe16aaSAlex Deucher uint64_t val64;
706c2fe16aaSAlex Deucher uint8_t attr = U8((*ptr)++);
707c2fe16aaSAlex Deucher uint32_t dst, src;
708c2fe16aaSAlex Deucher SDEBUG(" src1: ");
709c2fe16aaSAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
710c2fe16aaSAlex Deucher SDEBUG(" src2: ");
711c2fe16aaSAlex Deucher src = atom_get_src(ctx, attr, ptr);
712c2fe16aaSAlex Deucher if (src != 0) {
713c2fe16aaSAlex Deucher val64 = dst;
714c2fe16aaSAlex Deucher val64 |= ((uint64_t)ctx->ctx->divmul[1]) << 32;
715c2fe16aaSAlex Deucher do_div(val64, src);
716c2fe16aaSAlex Deucher ctx->ctx->divmul[0] = lower_32_bits(val64);
717c2fe16aaSAlex Deucher ctx->ctx->divmul[1] = upper_32_bits(val64);
718c2fe16aaSAlex Deucher } else {
719c2fe16aaSAlex Deucher ctx->ctx->divmul[0] = 0;
720c2fe16aaSAlex Deucher ctx->ctx->divmul[1] = 0;
721c2fe16aaSAlex Deucher }
722c2fe16aaSAlex Deucher }
723c2fe16aaSAlex Deucher
atom_op_eot(atom_exec_context * ctx,int * ptr,int arg)724d38ceaf9SAlex Deucher static void atom_op_eot(atom_exec_context *ctx, int *ptr, int arg)
725d38ceaf9SAlex Deucher {
726d38ceaf9SAlex Deucher /* functionally, a nop */
727d38ceaf9SAlex Deucher }
728d38ceaf9SAlex Deucher
atom_op_jump(atom_exec_context * ctx,int * ptr,int arg)729d38ceaf9SAlex Deucher static void atom_op_jump(atom_exec_context *ctx, int *ptr, int arg)
730d38ceaf9SAlex Deucher {
731d38ceaf9SAlex Deucher int execute = 0, target = U16(*ptr);
732d38ceaf9SAlex Deucher unsigned long cjiffies;
733d38ceaf9SAlex Deucher
734d38ceaf9SAlex Deucher (*ptr) += 2;
735d38ceaf9SAlex Deucher switch (arg) {
736d38ceaf9SAlex Deucher case ATOM_COND_ABOVE:
737d38ceaf9SAlex Deucher execute = ctx->ctx->cs_above;
738d38ceaf9SAlex Deucher break;
739d38ceaf9SAlex Deucher case ATOM_COND_ABOVEOREQUAL:
740d38ceaf9SAlex Deucher execute = ctx->ctx->cs_above || ctx->ctx->cs_equal;
741d38ceaf9SAlex Deucher break;
742d38ceaf9SAlex Deucher case ATOM_COND_ALWAYS:
743d38ceaf9SAlex Deucher execute = 1;
744d38ceaf9SAlex Deucher break;
745d38ceaf9SAlex Deucher case ATOM_COND_BELOW:
746d38ceaf9SAlex Deucher execute = !(ctx->ctx->cs_above || ctx->ctx->cs_equal);
747d38ceaf9SAlex Deucher break;
748d38ceaf9SAlex Deucher case ATOM_COND_BELOWOREQUAL:
749d38ceaf9SAlex Deucher execute = !ctx->ctx->cs_above;
750d38ceaf9SAlex Deucher break;
751d38ceaf9SAlex Deucher case ATOM_COND_EQUAL:
752d38ceaf9SAlex Deucher execute = ctx->ctx->cs_equal;
753d38ceaf9SAlex Deucher break;
754d38ceaf9SAlex Deucher case ATOM_COND_NOTEQUAL:
755d38ceaf9SAlex Deucher execute = !ctx->ctx->cs_equal;
756d38ceaf9SAlex Deucher break;
757d38ceaf9SAlex Deucher }
758d38ceaf9SAlex Deucher if (arg != ATOM_COND_ALWAYS)
759b8c75bd9SLucas De Marchi SDEBUG(" taken: %s\n", str_yes_no(execute));
760d38ceaf9SAlex Deucher SDEBUG(" target: 0x%04X\n", target);
761d38ceaf9SAlex Deucher if (execute) {
762d38ceaf9SAlex Deucher if (ctx->last_jump == (ctx->start + target)) {
763d38ceaf9SAlex Deucher cjiffies = jiffies;
764d38ceaf9SAlex Deucher if (time_after(cjiffies, ctx->last_jump_jiffies)) {
765d38ceaf9SAlex Deucher cjiffies -= ctx->last_jump_jiffies;
7669a785c7aSJohn Clements if ((jiffies_to_msecs(cjiffies) > ATOM_CMD_TIMEOUT_SEC*1000)) {
7679a785c7aSJohn Clements DRM_ERROR("atombios stuck in loop for more than %dsecs aborting\n",
7689a785c7aSJohn Clements ATOM_CMD_TIMEOUT_SEC);
769d38ceaf9SAlex Deucher ctx->abort = true;
770d38ceaf9SAlex Deucher }
771d38ceaf9SAlex Deucher } else {
772d38ceaf9SAlex Deucher /* jiffies wrap around we will just wait a little longer */
773d38ceaf9SAlex Deucher ctx->last_jump_jiffies = jiffies;
774d38ceaf9SAlex Deucher }
775d38ceaf9SAlex Deucher } else {
776d38ceaf9SAlex Deucher ctx->last_jump = ctx->start + target;
777d38ceaf9SAlex Deucher ctx->last_jump_jiffies = jiffies;
778d38ceaf9SAlex Deucher }
779d38ceaf9SAlex Deucher *ptr = ctx->start + target;
780d38ceaf9SAlex Deucher }
781d38ceaf9SAlex Deucher }
782d38ceaf9SAlex Deucher
atom_op_mask(atom_exec_context * ctx,int * ptr,int arg)783d38ceaf9SAlex Deucher static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg)
784d38ceaf9SAlex Deucher {
785d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
786d38ceaf9SAlex Deucher uint32_t dst, mask, src, saved;
787d38ceaf9SAlex Deucher int dptr = *ptr;
788d38ceaf9SAlex Deucher SDEBUG(" dst: ");
789d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
790d38ceaf9SAlex Deucher mask = atom_get_src_direct(ctx, ((attr >> 3) & 7), ptr);
791d38ceaf9SAlex Deucher SDEBUG(" mask: 0x%08x", mask);
792d38ceaf9SAlex Deucher SDEBUG(" src: ");
793d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
794d38ceaf9SAlex Deucher dst &= mask;
795d38ceaf9SAlex Deucher dst |= src;
796d38ceaf9SAlex Deucher SDEBUG(" dst: ");
797d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
798d38ceaf9SAlex Deucher }
799d38ceaf9SAlex Deucher
atom_op_move(atom_exec_context * ctx,int * ptr,int arg)800d38ceaf9SAlex Deucher static void atom_op_move(atom_exec_context *ctx, int *ptr, int arg)
801d38ceaf9SAlex Deucher {
802d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
803d38ceaf9SAlex Deucher uint32_t src, saved;
804d38ceaf9SAlex Deucher int dptr = *ptr;
805d38ceaf9SAlex Deucher if (((attr >> 3) & 7) != ATOM_SRC_DWORD)
806d38ceaf9SAlex Deucher atom_get_dst(ctx, arg, attr, ptr, &saved, 0);
807d38ceaf9SAlex Deucher else {
808d38ceaf9SAlex Deucher atom_skip_dst(ctx, arg, attr, ptr);
809d38ceaf9SAlex Deucher saved = 0xCDCDCDCD;
810d38ceaf9SAlex Deucher }
811d38ceaf9SAlex Deucher SDEBUG(" src: ");
812d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
813d38ceaf9SAlex Deucher SDEBUG(" dst: ");
814d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, src, saved);
815d38ceaf9SAlex Deucher }
816d38ceaf9SAlex Deucher
atom_op_mul(atom_exec_context * ctx,int * ptr,int arg)817d38ceaf9SAlex Deucher static void atom_op_mul(atom_exec_context *ctx, int *ptr, int arg)
818d38ceaf9SAlex Deucher {
819d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
820d38ceaf9SAlex Deucher uint32_t dst, src;
821d38ceaf9SAlex Deucher SDEBUG(" src1: ");
822d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
823d38ceaf9SAlex Deucher SDEBUG(" src2: ");
824d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
825d38ceaf9SAlex Deucher ctx->ctx->divmul[0] = dst * src;
826d38ceaf9SAlex Deucher }
827d38ceaf9SAlex Deucher
atom_op_mul32(atom_exec_context * ctx,int * ptr,int arg)828c9c14502SAlex Deucher static void atom_op_mul32(atom_exec_context *ctx, int *ptr, int arg)
829c9c14502SAlex Deucher {
830c9c14502SAlex Deucher uint64_t val64;
831c9c14502SAlex Deucher uint8_t attr = U8((*ptr)++);
832c9c14502SAlex Deucher uint32_t dst, src;
833c9c14502SAlex Deucher SDEBUG(" src1: ");
834c9c14502SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
835c9c14502SAlex Deucher SDEBUG(" src2: ");
836c9c14502SAlex Deucher src = atom_get_src(ctx, attr, ptr);
837c9c14502SAlex Deucher val64 = (uint64_t)dst * (uint64_t)src;
838c9c14502SAlex Deucher ctx->ctx->divmul[0] = lower_32_bits(val64);
839c9c14502SAlex Deucher ctx->ctx->divmul[1] = upper_32_bits(val64);
840c9c14502SAlex Deucher }
841c9c14502SAlex Deucher
atom_op_nop(atom_exec_context * ctx,int * ptr,int arg)842d38ceaf9SAlex Deucher static void atom_op_nop(atom_exec_context *ctx, int *ptr, int arg)
843d38ceaf9SAlex Deucher {
844d38ceaf9SAlex Deucher /* nothing */
845d38ceaf9SAlex Deucher }
846d38ceaf9SAlex Deucher
atom_op_or(atom_exec_context * ctx,int * ptr,int arg)847d38ceaf9SAlex Deucher static void atom_op_or(atom_exec_context *ctx, int *ptr, int arg)
848d38ceaf9SAlex Deucher {
849d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
850d38ceaf9SAlex Deucher uint32_t dst, src, saved;
851d38ceaf9SAlex Deucher int dptr = *ptr;
852d38ceaf9SAlex Deucher SDEBUG(" dst: ");
853d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
854d38ceaf9SAlex Deucher SDEBUG(" src: ");
855d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
856d38ceaf9SAlex Deucher dst |= src;
857d38ceaf9SAlex Deucher SDEBUG(" dst: ");
858d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
859d38ceaf9SAlex Deucher }
860d38ceaf9SAlex Deucher
atom_op_postcard(atom_exec_context * ctx,int * ptr,int arg)861d38ceaf9SAlex Deucher static void atom_op_postcard(atom_exec_context *ctx, int *ptr, int arg)
862d38ceaf9SAlex Deucher {
863d38ceaf9SAlex Deucher uint8_t val = U8((*ptr)++);
864d38ceaf9SAlex Deucher SDEBUG("POST card output: 0x%02X\n", val);
865d38ceaf9SAlex Deucher }
866d38ceaf9SAlex Deucher
atom_op_repeat(atom_exec_context * ctx,int * ptr,int arg)867d38ceaf9SAlex Deucher static void atom_op_repeat(atom_exec_context *ctx, int *ptr, int arg)
868d38ceaf9SAlex Deucher {
8697ca85295SJoe Perches pr_info("unimplemented!\n");
870d38ceaf9SAlex Deucher }
871d38ceaf9SAlex Deucher
atom_op_restorereg(atom_exec_context * ctx,int * ptr,int arg)872d38ceaf9SAlex Deucher static void atom_op_restorereg(atom_exec_context *ctx, int *ptr, int arg)
873d38ceaf9SAlex Deucher {
8747ca85295SJoe Perches pr_info("unimplemented!\n");
875d38ceaf9SAlex Deucher }
876d38ceaf9SAlex Deucher
atom_op_savereg(atom_exec_context * ctx,int * ptr,int arg)877d38ceaf9SAlex Deucher static void atom_op_savereg(atom_exec_context *ctx, int *ptr, int arg)
878d38ceaf9SAlex Deucher {
8797ca85295SJoe Perches pr_info("unimplemented!\n");
880d38ceaf9SAlex Deucher }
881d38ceaf9SAlex Deucher
atom_op_setdatablock(atom_exec_context * ctx,int * ptr,int arg)882d38ceaf9SAlex Deucher static void atom_op_setdatablock(atom_exec_context *ctx, int *ptr, int arg)
883d38ceaf9SAlex Deucher {
884d38ceaf9SAlex Deucher int idx = U8(*ptr);
885d38ceaf9SAlex Deucher (*ptr)++;
886d38ceaf9SAlex Deucher SDEBUG(" block: %d\n", idx);
887d38ceaf9SAlex Deucher if (!idx)
888d38ceaf9SAlex Deucher ctx->ctx->data_block = 0;
889d38ceaf9SAlex Deucher else if (idx == 255)
890d38ceaf9SAlex Deucher ctx->ctx->data_block = ctx->start;
891d38ceaf9SAlex Deucher else
892d38ceaf9SAlex Deucher ctx->ctx->data_block = U16(ctx->ctx->data_table + 4 + 2 * idx);
893d38ceaf9SAlex Deucher SDEBUG(" base: 0x%04X\n", ctx->ctx->data_block);
894d38ceaf9SAlex Deucher }
895d38ceaf9SAlex Deucher
atom_op_setfbbase(atom_exec_context * ctx,int * ptr,int arg)896d38ceaf9SAlex Deucher static void atom_op_setfbbase(atom_exec_context *ctx, int *ptr, int arg)
897d38ceaf9SAlex Deucher {
898d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
899d38ceaf9SAlex Deucher SDEBUG(" fb_base: ");
900d38ceaf9SAlex Deucher ctx->ctx->fb_base = atom_get_src(ctx, attr, ptr);
901d38ceaf9SAlex Deucher }
902d38ceaf9SAlex Deucher
atom_op_setport(atom_exec_context * ctx,int * ptr,int arg)903d38ceaf9SAlex Deucher static void atom_op_setport(atom_exec_context *ctx, int *ptr, int arg)
904d38ceaf9SAlex Deucher {
905d38ceaf9SAlex Deucher int port;
906d38ceaf9SAlex Deucher switch (arg) {
907d38ceaf9SAlex Deucher case ATOM_PORT_ATI:
908d38ceaf9SAlex Deucher port = U16(*ptr);
909d38ceaf9SAlex Deucher if (port < ATOM_IO_NAMES_CNT)
910d38ceaf9SAlex Deucher SDEBUG(" port: %d (%s)\n", port, atom_io_names[port]);
911d38ceaf9SAlex Deucher else
912d38ceaf9SAlex Deucher SDEBUG(" port: %d\n", port);
913d38ceaf9SAlex Deucher if (!port)
914d38ceaf9SAlex Deucher ctx->ctx->io_mode = ATOM_IO_MM;
915d38ceaf9SAlex Deucher else
916d38ceaf9SAlex Deucher ctx->ctx->io_mode = ATOM_IO_IIO | port;
917d38ceaf9SAlex Deucher (*ptr) += 2;
918d38ceaf9SAlex Deucher break;
919d38ceaf9SAlex Deucher case ATOM_PORT_PCI:
920d38ceaf9SAlex Deucher ctx->ctx->io_mode = ATOM_IO_PCI;
921d38ceaf9SAlex Deucher (*ptr)++;
922d38ceaf9SAlex Deucher break;
923d38ceaf9SAlex Deucher case ATOM_PORT_SYSIO:
924d38ceaf9SAlex Deucher ctx->ctx->io_mode = ATOM_IO_SYSIO;
925d38ceaf9SAlex Deucher (*ptr)++;
926d38ceaf9SAlex Deucher break;
927d38ceaf9SAlex Deucher }
928d38ceaf9SAlex Deucher }
929d38ceaf9SAlex Deucher
atom_op_setregblock(atom_exec_context * ctx,int * ptr,int arg)930d38ceaf9SAlex Deucher static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg)
931d38ceaf9SAlex Deucher {
932d38ceaf9SAlex Deucher ctx->ctx->reg_block = U16(*ptr);
933d38ceaf9SAlex Deucher (*ptr) += 2;
934d38ceaf9SAlex Deucher SDEBUG(" base: 0x%04X\n", ctx->ctx->reg_block);
935d38ceaf9SAlex Deucher }
936d38ceaf9SAlex Deucher
atom_op_shift_left(atom_exec_context * ctx,int * ptr,int arg)937d38ceaf9SAlex Deucher static void atom_op_shift_left(atom_exec_context *ctx, int *ptr, int arg)
938d38ceaf9SAlex Deucher {
939d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++), shift;
940d38ceaf9SAlex Deucher uint32_t saved, dst;
941d38ceaf9SAlex Deucher int dptr = *ptr;
942d38ceaf9SAlex Deucher attr &= 0x38;
943d38ceaf9SAlex Deucher attr |= atom_def_dst[attr >> 3] << 6;
944d38ceaf9SAlex Deucher SDEBUG(" dst: ");
945d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
946d38ceaf9SAlex Deucher shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
947d38ceaf9SAlex Deucher SDEBUG(" shift: %d\n", shift);
948d38ceaf9SAlex Deucher dst <<= shift;
949d38ceaf9SAlex Deucher SDEBUG(" dst: ");
950d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
951d38ceaf9SAlex Deucher }
952d38ceaf9SAlex Deucher
atom_op_shift_right(atom_exec_context * ctx,int * ptr,int arg)953d38ceaf9SAlex Deucher static void atom_op_shift_right(atom_exec_context *ctx, int *ptr, int arg)
954d38ceaf9SAlex Deucher {
955d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++), shift;
956d38ceaf9SAlex Deucher uint32_t saved, dst;
957d38ceaf9SAlex Deucher int dptr = *ptr;
958d38ceaf9SAlex Deucher attr &= 0x38;
959d38ceaf9SAlex Deucher attr |= atom_def_dst[attr >> 3] << 6;
960d38ceaf9SAlex Deucher SDEBUG(" dst: ");
961d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
962d38ceaf9SAlex Deucher shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
963d38ceaf9SAlex Deucher SDEBUG(" shift: %d\n", shift);
964d38ceaf9SAlex Deucher dst >>= shift;
965d38ceaf9SAlex Deucher SDEBUG(" dst: ");
966d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
967d38ceaf9SAlex Deucher }
968d38ceaf9SAlex Deucher
atom_op_shl(atom_exec_context * ctx,int * ptr,int arg)969d38ceaf9SAlex Deucher static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
970d38ceaf9SAlex Deucher {
971d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++), shift;
972d38ceaf9SAlex Deucher uint32_t saved, dst;
973d38ceaf9SAlex Deucher int dptr = *ptr;
974d38ceaf9SAlex Deucher uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3];
975d38ceaf9SAlex Deucher SDEBUG(" dst: ");
976d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
977d38ceaf9SAlex Deucher /* op needs to full dst value */
978d38ceaf9SAlex Deucher dst = saved;
979d38ceaf9SAlex Deucher shift = atom_get_src(ctx, attr, ptr);
980d38ceaf9SAlex Deucher SDEBUG(" shift: %d\n", shift);
981d38ceaf9SAlex Deucher dst <<= shift;
982d38ceaf9SAlex Deucher dst &= atom_arg_mask[dst_align];
983d38ceaf9SAlex Deucher dst >>= atom_arg_shift[dst_align];
984d38ceaf9SAlex Deucher SDEBUG(" dst: ");
985d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
986d38ceaf9SAlex Deucher }
987d38ceaf9SAlex Deucher
atom_op_shr(atom_exec_context * ctx,int * ptr,int arg)988d38ceaf9SAlex Deucher static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg)
989d38ceaf9SAlex Deucher {
990d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++), shift;
991d38ceaf9SAlex Deucher uint32_t saved, dst;
992d38ceaf9SAlex Deucher int dptr = *ptr;
993d38ceaf9SAlex Deucher uint32_t dst_align = atom_dst_to_src[(attr >> 3) & 7][(attr >> 6) & 3];
994d38ceaf9SAlex Deucher SDEBUG(" dst: ");
995d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
996d38ceaf9SAlex Deucher /* op needs to full dst value */
997d38ceaf9SAlex Deucher dst = saved;
998d38ceaf9SAlex Deucher shift = atom_get_src(ctx, attr, ptr);
999d38ceaf9SAlex Deucher SDEBUG(" shift: %d\n", shift);
1000d38ceaf9SAlex Deucher dst >>= shift;
1001d38ceaf9SAlex Deucher dst &= atom_arg_mask[dst_align];
1002d38ceaf9SAlex Deucher dst >>= atom_arg_shift[dst_align];
1003d38ceaf9SAlex Deucher SDEBUG(" dst: ");
1004d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
1005d38ceaf9SAlex Deucher }
1006d38ceaf9SAlex Deucher
atom_op_sub(atom_exec_context * ctx,int * ptr,int arg)1007d38ceaf9SAlex Deucher static void atom_op_sub(atom_exec_context *ctx, int *ptr, int arg)
1008d38ceaf9SAlex Deucher {
1009d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
1010d38ceaf9SAlex Deucher uint32_t dst, src, saved;
1011d38ceaf9SAlex Deucher int dptr = *ptr;
1012d38ceaf9SAlex Deucher SDEBUG(" dst: ");
1013d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
1014d38ceaf9SAlex Deucher SDEBUG(" src: ");
1015d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
1016d38ceaf9SAlex Deucher dst -= src;
1017d38ceaf9SAlex Deucher SDEBUG(" dst: ");
1018d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
1019d38ceaf9SAlex Deucher }
1020d38ceaf9SAlex Deucher
atom_op_switch(atom_exec_context * ctx,int * ptr,int arg)1021d38ceaf9SAlex Deucher static void atom_op_switch(atom_exec_context *ctx, int *ptr, int arg)
1022d38ceaf9SAlex Deucher {
1023d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
1024d38ceaf9SAlex Deucher uint32_t src, val, target;
1025d38ceaf9SAlex Deucher SDEBUG(" switch: ");
1026d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
1027d38ceaf9SAlex Deucher while (U16(*ptr) != ATOM_CASE_END)
1028d38ceaf9SAlex Deucher if (U8(*ptr) == ATOM_CASE_MAGIC) {
1029d38ceaf9SAlex Deucher (*ptr)++;
1030d38ceaf9SAlex Deucher SDEBUG(" case: ");
1031d38ceaf9SAlex Deucher val =
1032d38ceaf9SAlex Deucher atom_get_src(ctx, (attr & 0x38) | ATOM_ARG_IMM,
1033d38ceaf9SAlex Deucher ptr);
1034d38ceaf9SAlex Deucher target = U16(*ptr);
1035d38ceaf9SAlex Deucher if (val == src) {
1036d38ceaf9SAlex Deucher SDEBUG(" target: %04X\n", target);
1037d38ceaf9SAlex Deucher *ptr = ctx->start + target;
1038d38ceaf9SAlex Deucher return;
1039d38ceaf9SAlex Deucher }
1040d38ceaf9SAlex Deucher (*ptr) += 2;
1041d38ceaf9SAlex Deucher } else {
10427ca85295SJoe Perches pr_info("Bad case\n");
1043d38ceaf9SAlex Deucher return;
1044d38ceaf9SAlex Deucher }
1045d38ceaf9SAlex Deucher (*ptr) += 2;
1046d38ceaf9SAlex Deucher }
1047d38ceaf9SAlex Deucher
atom_op_test(atom_exec_context * ctx,int * ptr,int arg)1048d38ceaf9SAlex Deucher static void atom_op_test(atom_exec_context *ctx, int *ptr, int arg)
1049d38ceaf9SAlex Deucher {
1050d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
1051d38ceaf9SAlex Deucher uint32_t dst, src;
1052d38ceaf9SAlex Deucher SDEBUG(" src1: ");
1053d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, NULL, 1);
1054d38ceaf9SAlex Deucher SDEBUG(" src2: ");
1055d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
1056d38ceaf9SAlex Deucher ctx->ctx->cs_equal = ((dst & src) == 0);
1057d38ceaf9SAlex Deucher SDEBUG(" result: %s\n", ctx->ctx->cs_equal ? "EQ" : "NE");
1058d38ceaf9SAlex Deucher }
1059d38ceaf9SAlex Deucher
atom_op_xor(atom_exec_context * ctx,int * ptr,int arg)1060d38ceaf9SAlex Deucher static void atom_op_xor(atom_exec_context *ctx, int *ptr, int arg)
1061d38ceaf9SAlex Deucher {
1062d38ceaf9SAlex Deucher uint8_t attr = U8((*ptr)++);
1063d38ceaf9SAlex Deucher uint32_t dst, src, saved;
1064d38ceaf9SAlex Deucher int dptr = *ptr;
1065d38ceaf9SAlex Deucher SDEBUG(" dst: ");
1066d38ceaf9SAlex Deucher dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
1067d38ceaf9SAlex Deucher SDEBUG(" src: ");
1068d38ceaf9SAlex Deucher src = atom_get_src(ctx, attr, ptr);
1069d38ceaf9SAlex Deucher dst ^= src;
1070d38ceaf9SAlex Deucher SDEBUG(" dst: ");
1071d38ceaf9SAlex Deucher atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
1072d38ceaf9SAlex Deucher }
1073d38ceaf9SAlex Deucher
atom_op_debug(atom_exec_context * ctx,int * ptr,int arg)1074d38ceaf9SAlex Deucher static void atom_op_debug(atom_exec_context *ctx, int *ptr, int arg)
1075d38ceaf9SAlex Deucher {
1076f76097c0SAlex Deucher uint8_t val = U8((*ptr)++);
1077f76097c0SAlex Deucher SDEBUG("DEBUG output: 0x%02X\n", val);
1078d38ceaf9SAlex Deucher }
1079d38ceaf9SAlex Deucher
atom_op_processds(atom_exec_context * ctx,int * ptr,int arg)108055438419SAlex Deucher static void atom_op_processds(atom_exec_context *ctx, int *ptr, int arg)
108155438419SAlex Deucher {
108255438419SAlex Deucher uint16_t val = U16(*ptr);
108355438419SAlex Deucher (*ptr) += val + 2;
108455438419SAlex Deucher SDEBUG("PROCESSDS output: 0x%02X\n", val);
108555438419SAlex Deucher }
108655438419SAlex Deucher
1087d38ceaf9SAlex Deucher static struct {
1088d38ceaf9SAlex Deucher void (*func) (atom_exec_context *, int *, int);
1089d38ceaf9SAlex Deucher int arg;
1090d38ceaf9SAlex Deucher } opcode_table[ATOM_OP_CNT] = {
1091d38ceaf9SAlex Deucher {
1092d38ceaf9SAlex Deucher NULL, 0}, {
1093d38ceaf9SAlex Deucher atom_op_move, ATOM_ARG_REG}, {
1094d38ceaf9SAlex Deucher atom_op_move, ATOM_ARG_PS}, {
1095d38ceaf9SAlex Deucher atom_op_move, ATOM_ARG_WS}, {
1096d38ceaf9SAlex Deucher atom_op_move, ATOM_ARG_FB}, {
1097d38ceaf9SAlex Deucher atom_op_move, ATOM_ARG_PLL}, {
1098d38ceaf9SAlex Deucher atom_op_move, ATOM_ARG_MC}, {
1099d38ceaf9SAlex Deucher atom_op_and, ATOM_ARG_REG}, {
1100d38ceaf9SAlex Deucher atom_op_and, ATOM_ARG_PS}, {
1101d38ceaf9SAlex Deucher atom_op_and, ATOM_ARG_WS}, {
1102d38ceaf9SAlex Deucher atom_op_and, ATOM_ARG_FB}, {
1103d38ceaf9SAlex Deucher atom_op_and, ATOM_ARG_PLL}, {
1104d38ceaf9SAlex Deucher atom_op_and, ATOM_ARG_MC}, {
1105d38ceaf9SAlex Deucher atom_op_or, ATOM_ARG_REG}, {
1106d38ceaf9SAlex Deucher atom_op_or, ATOM_ARG_PS}, {
1107d38ceaf9SAlex Deucher atom_op_or, ATOM_ARG_WS}, {
1108d38ceaf9SAlex Deucher atom_op_or, ATOM_ARG_FB}, {
1109d38ceaf9SAlex Deucher atom_op_or, ATOM_ARG_PLL}, {
1110d38ceaf9SAlex Deucher atom_op_or, ATOM_ARG_MC}, {
1111d38ceaf9SAlex Deucher atom_op_shift_left, ATOM_ARG_REG}, {
1112d38ceaf9SAlex Deucher atom_op_shift_left, ATOM_ARG_PS}, {
1113d38ceaf9SAlex Deucher atom_op_shift_left, ATOM_ARG_WS}, {
1114d38ceaf9SAlex Deucher atom_op_shift_left, ATOM_ARG_FB}, {
1115d38ceaf9SAlex Deucher atom_op_shift_left, ATOM_ARG_PLL}, {
1116d38ceaf9SAlex Deucher atom_op_shift_left, ATOM_ARG_MC}, {
1117d38ceaf9SAlex Deucher atom_op_shift_right, ATOM_ARG_REG}, {
1118d38ceaf9SAlex Deucher atom_op_shift_right, ATOM_ARG_PS}, {
1119d38ceaf9SAlex Deucher atom_op_shift_right, ATOM_ARG_WS}, {
1120d38ceaf9SAlex Deucher atom_op_shift_right, ATOM_ARG_FB}, {
1121d38ceaf9SAlex Deucher atom_op_shift_right, ATOM_ARG_PLL}, {
1122d38ceaf9SAlex Deucher atom_op_shift_right, ATOM_ARG_MC}, {
1123d38ceaf9SAlex Deucher atom_op_mul, ATOM_ARG_REG}, {
1124d38ceaf9SAlex Deucher atom_op_mul, ATOM_ARG_PS}, {
1125d38ceaf9SAlex Deucher atom_op_mul, ATOM_ARG_WS}, {
1126d38ceaf9SAlex Deucher atom_op_mul, ATOM_ARG_FB}, {
1127d38ceaf9SAlex Deucher atom_op_mul, ATOM_ARG_PLL}, {
1128d38ceaf9SAlex Deucher atom_op_mul, ATOM_ARG_MC}, {
1129d38ceaf9SAlex Deucher atom_op_div, ATOM_ARG_REG}, {
1130d38ceaf9SAlex Deucher atom_op_div, ATOM_ARG_PS}, {
1131d38ceaf9SAlex Deucher atom_op_div, ATOM_ARG_WS}, {
1132d38ceaf9SAlex Deucher atom_op_div, ATOM_ARG_FB}, {
1133d38ceaf9SAlex Deucher atom_op_div, ATOM_ARG_PLL}, {
1134d38ceaf9SAlex Deucher atom_op_div, ATOM_ARG_MC}, {
1135d38ceaf9SAlex Deucher atom_op_add, ATOM_ARG_REG}, {
1136d38ceaf9SAlex Deucher atom_op_add, ATOM_ARG_PS}, {
1137d38ceaf9SAlex Deucher atom_op_add, ATOM_ARG_WS}, {
1138d38ceaf9SAlex Deucher atom_op_add, ATOM_ARG_FB}, {
1139d38ceaf9SAlex Deucher atom_op_add, ATOM_ARG_PLL}, {
1140d38ceaf9SAlex Deucher atom_op_add, ATOM_ARG_MC}, {
1141d38ceaf9SAlex Deucher atom_op_sub, ATOM_ARG_REG}, {
1142d38ceaf9SAlex Deucher atom_op_sub, ATOM_ARG_PS}, {
1143d38ceaf9SAlex Deucher atom_op_sub, ATOM_ARG_WS}, {
1144d38ceaf9SAlex Deucher atom_op_sub, ATOM_ARG_FB}, {
1145d38ceaf9SAlex Deucher atom_op_sub, ATOM_ARG_PLL}, {
1146d38ceaf9SAlex Deucher atom_op_sub, ATOM_ARG_MC}, {
1147d38ceaf9SAlex Deucher atom_op_setport, ATOM_PORT_ATI}, {
1148d38ceaf9SAlex Deucher atom_op_setport, ATOM_PORT_PCI}, {
1149d38ceaf9SAlex Deucher atom_op_setport, ATOM_PORT_SYSIO}, {
1150d38ceaf9SAlex Deucher atom_op_setregblock, 0}, {
1151d38ceaf9SAlex Deucher atom_op_setfbbase, 0}, {
1152d38ceaf9SAlex Deucher atom_op_compare, ATOM_ARG_REG}, {
1153d38ceaf9SAlex Deucher atom_op_compare, ATOM_ARG_PS}, {
1154d38ceaf9SAlex Deucher atom_op_compare, ATOM_ARG_WS}, {
1155d38ceaf9SAlex Deucher atom_op_compare, ATOM_ARG_FB}, {
1156d38ceaf9SAlex Deucher atom_op_compare, ATOM_ARG_PLL}, {
1157d38ceaf9SAlex Deucher atom_op_compare, ATOM_ARG_MC}, {
1158d38ceaf9SAlex Deucher atom_op_switch, 0}, {
1159d38ceaf9SAlex Deucher atom_op_jump, ATOM_COND_ALWAYS}, {
1160d38ceaf9SAlex Deucher atom_op_jump, ATOM_COND_EQUAL}, {
1161d38ceaf9SAlex Deucher atom_op_jump, ATOM_COND_BELOW}, {
1162d38ceaf9SAlex Deucher atom_op_jump, ATOM_COND_ABOVE}, {
1163d38ceaf9SAlex Deucher atom_op_jump, ATOM_COND_BELOWOREQUAL}, {
1164d38ceaf9SAlex Deucher atom_op_jump, ATOM_COND_ABOVEOREQUAL}, {
1165d38ceaf9SAlex Deucher atom_op_jump, ATOM_COND_NOTEQUAL}, {
1166d38ceaf9SAlex Deucher atom_op_test, ATOM_ARG_REG}, {
1167d38ceaf9SAlex Deucher atom_op_test, ATOM_ARG_PS}, {
1168d38ceaf9SAlex Deucher atom_op_test, ATOM_ARG_WS}, {
1169d38ceaf9SAlex Deucher atom_op_test, ATOM_ARG_FB}, {
1170d38ceaf9SAlex Deucher atom_op_test, ATOM_ARG_PLL}, {
1171d38ceaf9SAlex Deucher atom_op_test, ATOM_ARG_MC}, {
1172d38ceaf9SAlex Deucher atom_op_delay, ATOM_UNIT_MILLISEC}, {
1173d38ceaf9SAlex Deucher atom_op_delay, ATOM_UNIT_MICROSEC}, {
1174d38ceaf9SAlex Deucher atom_op_calltable, 0}, {
1175d38ceaf9SAlex Deucher atom_op_repeat, 0}, {
1176d38ceaf9SAlex Deucher atom_op_clear, ATOM_ARG_REG}, {
1177d38ceaf9SAlex Deucher atom_op_clear, ATOM_ARG_PS}, {
1178d38ceaf9SAlex Deucher atom_op_clear, ATOM_ARG_WS}, {
1179d38ceaf9SAlex Deucher atom_op_clear, ATOM_ARG_FB}, {
1180d38ceaf9SAlex Deucher atom_op_clear, ATOM_ARG_PLL}, {
1181d38ceaf9SAlex Deucher atom_op_clear, ATOM_ARG_MC}, {
1182d38ceaf9SAlex Deucher atom_op_nop, 0}, {
1183d38ceaf9SAlex Deucher atom_op_eot, 0}, {
1184d38ceaf9SAlex Deucher atom_op_mask, ATOM_ARG_REG}, {
1185d38ceaf9SAlex Deucher atom_op_mask, ATOM_ARG_PS}, {
1186d38ceaf9SAlex Deucher atom_op_mask, ATOM_ARG_WS}, {
1187d38ceaf9SAlex Deucher atom_op_mask, ATOM_ARG_FB}, {
1188d38ceaf9SAlex Deucher atom_op_mask, ATOM_ARG_PLL}, {
1189d38ceaf9SAlex Deucher atom_op_mask, ATOM_ARG_MC}, {
1190d38ceaf9SAlex Deucher atom_op_postcard, 0}, {
1191d38ceaf9SAlex Deucher atom_op_beep, 0}, {
1192d38ceaf9SAlex Deucher atom_op_savereg, 0}, {
1193d38ceaf9SAlex Deucher atom_op_restorereg, 0}, {
1194d38ceaf9SAlex Deucher atom_op_setdatablock, 0}, {
1195d38ceaf9SAlex Deucher atom_op_xor, ATOM_ARG_REG}, {
1196d38ceaf9SAlex Deucher atom_op_xor, ATOM_ARG_PS}, {
1197d38ceaf9SAlex Deucher atom_op_xor, ATOM_ARG_WS}, {
1198d38ceaf9SAlex Deucher atom_op_xor, ATOM_ARG_FB}, {
1199d38ceaf9SAlex Deucher atom_op_xor, ATOM_ARG_PLL}, {
1200d38ceaf9SAlex Deucher atom_op_xor, ATOM_ARG_MC}, {
1201d38ceaf9SAlex Deucher atom_op_shl, ATOM_ARG_REG}, {
1202d38ceaf9SAlex Deucher atom_op_shl, ATOM_ARG_PS}, {
1203d38ceaf9SAlex Deucher atom_op_shl, ATOM_ARG_WS}, {
1204d38ceaf9SAlex Deucher atom_op_shl, ATOM_ARG_FB}, {
1205d38ceaf9SAlex Deucher atom_op_shl, ATOM_ARG_PLL}, {
1206d38ceaf9SAlex Deucher atom_op_shl, ATOM_ARG_MC}, {
1207d38ceaf9SAlex Deucher atom_op_shr, ATOM_ARG_REG}, {
1208d38ceaf9SAlex Deucher atom_op_shr, ATOM_ARG_PS}, {
1209d38ceaf9SAlex Deucher atom_op_shr, ATOM_ARG_WS}, {
1210d38ceaf9SAlex Deucher atom_op_shr, ATOM_ARG_FB}, {
1211d38ceaf9SAlex Deucher atom_op_shr, ATOM_ARG_PLL}, {
1212d38ceaf9SAlex Deucher atom_op_shr, ATOM_ARG_MC}, {
121355438419SAlex Deucher atom_op_debug, 0}, {
1214c9c14502SAlex Deucher atom_op_processds, 0}, {
1215c9c14502SAlex Deucher atom_op_mul32, ATOM_ARG_PS}, {
1216c2fe16aaSAlex Deucher atom_op_mul32, ATOM_ARG_WS}, {
1217c2fe16aaSAlex Deucher atom_op_div32, ATOM_ARG_PS}, {
1218c2fe16aaSAlex Deucher atom_op_div32, ATOM_ARG_WS},
121955438419SAlex Deucher };
1220d38ceaf9SAlex Deucher
amdgpu_atom_execute_table_locked(struct atom_context * ctx,int index,uint32_t * params,int params_size)12214630d503SAlexander Richards static int amdgpu_atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t *params, int params_size)
1222d38ceaf9SAlex Deucher {
1223d38ceaf9SAlex Deucher int base = CU16(ctx->cmd_table + 4 + 2 * index);
1224d38ceaf9SAlex Deucher int len, ws, ps, ptr;
1225d38ceaf9SAlex Deucher unsigned char op;
1226d38ceaf9SAlex Deucher atom_exec_context ectx;
1227d38ceaf9SAlex Deucher int ret = 0;
1228d38ceaf9SAlex Deucher
1229d38ceaf9SAlex Deucher if (!base)
1230d38ceaf9SAlex Deucher return -EINVAL;
1231d38ceaf9SAlex Deucher
1232d38ceaf9SAlex Deucher len = CU16(base + ATOM_CT_SIZE_PTR);
1233d38ceaf9SAlex Deucher ws = CU8(base + ATOM_CT_WS_PTR);
1234d38ceaf9SAlex Deucher ps = CU8(base + ATOM_CT_PS_PTR) & ATOM_CT_PS_MASK;
1235d38ceaf9SAlex Deucher ptr = base + ATOM_CT_CODE_PTR;
1236d38ceaf9SAlex Deucher
1237d38ceaf9SAlex Deucher SDEBUG(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps);
1238d38ceaf9SAlex Deucher
1239d38ceaf9SAlex Deucher ectx.ctx = ctx;
1240d38ceaf9SAlex Deucher ectx.ps_shift = ps / 4;
1241d38ceaf9SAlex Deucher ectx.start = base;
1242d38ceaf9SAlex Deucher ectx.ps = params;
12434630d503SAlexander Richards ectx.ps_size = params_size;
1244d38ceaf9SAlex Deucher ectx.abort = false;
1245d38ceaf9SAlex Deucher ectx.last_jump = 0;
12467bfd16d0SJesse Zhang ectx.last_jump_jiffies = 0;
12474630d503SAlexander Richards if (ws) {
12486396bb22SKees Cook ectx.ws = kcalloc(4, ws, GFP_KERNEL);
12494630d503SAlexander Richards ectx.ws_size = ws;
12504630d503SAlexander Richards } else {
1251d38ceaf9SAlex Deucher ectx.ws = NULL;
12524630d503SAlexander Richards ectx.ws_size = 0;
12534630d503SAlexander Richards }
1254d38ceaf9SAlex Deucher
1255d38ceaf9SAlex Deucher debug_depth++;
1256d38ceaf9SAlex Deucher while (1) {
1257d38ceaf9SAlex Deucher op = CU8(ptr++);
1258d38ceaf9SAlex Deucher if (op < ATOM_OP_NAMES_CNT)
1259d38ceaf9SAlex Deucher SDEBUG("%s @ 0x%04X\n", atom_op_names[op], ptr - 1);
1260d38ceaf9SAlex Deucher else
1261d38ceaf9SAlex Deucher SDEBUG("[%d] @ 0x%04X\n", op, ptr - 1);
1262d38ceaf9SAlex Deucher if (ectx.abort) {
1263d38ceaf9SAlex Deucher DRM_ERROR("atombios stuck executing %04X (len %d, WS %d, PS %d) @ 0x%04X\n",
1264d38ceaf9SAlex Deucher base, len, ws, ps, ptr - 1);
1265d38ceaf9SAlex Deucher ret = -EINVAL;
1266d38ceaf9SAlex Deucher goto free;
1267d38ceaf9SAlex Deucher }
1268d38ceaf9SAlex Deucher
1269d38ceaf9SAlex Deucher if (op < ATOM_OP_CNT && op > 0)
1270d38ceaf9SAlex Deucher opcode_table[op].func(&ectx, &ptr,
1271d38ceaf9SAlex Deucher opcode_table[op].arg);
1272d38ceaf9SAlex Deucher else
1273d38ceaf9SAlex Deucher break;
1274d38ceaf9SAlex Deucher
1275d38ceaf9SAlex Deucher if (op == ATOM_OP_EOT)
1276d38ceaf9SAlex Deucher break;
1277d38ceaf9SAlex Deucher }
1278d38ceaf9SAlex Deucher debug_depth--;
1279d38ceaf9SAlex Deucher SDEBUG("<<\n");
1280d38ceaf9SAlex Deucher
1281d38ceaf9SAlex Deucher free:
1282d38ceaf9SAlex Deucher if (ws)
1283d38ceaf9SAlex Deucher kfree(ectx.ws);
1284d38ceaf9SAlex Deucher return ret;
1285d38ceaf9SAlex Deucher }
1286d38ceaf9SAlex Deucher
amdgpu_atom_execute_table(struct atom_context * ctx,int index,uint32_t * params,int params_size)12874630d503SAlexander Richards int amdgpu_atom_execute_table(struct atom_context *ctx, int index, uint32_t *params, int params_size)
1288d38ceaf9SAlex Deucher {
1289d38ceaf9SAlex Deucher int r;
1290d38ceaf9SAlex Deucher
1291d38ceaf9SAlex Deucher mutex_lock(&ctx->mutex);
1292d38ceaf9SAlex Deucher /* reset data block */
1293d38ceaf9SAlex Deucher ctx->data_block = 0;
1294d38ceaf9SAlex Deucher /* reset reg block */
1295d38ceaf9SAlex Deucher ctx->reg_block = 0;
1296d38ceaf9SAlex Deucher /* reset fb window */
1297d38ceaf9SAlex Deucher ctx->fb_base = 0;
1298d38ceaf9SAlex Deucher /* reset io mode */
1299d38ceaf9SAlex Deucher ctx->io_mode = ATOM_IO_MM;
1300d38ceaf9SAlex Deucher /* reset divmul */
1301d38ceaf9SAlex Deucher ctx->divmul[0] = 0;
1302d38ceaf9SAlex Deucher ctx->divmul[1] = 0;
13034630d503SAlexander Richards r = amdgpu_atom_execute_table_locked(ctx, index, params, params_size);
1304d38ceaf9SAlex Deucher mutex_unlock(&ctx->mutex);
1305d38ceaf9SAlex Deucher return r;
1306d38ceaf9SAlex Deucher }
1307d38ceaf9SAlex Deucher
1308d38ceaf9SAlex Deucher static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
1309d38ceaf9SAlex Deucher
atom_index_iio(struct atom_context * ctx,int base)1310d38ceaf9SAlex Deucher static void atom_index_iio(struct atom_context *ctx, int base)
1311d38ceaf9SAlex Deucher {
1312d38ceaf9SAlex Deucher ctx->iio = kzalloc(2 * 256, GFP_KERNEL);
1313d38ceaf9SAlex Deucher if (!ctx->iio)
1314d38ceaf9SAlex Deucher return;
1315d38ceaf9SAlex Deucher while (CU8(base) == ATOM_IIO_START) {
1316d38ceaf9SAlex Deucher ctx->iio[CU8(base + 1)] = base + 2;
1317d38ceaf9SAlex Deucher base += 2;
1318d38ceaf9SAlex Deucher while (CU8(base) != ATOM_IIO_END)
1319d38ceaf9SAlex Deucher base += atom_iio_len[CU8(base)];
1320d38ceaf9SAlex Deucher base += 3;
1321d38ceaf9SAlex Deucher }
1322d38ceaf9SAlex Deucher }
1323d38ceaf9SAlex Deucher
atom_get_vbios_name(struct atom_context * ctx)132429b4c589SJiawei Gu static void atom_get_vbios_name(struct atom_context *ctx)
132529b4c589SJiawei Gu {
132629b4c589SJiawei Gu unsigned char *p_rom;
132729b4c589SJiawei Gu unsigned char str_num;
132829b4c589SJiawei Gu unsigned short off_to_vbios_str;
132929b4c589SJiawei Gu unsigned char *c_ptr;
133029b4c589SJiawei Gu int name_size;
133129b4c589SJiawei Gu int i;
133229b4c589SJiawei Gu
133329b4c589SJiawei Gu const char *na = "--N/A--";
133429b4c589SJiawei Gu char *back;
133529b4c589SJiawei Gu
133629b4c589SJiawei Gu p_rom = ctx->bios;
133729b4c589SJiawei Gu
133829b4c589SJiawei Gu str_num = *(p_rom + OFFSET_TO_GET_ATOMBIOS_NUMBER_OF_STRINGS);
133929b4c589SJiawei Gu if (str_num != 0) {
134029b4c589SJiawei Gu off_to_vbios_str =
134129b4c589SJiawei Gu *(unsigned short *)(p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START);
134229b4c589SJiawei Gu
134329b4c589SJiawei Gu c_ptr = (unsigned char *)(p_rom + off_to_vbios_str);
134429b4c589SJiawei Gu } else {
134529b4c589SJiawei Gu /* do not know where to find name */
134629b4c589SJiawei Gu memcpy(ctx->name, na, 7);
134729b4c589SJiawei Gu ctx->name[7] = 0;
134829b4c589SJiawei Gu return;
134929b4c589SJiawei Gu }
135029b4c589SJiawei Gu
135129b4c589SJiawei Gu /*
135229b4c589SJiawei Gu * skip the atombios strings, usually 4
135329b4c589SJiawei Gu * 1st is P/N, 2nd is ASIC, 3rd is PCI type, 4th is Memory type
135429b4c589SJiawei Gu */
135529b4c589SJiawei Gu for (i = 0; i < str_num; i++) {
135629b4c589SJiawei Gu while (*c_ptr != 0)
135729b4c589SJiawei Gu c_ptr++;
135829b4c589SJiawei Gu c_ptr++;
135929b4c589SJiawei Gu }
136029b4c589SJiawei Gu
136129b4c589SJiawei Gu /* skip the following 2 chars: 0x0D 0x0A */
136229b4c589SJiawei Gu c_ptr += 2;
136329b4c589SJiawei Gu
136429b4c589SJiawei Gu name_size = strnlen(c_ptr, STRLEN_LONG - 1);
136529b4c589SJiawei Gu memcpy(ctx->name, c_ptr, name_size);
136629b4c589SJiawei Gu back = ctx->name + name_size;
136729b4c589SJiawei Gu while ((*--back) == ' ')
136829b4c589SJiawei Gu ;
136929b4c589SJiawei Gu *(back + 1) = '\0';
137029b4c589SJiawei Gu }
137129b4c589SJiawei Gu
atom_get_vbios_date(struct atom_context * ctx)137229b4c589SJiawei Gu static void atom_get_vbios_date(struct atom_context *ctx)
137329b4c589SJiawei Gu {
137429b4c589SJiawei Gu unsigned char *p_rom;
137529b4c589SJiawei Gu unsigned char *date_in_rom;
137629b4c589SJiawei Gu
137729b4c589SJiawei Gu p_rom = ctx->bios;
137829b4c589SJiawei Gu
137929b4c589SJiawei Gu date_in_rom = p_rom + OFFSET_TO_VBIOS_DATE;
138029b4c589SJiawei Gu
138129b4c589SJiawei Gu ctx->date[0] = '2';
138229b4c589SJiawei Gu ctx->date[1] = '0';
138329b4c589SJiawei Gu ctx->date[2] = date_in_rom[6];
138429b4c589SJiawei Gu ctx->date[3] = date_in_rom[7];
138529b4c589SJiawei Gu ctx->date[4] = '/';
138629b4c589SJiawei Gu ctx->date[5] = date_in_rom[0];
138729b4c589SJiawei Gu ctx->date[6] = date_in_rom[1];
138829b4c589SJiawei Gu ctx->date[7] = '/';
138929b4c589SJiawei Gu ctx->date[8] = date_in_rom[3];
139029b4c589SJiawei Gu ctx->date[9] = date_in_rom[4];
139129b4c589SJiawei Gu ctx->date[10] = ' ';
139229b4c589SJiawei Gu ctx->date[11] = date_in_rom[9];
139329b4c589SJiawei Gu ctx->date[12] = date_in_rom[10];
139429b4c589SJiawei Gu ctx->date[13] = date_in_rom[11];
139529b4c589SJiawei Gu ctx->date[14] = date_in_rom[12];
139629b4c589SJiawei Gu ctx->date[15] = date_in_rom[13];
139729b4c589SJiawei Gu ctx->date[16] = '\0';
139829b4c589SJiawei Gu }
139929b4c589SJiawei Gu
atom_find_str_in_rom(struct atom_context * ctx,char * str,int start,int end,int maxlen)140029b4c589SJiawei Gu static unsigned char *atom_find_str_in_rom(struct atom_context *ctx, char *str, int start,
140129b4c589SJiawei Gu int end, int maxlen)
140229b4c589SJiawei Gu {
140329b4c589SJiawei Gu unsigned long str_off;
140429b4c589SJiawei Gu unsigned char *p_rom;
140529b4c589SJiawei Gu unsigned short str_len;
140629b4c589SJiawei Gu
140729b4c589SJiawei Gu str_off = 0;
140829b4c589SJiawei Gu str_len = strnlen(str, maxlen);
140929b4c589SJiawei Gu p_rom = ctx->bios;
141029b4c589SJiawei Gu
141129b4c589SJiawei Gu for (; start <= end; ++start) {
141229b4c589SJiawei Gu for (str_off = 0; str_off < str_len; ++str_off) {
141329b4c589SJiawei Gu if (str[str_off] != *(p_rom + start + str_off))
141429b4c589SJiawei Gu break;
141529b4c589SJiawei Gu }
141629b4c589SJiawei Gu
141729b4c589SJiawei Gu if (str_off == str_len || str[str_off] == 0)
141829b4c589SJiawei Gu return p_rom + start;
141929b4c589SJiawei Gu }
142029b4c589SJiawei Gu return NULL;
142129b4c589SJiawei Gu }
142229b4c589SJiawei Gu
atom_get_vbios_pn(struct atom_context * ctx)142329b4c589SJiawei Gu static void atom_get_vbios_pn(struct atom_context *ctx)
142429b4c589SJiawei Gu {
142529b4c589SJiawei Gu unsigned char *p_rom;
142629b4c589SJiawei Gu unsigned short off_to_vbios_str;
142729b4c589SJiawei Gu unsigned char *vbios_str;
142829b4c589SJiawei Gu int count;
142929b4c589SJiawei Gu
143029b4c589SJiawei Gu off_to_vbios_str = 0;
143129b4c589SJiawei Gu p_rom = ctx->bios;
143229b4c589SJiawei Gu
143329b4c589SJiawei Gu if (*(p_rom + OFFSET_TO_GET_ATOMBIOS_NUMBER_OF_STRINGS) != 0) {
143429b4c589SJiawei Gu off_to_vbios_str =
143529b4c589SJiawei Gu *(unsigned short *)(p_rom + OFFSET_TO_GET_ATOMBIOS_STRING_START);
143629b4c589SJiawei Gu
143729b4c589SJiawei Gu vbios_str = (unsigned char *)(p_rom + off_to_vbios_str);
143829b4c589SJiawei Gu } else {
143929b4c589SJiawei Gu vbios_str = p_rom + OFFSET_TO_VBIOS_PART_NUMBER;
144029b4c589SJiawei Gu }
144129b4c589SJiawei Gu
144229b4c589SJiawei Gu if (*vbios_str == 0) {
144329b4c589SJiawei Gu vbios_str = atom_find_str_in_rom(ctx, BIOS_ATOM_PREFIX, 3, 1024, 64);
144429b4c589SJiawei Gu if (vbios_str == NULL)
144529b4c589SJiawei Gu vbios_str += sizeof(BIOS_ATOM_PREFIX) - 1;
144629b4c589SJiawei Gu }
144729b4c589SJiawei Gu if (vbios_str != NULL && *vbios_str == 0)
144829b4c589SJiawei Gu vbios_str++;
144929b4c589SJiawei Gu
145029b4c589SJiawei Gu if (vbios_str != NULL) {
145129b4c589SJiawei Gu count = 0;
145229b4c589SJiawei Gu while ((count < BIOS_STRING_LENGTH) && vbios_str[count] >= ' ' &&
145329b4c589SJiawei Gu vbios_str[count] <= 'z') {
145429b4c589SJiawei Gu ctx->vbios_pn[count] = vbios_str[count];
145529b4c589SJiawei Gu count++;
145629b4c589SJiawei Gu }
145729b4c589SJiawei Gu
145829b4c589SJiawei Gu ctx->vbios_pn[count] = 0;
145929b4c589SJiawei Gu }
1460adf64e21SMario Limonciello
1461adf64e21SMario Limonciello pr_info("ATOM BIOS: %s\n", ctx->vbios_pn);
146229b4c589SJiawei Gu }
146329b4c589SJiawei Gu
atom_get_vbios_version(struct atom_context * ctx)146429b4c589SJiawei Gu static void atom_get_vbios_version(struct atom_context *ctx)
146529b4c589SJiawei Gu {
146624f60ddcSLijo Lazar unsigned short start = 3, end;
146729b4c589SJiawei Gu unsigned char *vbios_ver;
146824f60ddcSLijo Lazar unsigned char *p_rom;
146924f60ddcSLijo Lazar
147024f60ddcSLijo Lazar p_rom = ctx->bios;
147124f60ddcSLijo Lazar /* Search from strings offset if it's present */
147224f60ddcSLijo Lazar start = *(unsigned short *)(p_rom +
147324f60ddcSLijo Lazar OFFSET_TO_GET_ATOMBIOS_STRING_START);
147424f60ddcSLijo Lazar
147524f60ddcSLijo Lazar /* Search till atom rom header start point */
147624f60ddcSLijo Lazar end = *(unsigned short *)(p_rom + OFFSET_TO_ATOM_ROM_HEADER_POINTER);
147724f60ddcSLijo Lazar
147824f60ddcSLijo Lazar /* Use hardcoded offsets, if the offsets are not populated */
147924f60ddcSLijo Lazar if (end <= start) {
148024f60ddcSLijo Lazar start = 3;
148124f60ddcSLijo Lazar end = 1024;
148224f60ddcSLijo Lazar }
148329b4c589SJiawei Gu
148429b4c589SJiawei Gu /* find anchor ATOMBIOSBK-AMD */
148524f60ddcSLijo Lazar vbios_ver =
148624f60ddcSLijo Lazar atom_find_str_in_rom(ctx, BIOS_VERSION_PREFIX, start, end, 64);
148729b4c589SJiawei Gu if (vbios_ver != NULL) {
148829b4c589SJiawei Gu /* skip ATOMBIOSBK-AMD VER */
148929b4c589SJiawei Gu vbios_ver += 18;
149029b4c589SJiawei Gu memcpy(ctx->vbios_ver_str, vbios_ver, STRLEN_NORMAL);
149129b4c589SJiawei Gu } else {
149229b4c589SJiawei Gu ctx->vbios_ver_str[0] = '\0';
149329b4c589SJiawei Gu }
149429b4c589SJiawei Gu }
149529b4c589SJiawei Gu
amdgpu_atom_parse(struct card_info * card,void * bios)1496d38ceaf9SAlex Deucher struct atom_context *amdgpu_atom_parse(struct card_info *card, void *bios)
1497d38ceaf9SAlex Deucher {
1498d38ceaf9SAlex Deucher int base;
1499d38ceaf9SAlex Deucher struct atom_context *ctx =
1500d38ceaf9SAlex Deucher kzalloc(sizeof(struct atom_context), GFP_KERNEL);
150129b4c589SJiawei Gu struct _ATOM_ROM_HEADER *atom_rom_header;
150229b4c589SJiawei Gu struct _ATOM_MASTER_DATA_TABLE *master_table;
150329b4c589SJiawei Gu struct _ATOM_FIRMWARE_INFO *atom_fw_info;
1504d38ceaf9SAlex Deucher
1505d38ceaf9SAlex Deucher if (!ctx)
1506d38ceaf9SAlex Deucher return NULL;
1507d38ceaf9SAlex Deucher
1508d38ceaf9SAlex Deucher ctx->card = card;
1509d38ceaf9SAlex Deucher ctx->bios = bios;
1510d38ceaf9SAlex Deucher
1511d38ceaf9SAlex Deucher if (CU16(0) != ATOM_BIOS_MAGIC) {
15127ca85295SJoe Perches pr_info("Invalid BIOS magic\n");
1513d38ceaf9SAlex Deucher kfree(ctx);
1514d38ceaf9SAlex Deucher return NULL;
1515d38ceaf9SAlex Deucher }
1516d38ceaf9SAlex Deucher if (strncmp
1517d38ceaf9SAlex Deucher (CSTR(ATOM_ATI_MAGIC_PTR), ATOM_ATI_MAGIC,
1518d38ceaf9SAlex Deucher strlen(ATOM_ATI_MAGIC))) {
15197ca85295SJoe Perches pr_info("Invalid ATI magic\n");
1520d38ceaf9SAlex Deucher kfree(ctx);
1521d38ceaf9SAlex Deucher return NULL;
1522d38ceaf9SAlex Deucher }
1523d38ceaf9SAlex Deucher
1524d38ceaf9SAlex Deucher base = CU16(ATOM_ROM_TABLE_PTR);
1525d38ceaf9SAlex Deucher if (strncmp
1526d38ceaf9SAlex Deucher (CSTR(base + ATOM_ROM_MAGIC_PTR), ATOM_ROM_MAGIC,
1527d38ceaf9SAlex Deucher strlen(ATOM_ROM_MAGIC))) {
15287ca85295SJoe Perches pr_info("Invalid ATOM magic\n");
1529d38ceaf9SAlex Deucher kfree(ctx);
1530d38ceaf9SAlex Deucher return NULL;
1531d38ceaf9SAlex Deucher }
1532d38ceaf9SAlex Deucher
1533d38ceaf9SAlex Deucher ctx->cmd_table = CU16(base + ATOM_ROM_CMD_PTR);
1534d38ceaf9SAlex Deucher ctx->data_table = CU16(base + ATOM_ROM_DATA_PTR);
1535d38ceaf9SAlex Deucher atom_index_iio(ctx, CU16(ctx->data_table + ATOM_DATA_IIO_PTR) + 4);
1536d38ceaf9SAlex Deucher if (!ctx->iio) {
1537d38ceaf9SAlex Deucher amdgpu_atom_destroy(ctx);
1538d38ceaf9SAlex Deucher return NULL;
1539d38ceaf9SAlex Deucher }
1540d38ceaf9SAlex Deucher
154129b4c589SJiawei Gu atom_rom_header = (struct _ATOM_ROM_HEADER *)CSTR(base);
154229b4c589SJiawei Gu if (atom_rom_header->usMasterDataTableOffset != 0) {
154329b4c589SJiawei Gu master_table = (struct _ATOM_MASTER_DATA_TABLE *)
154429b4c589SJiawei Gu CSTR(atom_rom_header->usMasterDataTableOffset);
154529b4c589SJiawei Gu if (master_table->ListOfDataTables.FirmwareInfo != 0) {
154629b4c589SJiawei Gu atom_fw_info = (struct _ATOM_FIRMWARE_INFO *)
154729b4c589SJiawei Gu CSTR(master_table->ListOfDataTables.FirmwareInfo);
154829b4c589SJiawei Gu ctx->version = atom_fw_info->ulFirmwareRevision;
154929b4c589SJiawei Gu }
155029b4c589SJiawei Gu }
155129b4c589SJiawei Gu
155229b4c589SJiawei Gu atom_get_vbios_name(ctx);
155329b4c589SJiawei Gu atom_get_vbios_pn(ctx);
155429b4c589SJiawei Gu atom_get_vbios_date(ctx);
155529b4c589SJiawei Gu atom_get_vbios_version(ctx);
1556d38ceaf9SAlex Deucher
1557d38ceaf9SAlex Deucher return ctx;
1558d38ceaf9SAlex Deucher }
1559d38ceaf9SAlex Deucher
amdgpu_atom_asic_init(struct atom_context * ctx)1560d38ceaf9SAlex Deucher int amdgpu_atom_asic_init(struct atom_context *ctx)
1561d38ceaf9SAlex Deucher {
1562d38ceaf9SAlex Deucher int hwi = CU16(ctx->data_table + ATOM_DATA_FWI_PTR);
1563d38ceaf9SAlex Deucher uint32_t ps[16];
1564d38ceaf9SAlex Deucher int ret;
1565d38ceaf9SAlex Deucher
1566d38ceaf9SAlex Deucher memset(ps, 0, 64);
1567d38ceaf9SAlex Deucher
1568d38ceaf9SAlex Deucher ps[0] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFSCLK_PTR));
1569d38ceaf9SAlex Deucher ps[1] = cpu_to_le32(CU32(hwi + ATOM_FWI_DEFMCLK_PTR));
1570d38ceaf9SAlex Deucher if (!ps[0] || !ps[1])
1571d38ceaf9SAlex Deucher return 1;
1572d38ceaf9SAlex Deucher
1573d38ceaf9SAlex Deucher if (!CU16(ctx->cmd_table + 4 + 2 * ATOM_CMD_INIT))
1574d38ceaf9SAlex Deucher return 1;
15754630d503SAlexander Richards ret = amdgpu_atom_execute_table(ctx, ATOM_CMD_INIT, ps, 16);
1576d38ceaf9SAlex Deucher if (ret)
1577d38ceaf9SAlex Deucher return ret;
1578d38ceaf9SAlex Deucher
1579d38ceaf9SAlex Deucher memset(ps, 0, 64);
1580d38ceaf9SAlex Deucher
1581d38ceaf9SAlex Deucher return ret;
1582d38ceaf9SAlex Deucher }
1583d38ceaf9SAlex Deucher
amdgpu_atom_destroy(struct atom_context * ctx)1584d38ceaf9SAlex Deucher void amdgpu_atom_destroy(struct atom_context *ctx)
1585d38ceaf9SAlex Deucher {
1586d38ceaf9SAlex Deucher kfree(ctx->iio);
1587d38ceaf9SAlex Deucher kfree(ctx);
1588d38ceaf9SAlex Deucher }
1589d38ceaf9SAlex Deucher
amdgpu_atom_parse_data_header(struct atom_context * ctx,int index,uint16_t * size,uint8_t * frev,uint8_t * crev,uint16_t * data_start)1590d38ceaf9SAlex Deucher bool amdgpu_atom_parse_data_header(struct atom_context *ctx, int index,
1591d38ceaf9SAlex Deucher uint16_t *size, uint8_t *frev, uint8_t *crev,
1592d38ceaf9SAlex Deucher uint16_t *data_start)
1593d38ceaf9SAlex Deucher {
1594d38ceaf9SAlex Deucher int offset = index * 2 + 4;
1595d38ceaf9SAlex Deucher int idx = CU16(ctx->data_table + offset);
1596d38ceaf9SAlex Deucher u16 *mdt = (u16 *)(ctx->bios + ctx->data_table + 4);
1597d38ceaf9SAlex Deucher
1598d38ceaf9SAlex Deucher if (!mdt[index])
1599d38ceaf9SAlex Deucher return false;
1600d38ceaf9SAlex Deucher
1601d38ceaf9SAlex Deucher if (size)
1602d38ceaf9SAlex Deucher *size = CU16(idx);
1603d38ceaf9SAlex Deucher if (frev)
1604d38ceaf9SAlex Deucher *frev = CU8(idx + 2);
1605d38ceaf9SAlex Deucher if (crev)
1606d38ceaf9SAlex Deucher *crev = CU8(idx + 3);
1607d38ceaf9SAlex Deucher *data_start = idx;
1608d38ceaf9SAlex Deucher return true;
1609d38ceaf9SAlex Deucher }
1610d38ceaf9SAlex Deucher
amdgpu_atom_parse_cmd_header(struct atom_context * ctx,int index,uint8_t * frev,uint8_t * crev)1611d38ceaf9SAlex Deucher bool amdgpu_atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev,
1612d38ceaf9SAlex Deucher uint8_t *crev)
1613d38ceaf9SAlex Deucher {
1614d38ceaf9SAlex Deucher int offset = index * 2 + 4;
1615d38ceaf9SAlex Deucher int idx = CU16(ctx->cmd_table + offset);
1616d38ceaf9SAlex Deucher u16 *mct = (u16 *)(ctx->bios + ctx->cmd_table + 4);
1617d38ceaf9SAlex Deucher
1618d38ceaf9SAlex Deucher if (!mct[index])
1619d38ceaf9SAlex Deucher return false;
1620d38ceaf9SAlex Deucher
1621d38ceaf9SAlex Deucher if (frev)
1622d38ceaf9SAlex Deucher *frev = CU8(idx + 2);
1623d38ceaf9SAlex Deucher if (crev)
1624d38ceaf9SAlex Deucher *crev = CU8(idx + 3);
1625d38ceaf9SAlex Deucher return true;
1626d38ceaf9SAlex Deucher }
1627d38ceaf9SAlex Deucher
1628