LCOV - code coverage report
Current view: top level - drivers/gpu/drm/i915/display - intel_dsb.c (source / functions) Hit Total Coverage
Test: combined.info Lines: 0 137 0.0 %
Date: 2022-04-01 13:59:58 Functions: 0 7 0.0 %
Branches: 0 66 0.0 %

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

Generated by: LCOV version 1.14