Home | History | Annotate | Line # | Download | only in display
      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