1 /* $NetBSD: intel_dsb.c,v 1.3 2021/12/19 11:38:26 riastradh Exp $ */ 2 3 // SPDX-License-Identifier: MIT 4 /* 5 * Copyright 2019 Intel Corporation 6 * 7 */ 8 9 #include <sys/cdefs.h> 10 __KERNEL_RCSID(0, "$NetBSD: intel_dsb.c,v 1.3 2021/12/19 11:38:26 riastradh Exp $"); 11 12 #include "i915_drv.h" 13 #include "intel_display_types.h" 14 15 #include <linux/nbsd-namespace.h> 16 17 #define DSB_BUF_SIZE (2 * PAGE_SIZE) 18 19 /** 20 * DOC: DSB 21 * 22 * A DSB (Display State Buffer) is a queue of MMIO instructions in the memory 23 * which can be offloaded to DSB HW in Display Controller. DSB HW is a DMA 24 * engine that can be programmed to download the DSB from memory. 25 * It allows driver to batch submit display HW programming. This helps to 26 * reduce loading time and CPU activity, thereby making the context switch 27 * faster. DSB Support added from Gen12 Intel graphics based platform. 28 * 29 * DSB's can access only the pipe, plane, and transcoder Data Island Packet 30 * registers. 31 * 32 * DSB HW can support only register writes (both indexed and direct MMIO 33 * writes). There are no registers reads possible with DSB HW engine. 34 */ 35 36 /* DSB opcodes. */ 37 #define DSB_OPCODE_SHIFT 24 38 #define DSB_OPCODE_MMIO_WRITE 0x1 39 #define DSB_OPCODE_INDEXED_WRITE 0x9 40 #define DSB_BYTE_EN 0xF 41 #define DSB_BYTE_EN_SHIFT 20 42 #define DSB_REG_VALUE_MASK 0xfffff 43 44 static inline bool is_dsb_busy(struct intel_dsb *dsb) 45 { 46 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); 47 struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 48 enum pipe pipe = crtc->pipe; 49 50 return DSB_STATUS & I915_READ(DSB_CTRL(pipe, dsb->id)); 51 } 52 53 static inline bool intel_dsb_enable_engine(struct intel_dsb *dsb) 54 { 55 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); 56 struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 57 enum pipe pipe = crtc->pipe; 58 u32 dsb_ctrl; 59 60 dsb_ctrl = I915_READ(DSB_CTRL(pipe, dsb->id)); 61 if (DSB_STATUS & dsb_ctrl) { 62 DRM_DEBUG_KMS("DSB engine is busy.\n"); 63 return false; 64 } 65 66 dsb_ctrl |= DSB_ENABLE; 67 I915_WRITE(DSB_CTRL(pipe, dsb->id), dsb_ctrl); 68 69 POSTING_READ(DSB_CTRL(pipe, dsb->id)); 70 return true; 71 } 72 73 static inline bool intel_dsb_disable_engine(struct intel_dsb *dsb) 74 { 75 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); 76 struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 77 enum pipe pipe = crtc->pipe; 78 u32 dsb_ctrl; 79 80 dsb_ctrl = I915_READ(DSB_CTRL(pipe, dsb->id)); 81 if (DSB_STATUS & dsb_ctrl) { 82 DRM_DEBUG_KMS("DSB engine is busy.\n"); 83 return false; 84 } 85 86 dsb_ctrl &= ~DSB_ENABLE; 87 I915_WRITE(DSB_CTRL(pipe, dsb->id), dsb_ctrl); 88 89 POSTING_READ(DSB_CTRL(pipe, dsb->id)); 90 return true; 91 } 92 93 /** 94 * intel_dsb_get() - Allocate DSB context and return a DSB instance. 95 * @crtc: intel_crtc structure to get pipe info. 96 * 97 * This function provides handle of a DSB instance, for the further DSB 98 * operations. 99 * 100 * Returns: address of Intel_dsb instance requested for. 101 * Failure: Returns the same DSB instance, but without a command buffer. 102 */ 103 104 struct intel_dsb * 105 intel_dsb_get(struct intel_crtc *crtc) 106 { 107 struct drm_device *dev = crtc->base.dev; 108 struct drm_i915_private *i915 = to_i915(dev); 109 struct intel_dsb *dsb = &crtc->dsb; 110 struct drm_i915_gem_object *obj; 111 struct i915_vma *vma; 112 u32 *buf; 113 intel_wakeref_t wakeref; 114 115 if (!HAS_DSB(i915)) 116 return dsb; 117 118 if (dsb->refcount++ != 0) 119 return dsb; 120 121 wakeref = intel_runtime_pm_get(&i915->runtime_pm); 122 123 obj = i915_gem_object_create_internal(i915, DSB_BUF_SIZE); 124 if (IS_ERR(obj)) { 125 DRM_ERROR("Gem object creation failed\n"); 126 goto out; 127 } 128 129 vma = i915_gem_object_ggtt_pin(obj, NULL, 0, 0, 0); 130 if (IS_ERR(vma)) { 131 DRM_ERROR("Vma creation failed\n"); 132 i915_gem_object_put(obj); 133 goto out; 134 } 135 136 buf = i915_gem_object_pin_map(vma->obj, I915_MAP_WC); 137 if (IS_ERR(buf)) { 138 DRM_ERROR("Command buffer creation failed\n"); 139 goto out; 140 } 141 142 dsb->id = DSB1; 143 dsb->vma = vma; 144 dsb->cmd_buf = buf; 145 146 out: 147 /* 148 * On error dsb->cmd_buf will continue to be NULL, making the writes 149 * pass-through. Leave the dangling ref to be removed later by the 150 * corresponding intel_dsb_put(): the important error message will 151 * already be logged above. 152 */ 153 154 intel_runtime_pm_put(&i915->runtime_pm, wakeref); 155 156 return dsb; 157 } 158 159 /** 160 * intel_dsb_put() - To destroy DSB context. 161 * @dsb: intel_dsb structure. 162 * 163 * This function destroys the DSB context allocated by a dsb_get(), by 164 * unpinning and releasing the VMA object associated with it. 165 */ 166 167 void intel_dsb_put(struct intel_dsb *dsb) 168 { 169 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); 170 struct drm_i915_private *i915 = to_i915(crtc->base.dev); 171 172 if (!HAS_DSB(i915)) 173 return; 174 175 if (WARN_ON(dsb->refcount == 0)) 176 return; 177 178 if (--dsb->refcount == 0) { 179 i915_vma_unpin_and_release(&dsb->vma, I915_VMA_RELEASE_MAP); 180 dsb->cmd_buf = NULL; 181 dsb->free_pos = 0; 182 dsb->ins_start_offset = 0; 183 } 184 } 185 186 /** 187 * intel_dsb_indexed_reg_write() -Write to the DSB context for auto 188 * increment register. 189 * @dsb: intel_dsb structure. 190 * @reg: register address. 191 * @val: value. 192 * 193 * This function is used for writing register-value pair in command 194 * buffer of DSB for auto-increment register. During command buffer overflow, 195 * a warning is thrown and rest all erroneous condition register programming 196 * is done through mmio write. 197 */ 198 199 void intel_dsb_indexed_reg_write(struct intel_dsb *dsb, i915_reg_t reg, 200 u32 val) 201 { 202 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); 203 struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 204 u32 *buf = dsb->cmd_buf; 205 u32 reg_val; 206 207 if (!buf) { 208 I915_WRITE(reg, val); 209 return; 210 } 211 212 if (WARN_ON(dsb->free_pos >= DSB_BUF_SIZE)) { 213 DRM_DEBUG_KMS("DSB buffer overflow\n"); 214 return; 215 } 216 217 /* 218 * For example the buffer will look like below for 3 dwords for auto 219 * increment register: 220 * +--------------------------------------------------------+ 221 * | size = 3 | offset &| value1 | value2 | value3 | zero | 222 * | | opcode | | | | | 223 * +--------------------------------------------------------+ 224 * + + + + + + + 225 * 0 4 8 12 16 20 24 226 * Byte 227 * 228 * As every instruction is 8 byte aligned the index of dsb instruction 229 * will start always from even number while dealing with u32 array. If 230 * we are writing odd no of dwords, Zeros will be added in the end for 231 * padding. 232 */ 233 reg_val = buf[dsb->ins_start_offset + 1] & DSB_REG_VALUE_MASK; 234 if (reg_val != i915_mmio_reg_offset(reg)) { 235 /* Every instruction should be 8 byte aligned. */ 236 dsb->free_pos = ALIGN(dsb->free_pos, 2); 237 238 dsb->ins_start_offset = dsb->free_pos; 239 240 /* Update the size. */ 241 buf[dsb->free_pos++] = 1; 242 243 /* Update the opcode and reg. */ 244 buf[dsb->free_pos++] = (DSB_OPCODE_INDEXED_WRITE << 245 DSB_OPCODE_SHIFT) | 246 i915_mmio_reg_offset(reg); 247 248 /* Update the value. */ 249 buf[dsb->free_pos++] = val; 250 } else { 251 /* Update the new value. */ 252 buf[dsb->free_pos++] = val; 253 254 /* Update the size. */ 255 buf[dsb->ins_start_offset]++; 256 } 257 258 /* if number of data words is odd, then the last dword should be 0.*/ 259 if (dsb->free_pos & 0x1) 260 buf[dsb->free_pos] = 0; 261 } 262 263 /** 264 * intel_dsb_reg_write() -Write to the DSB context for normal 265 * register. 266 * @dsb: intel_dsb structure. 267 * @reg: register address. 268 * @val: value. 269 * 270 * This function is used for writing register-value pair in command 271 * buffer of DSB. During command buffer overflow, a warning is thrown 272 * and rest all erroneous condition register programming is done 273 * through mmio write. 274 */ 275 void intel_dsb_reg_write(struct intel_dsb *dsb, i915_reg_t reg, u32 val) 276 { 277 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); 278 struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); 279 u32 *buf = dsb->cmd_buf; 280 281 if (!buf) { 282 I915_WRITE(reg, val); 283 return; 284 } 285 286 if (WARN_ON(dsb->free_pos >= DSB_BUF_SIZE)) { 287 DRM_DEBUG_KMS("DSB buffer overflow\n"); 288 return; 289 } 290 291 dsb->ins_start_offset = dsb->free_pos; 292 buf[dsb->free_pos++] = val; 293 buf[dsb->free_pos++] = (DSB_OPCODE_MMIO_WRITE << DSB_OPCODE_SHIFT) | 294 (DSB_BYTE_EN << DSB_BYTE_EN_SHIFT) | 295 i915_mmio_reg_offset(reg); 296 } 297 298 /** 299 * intel_dsb_commit() - Trigger workload execution of DSB. 300 * @dsb: intel_dsb structure. 301 * 302 * This function is used to do actual write to hardware using DSB. 303 * On errors, fall back to MMIO. Also this function help to reset the context. 304 */ 305 void intel_dsb_commit(struct intel_dsb *dsb) 306 { 307 struct intel_crtc *crtc = container_of(dsb, typeof(*crtc), dsb); 308 struct drm_device *dev = crtc->base.dev; 309 struct drm_i915_private *dev_priv = to_i915(dev); 310 enum pipe pipe = crtc->pipe; 311 u32 tail; 312 313 if (!dsb->free_pos) 314 return; 315 316 if (!intel_dsb_enable_engine(dsb)) 317 goto reset; 318 319 if (is_dsb_busy(dsb)) { 320 DRM_ERROR("HEAD_PTR write failed - dsb engine is busy.\n"); 321 goto reset; 322 } 323 I915_WRITE(DSB_HEAD(pipe, dsb->id), i915_ggtt_offset(dsb->vma)); 324 325 tail = ALIGN(dsb->free_pos * 4, CACHELINE_BYTES); 326 if (tail > dsb->free_pos * 4) 327 memset(&dsb->cmd_buf[dsb->free_pos], 0, 328 (tail - dsb->free_pos * 4)); 329 330 if (is_dsb_busy(dsb)) { 331 DRM_ERROR("TAIL_PTR write failed - dsb engine is busy.\n"); 332 goto reset; 333 } 334 DRM_DEBUG_KMS("DSB execution started - head 0x%x, tail 0x%x\n", 335 i915_ggtt_offset(dsb->vma), tail); 336 I915_WRITE(DSB_TAIL(pipe, dsb->id), i915_ggtt_offset(dsb->vma) + tail); 337 if (wait_for(!is_dsb_busy(dsb), 1)) { 338 DRM_ERROR("Timed out waiting for DSB workload completion.\n"); 339 goto reset; 340 } 341 342 reset: 343 dsb->free_pos = 0; 344 dsb->ins_start_offset = 0; 345 intel_dsb_disable_engine(dsb); 346 } 347