1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2025 Etnaviv Project
4 */
5
6 #include <linux/errno.h>
7 #include <linux/dev_printk.h>
8 #include <linux/string.h>
9 #include <linux/types.h>
10
11 #include "etnaviv_buffer.h"
12 #include "etnaviv_cmdbuf.h"
13 #include "etnaviv_gpu.h"
14 #include "state_3d.xml.h"
15
16 #include "etnaviv_flop_reset.h"
17
18 static int etnaviv_force_flop_reset;
19 module_param_named(force_flop_reset, etnaviv_force_flop_reset, int, 0);
20
21 #define PPU_IMAGE_STRIDE 64
22 #define PPU_IMAGE_XSIZE 64
23 #define PPU_IMAGE_YSIZE 6
24
25 #define PPU_FLOP_RESET_INSTR_DWORD_COUNT 16
26
etnaviv_emit_flop_reset_state_ppu(struct etnaviv_cmdbuf * cmdbuf,u32 buffer_base,u32 input_offset,u32 output_offset,u32 shader_offset,u32 shader_size,u32 shader_register_count)27 static void etnaviv_emit_flop_reset_state_ppu(struct etnaviv_cmdbuf *cmdbuf,
28 u32 buffer_base, u32 input_offset,
29 u32 output_offset,
30 u32 shader_offset,
31 u32 shader_size,
32 u32 shader_register_count)
33 {
34 CMD_LOAD_STATE(cmdbuf, VIVS_GL_API_MODE, VIVS_GL_API_MODE_OPENCL);
35 CMD_SEM(cmdbuf, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
36 CMD_STALL(cmdbuf, SYNC_RECIPIENT_FE, SYNC_RECIPIENT_PE);
37
38 CMD_LOAD_STATES_START(cmdbuf, VIVS_SH_HALTI5_UNIFORMS(0), 4);
39
40 OUT(cmdbuf, buffer_base + input_offset);
41 OUT(cmdbuf, PPU_IMAGE_STRIDE);
42 OUT(cmdbuf, PPU_IMAGE_XSIZE | (PPU_IMAGE_YSIZE << 16));
43 OUT(cmdbuf, 0x444051f0);
44 OUT(cmdbuf, 0xffffffff);
45
46 CMD_LOAD_STATES_START(cmdbuf, VIVS_SH_HALTI5_UNIFORMS(4), 4);
47 OUT(cmdbuf, buffer_base + output_offset);
48 OUT(cmdbuf, PPU_IMAGE_STRIDE);
49 OUT(cmdbuf, PPU_IMAGE_XSIZE | (PPU_IMAGE_YSIZE << 16));
50 OUT(cmdbuf, 0x444051f0);
51 OUT(cmdbuf, 0xffffffff);
52
53 CMD_LOAD_STATE(cmdbuf, VIVS_CL_CONFIG,
54 VIVS_CL_CONFIG_DIMENSIONS(2) |
55 VIVS_CL_CONFIG_VALUE_ORDER(3));
56 CMD_LOAD_STATE(cmdbuf, VIVS_VS_ICACHE_INVALIDATE, 0x1f);
57 CMD_LOAD_STATE(cmdbuf, VIVS_PS_VARYING_NUM_COMPONENTS(0), 0);
58 CMD_LOAD_STATE(cmdbuf, VIVS_PS_TEMP_REGISTER_CONTROL,
59 shader_register_count);
60 CMD_LOAD_STATE(cmdbuf, VIVS_PS_SAMPLER_BASE, 0x0);
61 CMD_LOAD_STATE(cmdbuf, VIVS_PS_UNIFORM_BASE, 0x0);
62 CMD_LOAD_STATE(cmdbuf, VIVS_PS_NEWRANGE_LOW, 0x0);
63 CMD_LOAD_STATE(cmdbuf, VIVS_PS_NEWRANGE_HIGH, shader_size / 16);
64 CMD_LOAD_STATE(cmdbuf, VIVS_PS_INST_ADDR, buffer_base + shader_offset);
65 CMD_LOAD_STATE(cmdbuf, VIVS_SH_CONFIG, VIVS_SH_CONFIG_RTNE_ROUNDING);
66 CMD_LOAD_STATE(cmdbuf, VIVS_VS_ICACHE_CONTROL,
67 VIVS_VS_ICACHE_CONTROL_ENABLE);
68 CMD_LOAD_STATE(cmdbuf, VIVS_PS_ICACHE_COUNT, shader_size / 16 - 1);
69 CMD_LOAD_STATE(cmdbuf, VIVS_PS_INPUT_COUNT, 0x1f01);
70 CMD_LOAD_STATE(cmdbuf, VIVS_VS_HALTI5_UNK008A0, 0x0);
71 CMD_LOAD_STATE(cmdbuf, VIVS_PA_VS_OUTPUT_COUNT, 0x0);
72 CMD_LOAD_STATE(cmdbuf, VIVS_GL_VARYING_TOTAL_COMPONENTS, 0x0);
73 CMD_LOAD_STATE(cmdbuf, VIVS_PS_CONTROL_EXT, 0x0);
74 CMD_LOAD_STATE(cmdbuf, VIVS_VS_OUTPUT_COUNT, 0x1);
75 CMD_LOAD_STATE(cmdbuf, VIVS_GL_HALTI5_SH_SPECIALS, 0x0);
76 CMD_LOAD_STATE(cmdbuf, VIVS_PS_ICACHE_PREFETCH, 0x0);
77 CMD_LOAD_STATE(cmdbuf, VIVS_CL_UNK00924, 0x0);
78 CMD_LOAD_STATE(cmdbuf, VIVS_CL_THREAD_ALLOCATION, 0x1);
79
80 CMD_LOAD_STATE(cmdbuf, VIVS_CL_GLOBAL_WORK_OFFSET_X, 0x0);
81 CMD_LOAD_STATE(cmdbuf, VIVS_CL_GLOBAL_WORK_OFFSET_Y, 0x0);
82 CMD_LOAD_STATE(cmdbuf, VIVS_CL_GLOBAL_WORK_OFFSET_Z, 0x0);
83
84 CMD_LOAD_STATES_START(cmdbuf, VIVS_CL_WORKGROUP_COUNT_X, 9);
85 OUT(cmdbuf, 0xf);
86 OUT(cmdbuf, 0x5);
87 OUT(cmdbuf, 0xffffffff);
88 OUT(cmdbuf, 0x0);
89 OUT(cmdbuf, 0x0);
90 OUT(cmdbuf, 0x3ff);
91 OUT(cmdbuf, 0x0);
92 OUT(cmdbuf, 0x4);
93 OUT(cmdbuf, 0x1);
94 OUT(cmdbuf, 0x0);
95
96 CMD_LOAD_STATE(cmdbuf, VIVS_CL_KICKER, 0xbadabeeb);
97 CMD_LOAD_STATE(cmdbuf, VIVS_GL_FLUSH_CACHE,
98 VIVS_GL_FLUSH_CACHE_SHADER_L1 |
99 VIVS_GL_FLUSH_CACHE_UNK10 |
100 VIVS_GL_FLUSH_CACHE_UNK11);
101 }
102
etnaviv_flop_reset_ppu_fill_input(u32 * buffer,u32 size)103 static void etnaviv_flop_reset_ppu_fill_input(u32 *buffer, u32 size)
104 {
105 memset32(buffer, 0x01010101, size / 4);
106 }
107
etnaviv_flop_reset_ppu_set_shader(u8 * dest)108 static void etnaviv_flop_reset_ppu_set_shader(u8 *dest)
109 {
110 static const u32 inst[PPU_FLOP_RESET_INSTR_DWORD_COUNT] = {
111 /* img_load.u8 r1, c0, r0.xy */
112 0x78011779,
113 0x39000804,
114 0x00A90050,
115 0x00000000,
116 /* img_load.u8 r2, c0, r0.xy */
117 0x78021779,
118 0x39000804,
119 0x00A90050,
120 0x00000000,
121 /* dp2x8 r1, r1, r2, c3_512 */
122 0xB8017145,
123 0x390018FC,
124 0x01C90140,
125 0x40390028,
126 /* img_store.u8 r1, c2, r0.xy, r1 */
127 0x380007BA,
128 0x39001804,
129 0x00A90050,
130 0x00390018,
131 };
132 memcpy(dest, inst, sizeof(inst));
133 }
134
135 static const struct etnaviv_flop_reset_entry {
136 u16 chip_model;
137 u16 revision;
138 u32 flags;
139 } etnaviv_flop_reset_db[] = {
140 {
141 .chip_model = 0x8000,
142 .revision = 0x6205,
143 },
144 };
145
etnaviv_flop_reset_ppu_require(const struct etnaviv_chip_identity * chip_id)146 bool etnaviv_flop_reset_ppu_require(const struct etnaviv_chip_identity *chip_id)
147 {
148 const struct etnaviv_flop_reset_entry *e = etnaviv_flop_reset_db;
149
150 for (int i = 0; i < ARRAY_SIZE(etnaviv_flop_reset_db); ++i, ++e) {
151 if (chip_id->model == e->chip_model &&
152 chip_id->revision == e->revision)
153 return true;
154 }
155
156 if (etnaviv_force_flop_reset) {
157 if (!(chip_id->features & chipFeatures_PIPE_3D)) {
158 pr_warn("Etnaviv: model: 0x%04x, revision: 0x%04x does not support PIPE_3D\n",
159 chip_id->model, chip_id->revision);
160 pr_warn("Request to force PPU flop reset ignored.\n");
161 return false;
162 }
163
164 pr_info("Force PPU flop reset for model: 0x%04x, revision: 0x%04x\n",
165 chip_id->model, chip_id->revision);
166 return true;
167 }
168
169 return false;
170 }
171
172 static const u32 image_data_size = PPU_IMAGE_STRIDE * PPU_IMAGE_YSIZE;
173 static const u32 output_offset = ALIGN(image_data_size, 64);
174 static const u32 shader_offset = ALIGN(output_offset + image_data_size, 64);
175 static const u32 shader_size = PPU_FLOP_RESET_INSTR_DWORD_COUNT * sizeof(u32);
176 static const u32 shader_register_count = 3;
177 static const u32 buffer_size = shader_offset + shader_size;
178
etnaviv_flop_reset_ppu_init(struct etnaviv_drm_private * priv)179 int etnaviv_flop_reset_ppu_init(struct etnaviv_drm_private *priv)
180 {
181 /* Get some space from the ring buffer to put the payload
182 * (input and output image, and shader), we keep this buffer
183 * for the whole life time the driver is bound
184 */
185 priv->flop_reset_data_ppu = kzalloc_obj(*priv->flop_reset_data_ppu);
186
187 if (!priv->flop_reset_data_ppu)
188 return -ENOMEM;
189
190 int ret = etnaviv_cmdbuf_init(priv->cmdbuf_suballoc,
191 priv->flop_reset_data_ppu, buffer_size);
192 if (ret) {
193 kfree(priv->flop_reset_data_ppu);
194 return ret;
195 }
196
197 void *buffer_base = priv->flop_reset_data_ppu->vaddr;
198 u32 *input_data = (u32 *)buffer_base;
199 u8 *shader_data = (u8 *)buffer_base + shader_offset;
200
201 etnaviv_flop_reset_ppu_fill_input(input_data, image_data_size);
202 etnaviv_flop_reset_ppu_set_shader(shader_data);
203
204 return 0;
205 }
206
etnaviv_flop_reset_ppu_run(struct etnaviv_gpu * gpu)207 void etnaviv_flop_reset_ppu_run(struct etnaviv_gpu *gpu)
208 {
209 struct etnaviv_drm_private *priv = gpu->drm->dev_private;
210
211 if (!priv->flop_reset_data_ppu) {
212 dev_err(gpu->dev,
213 "Oops: Flop reset data was not initialized, skipping\n");
214 return;
215 }
216
217 u32 buffer_base = etnaviv_cmdbuf_get_va(priv->flop_reset_data_ppu,
218 &gpu->mmu_context->cmdbuf_mapping);
219
220 etnaviv_emit_flop_reset_state_ppu(&gpu->buffer, buffer_base, 0,
221 output_offset, shader_offset,
222 shader_size, shader_register_count);
223 }
224