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 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 103 static void etnaviv_flop_reset_ppu_fill_input(u32 *buffer, u32 size) 104 { 105 memset32(buffer, 0x01010101, size / 4); 106 } 107 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 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 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 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