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