Pull drm fixes from Dave Airlie:
 "The weekly drm fixes. This is mostly msm fixes across the functions,
  with amdgpu and i915. It also has a core rust fix and changes in
  nova-core to take advantage of it, and otherwise just has some minor
  driver fixes, and marks loongsoon as orphaned.

  rust:
   - Fix safety issue in dma_read! and dma_write!

  nova-core:
   - Fix UB in DmaGspMem pointer accessors
   - Fix stack overflow in GSP memory allocation

  loongsoon:
   - mark drm driver as unmaintained

  msm:
   - Core:
      - Adjusted msm_iommu_pagetable_prealloc_allocate() allocation type
   - DPU:
      - Fixed blue screens on Hamoa laptops by reverting the LM
        reservation
      - Fixed the size of the LM block on several platforms
      - Dropped usage of %pK (again)
      - Fixed smatch warning on SSPP v13+ code
      - Fixed INTF_6 interrupts on Lemans
   - DSI:
      - Fixed DSI PHY revision on Kaanapali
      - Fixed pixel clock calculation for the bonded DSI mode panels
        with compression enabled
   - DT bindings:
      - Fixed DisplayPort description on Glymur
      - Fixed model name in SM8750 MDSS schema
   - GPU:
      - Added MODULE_DEVICE_TABLE to the GPU driver
      - Fix bogus protect error on X2-85
      - Fix dma_free_attrs() buffer size
      - Gen8 UBWC fix for Glymur

  i915:
   - Avoid hang when configuring VRR [icl]
   - Fix sg_table overflow with >4GB folios
   - Fix PSR Selective Update handling
   - Fix eDP ALPM read-out sequence

  amdgpu:
   - SMU13 fix
   - SMU14 fix
   - Fixes for bringup hw testing
   - Kerneldoc fix
   - GC12 idle power fix for compute workloads
   - DCCG fixes

  amdkfd:
   - Fix missing BO unreserve in an error path

  ivpu:
   - drop unnecessary bootparams register setting

  amdxdna:
   - fix runtime/suspend resume deadlock

  bridge:
   - ti-sn65dsi83: fix DSI rounding and dual LVDS

  gud:
   - fix NULL crtc dereference on display disable"

* tag 'drm-fixes-2026-03-14' of https://gitlab.freedesktop.org/drm/kernel: (44 commits)
  drm/amd: Set num IP blocks to 0 if discovery fails
  drm/amdkfd: Unreserve bo if queue update failed
  drm/amd/display: Check for S0i3 to be done before DCCG init on DCN21
  drm/amd/display: Add missing DCCG register entries for DCN20-DCN316
  gpu: nova-core: gsp: fix UB in DmaGspMem pointer accessors
  drm/loongson: Mark driver as orphaned
  accel/amdxdna: Fix runtime suspend deadlock when there is pending job
  gpu: nova-core: fix stack overflow in GSP memory allocation
  accel/ivpu: Remove boot params address setting via MMIO register
  drm/i915/dp: Read ALPM caps after DPCD init
  drm/i915/psr: Write DSC parameters on Selective Update in ET mode
  drm/i915/dsc: Add helper for writing DSC Selective Update ET parameters
  drm/i915/dsc: Add Selective Update register definitions
  drm/i915/psr: Repeat Selective Update area alignment
  drm/i915: Fix potential overflow of shmem scatterlist length
  drm/i915/vrr: Configure VRR timings after enabling TRANS_DDI_FUNC_CTL
  drm/bridge: ti-sn65dsi83: halve horizontal syncs for dual LVDS output
  drm/bridge: ti-sn65dsi83: fix CHA_DSI_CLK_RANGE rounding
  drm/gud: fix NULL crtc dereference on display disable
  drm/sitronix/st7586: fix bad pixel data due to byte swap
  ...
This commit is contained in:
Linus Torvalds
2026-03-13 15:38:55 -07:00
60 changed files with 935 additions and 399 deletions

View File

@@ -253,7 +253,6 @@ allOf:
enum: enum:
# these platforms support 2 streams MST on some interfaces, # these platforms support 2 streams MST on some interfaces,
# others are SST only # others are SST only
- qcom,glymur-dp
- qcom,sc8280xp-dp - qcom,sc8280xp-dp
- qcom,x1e80100-dp - qcom,x1e80100-dp
then: then:
@@ -310,6 +309,26 @@ allOf:
minItems: 6 minItems: 6
maxItems: 8 maxItems: 8
- if:
properties:
compatible:
contains:
enum:
# these platforms support 2 streams MST on some interfaces,
# others are SST only, but all controllers have 4 ports
- qcom,glymur-dp
then:
properties:
reg:
minItems: 9
maxItems: 9
clocks:
minItems: 5
maxItems: 6
clocks-names:
minItems: 5
maxItems: 6
unevaluatedProperties: false unevaluatedProperties: false
examples: examples:

View File

@@ -176,13 +176,17 @@ examples:
}; };
}; };
displayport-controller@ae90000 { displayport-controller@af54000 {
compatible = "qcom,glymur-dp"; compatible = "qcom,glymur-dp";
reg = <0xae90000 0x200>, reg = <0xaf54000 0x200>,
<0xae90200 0x200>, <0xaf54200 0x200>,
<0xae90400 0x600>, <0xaf55000 0xc00>,
<0xae91000 0x400>, <0xaf56000 0x400>,
<0xae91400 0x400>; <0xaf57000 0x400>,
<0xaf58000 0x400>,
<0xaf59000 0x400>,
<0xaf5a000 0x600>,
<0xaf5b000 0x600>;
interrupt-parent = <&mdss>; interrupt-parent = <&mdss>;
interrupts = <12>; interrupts = <12>;

View File

@@ -10,7 +10,7 @@ maintainers:
- Krzysztof Kozlowski <krzk@kernel.org> - Krzysztof Kozlowski <krzk@kernel.org>
description: description:
SM8650 MSM Mobile Display Subsystem(MDSS), which encapsulates sub-blocks like SM8750 MSM Mobile Display Subsystem(MDSS), which encapsulates sub-blocks like
DPU display controller, DSI and DP interfaces etc. DPU display controller, DSI and DP interfaces etc.
$ref: /schemas/display/msm/mdss-common.yaml# $ref: /schemas/display/msm/mdss-common.yaml#

View File

@@ -8626,9 +8626,8 @@ F: drivers/gpu/drm/lima/
F: include/uapi/drm/lima_drm.h F: include/uapi/drm/lima_drm.h
DRM DRIVERS FOR LOONGSON DRM DRIVERS FOR LOONGSON
M: Sui Jingfeng <suijingfeng@loongson.cn>
L: dri-devel@lists.freedesktop.org L: dri-devel@lists.freedesktop.org
S: Supported S: Orphan
T: git https://gitlab.freedesktop.org/drm/misc/kernel.git T: git https://gitlab.freedesktop.org/drm/misc/kernel.git
F: drivers/gpu/drm/loongson/ F: drivers/gpu/drm/loongson/

View File

@@ -165,7 +165,6 @@ aie2_sched_notify(struct amdxdna_sched_job *job)
trace_xdna_job(&job->base, job->hwctx->name, "signaled fence", job->seq); trace_xdna_job(&job->base, job->hwctx->name, "signaled fence", job->seq);
amdxdna_pm_suspend_put(job->hwctx->client->xdna);
job->hwctx->priv->completed++; job->hwctx->priv->completed++;
dma_fence_signal(fence); dma_fence_signal(fence);
@@ -290,19 +289,11 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
struct dma_fence *fence; struct dma_fence *fence;
int ret; int ret;
ret = amdxdna_pm_resume_get(hwctx->client->xdna); if (!hwctx->priv->mbox_chann)
if (ret)
return NULL; return NULL;
if (!hwctx->priv->mbox_chann) { if (!mmget_not_zero(job->mm))
amdxdna_pm_suspend_put(hwctx->client->xdna);
return NULL;
}
if (!mmget_not_zero(job->mm)) {
amdxdna_pm_suspend_put(hwctx->client->xdna);
return ERR_PTR(-ESRCH); return ERR_PTR(-ESRCH);
}
kref_get(&job->refcnt); kref_get(&job->refcnt);
fence = dma_fence_get(job->fence); fence = dma_fence_get(job->fence);
@@ -333,7 +324,6 @@ aie2_sched_job_run(struct drm_sched_job *sched_job)
out: out:
if (ret) { if (ret) {
amdxdna_pm_suspend_put(hwctx->client->xdna);
dma_fence_put(job->fence); dma_fence_put(job->fence);
aie2_job_put(job); aie2_job_put(job);
mmput(job->mm); mmput(job->mm);

View File

@@ -17,6 +17,7 @@
#include "amdxdna_ctx.h" #include "amdxdna_ctx.h"
#include "amdxdna_gem.h" #include "amdxdna_gem.h"
#include "amdxdna_pci_drv.h" #include "amdxdna_pci_drv.h"
#include "amdxdna_pm.h"
#define MAX_HWCTX_ID 255 #define MAX_HWCTX_ID 255
#define MAX_ARG_COUNT 4095 #define MAX_ARG_COUNT 4095
@@ -445,6 +446,7 @@ put_shmem_bo:
void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job) void amdxdna_sched_job_cleanup(struct amdxdna_sched_job *job)
{ {
trace_amdxdna_debug_point(job->hwctx->name, job->seq, "job release"); trace_amdxdna_debug_point(job->hwctx->name, job->seq, "job release");
amdxdna_pm_suspend_put(job->hwctx->client->xdna);
amdxdna_arg_bos_put(job); amdxdna_arg_bos_put(job);
amdxdna_gem_put_obj(job->cmd_bo); amdxdna_gem_put_obj(job->cmd_bo);
dma_fence_put(job->fence); dma_fence_put(job->fence);
@@ -482,6 +484,12 @@ int amdxdna_cmd_submit(struct amdxdna_client *client,
goto cmd_put; goto cmd_put;
} }
ret = amdxdna_pm_resume_get(xdna);
if (ret) {
XDNA_ERR(xdna, "Resume failed, ret %d", ret);
goto put_bos;
}
idx = srcu_read_lock(&client->hwctx_srcu); idx = srcu_read_lock(&client->hwctx_srcu);
hwctx = xa_load(&client->hwctx_xa, hwctx_hdl); hwctx = xa_load(&client->hwctx_xa, hwctx_hdl);
if (!hwctx) { if (!hwctx) {
@@ -522,6 +530,8 @@ put_fence:
dma_fence_put(job->fence); dma_fence_put(job->fence);
unlock_srcu: unlock_srcu:
srcu_read_unlock(&client->hwctx_srcu, idx); srcu_read_unlock(&client->hwctx_srcu, idx);
amdxdna_pm_suspend_put(xdna);
put_bos:
amdxdna_arg_bos_put(job); amdxdna_arg_bos_put(job);
cmd_put: cmd_put:
amdxdna_gem_put_obj(job->cmd_bo); amdxdna_gem_put_obj(job->cmd_bo);

View File

@@ -121,12 +121,6 @@
#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY 0x0003006cu #define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY 0x0003006cu
#define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY_STATUS_DLY_MASK GENMASK(7, 0) #define VPU_50XX_HOST_SS_AON_PWR_ISLAND_STATUS_DLY_STATUS_DLY_MASK GENMASK(7, 0)
#define VPU_40XX_HOST_SS_AON_RETENTION0 0x0003000cu
#define VPU_40XX_HOST_SS_AON_RETENTION1 0x00030010u
#define VPU_40XX_HOST_SS_AON_RETENTION2 0x00030014u
#define VPU_40XX_HOST_SS_AON_RETENTION3 0x00030018u
#define VPU_40XX_HOST_SS_AON_RETENTION4 0x0003001cu
#define VPU_40XX_HOST_SS_AON_IDLE_GEN 0x00030200u #define VPU_40XX_HOST_SS_AON_IDLE_GEN 0x00030200u
#define VPU_40XX_HOST_SS_AON_IDLE_GEN_EN_MASK BIT_MASK(0) #define VPU_40XX_HOST_SS_AON_IDLE_GEN_EN_MASK BIT_MASK(0)
#define VPU_40XX_HOST_SS_AON_IDLE_GEN_HW_PG_EN_MASK BIT_MASK(1) #define VPU_40XX_HOST_SS_AON_IDLE_GEN_HW_PG_EN_MASK BIT_MASK(1)

View File

@@ -931,7 +931,6 @@ static int soc_cpu_boot_40xx(struct ivpu_device *vdev)
static int soc_cpu_boot_60xx(struct ivpu_device *vdev) static int soc_cpu_boot_60xx(struct ivpu_device *vdev)
{ {
REGV_WR64(VPU_40XX_HOST_SS_AON_RETENTION1, vdev->fw->mem_bp->vpu_addr);
soc_cpu_set_entry_point_40xx(vdev, vdev->fw->cold_boot_entry_point); soc_cpu_set_entry_point_40xx(vdev, vdev->fw->cold_boot_entry_point);
return 0; return 0;

View File

@@ -2690,8 +2690,10 @@ static int amdgpu_device_ip_early_init(struct amdgpu_device *adev)
break; break;
default: default:
r = amdgpu_discovery_set_ip_blocks(adev); r = amdgpu_discovery_set_ip_blocks(adev);
if (r) if (r) {
adev->num_ip_blocks = 0;
return r; return r;
}
break; break;
} }
@@ -3247,6 +3249,8 @@ int amdgpu_device_set_cg_state(struct amdgpu_device *adev,
i = state == AMD_CG_STATE_GATE ? j : adev->num_ip_blocks - j - 1; i = state == AMD_CG_STATE_GATE ? j : adev->num_ip_blocks - j - 1;
if (!adev->ip_blocks[i].status.late_initialized) if (!adev->ip_blocks[i].status.late_initialized)
continue; continue;
if (!adev->ip_blocks[i].version)
continue;
/* skip CG for GFX, SDMA on S0ix */ /* skip CG for GFX, SDMA on S0ix */
if (adev->in_s0ix && if (adev->in_s0ix &&
(adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GFX || (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GFX ||
@@ -3286,6 +3290,8 @@ int amdgpu_device_set_pg_state(struct amdgpu_device *adev,
i = state == AMD_PG_STATE_GATE ? j : adev->num_ip_blocks - j - 1; i = state == AMD_PG_STATE_GATE ? j : adev->num_ip_blocks - j - 1;
if (!adev->ip_blocks[i].status.late_initialized) if (!adev->ip_blocks[i].status.late_initialized)
continue; continue;
if (!adev->ip_blocks[i].version)
continue;
/* skip PG for GFX, SDMA on S0ix */ /* skip PG for GFX, SDMA on S0ix */
if (adev->in_s0ix && if (adev->in_s0ix &&
(adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GFX || (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GFX ||
@@ -3493,6 +3499,8 @@ static int amdgpu_device_ip_fini_early(struct amdgpu_device *adev)
int i, r; int i, r;
for (i = 0; i < adev->num_ip_blocks; i++) { for (i = 0; i < adev->num_ip_blocks; i++) {
if (!adev->ip_blocks[i].version)
continue;
if (!adev->ip_blocks[i].version->funcs->early_fini) if (!adev->ip_blocks[i].version->funcs->early_fini)
continue; continue;
@@ -3570,6 +3578,8 @@ static int amdgpu_device_ip_fini(struct amdgpu_device *adev)
if (!adev->ip_blocks[i].status.sw) if (!adev->ip_blocks[i].status.sw)
continue; continue;
if (!adev->ip_blocks[i].version)
continue;
if (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GMC) { if (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GMC) {
amdgpu_ucode_free_bo(adev); amdgpu_ucode_free_bo(adev);
amdgpu_free_static_csa(&adev->virt.csa_obj); amdgpu_free_static_csa(&adev->virt.csa_obj);
@@ -3596,6 +3606,8 @@ static int amdgpu_device_ip_fini(struct amdgpu_device *adev)
for (i = adev->num_ip_blocks - 1; i >= 0; i--) { for (i = adev->num_ip_blocks - 1; i >= 0; i--) {
if (!adev->ip_blocks[i].status.late_initialized) if (!adev->ip_blocks[i].status.late_initialized)
continue; continue;
if (!adev->ip_blocks[i].version)
continue;
if (adev->ip_blocks[i].version->funcs->late_fini) if (adev->ip_blocks[i].version->funcs->late_fini)
adev->ip_blocks[i].version->funcs->late_fini(&adev->ip_blocks[i]); adev->ip_blocks[i].version->funcs->late_fini(&adev->ip_blocks[i]);
adev->ip_blocks[i].status.late_initialized = false; adev->ip_blocks[i].status.late_initialized = false;

View File

@@ -83,7 +83,7 @@ void amdgpu_driver_unload_kms(struct drm_device *dev)
{ {
struct amdgpu_device *adev = drm_to_adev(dev); struct amdgpu_device *adev = drm_to_adev(dev);
if (adev == NULL) if (adev == NULL || !adev->num_ip_blocks)
return; return;
amdgpu_unregister_gpu_instance(adev); amdgpu_unregister_gpu_instance(adev);

View File

@@ -368,15 +368,15 @@ struct amdgpu_mode_info {
struct drm_property *plane_ctm_property; struct drm_property *plane_ctm_property;
/** /**
* @shaper_lut_property: Plane property to set pre-blending shaper LUT * @plane_shaper_lut_property: Plane property to set pre-blending
* that converts color content before 3D LUT. If * shaper LUT that converts color content before 3D LUT.
* plane_shaper_tf_property != Identity TF, AMD color module will * If plane_shaper_tf_property != Identity TF, AMD color module will
* combine the user LUT values with pre-defined TF into the LUT * combine the user LUT values with pre-defined TF into the LUT
* parameters to be programmed. * parameters to be programmed.
*/ */
struct drm_property *plane_shaper_lut_property; struct drm_property *plane_shaper_lut_property;
/** /**
* @shaper_lut_size_property: Plane property for the size of * @plane_shaper_lut_size_property: Plane property for the size of
* pre-blending shaper LUT as supported by the driver (read-only). * pre-blending shaper LUT as supported by the driver (read-only).
*/ */
struct drm_property *plane_shaper_lut_size_property; struct drm_property *plane_shaper_lut_size_property;
@@ -400,10 +400,10 @@ struct amdgpu_mode_info {
*/ */
struct drm_property *plane_lut3d_property; struct drm_property *plane_lut3d_property;
/** /**
* @plane_degamma_lut_size_property: Plane property to define the max * @plane_lut3d_size_property: Plane property to define the max size
* size of 3D LUT as supported by the driver (read-only). The max size * of 3D LUT as supported by the driver (read-only). The max size is
* is the max size of one dimension and, therefore, the max number of * the max size of one dimension and, therefore, the max number of
* entries for 3D LUT array is the 3D LUT size cubed; * entries for 3D LUT array is the 3D LUT size cubed.
*/ */
struct drm_property *plane_lut3d_size_property; struct drm_property *plane_lut3d_size_property;
/** /**

View File

@@ -731,6 +731,9 @@ static int mes_v12_0_set_hw_resources(struct amdgpu_mes *mes, int pipe)
int i; int i;
struct amdgpu_device *adev = mes->adev; struct amdgpu_device *adev = mes->adev;
union MESAPI_SET_HW_RESOURCES mes_set_hw_res_pkt; union MESAPI_SET_HW_RESOURCES mes_set_hw_res_pkt;
uint32_t mes_rev = (pipe == AMDGPU_MES_SCHED_PIPE) ?
(mes->sched_version & AMDGPU_MES_VERSION_MASK) :
(mes->kiq_version & AMDGPU_MES_VERSION_MASK);
memset(&mes_set_hw_res_pkt, 0, sizeof(mes_set_hw_res_pkt)); memset(&mes_set_hw_res_pkt, 0, sizeof(mes_set_hw_res_pkt));
@@ -785,7 +788,7 @@ static int mes_v12_0_set_hw_resources(struct amdgpu_mes *mes, int pipe)
* handling support, other queue will not use the oversubscribe timer. * handling support, other queue will not use the oversubscribe timer.
* handling mode - 0: disabled; 1: basic version; 2: basic+ version * handling mode - 0: disabled; 1: basic version; 2: basic+ version
*/ */
mes_set_hw_res_pkt.oversubscription_timer = 50; mes_set_hw_res_pkt.oversubscription_timer = mes_rev < 0x8b ? 0 : 50;
mes_set_hw_res_pkt.unmapped_doorbell_handling = 1; mes_set_hw_res_pkt.unmapped_doorbell_handling = 1;
if (amdgpu_mes_log_enable) { if (amdgpu_mes_log_enable) {

View File

@@ -593,6 +593,7 @@ int pqm_update_queue_properties(struct process_queue_manager *pqm,
p->queue_size)) { p->queue_size)) {
pr_debug("ring buf 0x%llx size 0x%llx not mapped on GPU\n", pr_debug("ring buf 0x%llx size 0x%llx not mapped on GPU\n",
p->queue_address, p->queue_size); p->queue_address, p->queue_size);
amdgpu_bo_unreserve(vm->root.bo);
return -EFAULT; return -EFAULT;
} }

View File

@@ -38,7 +38,11 @@
DCCG_SRII(PIXEL_RATE_CNTL, OTG, 0),\ DCCG_SRII(PIXEL_RATE_CNTL, OTG, 0),\
DCCG_SRII(PIXEL_RATE_CNTL, OTG, 1),\ DCCG_SRII(PIXEL_RATE_CNTL, OTG, 1),\
SR(DISPCLK_FREQ_CHANGE_CNTL),\ SR(DISPCLK_FREQ_CHANGE_CNTL),\
SR(DC_MEM_GLOBAL_PWR_REQ_CNTL) SR(DC_MEM_GLOBAL_PWR_REQ_CNTL),\
SR(MICROSECOND_TIME_BASE_DIV),\
SR(MILLISECOND_TIME_BASE_DIV),\
SR(DCCG_GATE_DISABLE_CNTL),\
SR(DCCG_GATE_DISABLE_CNTL2)
#define DCCG_REG_LIST_DCN2() \ #define DCCG_REG_LIST_DCN2() \
DCCG_COMMON_REG_LIST_DCN_BASE(),\ DCCG_COMMON_REG_LIST_DCN_BASE(),\

View File

@@ -96,6 +96,25 @@ static void dccg21_update_dpp_dto(struct dccg *dccg, int dpp_inst, int req_dppcl
dccg->pipe_dppclk_khz[dpp_inst] = req_dppclk; dccg->pipe_dppclk_khz[dpp_inst] = req_dppclk;
} }
/*
* On DCN21 S0i3 resume, BIOS programs MICROSECOND_TIME_BASE_DIV to
* 0x00120464 as a marker that golden init has already been done.
* dcn21_s0i3_golden_init_wa() reads this marker later in bios_golden_init()
* to decide whether to skip golden init.
*
* dccg2_init() unconditionally overwrites MICROSECOND_TIME_BASE_DIV to
* 0x00120264, destroying the marker before it can be read.
*
* Guard the call: if the S0i3 marker is present, skip dccg2_init() so the
* WA can function correctly. bios_golden_init() will handle init in that case.
*/
static void dccg21_init(struct dccg *dccg)
{
if (dccg2_is_s0i3_golden_init_wa_done(dccg))
return;
dccg2_init(dccg);
}
static const struct dccg_funcs dccg21_funcs = { static const struct dccg_funcs dccg21_funcs = {
.update_dpp_dto = dccg21_update_dpp_dto, .update_dpp_dto = dccg21_update_dpp_dto,
@@ -103,7 +122,7 @@ static const struct dccg_funcs dccg21_funcs = {
.set_fifo_errdet_ovr_en = dccg2_set_fifo_errdet_ovr_en, .set_fifo_errdet_ovr_en = dccg2_set_fifo_errdet_ovr_en,
.otg_add_pixel = dccg2_otg_add_pixel, .otg_add_pixel = dccg2_otg_add_pixel,
.otg_drop_pixel = dccg2_otg_drop_pixel, .otg_drop_pixel = dccg2_otg_drop_pixel,
.dccg_init = dccg2_init, .dccg_init = dccg21_init,
.refclk_setup = dccg2_refclk_setup, /* Deprecated - for backward compatibility only */ .refclk_setup = dccg2_refclk_setup, /* Deprecated - for backward compatibility only */
.allow_clock_gating = dccg2_allow_clock_gating, .allow_clock_gating = dccg2_allow_clock_gating,
.enable_memory_low_power = dccg2_enable_memory_low_power, .enable_memory_low_power = dccg2_enable_memory_low_power,

View File

@@ -34,7 +34,13 @@
DCCG_SRII(DTO_PARAM, DPPCLK, 1),\ DCCG_SRII(DTO_PARAM, DPPCLK, 1),\
DCCG_SRII(DTO_PARAM, DPPCLK, 2),\ DCCG_SRII(DTO_PARAM, DPPCLK, 2),\
DCCG_SRII(DTO_PARAM, DPPCLK, 3),\ DCCG_SRII(DTO_PARAM, DPPCLK, 3),\
SR(REFCLK_CNTL) SR(REFCLK_CNTL),\
SR(DISPCLK_FREQ_CHANGE_CNTL),\
SR(DC_MEM_GLOBAL_PWR_REQ_CNTL),\
SR(MICROSECOND_TIME_BASE_DIV),\
SR(MILLISECOND_TIME_BASE_DIV),\
SR(DCCG_GATE_DISABLE_CNTL),\
SR(DCCG_GATE_DISABLE_CNTL2)
#define DCCG_MASK_SH_LIST_DCN301(mask_sh) \ #define DCCG_MASK_SH_LIST_DCN301(mask_sh) \
DCCG_SFI(DPPCLK_DTO_CTRL, DTO_ENABLE, DPPCLK, 0, mask_sh),\ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_ENABLE, DPPCLK, 0, mask_sh),\

View File

@@ -64,9 +64,12 @@
SR(DSCCLK1_DTO_PARAM),\ SR(DSCCLK1_DTO_PARAM),\
SR(DSCCLK2_DTO_PARAM),\ SR(DSCCLK2_DTO_PARAM),\
SR(DSCCLK_DTO_CTRL),\ SR(DSCCLK_DTO_CTRL),\
SR(DCCG_GATE_DISABLE_CNTL),\
SR(DCCG_GATE_DISABLE_CNTL2),\ SR(DCCG_GATE_DISABLE_CNTL2),\
SR(DCCG_GATE_DISABLE_CNTL3),\ SR(DCCG_GATE_DISABLE_CNTL3),\
SR(HDMISTREAMCLK0_DTO_PARAM) SR(HDMISTREAMCLK0_DTO_PARAM),\
SR(DC_MEM_GLOBAL_PWR_REQ_CNTL),\
SR(MICROSECOND_TIME_BASE_DIV)
#define DCCG_MASK_SH_LIST_DCN31(mask_sh) \ #define DCCG_MASK_SH_LIST_DCN31(mask_sh) \

View File

@@ -70,11 +70,14 @@
SR(DSCCLK2_DTO_PARAM),\ SR(DSCCLK2_DTO_PARAM),\
SR(DSCCLK3_DTO_PARAM),\ SR(DSCCLK3_DTO_PARAM),\
SR(DSCCLK_DTO_CTRL),\ SR(DSCCLK_DTO_CTRL),\
SR(DCCG_GATE_DISABLE_CNTL),\
SR(DCCG_GATE_DISABLE_CNTL2),\ SR(DCCG_GATE_DISABLE_CNTL2),\
SR(DCCG_GATE_DISABLE_CNTL3),\ SR(DCCG_GATE_DISABLE_CNTL3),\
SR(HDMISTREAMCLK0_DTO_PARAM),\ SR(HDMISTREAMCLK0_DTO_PARAM),\
SR(OTG_PIXEL_RATE_DIV),\ SR(OTG_PIXEL_RATE_DIV),\
SR(DTBCLK_P_CNTL) SR(DTBCLK_P_CNTL),\
SR(DC_MEM_GLOBAL_PWR_REQ_CNTL),\
SR(MICROSECOND_TIME_BASE_DIV)
#define DCCG_MASK_SH_LIST_DCN314_COMMON(mask_sh) \ #define DCCG_MASK_SH_LIST_DCN314_COMMON(mask_sh) \
DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 0, mask_sh),\ DCCG_SFI(DPPCLK_DTO_CTRL, DTO_DB_EN, DPPCLK, 0, mask_sh),\

View File

@@ -2222,7 +2222,8 @@ static int smu_v13_0_0_restore_user_od_settings(struct smu_context *smu)
user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) | user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) |
BIT(PP_OD_FEATURE_UCLK_BIT) | BIT(PP_OD_FEATURE_UCLK_BIT) |
BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) | BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) |
BIT(PP_OD_FEATURE_FAN_CURVE_BIT); BIT(PP_OD_FEATURE_FAN_CURVE_BIT) |
BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
res = smu_v13_0_0_upload_overdrive_table(smu, user_od_table); res = smu_v13_0_0_upload_overdrive_table(smu, user_od_table);
user_od_table->OverDriveTable.FeatureCtrlMask = 0; user_od_table->OverDriveTable.FeatureCtrlMask = 0;
if (res == 0) if (res == 0)

View File

@@ -2224,7 +2224,8 @@ static int smu_v13_0_7_restore_user_od_settings(struct smu_context *smu)
user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) | user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) |
BIT(PP_OD_FEATURE_UCLK_BIT) | BIT(PP_OD_FEATURE_UCLK_BIT) |
BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) | BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) |
BIT(PP_OD_FEATURE_FAN_CURVE_BIT); BIT(PP_OD_FEATURE_FAN_CURVE_BIT) |
BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
res = smu_v13_0_7_upload_overdrive_table(smu, user_od_table); res = smu_v13_0_7_upload_overdrive_table(smu, user_od_table);
user_od_table->OverDriveTable.FeatureCtrlMask = 0; user_od_table->OverDriveTable.FeatureCtrlMask = 0;
if (res == 0) if (res == 0)

View File

@@ -2311,7 +2311,8 @@ static int smu_v14_0_2_restore_user_od_settings(struct smu_context *smu)
user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) | user_od_table->OverDriveTable.FeatureCtrlMask = BIT(PP_OD_FEATURE_GFXCLK_BIT) |
BIT(PP_OD_FEATURE_UCLK_BIT) | BIT(PP_OD_FEATURE_UCLK_BIT) |
BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) | BIT(PP_OD_FEATURE_GFX_VF_CURVE_BIT) |
BIT(PP_OD_FEATURE_FAN_CURVE_BIT); BIT(PP_OD_FEATURE_FAN_CURVE_BIT) |
BIT(PP_OD_FEATURE_ZERO_FAN_BIT);
res = smu_v14_0_2_upload_overdrive_table(smu, user_od_table); res = smu_v14_0_2_upload_overdrive_table(smu, user_od_table);
user_od_table->OverDriveTable.FeatureCtrlMask = 0; user_od_table->OverDriveTable.FeatureCtrlMask = 0;
if (res == 0) if (res == 0)

View File

@@ -351,9 +351,9 @@ static u8 sn65dsi83_get_dsi_range(struct sn65dsi83 *ctx,
* DSI_CLK = mode clock * bpp / dsi_data_lanes / 2 * DSI_CLK = mode clock * bpp / dsi_data_lanes / 2
* the 2 is there because the bus is DDR. * the 2 is there because the bus is DDR.
*/ */
return DIV_ROUND_UP(clamp((unsigned int)mode->clock * return clamp((unsigned int)mode->clock *
mipi_dsi_pixel_format_to_bpp(ctx->dsi->format) / mipi_dsi_pixel_format_to_bpp(ctx->dsi->format) /
ctx->dsi->lanes / 2, 40000U, 500000U), 5000U); ctx->dsi->lanes / 2, 40000U, 500000U) / 5000U;
} }
static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx) static u8 sn65dsi83_get_dsi_div(struct sn65dsi83 *ctx)
@@ -517,6 +517,7 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
struct drm_atomic_state *state) struct drm_atomic_state *state)
{ {
struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge); struct sn65dsi83 *ctx = bridge_to_sn65dsi83(bridge);
const unsigned int dual_factor = ctx->lvds_dual_link ? 2 : 1;
const struct drm_bridge_state *bridge_state; const struct drm_bridge_state *bridge_state;
const struct drm_crtc_state *crtc_state; const struct drm_crtc_state *crtc_state;
const struct drm_display_mode *mode; const struct drm_display_mode *mode;
@@ -653,18 +654,18 @@ static void sn65dsi83_atomic_pre_enable(struct drm_bridge *bridge,
/* 32 + 1 pixel clock to ensure proper operation */ /* 32 + 1 pixel clock to ensure proper operation */
le16val = cpu_to_le16(32 + 1); le16val = cpu_to_le16(32 + 1);
regmap_bulk_write(ctx->regmap, REG_VID_CHA_SYNC_DELAY_LOW, &le16val, 2); regmap_bulk_write(ctx->regmap, REG_VID_CHA_SYNC_DELAY_LOW, &le16val, 2);
le16val = cpu_to_le16(mode->hsync_end - mode->hsync_start); le16val = cpu_to_le16((mode->hsync_end - mode->hsync_start) / dual_factor);
regmap_bulk_write(ctx->regmap, REG_VID_CHA_HSYNC_PULSE_WIDTH_LOW, regmap_bulk_write(ctx->regmap, REG_VID_CHA_HSYNC_PULSE_WIDTH_LOW,
&le16val, 2); &le16val, 2);
le16val = cpu_to_le16(mode->vsync_end - mode->vsync_start); le16val = cpu_to_le16(mode->vsync_end - mode->vsync_start);
regmap_bulk_write(ctx->regmap, REG_VID_CHA_VSYNC_PULSE_WIDTH_LOW, regmap_bulk_write(ctx->regmap, REG_VID_CHA_VSYNC_PULSE_WIDTH_LOW,
&le16val, 2); &le16val, 2);
regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_BACK_PORCH, regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_BACK_PORCH,
mode->htotal - mode->hsync_end); (mode->htotal - mode->hsync_end) / dual_factor);
regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_BACK_PORCH, regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_BACK_PORCH,
mode->vtotal - mode->vsync_end); mode->vtotal - mode->vsync_end);
regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_FRONT_PORCH, regmap_write(ctx->regmap, REG_VID_CHA_HORIZONTAL_FRONT_PORCH,
mode->hsync_start - mode->hdisplay); (mode->hsync_start - mode->hdisplay) / dual_factor);
regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_FRONT_PORCH, regmap_write(ctx->regmap, REG_VID_CHA_VERTICAL_FRONT_PORCH,
mode->vsync_start - mode->vdisplay); mode->vsync_start - mode->vdisplay);
regmap_write(ctx->regmap, REG_VID_CHA_TEST_PATTERN, 0x00); regmap_write(ctx->regmap, REG_VID_CHA_TEST_PATTERN, 0x00);

View File

@@ -339,7 +339,9 @@ static int gud_stats_debugfs(struct seq_file *m, void *data)
} }
static const struct drm_crtc_helper_funcs gud_crtc_helper_funcs = { static const struct drm_crtc_helper_funcs gud_crtc_helper_funcs = {
.atomic_check = drm_crtc_helper_atomic_check .atomic_check = drm_crtc_helper_atomic_check,
.atomic_enable = gud_crtc_atomic_enable,
.atomic_disable = gud_crtc_atomic_disable,
}; };
static const struct drm_crtc_funcs gud_crtc_funcs = { static const struct drm_crtc_funcs gud_crtc_funcs = {
@@ -364,6 +366,10 @@ static const struct drm_plane_funcs gud_plane_funcs = {
DRM_GEM_SHADOW_PLANE_FUNCS, DRM_GEM_SHADOW_PLANE_FUNCS,
}; };
static const struct drm_mode_config_helper_funcs gud_mode_config_helpers = {
.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
};
static const struct drm_mode_config_funcs gud_mode_config_funcs = { static const struct drm_mode_config_funcs gud_mode_config_funcs = {
.fb_create = drm_gem_fb_create_with_dirty, .fb_create = drm_gem_fb_create_with_dirty,
.atomic_check = drm_atomic_helper_check, .atomic_check = drm_atomic_helper_check,
@@ -499,6 +505,7 @@ static int gud_probe(struct usb_interface *intf, const struct usb_device_id *id)
drm->mode_config.min_height = le32_to_cpu(desc.min_height); drm->mode_config.min_height = le32_to_cpu(desc.min_height);
drm->mode_config.max_height = le32_to_cpu(desc.max_height); drm->mode_config.max_height = le32_to_cpu(desc.max_height);
drm->mode_config.funcs = &gud_mode_config_funcs; drm->mode_config.funcs = &gud_mode_config_funcs;
drm->mode_config.helper_private = &gud_mode_config_helpers;
/* Format init */ /* Format init */
formats_dev = devm_kmalloc(dev, GUD_FORMATS_MAX_NUM, GFP_KERNEL); formats_dev = devm_kmalloc(dev, GUD_FORMATS_MAX_NUM, GFP_KERNEL);

View File

@@ -62,6 +62,10 @@ int gud_usb_set_u8(struct gud_device *gdrm, u8 request, u8 val);
void gud_clear_damage(struct gud_device *gdrm); void gud_clear_damage(struct gud_device *gdrm);
void gud_flush_work(struct work_struct *work); void gud_flush_work(struct work_struct *work);
void gud_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state);
void gud_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state);
int gud_plane_atomic_check(struct drm_plane *plane, int gud_plane_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *state); struct drm_atomic_state *state);
void gud_plane_atomic_update(struct drm_plane *plane, void gud_plane_atomic_update(struct drm_plane *plane,

View File

@@ -580,6 +580,39 @@ out:
return ret; return ret;
} }
void gud_crtc_atomic_enable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_device *drm = crtc->dev;
struct gud_device *gdrm = to_gud_device(drm);
int idx;
if (!drm_dev_enter(drm, &idx))
return;
gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 1);
gud_usb_set(gdrm, GUD_REQ_SET_STATE_COMMIT, 0, NULL, 0);
gud_usb_set_u8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, 1);
drm_dev_exit(idx);
}
void gud_crtc_atomic_disable(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_device *drm = crtc->dev;
struct gud_device *gdrm = to_gud_device(drm);
int idx;
if (!drm_dev_enter(drm, &idx))
return;
gud_usb_set_u8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, 0);
gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0);
drm_dev_exit(idx);
}
void gud_plane_atomic_update(struct drm_plane *plane, void gud_plane_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *atomic_state) struct drm_atomic_state *atomic_state)
{ {
@@ -607,24 +640,12 @@ void gud_plane_atomic_update(struct drm_plane *plane,
mutex_unlock(&gdrm->damage_lock); mutex_unlock(&gdrm->damage_lock);
} }
if (!drm_dev_enter(drm, &idx)) if (!crtc || !drm_dev_enter(drm, &idx))
return; return;
if (!old_state->fb)
gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 1);
if (fb && (crtc->state->mode_changed || crtc->state->connectors_changed))
gud_usb_set(gdrm, GUD_REQ_SET_STATE_COMMIT, 0, NULL, 0);
if (crtc->state->active_changed)
gud_usb_set_u8(gdrm, GUD_REQ_SET_DISPLAY_ENABLE, crtc->state->active);
if (!fb)
goto ctrl_disable;
ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
if (ret) if (ret)
goto ctrl_disable; goto out;
drm_atomic_helper_damage_iter_init(&iter, old_state, new_state); drm_atomic_helper_damage_iter_init(&iter, old_state, new_state);
drm_atomic_for_each_plane_damage(&iter, &damage) drm_atomic_for_each_plane_damage(&iter, &damage)
@@ -632,9 +653,6 @@ void gud_plane_atomic_update(struct drm_plane *plane,
drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
ctrl_disable: out:
if (!crtc->state->enable)
gud_usb_set_u8(gdrm, GUD_REQ_SET_CONTROLLER_ENABLE, 0);
drm_dev_exit(idx); drm_dev_exit(idx);
} }

View File

@@ -43,12 +43,6 @@ bool intel_alpm_is_alpm_aux_less(struct intel_dp *intel_dp,
void intel_alpm_init(struct intel_dp *intel_dp) void intel_alpm_init(struct intel_dp *intel_dp)
{ {
u8 dpcd;
if (drm_dp_dpcd_readb(&intel_dp->aux, DP_RECEIVER_ALPM_CAP, &dpcd) < 0)
return;
intel_dp->alpm_dpcd = dpcd;
mutex_init(&intel_dp->alpm.lock); mutex_init(&intel_dp->alpm.lock);
} }

View File

@@ -1614,7 +1614,6 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta
} }
intel_set_transcoder_timings(crtc_state); intel_set_transcoder_timings(crtc_state);
intel_vrr_set_transcoder_timings(crtc_state);
if (cpu_transcoder != TRANSCODER_EDP) if (cpu_transcoder != TRANSCODER_EDP)
intel_de_write(display, TRANS_MULT(display, cpu_transcoder), intel_de_write(display, TRANS_MULT(display, cpu_transcoder),

View File

@@ -4577,6 +4577,7 @@ static bool
intel_edp_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector) intel_edp_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector)
{ {
struct intel_display *display = to_intel_display(intel_dp); struct intel_display *display = to_intel_display(intel_dp);
int ret;
/* this function is meant to be called only once */ /* this function is meant to be called only once */
drm_WARN_ON(display->drm, intel_dp->dpcd[DP_DPCD_REV] != 0); drm_WARN_ON(display->drm, intel_dp->dpcd[DP_DPCD_REV] != 0);
@@ -4616,6 +4617,12 @@ intel_edp_init_dpcd(struct intel_dp *intel_dp, struct intel_connector *connector
*/ */
intel_dp_init_source_oui(intel_dp); intel_dp_init_source_oui(intel_dp);
/* Read the ALPM DPCD caps */
ret = drm_dp_dpcd_read_byte(&intel_dp->aux, DP_RECEIVER_ALPM_CAP,
&intel_dp->alpm_dpcd);
if (ret < 0)
return false;
/* /*
* This has to be called after intel_dp->edp_dpcd is filled, PSR checks * This has to be called after intel_dp->edp_dpcd is filled, PSR checks
* for SET_POWER_CAPABLE bit in intel_dp->edp_dpcd[1] * for SET_POWER_CAPABLE bit in intel_dp->edp_dpcd[1]

View File

@@ -2619,6 +2619,12 @@ void intel_psr2_program_trans_man_trk_ctl(struct intel_dsb *dsb,
intel_de_write_dsb(display, dsb, PIPE_SRCSZ_ERLY_TPT(crtc->pipe), intel_de_write_dsb(display, dsb, PIPE_SRCSZ_ERLY_TPT(crtc->pipe),
crtc_state->pipe_srcsz_early_tpt); crtc_state->pipe_srcsz_early_tpt);
if (!crtc_state->dsc.compression_enable)
return;
intel_dsc_su_et_parameters_configure(dsb, encoder, crtc_state,
drm_rect_height(&crtc_state->psr2_su_area));
} }
static void psr2_man_trk_ctl_calc(struct intel_crtc_state *crtc_state, static void psr2_man_trk_ctl_calc(struct intel_crtc_state *crtc_state,
@@ -2689,11 +2695,12 @@ static void clip_area_update(struct drm_rect *overlap_damage_area,
overlap_damage_area->y2 = damage_area->y2; overlap_damage_area->y2 = damage_area->y2;
} }
static void intel_psr2_sel_fetch_pipe_alignment(struct intel_crtc_state *crtc_state) static bool intel_psr2_sel_fetch_pipe_alignment(struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(crtc_state); struct intel_display *display = to_intel_display(crtc_state);
const struct drm_dsc_config *vdsc_cfg = &crtc_state->dsc.config; const struct drm_dsc_config *vdsc_cfg = &crtc_state->dsc.config;
u16 y_alignment; u16 y_alignment;
bool su_area_changed = false;
/* ADLP aligns the SU region to vdsc slice height in case dsc is enabled */ /* ADLP aligns the SU region to vdsc slice height in case dsc is enabled */
if (crtc_state->dsc.compression_enable && if (crtc_state->dsc.compression_enable &&
@@ -2702,10 +2709,18 @@ static void intel_psr2_sel_fetch_pipe_alignment(struct intel_crtc_state *crtc_st
else else
y_alignment = crtc_state->su_y_granularity; y_alignment = crtc_state->su_y_granularity;
if (crtc_state->psr2_su_area.y1 % y_alignment) {
crtc_state->psr2_su_area.y1 -= crtc_state->psr2_su_area.y1 % y_alignment; crtc_state->psr2_su_area.y1 -= crtc_state->psr2_su_area.y1 % y_alignment;
if (crtc_state->psr2_su_area.y2 % y_alignment) su_area_changed = true;
}
if (crtc_state->psr2_su_area.y2 % y_alignment) {
crtc_state->psr2_su_area.y2 = ((crtc_state->psr2_su_area.y2 / crtc_state->psr2_su_area.y2 = ((crtc_state->psr2_su_area.y2 /
y_alignment) + 1) * y_alignment; y_alignment) + 1) * y_alignment;
su_area_changed = true;
}
return su_area_changed;
} }
/* /*
@@ -2839,7 +2854,7 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,
struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
struct intel_plane_state *new_plane_state, *old_plane_state; struct intel_plane_state *new_plane_state, *old_plane_state;
struct intel_plane *plane; struct intel_plane *plane;
bool full_update = false, cursor_in_su_area = false; bool full_update = false, su_area_changed;
int i, ret; int i, ret;
if (!crtc_state->enable_psr2_sel_fetch) if (!crtc_state->enable_psr2_sel_fetch)
@@ -2946,15 +2961,32 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,
if (ret) if (ret)
return ret; return ret;
do {
bool cursor_in_su_area;
/* /*
* Adjust su area to cover cursor fully as necessary (early * Adjust su area to cover cursor fully as necessary
* transport). This needs to be done after * (early transport). This needs to be done after
* drm_atomic_add_affected_planes to ensure visible cursor is added into * drm_atomic_add_affected_planes to ensure visible
* affected planes even when cursor is not updated by itself. * cursor is added into affected planes even when
* cursor is not updated by itself.
*/ */
intel_psr2_sel_fetch_et_alignment(state, crtc, &cursor_in_su_area); intel_psr2_sel_fetch_et_alignment(state, crtc, &cursor_in_su_area);
intel_psr2_sel_fetch_pipe_alignment(crtc_state); su_area_changed = intel_psr2_sel_fetch_pipe_alignment(crtc_state);
/*
* If the cursor was outside the SU area before
* alignment, the alignment step (which only expands
* SU) may pull the cursor partially inside, so we
* must run ET alignment again to fully cover it. But
* if the cursor was already fully inside before
* alignment, expanding the SU area won't change that,
* so no further work is needed.
*/
if (cursor_in_su_area)
break;
} while (su_area_changed);
/* /*
* Now that we have the pipe damaged area check if it intersect with * Now that we have the pipe damaged area check if it intersect with
@@ -3014,6 +3046,10 @@ int intel_psr2_sel_fetch_update(struct intel_atomic_state *state,
} }
skip_sel_fetch_set_loop: skip_sel_fetch_set_loop:
if (full_update)
clip_area_update(&crtc_state->psr2_su_area, &crtc_state->pipe_src,
&crtc_state->pipe_src);
psr2_man_trk_ctl_calc(crtc_state, full_update); psr2_man_trk_ctl_calc(crtc_state, full_update);
crtc_state->pipe_srcsz_early_tpt = crtc_state->pipe_srcsz_early_tpt =
psr2_pipe_srcsz_early_tpt_calc(crtc_state, full_update); psr2_pipe_srcsz_early_tpt_calc(crtc_state, full_update);

View File

@@ -767,6 +767,29 @@ void intel_dsc_dp_pps_write(struct intel_encoder *encoder,
sizeof(dp_dsc_pps_sdp)); sizeof(dp_dsc_pps_sdp));
} }
void intel_dsc_su_et_parameters_configure(struct intel_dsb *dsb, struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state, int su_lines)
{
struct intel_display *display = to_intel_display(crtc_state);
struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
const struct drm_dsc_config *vdsc_cfg = &crtc_state->dsc.config;
enum pipe pipe = crtc->pipe;
int vdsc_instances_per_pipe = intel_dsc_get_vdsc_per_pipe(crtc_state);
int slice_row_per_frame = su_lines / vdsc_cfg->slice_height;
u32 val;
drm_WARN_ON_ONCE(display->drm, su_lines % vdsc_cfg->slice_height);
drm_WARN_ON_ONCE(display->drm, vdsc_instances_per_pipe > 2);
val = DSC_SUPS0_SU_SLICE_ROW_PER_FRAME(slice_row_per_frame);
val |= DSC_SUPS0_SU_PIC_HEIGHT(su_lines);
intel_de_write_dsb(display, dsb, LNL_DSC0_SU_PARAMETER_SET_0(pipe), val);
if (vdsc_instances_per_pipe == 2)
intel_de_write_dsb(display, dsb, LNL_DSC1_SU_PARAMETER_SET_0(pipe), val);
}
static i915_reg_t dss_ctl1_reg(struct intel_crtc *crtc, enum transcoder cpu_transcoder) static i915_reg_t dss_ctl1_reg(struct intel_crtc *crtc, enum transcoder cpu_transcoder)
{ {
return is_pipe_dsc(crtc, cpu_transcoder) ? return is_pipe_dsc(crtc, cpu_transcoder) ?

View File

@@ -13,6 +13,7 @@ struct drm_printer;
enum transcoder; enum transcoder;
struct intel_crtc; struct intel_crtc;
struct intel_crtc_state; struct intel_crtc_state;
struct intel_dsb;
struct intel_encoder; struct intel_encoder;
bool intel_dsc_source_support(const struct intel_crtc_state *crtc_state); bool intel_dsc_source_support(const struct intel_crtc_state *crtc_state);
@@ -31,6 +32,8 @@ void intel_dsc_dsi_pps_write(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state); const struct intel_crtc_state *crtc_state);
void intel_dsc_dp_pps_write(struct intel_encoder *encoder, void intel_dsc_dp_pps_write(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state); const struct intel_crtc_state *crtc_state);
void intel_dsc_su_et_parameters_configure(struct intel_dsb *dsb, struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state, int su_lines);
void intel_vdsc_state_dump(struct drm_printer *p, int indent, void intel_vdsc_state_dump(struct drm_printer *p, int indent,
const struct intel_crtc_state *crtc_state); const struct intel_crtc_state *crtc_state);
int intel_vdsc_min_cdclk(const struct intel_crtc_state *crtc_state); int intel_vdsc_min_cdclk(const struct intel_crtc_state *crtc_state);

View File

@@ -196,6 +196,18 @@
#define DSC_PPS18_NSL_BPG_OFFSET(offset) REG_FIELD_PREP(DSC_PPS18_NSL_BPG_OFFSET_MASK, offset) #define DSC_PPS18_NSL_BPG_OFFSET(offset) REG_FIELD_PREP(DSC_PPS18_NSL_BPG_OFFSET_MASK, offset)
#define DSC_PPS18_SL_OFFSET_ADJ(offset) REG_FIELD_PREP(DSC_PPS18_SL_OFFSET_ADJ_MASK, offset) #define DSC_PPS18_SL_OFFSET_ADJ(offset) REG_FIELD_PREP(DSC_PPS18_SL_OFFSET_ADJ_MASK, offset)
#define _LNL_DSC0_SU_PARAMETER_SET_0_PA 0x78064
#define _LNL_DSC1_SU_PARAMETER_SET_0_PA 0x78164
#define _LNL_DSC0_SU_PARAMETER_SET_0_PB 0x78264
#define _LNL_DSC1_SU_PARAMETER_SET_0_PB 0x78364
#define LNL_DSC0_SU_PARAMETER_SET_0(pipe) _MMIO_PIPE((pipe), _LNL_DSC0_SU_PARAMETER_SET_0_PA, _LNL_DSC0_SU_PARAMETER_SET_0_PB)
#define LNL_DSC1_SU_PARAMETER_SET_0(pipe) _MMIO_PIPE((pipe), _LNL_DSC1_SU_PARAMETER_SET_0_PA, _LNL_DSC1_SU_PARAMETER_SET_0_PB)
#define DSC_SUPS0_SU_SLICE_ROW_PER_FRAME_MASK REG_GENMASK(31, 20)
#define DSC_SUPS0_SU_SLICE_ROW_PER_FRAME(rows) REG_FIELD_PREP(DSC_SUPS0_SU_SLICE_ROW_PER_FRAME_MASK, (rows))
#define DSC_SUPS0_SU_PIC_HEIGHT_MASK REG_GENMASK(15, 0)
#define DSC_SUPS0_SU_PIC_HEIGHT(h) REG_FIELD_PREP(DSC_SUPS0_SU_PIC_HEIGHT_MASK, (h))
/* Icelake Rate Control Buffer Threshold Registers */ /* Icelake Rate Control Buffer Threshold Registers */
#define DSCA_RC_BUF_THRESH_0 _MMIO(0x6B230) #define DSCA_RC_BUF_THRESH_0 _MMIO(0x6B230)
#define DSCA_RC_BUF_THRESH_0_UDW _MMIO(0x6B230 + 4) #define DSCA_RC_BUF_THRESH_0_UDW _MMIO(0x6B230 + 4)

View File

@@ -597,6 +597,18 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state)
if (!HAS_VRR(display)) if (!HAS_VRR(display))
return; return;
/*
* Bspec says:
* "(note: VRR needs to be programmed after
* TRANS_DDI_FUNC_CTL and before TRANS_CONF)."
*
* In practice it turns out that ICL can hang if
* TRANS_VRR_VMAX/FLIPLINE are written before
* enabling TRANS_DDI_FUNC_CTL.
*/
drm_WARN_ON(display->drm,
!(intel_de_read(display, TRANS_DDI_FUNC_CTL(display, cpu_transcoder)) & TRANS_DDI_FUNC_ENABLE));
/* /*
* This bit seems to have two meanings depending on the platform: * This bit seems to have two meanings depending on the platform:
* TGL: generate VRR "safe window" for DSB vblank waits * TGL: generate VRR "safe window" for DSB vblank waits
@@ -939,6 +951,8 @@ void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state)
{ {
struct intel_display *display = to_intel_display(crtc_state); struct intel_display *display = to_intel_display(crtc_state);
intel_vrr_set_transcoder_timings(crtc_state);
if (!intel_vrr_possible(crtc_state)) if (!intel_vrr_possible(crtc_state))
return; return;

View File

@@ -153,8 +153,12 @@ int shmem_sg_alloc_table(struct drm_i915_private *i915, struct sg_table *st,
} }
} while (1); } while (1);
nr_pages = min_t(unsigned long, nr_pages = min_array(((unsigned long[]) {
folio_nr_pages(folio), page_count - i); folio_nr_pages(folio),
page_count - i,
max_segment / PAGE_SIZE,
}), 3);
if (!i || if (!i ||
sg->length >= max_segment || sg->length >= max_segment ||
folio_pfn(folio) != next_pfn) { folio_pfn(folio) != next_pfn) {
@@ -164,7 +168,9 @@ int shmem_sg_alloc_table(struct drm_i915_private *i915, struct sg_table *st,
st->nents++; st->nents++;
sg_set_folio(sg, folio, nr_pages * PAGE_SIZE, 0); sg_set_folio(sg, folio, nr_pages * PAGE_SIZE, 0);
} else { } else {
/* XXX: could overflow? */ nr_pages = min_t(unsigned long, nr_pages,
(max_segment - sg->length) / PAGE_SIZE);
sg->length += nr_pages * PAGE_SIZE; sg->length += nr_pages * PAGE_SIZE;
} }
next_pfn = folio_pfn(folio) + nr_pages; next_pfn = folio_pfn(folio) + nr_pages;

View File

@@ -78,7 +78,7 @@ static void a2xx_gpummu_destroy(struct msm_mmu *mmu)
{ {
struct a2xx_gpummu *gpummu = to_a2xx_gpummu(mmu); struct a2xx_gpummu *gpummu = to_a2xx_gpummu(mmu);
dma_free_attrs(mmu->dev, TABLE_SIZE, gpummu->table, gpummu->pt_base, dma_free_attrs(mmu->dev, TABLE_SIZE + 32, gpummu->table, gpummu->pt_base,
DMA_ATTR_FORCE_CONTIGUOUS); DMA_ATTR_FORCE_CONTIGUOUS);
kfree(gpummu); kfree(gpummu);

View File

@@ -1759,7 +1759,7 @@ static const u32 x285_protect_regs[] = {
A6XX_PROTECT_NORDWR(0x27c06, 0x0000), A6XX_PROTECT_NORDWR(0x27c06, 0x0000),
}; };
DECLARE_ADRENO_PROTECT(x285_protect, 64); DECLARE_ADRENO_PROTECT(x285_protect, 15);
static const struct adreno_reglist_pipe a840_nonctxt_regs[] = { static const struct adreno_reglist_pipe a840_nonctxt_regs[] = {
{ REG_A8XX_CP_SMMU_STREAM_ID_LPAC, 0x00000101, BIT(PIPE_NONE) }, { REG_A8XX_CP_SMMU_STREAM_ID_LPAC, 0x00000101, BIT(PIPE_NONE) },
@@ -1966,5 +1966,4 @@ static inline __always_unused void __build_asserts(void)
BUILD_BUG_ON(a660_protect.count > a660_protect.count_max); BUILD_BUG_ON(a660_protect.count > a660_protect.count_max);
BUILD_BUG_ON(a690_protect.count > a690_protect.count_max); BUILD_BUG_ON(a690_protect.count > a690_protect.count_max);
BUILD_BUG_ON(a730_protect.count > a730_protect.count_max); BUILD_BUG_ON(a730_protect.count > a730_protect.count_max);
BUILD_BUG_ON(a840_protect.count > a840_protect.count_max);
} }

View File

@@ -310,11 +310,21 @@ static void a8xx_set_ubwc_config(struct msm_gpu *gpu)
hbb = cfg->highest_bank_bit - 13; hbb = cfg->highest_bank_bit - 13;
hbb_hi = hbb >> 2; hbb_hi = hbb >> 2;
hbb_lo = hbb & 3; hbb_lo = hbb & 3;
a8xx_write_pipe(gpu, PIPE_BV, REG_A8XX_GRAS_NC_MODE_CNTL, hbb << 5);
a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_GRAS_NC_MODE_CNTL, hbb << 5); a8xx_write_pipe(gpu, PIPE_BV, REG_A8XX_GRAS_NC_MODE_CNTL,
hbb << 5 |
level3_swizzling_dis << 4 |
level2_swizzling_dis << 3);
a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_GRAS_NC_MODE_CNTL,
hbb << 5 |
level3_swizzling_dis << 4 |
level2_swizzling_dis << 3);
a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_RB_CCU_NC_MODE_CNTL, a8xx_write_pipe(gpu, PIPE_BR, REG_A8XX_RB_CCU_NC_MODE_CNTL,
yuvnotcomptofc << 6 | yuvnotcomptofc << 6 |
level3_swizzling_dis << 5 |
level2_swizzling_dis << 4 |
hbb_hi << 3 | hbb_hi << 3 |
hbb_lo << 1); hbb_lo << 1);

View File

@@ -302,6 +302,7 @@ static const struct of_device_id dt_match[] = {
{ .compatible = "qcom,kgsl-3d0" }, { .compatible = "qcom,kgsl-3d0" },
{} {}
}; };
MODULE_DEVICE_TABLE(of, dt_match);
static int adreno_runtime_resume(struct device *dev) static int adreno_runtime_resume(struct device *dev)
{ {

View File

@@ -133,7 +133,7 @@ static const struct dpu_sspp_cfg sc8280xp_sspp[] = {
static const struct dpu_lm_cfg sc8280xp_lm[] = { static const struct dpu_lm_cfg sc8280xp_lm[] = {
{ {
.name = "lm_0", .id = LM_0, .name = "lm_0", .id = LM_0,
.base = 0x44000, .len = 0x320, .base = 0x44000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_1, .lm_pair = LM_1,
@@ -141,7 +141,7 @@ static const struct dpu_lm_cfg sc8280xp_lm[] = {
.dspp = DSPP_0, .dspp = DSPP_0,
}, { }, {
.name = "lm_1", .id = LM_1, .name = "lm_1", .id = LM_1,
.base = 0x45000, .len = 0x320, .base = 0x45000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_0, .lm_pair = LM_0,
@@ -149,7 +149,7 @@ static const struct dpu_lm_cfg sc8280xp_lm[] = {
.dspp = DSPP_1, .dspp = DSPP_1,
}, { }, {
.name = "lm_2", .id = LM_2, .name = "lm_2", .id = LM_2,
.base = 0x46000, .len = 0x320, .base = 0x46000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_3, .lm_pair = LM_3,
@@ -157,7 +157,7 @@ static const struct dpu_lm_cfg sc8280xp_lm[] = {
.dspp = DSPP_2, .dspp = DSPP_2,
}, { }, {
.name = "lm_3", .id = LM_3, .name = "lm_3", .id = LM_3,
.base = 0x47000, .len = 0x320, .base = 0x47000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_2, .lm_pair = LM_2,
@@ -165,14 +165,14 @@ static const struct dpu_lm_cfg sc8280xp_lm[] = {
.dspp = DSPP_3, .dspp = DSPP_3,
}, { }, {
.name = "lm_4", .id = LM_4, .name = "lm_4", .id = LM_4,
.base = 0x48000, .len = 0x320, .base = 0x48000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_5, .lm_pair = LM_5,
.pingpong = PINGPONG_4, .pingpong = PINGPONG_4,
}, { }, {
.name = "lm_5", .id = LM_5, .name = "lm_5", .id = LM_5,
.base = 0x49000, .len = 0x320, .base = 0x49000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_4, .lm_pair = LM_4,

View File

@@ -134,7 +134,7 @@ static const struct dpu_sspp_cfg sm8450_sspp[] = {
static const struct dpu_lm_cfg sm8450_lm[] = { static const struct dpu_lm_cfg sm8450_lm[] = {
{ {
.name = "lm_0", .id = LM_0, .name = "lm_0", .id = LM_0,
.base = 0x44000, .len = 0x320, .base = 0x44000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_1, .lm_pair = LM_1,
@@ -142,7 +142,7 @@ static const struct dpu_lm_cfg sm8450_lm[] = {
.dspp = DSPP_0, .dspp = DSPP_0,
}, { }, {
.name = "lm_1", .id = LM_1, .name = "lm_1", .id = LM_1,
.base = 0x45000, .len = 0x320, .base = 0x45000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_0, .lm_pair = LM_0,
@@ -150,7 +150,7 @@ static const struct dpu_lm_cfg sm8450_lm[] = {
.dspp = DSPP_1, .dspp = DSPP_1,
}, { }, {
.name = "lm_2", .id = LM_2, .name = "lm_2", .id = LM_2,
.base = 0x46000, .len = 0x320, .base = 0x46000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_3, .lm_pair = LM_3,
@@ -158,7 +158,7 @@ static const struct dpu_lm_cfg sm8450_lm[] = {
.dspp = DSPP_2, .dspp = DSPP_2,
}, { }, {
.name = "lm_3", .id = LM_3, .name = "lm_3", .id = LM_3,
.base = 0x47000, .len = 0x320, .base = 0x47000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_2, .lm_pair = LM_2,
@@ -166,14 +166,14 @@ static const struct dpu_lm_cfg sm8450_lm[] = {
.dspp = DSPP_3, .dspp = DSPP_3,
}, { }, {
.name = "lm_4", .id = LM_4, .name = "lm_4", .id = LM_4,
.base = 0x48000, .len = 0x320, .base = 0x48000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_5, .lm_pair = LM_5,
.pingpong = PINGPONG_4, .pingpong = PINGPONG_4,
}, { }, {
.name = "lm_5", .id = LM_5, .name = "lm_5", .id = LM_5,
.base = 0x49000, .len = 0x320, .base = 0x49000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_4, .lm_pair = LM_4,

View File

@@ -366,8 +366,8 @@ static const struct dpu_intf_cfg sa8775p_intf[] = {
.type = INTF_NONE, .type = INTF_NONE,
.controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */ .controller_id = MSM_DP_CONTROLLER_0, /* pair with intf_0 for DP MST */
.prog_fetch_lines_worst_case = 24, .prog_fetch_lines_worst_case = 24,
.intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 17), .intr_underrun = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 16),
.intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 16), .intr_vsync = DPU_IRQ_IDX(MDP_SSPP_TOP0_INTR, 17),
}, { }, {
.name = "intf_7", .id = INTF_7, .name = "intf_7", .id = INTF_7,
.base = 0x3b000, .len = 0x280, .base = 0x3b000, .len = 0x280,

View File

@@ -131,7 +131,7 @@ static const struct dpu_sspp_cfg sm8550_sspp[] = {
static const struct dpu_lm_cfg sm8550_lm[] = { static const struct dpu_lm_cfg sm8550_lm[] = {
{ {
.name = "lm_0", .id = LM_0, .name = "lm_0", .id = LM_0,
.base = 0x44000, .len = 0x320, .base = 0x44000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_1, .lm_pair = LM_1,
@@ -139,7 +139,7 @@ static const struct dpu_lm_cfg sm8550_lm[] = {
.dspp = DSPP_0, .dspp = DSPP_0,
}, { }, {
.name = "lm_1", .id = LM_1, .name = "lm_1", .id = LM_1,
.base = 0x45000, .len = 0x320, .base = 0x45000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_0, .lm_pair = LM_0,
@@ -147,7 +147,7 @@ static const struct dpu_lm_cfg sm8550_lm[] = {
.dspp = DSPP_1, .dspp = DSPP_1,
}, { }, {
.name = "lm_2", .id = LM_2, .name = "lm_2", .id = LM_2,
.base = 0x46000, .len = 0x320, .base = 0x46000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_3, .lm_pair = LM_3,
@@ -155,7 +155,7 @@ static const struct dpu_lm_cfg sm8550_lm[] = {
.dspp = DSPP_2, .dspp = DSPP_2,
}, { }, {
.name = "lm_3", .id = LM_3, .name = "lm_3", .id = LM_3,
.base = 0x47000, .len = 0x320, .base = 0x47000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_2, .lm_pair = LM_2,
@@ -163,14 +163,14 @@ static const struct dpu_lm_cfg sm8550_lm[] = {
.dspp = DSPP_3, .dspp = DSPP_3,
}, { }, {
.name = "lm_4", .id = LM_4, .name = "lm_4", .id = LM_4,
.base = 0x48000, .len = 0x320, .base = 0x48000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_5, .lm_pair = LM_5,
.pingpong = PINGPONG_4, .pingpong = PINGPONG_4,
}, { }, {
.name = "lm_5", .id = LM_5, .name = "lm_5", .id = LM_5,
.base = 0x49000, .len = 0x320, .base = 0x49000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_4, .lm_pair = LM_4,

View File

@@ -131,7 +131,7 @@ static const struct dpu_sspp_cfg sar2130p_sspp[] = {
static const struct dpu_lm_cfg sar2130p_lm[] = { static const struct dpu_lm_cfg sar2130p_lm[] = {
{ {
.name = "lm_0", .id = LM_0, .name = "lm_0", .id = LM_0,
.base = 0x44000, .len = 0x320, .base = 0x44000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_1, .lm_pair = LM_1,
@@ -139,7 +139,7 @@ static const struct dpu_lm_cfg sar2130p_lm[] = {
.dspp = DSPP_0, .dspp = DSPP_0,
}, { }, {
.name = "lm_1", .id = LM_1, .name = "lm_1", .id = LM_1,
.base = 0x45000, .len = 0x320, .base = 0x45000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_0, .lm_pair = LM_0,
@@ -147,7 +147,7 @@ static const struct dpu_lm_cfg sar2130p_lm[] = {
.dspp = DSPP_1, .dspp = DSPP_1,
}, { }, {
.name = "lm_2", .id = LM_2, .name = "lm_2", .id = LM_2,
.base = 0x46000, .len = 0x320, .base = 0x46000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_3, .lm_pair = LM_3,
@@ -155,7 +155,7 @@ static const struct dpu_lm_cfg sar2130p_lm[] = {
.dspp = DSPP_2, .dspp = DSPP_2,
}, { }, {
.name = "lm_3", .id = LM_3, .name = "lm_3", .id = LM_3,
.base = 0x47000, .len = 0x320, .base = 0x47000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_2, .lm_pair = LM_2,
@@ -163,14 +163,14 @@ static const struct dpu_lm_cfg sar2130p_lm[] = {
.dspp = DSPP_3, .dspp = DSPP_3,
}, { }, {
.name = "lm_4", .id = LM_4, .name = "lm_4", .id = LM_4,
.base = 0x48000, .len = 0x320, .base = 0x48000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_5, .lm_pair = LM_5,
.pingpong = PINGPONG_4, .pingpong = PINGPONG_4,
}, { }, {
.name = "lm_5", .id = LM_5, .name = "lm_5", .id = LM_5,
.base = 0x49000, .len = 0x320, .base = 0x49000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_4, .lm_pair = LM_4,

View File

@@ -130,7 +130,7 @@ static const struct dpu_sspp_cfg x1e80100_sspp[] = {
static const struct dpu_lm_cfg x1e80100_lm[] = { static const struct dpu_lm_cfg x1e80100_lm[] = {
{ {
.name = "lm_0", .id = LM_0, .name = "lm_0", .id = LM_0,
.base = 0x44000, .len = 0x320, .base = 0x44000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_1, .lm_pair = LM_1,
@@ -138,7 +138,7 @@ static const struct dpu_lm_cfg x1e80100_lm[] = {
.dspp = DSPP_0, .dspp = DSPP_0,
}, { }, {
.name = "lm_1", .id = LM_1, .name = "lm_1", .id = LM_1,
.base = 0x45000, .len = 0x320, .base = 0x45000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_0, .lm_pair = LM_0,
@@ -146,7 +146,7 @@ static const struct dpu_lm_cfg x1e80100_lm[] = {
.dspp = DSPP_1, .dspp = DSPP_1,
}, { }, {
.name = "lm_2", .id = LM_2, .name = "lm_2", .id = LM_2,
.base = 0x46000, .len = 0x320, .base = 0x46000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_3, .lm_pair = LM_3,
@@ -154,7 +154,7 @@ static const struct dpu_lm_cfg x1e80100_lm[] = {
.dspp = DSPP_2, .dspp = DSPP_2,
}, { }, {
.name = "lm_3", .id = LM_3, .name = "lm_3", .id = LM_3,
.base = 0x47000, .len = 0x320, .base = 0x47000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_2, .lm_pair = LM_2,
@@ -162,14 +162,14 @@ static const struct dpu_lm_cfg x1e80100_lm[] = {
.dspp = DSPP_3, .dspp = DSPP_3,
}, { }, {
.name = "lm_4", .id = LM_4, .name = "lm_4", .id = LM_4,
.base = 0x48000, .len = 0x320, .base = 0x48000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_5, .lm_pair = LM_5,
.pingpong = PINGPONG_4, .pingpong = PINGPONG_4,
}, { }, {
.name = "lm_5", .id = LM_5, .name = "lm_5", .id = LM_5,
.base = 0x49000, .len = 0x320, .base = 0x49000, .len = 0x400,
.features = MIXER_MSM8998_MASK, .features = MIXER_MSM8998_MASK,
.sblk = &sdm845_lm_sblk, .sblk = &sdm845_lm_sblk,
.lm_pair = LM_4, .lm_pair = LM_4,

View File

@@ -89,7 +89,7 @@ static void dpu_setup_dspp_gc(struct dpu_hw_dspp *ctx,
base = ctx->cap->sblk->gc.base; base = ctx->cap->sblk->gc.base;
if (!base) { if (!base) {
DRM_ERROR("invalid ctx %pK gc base\n", ctx); DRM_ERROR("invalid ctx %p gc base\n", ctx);
return; return;
} }

View File

@@ -156,11 +156,13 @@ static void dpu_hw_sspp_setup_pe_config_v13(struct dpu_hw_sspp *ctx,
u8 color; u8 color;
u32 lr_pe[4], tb_pe[4]; u32 lr_pe[4], tb_pe[4];
const u32 bytemask = 0xff; const u32 bytemask = 0xff;
u32 offset = ctx->cap->sblk->sspp_rec0_blk.base; u32 offset;
if (!ctx || !pe_ext) if (!ctx || !pe_ext)
return; return;
offset = ctx->cap->sblk->sspp_rec0_blk.base;
c = &ctx->hw; c = &ctx->hw;
/* program SW pixel extension override for all pipes*/ /* program SW pixel extension override for all pipes*/
for (color = 0; color < DPU_MAX_PLANES; color++) { for (color = 0; color < DPU_MAX_PLANES; color++) {

View File

@@ -350,26 +350,28 @@ static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm,
return true; return true;
} }
static bool dpu_rm_find_lms(struct dpu_rm *rm, static int _dpu_rm_reserve_lms(struct dpu_rm *rm,
struct dpu_global_state *global_state, struct dpu_global_state *global_state,
uint32_t crtc_id, bool skip_dspp, uint32_t crtc_id,
struct msm_display_topology *topology, struct msm_display_topology *topology)
int *lm_idx, int *pp_idx, int *dspp_idx)
{ {
int lm_idx[MAX_BLOCKS];
int pp_idx[MAX_BLOCKS];
int dspp_idx[MAX_BLOCKS] = {0};
int i, lm_count = 0; int i, lm_count = 0;
if (!topology->num_lm) {
DPU_ERROR("zero LMs in topology\n");
return -EINVAL;
}
/* Find a primary mixer */ /* Find a primary mixer */
for (i = 0; i < ARRAY_SIZE(rm->mixer_blks) && for (i = 0; i < ARRAY_SIZE(rm->mixer_blks) &&
lm_count < topology->num_lm; i++) { lm_count < topology->num_lm; i++) {
if (!rm->mixer_blks[i]) if (!rm->mixer_blks[i])
continue; continue;
if (skip_dspp && to_dpu_hw_mixer(rm->mixer_blks[i])->cap->dspp) {
DPU_DEBUG("Skipping LM_%d, skipping LMs with DSPPs\n", i);
continue;
}
/* /*
* Reset lm_count to an even index. This will drop the previous * Reset lm_count to an even index. This will drop the previous
* primary mixer if failed to find its peer. * primary mixer if failed to find its peer.
@@ -408,38 +410,12 @@ static bool dpu_rm_find_lms(struct dpu_rm *rm,
} }
} }
return lm_count == topology->num_lm; if (lm_count != topology->num_lm) {
}
static int _dpu_rm_reserve_lms(struct dpu_rm *rm,
struct dpu_global_state *global_state,
uint32_t crtc_id,
struct msm_display_topology *topology)
{
int lm_idx[MAX_BLOCKS];
int pp_idx[MAX_BLOCKS];
int dspp_idx[MAX_BLOCKS] = {0};
int i;
bool found;
if (!topology->num_lm) {
DPU_ERROR("zero LMs in topology\n");
return -EINVAL;
}
/* Try using non-DSPP LM blocks first */
found = dpu_rm_find_lms(rm, global_state, crtc_id, !topology->num_dspp,
topology, lm_idx, pp_idx, dspp_idx);
if (!found && !topology->num_dspp)
found = dpu_rm_find_lms(rm, global_state, crtc_id, false,
topology, lm_idx, pp_idx, dspp_idx);
if (!found) {
DPU_DEBUG("unable to find appropriate mixers\n"); DPU_DEBUG("unable to find appropriate mixers\n");
return -ENAVAIL; return -ENAVAIL;
} }
for (i = 0; i < topology->num_lm; i++) { for (i = 0; i < lm_count; i++) {
global_state->mixer_to_crtc_id[lm_idx[i]] = crtc_id; global_state->mixer_to_crtc_id[lm_idx[i]] = crtc_id;
global_state->pingpong_to_crtc_id[pp_idx[i]] = crtc_id; global_state->pingpong_to_crtc_id[pp_idx[i]] = crtc_id;
global_state->dspp_to_crtc_id[dspp_idx[i]] = global_state->dspp_to_crtc_id[dspp_idx[i]] =

View File

@@ -584,13 +584,30 @@ void dsi_link_clk_disable_v2(struct msm_dsi_host *msm_host)
* FIXME: Reconsider this if/when CMD mode handling is rewritten to use * FIXME: Reconsider this if/when CMD mode handling is rewritten to use
* transfer time and data overhead as a starting point of the calculations. * transfer time and data overhead as a starting point of the calculations.
*/ */
static unsigned long dsi_adjust_pclk_for_compression(const struct drm_display_mode *mode, static unsigned long
const struct drm_dsc_config *dsc) dsi_adjust_pclk_for_compression(const struct drm_display_mode *mode,
const struct drm_dsc_config *dsc,
bool is_bonded_dsi)
{ {
int new_hdisplay = DIV_ROUND_UP(mode->hdisplay * drm_dsc_get_bpp_int(dsc), int hdisplay, new_hdisplay, new_htotal;
/*
* For bonded DSI, split hdisplay across two links and round up each
* half separately, passing the full hdisplay would only round up once.
* This also aligns with the hdisplay we program later in
* dsi_timing_setup()
*/
hdisplay = mode->hdisplay;
if (is_bonded_dsi)
hdisplay /= 2;
new_hdisplay = DIV_ROUND_UP(hdisplay * drm_dsc_get_bpp_int(dsc),
dsc->bits_per_component * 3); dsc->bits_per_component * 3);
int new_htotal = mode->htotal - mode->hdisplay + new_hdisplay; if (is_bonded_dsi)
new_hdisplay *= 2;
new_htotal = mode->htotal - mode->hdisplay + new_hdisplay;
return mult_frac(mode->clock * 1000u, new_htotal, mode->htotal); return mult_frac(mode->clock * 1000u, new_htotal, mode->htotal);
} }
@@ -603,7 +620,7 @@ static unsigned long dsi_get_pclk_rate(const struct drm_display_mode *mode,
pclk_rate = mode->clock * 1000u; pclk_rate = mode->clock * 1000u;
if (dsc) if (dsc)
pclk_rate = dsi_adjust_pclk_for_compression(mode, dsc); pclk_rate = dsi_adjust_pclk_for_compression(mode, dsc, is_bonded_dsi);
/* /*
* For bonded DSI mode, the current DRM mode has the complete width of the * For bonded DSI mode, the current DRM mode has the complete width of the
@@ -993,7 +1010,7 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
if (msm_host->dsc) { if (msm_host->dsc) {
struct drm_dsc_config *dsc = msm_host->dsc; struct drm_dsc_config *dsc = msm_host->dsc;
u32 bytes_per_pclk; u32 bits_per_pclk;
/* update dsc params with timing params */ /* update dsc params with timing params */
if (!dsc || !mode->hdisplay || !mode->vdisplay) { if (!dsc || !mode->hdisplay || !mode->vdisplay) {
@@ -1015,7 +1032,9 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
/* /*
* DPU sends 3 bytes per pclk cycle to DSI. If widebus is * DPU sends 3 bytes per pclk cycle to DSI. If widebus is
* enabled, bus width is extended to 6 bytes. * enabled, MDP always sends out 48-bit compressed data per
* pclk and on average, DSI consumes an amount of compressed
* data equivalent to the uncompressed pixel depth per pclk.
* *
* Calculate the number of pclks needed to transmit one line of * Calculate the number of pclks needed to transmit one line of
* the compressed data. * the compressed data.
@@ -1027,12 +1046,12 @@ static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi)
* unused anyway. * unused anyway.
*/ */
h_total -= hdisplay; h_total -= hdisplay;
if (wide_bus_enabled && !(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO)) if (wide_bus_enabled)
bytes_per_pclk = 6; bits_per_pclk = mipi_dsi_pixel_format_to_bpp(msm_host->format);
else else
bytes_per_pclk = 3; bits_per_pclk = 24;
hdisplay = DIV_ROUND_UP(msm_dsc_get_bytes_per_line(msm_host->dsc), bytes_per_pclk); hdisplay = DIV_ROUND_UP(msm_dsc_get_bytes_per_line(msm_host->dsc) * 8, bits_per_pclk);
h_total += hdisplay; h_total += hdisplay;
ha_end = ha_start + hdisplay; ha_end = ha_start + hdisplay;

View File

@@ -51,8 +51,8 @@
#define DSI_PHY_7NM_QUIRK_V4_3 BIT(3) #define DSI_PHY_7NM_QUIRK_V4_3 BIT(3)
/* Hardware is V5.2 */ /* Hardware is V5.2 */
#define DSI_PHY_7NM_QUIRK_V5_2 BIT(4) #define DSI_PHY_7NM_QUIRK_V5_2 BIT(4)
/* Hardware is V7.0 */ /* Hardware is V7.2 */
#define DSI_PHY_7NM_QUIRK_V7_0 BIT(5) #define DSI_PHY_7NM_QUIRK_V7_2 BIT(5)
struct dsi_pll_config { struct dsi_pll_config {
bool enable_ssc; bool enable_ssc;
@@ -143,7 +143,7 @@ static void dsi_pll_calc_dec_frac(struct dsi_pll_7nm *pll, struct dsi_pll_config
if (pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_PRE_V4_1) { if (pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_PRE_V4_1) {
config->pll_clock_inverters = 0x28; config->pll_clock_inverters = 0x28;
} else if ((pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_0)) { } else if ((pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_2)) {
if (pll_freq < 163000000ULL) if (pll_freq < 163000000ULL)
config->pll_clock_inverters = 0xa0; config->pll_clock_inverters = 0xa0;
else if (pll_freq < 175000000ULL) else if (pll_freq < 175000000ULL)
@@ -284,7 +284,7 @@ static void dsi_pll_config_hzindep_reg(struct dsi_pll_7nm *pll)
} }
if ((pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V5_2) || if ((pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V5_2) ||
(pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_0)) { (pll->phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_2)) {
if (pll->vco_current_rate < 1557000000ULL) if (pll->vco_current_rate < 1557000000ULL)
vco_config_1 = 0x08; vco_config_1 = 0x08;
else else
@@ -699,7 +699,7 @@ static int dsi_7nm_set_usecase(struct msm_dsi_phy *phy)
case MSM_DSI_PHY_MASTER: case MSM_DSI_PHY_MASTER:
pll_7nm->slave = pll_7nm_list[(pll_7nm->phy->id + 1) % DSI_MAX]; pll_7nm->slave = pll_7nm_list[(pll_7nm->phy->id + 1) % DSI_MAX];
/* v7.0: Enable ATB_EN0 and alternate clock output to external phy */ /* v7.0: Enable ATB_EN0 and alternate clock output to external phy */
if (phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_0) if (phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_2)
writel(0x07, base + REG_DSI_7nm_PHY_CMN_CTRL_5); writel(0x07, base + REG_DSI_7nm_PHY_CMN_CTRL_5);
break; break;
case MSM_DSI_PHY_SLAVE: case MSM_DSI_PHY_SLAVE:
@@ -987,7 +987,7 @@ static int dsi_7nm_phy_enable(struct msm_dsi_phy *phy,
/* Request for REFGEN READY */ /* Request for REFGEN READY */
if ((phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V4_3) || if ((phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V4_3) ||
(phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V5_2) || (phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V5_2) ||
(phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_0)) { (phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_2)) {
writel(0x1, phy->base + REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE10); writel(0x1, phy->base + REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE10);
udelay(500); udelay(500);
} }
@@ -1021,7 +1021,7 @@ static int dsi_7nm_phy_enable(struct msm_dsi_phy *phy,
lane_ctrl0 = 0x1f; lane_ctrl0 = 0x1f;
} }
if ((phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_0)) { if ((phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_2)) {
if (phy->cphy_mode) { if (phy->cphy_mode) {
/* TODO: different for second phy */ /* TODO: different for second phy */
vreg_ctrl_0 = 0x57; vreg_ctrl_0 = 0x57;
@@ -1097,7 +1097,7 @@ static int dsi_7nm_phy_enable(struct msm_dsi_phy *phy,
/* program CMN_CTRL_4 for minor_ver 2 chipsets*/ /* program CMN_CTRL_4 for minor_ver 2 chipsets*/
if ((phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V5_2) || if ((phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V5_2) ||
(phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_0) || (phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_2) ||
(readl(base + REG_DSI_7nm_PHY_CMN_REVISION_ID0) & (0xf0)) == 0x20) (readl(base + REG_DSI_7nm_PHY_CMN_REVISION_ID0) & (0xf0)) == 0x20)
writel(0x04, base + REG_DSI_7nm_PHY_CMN_CTRL_4); writel(0x04, base + REG_DSI_7nm_PHY_CMN_CTRL_4);
@@ -1213,7 +1213,7 @@ static void dsi_7nm_phy_disable(struct msm_dsi_phy *phy)
/* Turn off REFGEN Vote */ /* Turn off REFGEN Vote */
if ((phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V4_3) || if ((phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V4_3) ||
(phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V5_2) || (phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V5_2) ||
(phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_0)) { (phy->cfg->quirks & DSI_PHY_7NM_QUIRK_V7_2)) {
writel(0x0, base + REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE10); writel(0x0, base + REG_DSI_7nm_PHY_CMN_GLBL_DIGTOP_SPARE10);
wmb(); wmb();
/* Delay to ensure HW removes vote before PHY shut down */ /* Delay to ensure HW removes vote before PHY shut down */
@@ -1502,7 +1502,7 @@ const struct msm_dsi_phy_cfg dsi_phy_3nm_8750_cfgs = {
#endif #endif
.io_start = { 0xae95000, 0xae97000 }, .io_start = { 0xae95000, 0xae97000 },
.num_dsi_phy = 2, .num_dsi_phy = 2,
.quirks = DSI_PHY_7NM_QUIRK_V7_0, .quirks = DSI_PHY_7NM_QUIRK_V7_2,
}; };
const struct msm_dsi_phy_cfg dsi_phy_3nm_kaanapali_cfgs = { const struct msm_dsi_phy_cfg dsi_phy_3nm_kaanapali_cfgs = {
@@ -1525,5 +1525,5 @@ const struct msm_dsi_phy_cfg dsi_phy_3nm_kaanapali_cfgs = {
#endif #endif
.io_start = { 0x9ac1000, 0x9ac4000 }, .io_start = { 0x9ac1000, 0x9ac4000 },
.num_dsi_phy = 2, .num_dsi_phy = 2,
.quirks = DSI_PHY_7NM_QUIRK_V7_0, .quirks = DSI_PHY_7NM_QUIRK_V7_2,
}; };

View File

@@ -347,6 +347,12 @@ static int st7586_probe(struct spi_device *spi)
if (ret) if (ret)
return ret; return ret;
/*
* Override value set by mipi_dbi_spi_init(). This driver is a bit
* non-standard, so best to set it explicitly here.
*/
dbi->write_memory_bpw = 8;
/* Cannot read from this controller via SPI */ /* Cannot read from this controller via SPI */
dbi->read_commands = NULL; dbi->read_commands = NULL;
@@ -356,15 +362,6 @@ static int st7586_probe(struct spi_device *spi)
if (ret) if (ret)
return ret; return ret;
/*
* we are using 8-bit data, so we are not actually swapping anything,
* but setting mipi->swap_bytes makes mipi_dbi_typec3_command() do the
* right thing and not use 16-bit transfers (which results in swapped
* bytes on little-endian systems and causes out of order data to be
* sent to the display).
*/
dbi->swap_bytes = true;
drm_mode_config_reset(drm); drm_mode_config_reset(drm);
ret = drm_dev_register(drm, 0); ret = drm_dev_register(drm, 0);

View File

@@ -47,16 +47,12 @@ struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]);
unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {} unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {}
impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> { impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> {
/// Creates a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`. /// Returns the page table entry for `index`, for a mapping starting at `start`.
fn new(start: DmaAddress) -> Result<Self> { // TODO: Replace with `IoView` projection once available.
let mut ptes = [0u64; NUM_PAGES]; fn entry(start: DmaAddress, index: usize) -> Result<u64> {
for (i, pte) in ptes.iter_mut().enumerate() { start
*pte = start .checked_add(num::usize_as_u64(index) << GSP_PAGE_SHIFT)
.checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT) .ok_or(EOVERFLOW)
.ok_or(EOVERFLOW)?;
}
Ok(Self(ptes))
} }
} }
@@ -86,16 +82,22 @@ impl LogBuffer {
NUM_PAGES * GSP_PAGE_SIZE, NUM_PAGES * GSP_PAGE_SIZE,
GFP_KERNEL | __GFP_ZERO, GFP_KERNEL | __GFP_ZERO,
)?); )?);
let ptes = PteArray::<NUM_PAGES>::new(obj.0.dma_handle())?;
let start_addr = obj.0.dma_handle();
// SAFETY: `obj` has just been created and we are its sole user. // SAFETY: `obj` has just been created and we are its sole user.
unsafe { let pte_region = unsafe {
// Copy the self-mapping PTE at the expected location.
obj.0 obj.0
.as_slice_mut(size_of::<u64>(), size_of_val(&ptes))? .as_slice_mut(size_of::<u64>(), NUM_PAGES * size_of::<u64>())?
.copy_from_slice(ptes.as_bytes())
}; };
// Write values one by one to avoid an on-stack instance of `PteArray`.
for (i, chunk) in pte_region.chunks_exact_mut(size_of::<u64>()).enumerate() {
let pte_value = PteArray::<0>::entry(start_addr, i)?;
chunk.copy_from_slice(&pte_value.to_ne_bytes());
}
Ok(obj) Ok(obj)
} }
} }
@@ -143,14 +145,14 @@ impl Gsp {
// _kgspInitLibosLoggingStructures (allocates memory for buffers) // _kgspInitLibosLoggingStructures (allocates memory for buffers)
// kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array) // kgspSetupLibosInitArgs_IMPL (creates pLibosInitArgs[] array)
dma_write!( dma_write!(
libos[0] = LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0) libos, [0]?, LibosMemoryRegionInitArgument::new("LOGINIT", &loginit.0)
)?; );
dma_write!( dma_write!(
libos[1] = LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0) libos, [1]?, LibosMemoryRegionInitArgument::new("LOGINTR", &logintr.0)
)?; );
dma_write!(libos[2] = LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0))?; dma_write!(libos, [2]?, LibosMemoryRegionInitArgument::new("LOGRM", &logrm.0));
dma_write!(rmargs[0].inner = fw::GspArgumentsCached::new(cmdq))?; dma_write!(rmargs, [0]?.inner, fw::GspArgumentsCached::new(cmdq));
dma_write!(libos[3] = LibosMemoryRegionInitArgument::new("RMARGS", rmargs))?; dma_write!(libos, [3]?, LibosMemoryRegionInitArgument::new("RMARGS", rmargs));
}, },
})) }))
}) })

View File

@@ -157,7 +157,7 @@ impl super::Gsp {
let wpr_meta = let wpr_meta =
CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?; CoherentAllocation::<GspFwWprMeta>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
dma_write!(wpr_meta[0] = GspFwWprMeta::new(&gsp_fw, &fb_layout))?; dma_write!(wpr_meta, [0]?, GspFwWprMeta::new(&gsp_fw, &fb_layout));
self.cmdq self.cmdq
.send_command(bar, commands::SetSystemInfo::new(pdev))?; .send_command(bar, commands::SetSystemInfo::new(pdev))?;

View File

@@ -2,11 +2,7 @@
use core::{ use core::{
cmp, cmp,
mem, mem, //
sync::atomic::{
fence,
Ordering, //
}, //
}; };
use kernel::{ use kernel::{
@@ -146,30 +142,36 @@ static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);
#[repr(C)] #[repr(C)]
// There is no struct defined for this in the open-gpu-kernel-source headers. // There is no struct defined for this in the open-gpu-kernel-source headers.
// Instead it is defined by code in `GspMsgQueuesInit()`. // Instead it is defined by code in `GspMsgQueuesInit()`.
struct Msgq { // TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
pub(super) struct Msgq {
/// Header for sending messages, including the write pointer. /// Header for sending messages, including the write pointer.
tx: MsgqTxHeader, pub(super) tx: MsgqTxHeader,
/// Header for receiving messages, including the read pointer. /// Header for receiving messages, including the read pointer.
rx: MsgqRxHeader, pub(super) rx: MsgqRxHeader,
/// The message queue proper. /// The message queue proper.
msgq: MsgqData, msgq: MsgqData,
} }
/// Structure shared between the driver and the GSP and containing the command and message queues. /// Structure shared between the driver and the GSP and containing the command and message queues.
#[repr(C)] #[repr(C)]
struct GspMem { // TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
pub(super) struct GspMem {
/// Self-mapping page table entries. /// Self-mapping page table entries.
ptes: PteArray<{ GSP_PAGE_SIZE / size_of::<u64>() }>, ptes: PteArray<{ Self::PTE_ARRAY_SIZE }>,
/// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the /// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the
/// write and read pointers that the CPU updates. /// write and read pointers that the CPU updates.
/// ///
/// This member is read-only for the GSP. /// This member is read-only for the GSP.
cpuq: Msgq, pub(super) cpuq: Msgq,
/// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the /// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the
/// write and read pointers that the GSP updates. /// write and read pointers that the GSP updates.
/// ///
/// This member is read-only for the driver. /// This member is read-only for the driver.
gspq: Msgq, pub(super) gspq: Msgq,
}
impl GspMem {
const PTE_ARRAY_SIZE: usize = GSP_PAGE_SIZE / size_of::<u64>();
} }
// SAFETY: These structs don't meet the no-padding requirements of AsBytes but // SAFETY: These structs don't meet the no-padding requirements of AsBytes but
@@ -201,9 +203,19 @@ impl DmaGspMem {
let gsp_mem = let gsp_mem =
CoherentAllocation::<GspMem>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?; CoherentAllocation::<GspMem>::alloc_coherent(dev, 1, GFP_KERNEL | __GFP_ZERO)?;
dma_write!(gsp_mem[0].ptes = PteArray::new(gsp_mem.dma_handle())?)?;
dma_write!(gsp_mem[0].cpuq.tx = MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES))?; let start = gsp_mem.dma_handle();
dma_write!(gsp_mem[0].cpuq.rx = MsgqRxHeader::new())?; // Write values one by one to avoid an on-stack instance of `PteArray`.
for i in 0..GspMem::PTE_ARRAY_SIZE {
dma_write!(gsp_mem, [0]?.ptes.0[i], PteArray::<0>::entry(start, i)?);
}
dma_write!(
gsp_mem,
[0]?.cpuq.tx,
MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES)
);
dma_write!(gsp_mem, [0]?.cpuq.rx, MsgqRxHeader::new());
Ok(Self(gsp_mem)) Ok(Self(gsp_mem))
} }
@@ -317,12 +329,7 @@ impl DmaGspMem {
// //
// - The returned value is between `0` and `MSGQ_NUM_PAGES`. // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
fn gsp_write_ptr(&self) -> u32 { fn gsp_write_ptr(&self) -> u32 {
let gsp_mem = self.0.start_ptr(); super::fw::gsp_mem::gsp_write_ptr(&self.0)
// SAFETY:
// - The 'CoherentAllocation' contains at least one object.
// - By the invariants of `CoherentAllocation` the pointer is valid.
(unsafe { (*gsp_mem).gspq.tx.write_ptr() } % MSGQ_NUM_PAGES)
} }
// Returns the index of the memory page the GSP will read the next command from. // Returns the index of the memory page the GSP will read the next command from.
@@ -331,12 +338,7 @@ impl DmaGspMem {
// //
// - The returned value is between `0` and `MSGQ_NUM_PAGES`. // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
fn gsp_read_ptr(&self) -> u32 { fn gsp_read_ptr(&self) -> u32 {
let gsp_mem = self.0.start_ptr(); super::fw::gsp_mem::gsp_read_ptr(&self.0)
// SAFETY:
// - The 'CoherentAllocation' contains at least one object.
// - By the invariants of `CoherentAllocation` the pointer is valid.
(unsafe { (*gsp_mem).gspq.rx.read_ptr() } % MSGQ_NUM_PAGES)
} }
// Returns the index of the memory page the CPU can read the next message from. // Returns the index of the memory page the CPU can read the next message from.
@@ -345,27 +347,12 @@ impl DmaGspMem {
// //
// - The returned value is between `0` and `MSGQ_NUM_PAGES`. // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
fn cpu_read_ptr(&self) -> u32 { fn cpu_read_ptr(&self) -> u32 {
let gsp_mem = self.0.start_ptr(); super::fw::gsp_mem::cpu_read_ptr(&self.0)
// SAFETY:
// - The ['CoherentAllocation'] contains at least one object.
// - By the invariants of CoherentAllocation the pointer is valid.
(unsafe { (*gsp_mem).cpuq.rx.read_ptr() } % MSGQ_NUM_PAGES)
} }
// Informs the GSP that it can send `elem_count` new pages into the message queue. // Informs the GSP that it can send `elem_count` new pages into the message queue.
fn advance_cpu_read_ptr(&mut self, elem_count: u32) { fn advance_cpu_read_ptr(&mut self, elem_count: u32) {
let rptr = self.cpu_read_ptr().wrapping_add(elem_count) % MSGQ_NUM_PAGES; super::fw::gsp_mem::advance_cpu_read_ptr(&self.0, elem_count)
// Ensure read pointer is properly ordered.
fence(Ordering::SeqCst);
let gsp_mem = self.0.start_ptr_mut();
// SAFETY:
// - The 'CoherentAllocation' contains at least one object.
// - By the invariants of `CoherentAllocation` the pointer is valid.
unsafe { (*gsp_mem).cpuq.rx.set_read_ptr(rptr) };
} }
// Returns the index of the memory page the CPU can write the next command to. // Returns the index of the memory page the CPU can write the next command to.
@@ -374,26 +361,12 @@ impl DmaGspMem {
// //
// - The returned value is between `0` and `MSGQ_NUM_PAGES`. // - The returned value is between `0` and `MSGQ_NUM_PAGES`.
fn cpu_write_ptr(&self) -> u32 { fn cpu_write_ptr(&self) -> u32 {
let gsp_mem = self.0.start_ptr(); super::fw::gsp_mem::cpu_write_ptr(&self.0)
// SAFETY:
// - The 'CoherentAllocation' contains at least one object.
// - By the invariants of `CoherentAllocation` the pointer is valid.
(unsafe { (*gsp_mem).cpuq.tx.write_ptr() } % MSGQ_NUM_PAGES)
} }
// Informs the GSP that it can process `elem_count` new pages from the command queue. // Informs the GSP that it can process `elem_count` new pages from the command queue.
fn advance_cpu_write_ptr(&mut self, elem_count: u32) { fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
let wptr = self.cpu_write_ptr().wrapping_add(elem_count) & MSGQ_NUM_PAGES; super::fw::gsp_mem::advance_cpu_write_ptr(&self.0, elem_count)
let gsp_mem = self.0.start_ptr_mut();
// SAFETY:
// - The 'CoherentAllocation' contains at least one object.
// - By the invariants of `CoherentAllocation` the pointer is valid.
unsafe { (*gsp_mem).cpuq.tx.set_write_ptr(wptr) };
// Ensure all command data is visible before triggering the GSP read.
fence(Ordering::SeqCst);
} }
} }

View File

@@ -40,6 +40,75 @@ use crate::{
}, },
}; };
// TODO: Replace with `IoView` projections once available; the `unwrap()` calls go away once we
// switch to the new `dma::Coherent` API.
pub(super) mod gsp_mem {
use core::sync::atomic::{
fence,
Ordering, //
};
use kernel::{
dma::CoherentAllocation,
dma_read,
dma_write,
prelude::*, //
};
use crate::gsp::cmdq::{
GspMem,
MSGQ_NUM_PAGES, //
};
pub(in crate::gsp) fn gsp_write_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
|| -> Result<u32> { Ok(dma_read!(qs, [0]?.gspq.tx.0.writePtr) % MSGQ_NUM_PAGES) }().unwrap()
}
pub(in crate::gsp) fn gsp_read_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
|| -> Result<u32> { Ok(dma_read!(qs, [0]?.gspq.rx.0.readPtr) % MSGQ_NUM_PAGES) }().unwrap()
}
pub(in crate::gsp) fn cpu_read_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
|| -> Result<u32> { Ok(dma_read!(qs, [0]?.cpuq.rx.0.readPtr) % MSGQ_NUM_PAGES) }().unwrap()
}
pub(in crate::gsp) fn advance_cpu_read_ptr(qs: &CoherentAllocation<GspMem>, count: u32) {
let rptr = cpu_read_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
// Ensure read pointer is properly ordered.
fence(Ordering::SeqCst);
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
|| -> Result {
dma_write!(qs, [0]?.cpuq.rx.0.readPtr, rptr);
Ok(())
}()
.unwrap()
}
pub(in crate::gsp) fn cpu_write_ptr(qs: &CoherentAllocation<GspMem>) -> u32 {
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
|| -> Result<u32> { Ok(dma_read!(qs, [0]?.cpuq.tx.0.writePtr) % MSGQ_NUM_PAGES) }().unwrap()
}
pub(in crate::gsp) fn advance_cpu_write_ptr(qs: &CoherentAllocation<GspMem>, count: u32) {
let wptr = cpu_write_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
// PANIC: A `dma::CoherentAllocation` always contains at least one element.
|| -> Result {
dma_write!(qs, [0]?.cpuq.tx.0.writePtr, wptr);
Ok(())
}()
.unwrap();
// Ensure all command data is visible before triggering the GSP read.
fence(Ordering::SeqCst);
}
}
/// Empty type to group methods related to heap parameters for running the GSP firmware. /// Empty type to group methods related to heap parameters for running the GSP firmware.
enum GspFwHeapParams {} enum GspFwHeapParams {}
@@ -708,22 +777,6 @@ impl MsgqTxHeader {
entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(), entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(),
}) })
} }
/// Returns the value of the write pointer for this queue.
pub(crate) fn write_ptr(&self) -> u32 {
let ptr = core::ptr::from_ref(&self.0.writePtr);
// SAFETY: `ptr` is a valid pointer to a `u32`.
unsafe { ptr.read_volatile() }
}
/// Sets the value of the write pointer for this queue.
pub(crate) fn set_write_ptr(&mut self, val: u32) {
let ptr = core::ptr::from_mut(&mut self.0.writePtr);
// SAFETY: `ptr` is a valid pointer to a `u32`.
unsafe { ptr.write_volatile(val) }
}
} }
// SAFETY: Padding is explicit and does not contain uninitialized data. // SAFETY: Padding is explicit and does not contain uninitialized data.
@@ -739,22 +792,6 @@ impl MsgqRxHeader {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
Self(Default::default()) Self(Default::default())
} }
/// Returns the value of the read pointer for this queue.
pub(crate) fn read_ptr(&self) -> u32 {
let ptr = core::ptr::from_ref(&self.0.readPtr);
// SAFETY: `ptr` is a valid pointer to a `u32`.
unsafe { ptr.read_volatile() }
}
/// Sets the value of the read pointer for this queue.
pub(crate) fn set_read_ptr(&mut self, val: u32) {
let ptr = core::ptr::from_mut(&mut self.0.readPtr);
// SAFETY: `ptr` is a valid pointer to a `u32`.
unsafe { ptr.write_volatile(val) }
}
} }
// SAFETY: Padding is explicit and does not contain uninitialized data. // SAFETY: Padding is explicit and does not contain uninitialized data.

View File

@@ -461,6 +461,19 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
self.count * core::mem::size_of::<T>() self.count * core::mem::size_of::<T>()
} }
/// Returns the raw pointer to the allocated region in the CPU's virtual address space.
#[inline]
pub fn as_ptr(&self) -> *const [T] {
core::ptr::slice_from_raw_parts(self.cpu_addr.as_ptr(), self.count)
}
/// Returns the raw pointer to the allocated region in the CPU's virtual address space as
/// a mutable pointer.
#[inline]
pub fn as_mut_ptr(&self) -> *mut [T] {
core::ptr::slice_from_raw_parts_mut(self.cpu_addr.as_ptr(), self.count)
}
/// Returns the base address to the allocated region in the CPU's virtual address space. /// Returns the base address to the allocated region in the CPU's virtual address space.
pub fn start_ptr(&self) -> *const T { pub fn start_ptr(&self) -> *const T {
self.cpu_addr.as_ptr() self.cpu_addr.as_ptr()
@@ -581,23 +594,6 @@ impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
Ok(()) Ok(())
} }
/// Returns a pointer to an element from the region with bounds checking. `offset` is in
/// units of `T`, not the number of bytes.
///
/// Public but hidden since it should only be used from [`dma_read`] and [`dma_write`] macros.
#[doc(hidden)]
pub fn item_from_index(&self, offset: usize) -> Result<*mut T> {
if offset >= self.count {
return Err(EINVAL);
}
// SAFETY:
// - The pointer is valid due to type invariant on `CoherentAllocation`
// and we've just checked that the range and index is within bounds.
// - `offset` can't overflow since it is smaller than `self.count` and we've checked
// that `self.count` won't overflow early in the constructor.
Ok(unsafe { self.cpu_addr.as_ptr().add(offset) })
}
/// Reads the value of `field` and ensures that its type is [`FromBytes`]. /// Reads the value of `field` and ensures that its type is [`FromBytes`].
/// ///
/// # Safety /// # Safety
@@ -670,6 +666,9 @@ unsafe impl<T: AsBytes + FromBytes + Send> Send for CoherentAllocation<T> {}
/// Reads a field of an item from an allocated region of structs. /// Reads a field of an item from an allocated region of structs.
/// ///
/// The syntax is of the form `kernel::dma_read!(dma, proj)` where `dma` is an expression evaluating
/// to a [`CoherentAllocation`] and `proj` is a [projection specification](kernel::ptr::project!).
///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
@@ -684,36 +683,29 @@ unsafe impl<T: AsBytes + FromBytes + Send> Send for CoherentAllocation<T> {}
/// unsafe impl kernel::transmute::AsBytes for MyStruct{}; /// unsafe impl kernel::transmute::AsBytes for MyStruct{};
/// ///
/// # fn test(alloc: &kernel::dma::CoherentAllocation<MyStruct>) -> Result { /// # fn test(alloc: &kernel::dma::CoherentAllocation<MyStruct>) -> Result {
/// let whole = kernel::dma_read!(alloc[2]); /// let whole = kernel::dma_read!(alloc, [2]?);
/// let field = kernel::dma_read!(alloc[1].field); /// let field = kernel::dma_read!(alloc, [1]?.field);
/// # Ok::<(), Error>(()) } /// # Ok::<(), Error>(()) }
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! dma_read { macro_rules! dma_read {
($dma:expr, $idx: expr, $($field:tt)*) => {{ ($dma:expr, $($proj:tt)*) => {{
(|| -> ::core::result::Result<_, $crate::error::Error> { let dma = &$dma;
let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?; let ptr = $crate::ptr::project!(
// SAFETY: `item_from_index` ensures that `item` is always a valid pointer and can be $crate::dma::CoherentAllocation::as_ptr(dma), $($proj)*
// dereferenced. The compiler also further validates the expression on whether `field` );
// is a member of `item` when expanded by the macro. // SAFETY: The pointer created by the projection is within the DMA region.
unsafe { unsafe { $crate::dma::CoherentAllocation::field_read(dma, ptr) }
let ptr_field = ::core::ptr::addr_of!((*item) $($field)*);
::core::result::Result::Ok(
$crate::dma::CoherentAllocation::field_read(&$dma, ptr_field)
)
}
})()
}}; }};
($dma:ident [ $idx:expr ] $($field:tt)* ) => {
$crate::dma_read!($dma, $idx, $($field)*)
};
($($dma:ident).* [ $idx:expr ] $($field:tt)* ) => {
$crate::dma_read!($($dma).*, $idx, $($field)*)
};
} }
/// Writes to a field of an item from an allocated region of structs. /// Writes to a field of an item from an allocated region of structs.
/// ///
/// The syntax is of the form `kernel::dma_write!(dma, proj, val)` where `dma` is an expression
/// evaluating to a [`CoherentAllocation`], `proj` is a
/// [projection specification](kernel::ptr::project!), and `val` is the value to be written to the
/// projected location.
///
/// # Examples /// # Examples
/// ///
/// ``` /// ```
@@ -728,37 +720,31 @@ macro_rules! dma_read {
/// unsafe impl kernel::transmute::AsBytes for MyStruct{}; /// unsafe impl kernel::transmute::AsBytes for MyStruct{};
/// ///
/// # fn test(alloc: &kernel::dma::CoherentAllocation<MyStruct>) -> Result { /// # fn test(alloc: &kernel::dma::CoherentAllocation<MyStruct>) -> Result {
/// kernel::dma_write!(alloc[2].member = 0xf); /// kernel::dma_write!(alloc, [2]?.member, 0xf);
/// kernel::dma_write!(alloc[1] = MyStruct { member: 0xf }); /// kernel::dma_write!(alloc, [1]?, MyStruct { member: 0xf });
/// # Ok::<(), Error>(()) } /// # Ok::<(), Error>(()) }
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! dma_write { macro_rules! dma_write {
($dma:ident [ $idx:expr ] $($field:tt)*) => {{ (@parse [$dma:expr] [$($proj:tt)*] [, $val:expr]) => {{
$crate::dma_write!($dma, $idx, $($field)*) let dma = &$dma;
let ptr = $crate::ptr::project!(
mut $crate::dma::CoherentAllocation::as_mut_ptr(dma), $($proj)*
);
let val = $val;
// SAFETY: The pointer created by the projection is within the DMA region.
unsafe { $crate::dma::CoherentAllocation::field_write(dma, ptr, val) }
}}; }};
($($dma:ident).* [ $idx:expr ] $($field:tt)* ) => {{ (@parse [$dma:expr] [$($proj:tt)*] [.$field:tt $($rest:tt)*]) => {
$crate::dma_write!($($dma).*, $idx, $($field)*) $crate::dma_write!(@parse [$dma] [$($proj)* .$field] [$($rest)*])
}};
($dma:expr, $idx: expr, = $val:expr) => {
(|| -> ::core::result::Result<_, $crate::error::Error> {
let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?;
// SAFETY: `item_from_index` ensures that `item` is always a valid item.
unsafe { $crate::dma::CoherentAllocation::field_write(&$dma, item, $val) }
::core::result::Result::Ok(())
})()
}; };
($dma:expr, $idx: expr, $(.$field:ident)* = $val:expr) => { (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr]? $($rest:tt)*]) => {
(|| -> ::core::result::Result<_, $crate::error::Error> { $crate::dma_write!(@parse [$dma] [$($proj)* [$index]?] [$($rest)*])
let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?; };
// SAFETY: `item_from_index` ensures that `item` is always a valid pointer and can be (@parse [$dma:expr] [$($proj:tt)*] [[$index:expr] $($rest:tt)*]) => {
// dereferenced. The compiler also further validates the expression on whether `field` $crate::dma_write!(@parse [$dma] [$($proj)* [$index]] [$($rest)*])
// is a member of `item` when expanded by the macro. };
unsafe { ($dma:expr, $($rest:tt)*) => {
let ptr_field = ::core::ptr::addr_of_mut!((*item) $(.$field)*); $crate::dma_write!(@parse [$dma] [] [$($rest)*])
$crate::dma::CoherentAllocation::field_write(&$dma, ptr_field, $val)
}
::core::result::Result::Ok(())
})()
}; };
} }

View File

@@ -20,6 +20,7 @@
#![feature(generic_nonzero)] #![feature(generic_nonzero)]
#![feature(inline_const)] #![feature(inline_const)]
#![feature(pointer_is_aligned)] #![feature(pointer_is_aligned)]
#![feature(slice_ptr_len)]
// //
// Stable since Rust 1.80.0. // Stable since Rust 1.80.0.
#![feature(slice_flatten)] #![feature(slice_flatten)]
@@ -37,6 +38,9 @@
#![feature(const_ptr_write)] #![feature(const_ptr_write)]
#![feature(const_refs_to_cell)] #![feature(const_refs_to_cell)]
// //
// Stable since Rust 1.84.0.
#![feature(strict_provenance)]
//
// Expected to become stable. // Expected to become stable.
#![feature(arbitrary_self_types)] #![feature(arbitrary_self_types)]
// //

View File

@@ -2,7 +2,13 @@
//! Types and functions to work with pointers and addresses. //! Types and functions to work with pointers and addresses.
use core::mem::align_of; pub mod projection;
pub use crate::project_pointer as project;
use core::mem::{
align_of,
size_of, //
};
use core::num::NonZero; use core::num::NonZero;
/// Type representing an alignment, which is always a power of two. /// Type representing an alignment, which is always a power of two.
@@ -225,3 +231,25 @@ macro_rules! impl_alignable_uint {
} }
impl_alignable_uint!(u8, u16, u32, u64, usize); impl_alignable_uint!(u8, u16, u32, u64, usize);
/// Trait to represent compile-time known size information.
///
/// This is a generalization of [`size_of`] that works for dynamically sized types.
pub trait KnownSize {
/// Get the size of an object of this type in bytes, with the metadata of the given pointer.
fn size(p: *const Self) -> usize;
}
impl<T> KnownSize for T {
#[inline(always)]
fn size(_: *const Self) -> usize {
size_of::<T>()
}
}
impl<T> KnownSize for [T] {
#[inline(always)]
fn size(p: *const Self) -> usize {
p.len() * size_of::<T>()
}
}

View File

@@ -0,0 +1,305 @@
// SPDX-License-Identifier: GPL-2.0
//! Infrastructure for handling projections.
use core::{
mem::MaybeUninit,
ops::Deref, //
};
use crate::prelude::*;
/// Error raised when a projection is attempted on an array or slice out of bounds.
pub struct OutOfBound;
impl From<OutOfBound> for Error {
#[inline(always)]
fn from(_: OutOfBound) -> Self {
ERANGE
}
}
/// A helper trait to perform index projection.
///
/// This is similar to [`core::slice::SliceIndex`], but operates on raw pointers safely and
/// fallibly.
///
/// # Safety
///
/// The implementation of `index` and `get` (if [`Some`] is returned) must ensure that, if provided
/// input pointer `slice` and returned pointer `output`, then:
/// - `output` has the same provenance as `slice`;
/// - `output.byte_offset_from(slice)` is between 0 to
/// `KnownSize::size(slice) - KnownSize::size(output)`.
///
/// This means that if the input pointer is valid, then pointer returned by `get` or `index` is
/// also valid.
#[diagnostic::on_unimplemented(message = "`{Self}` cannot be used to index `{T}`")]
#[doc(hidden)]
pub unsafe trait ProjectIndex<T: ?Sized>: Sized {
type Output: ?Sized;
/// Returns an index-projected pointer, if in bounds.
fn get(self, slice: *mut T) -> Option<*mut Self::Output>;
/// Returns an index-projected pointer; fail the build if it cannot be proved to be in bounds.
#[inline(always)]
fn index(self, slice: *mut T) -> *mut Self::Output {
Self::get(self, slice).unwrap_or_else(|| build_error!())
}
}
// Forward array impl to slice impl.
//
// SAFETY: Safety requirement guaranteed by the forwarded impl.
unsafe impl<T, I, const N: usize> ProjectIndex<[T; N]> for I
where
I: ProjectIndex<[T]>,
{
type Output = <I as ProjectIndex<[T]>>::Output;
#[inline(always)]
fn get(self, slice: *mut [T; N]) -> Option<*mut Self::Output> {
<I as ProjectIndex<[T]>>::get(self, slice)
}
#[inline(always)]
fn index(self, slice: *mut [T; N]) -> *mut Self::Output {
<I as ProjectIndex<[T]>>::index(self, slice)
}
}
// SAFETY: `get`-returned pointer has the same provenance as `slice` and the offset is checked to
// not exceed the required bound.
unsafe impl<T> ProjectIndex<[T]> for usize {
type Output = T;
#[inline(always)]
fn get(self, slice: *mut [T]) -> Option<*mut T> {
if self >= slice.len() {
None
} else {
Some(slice.cast::<T>().wrapping_add(self))
}
}
}
// SAFETY: `get`-returned pointer has the same provenance as `slice` and the offset is checked to
// not exceed the required bound.
unsafe impl<T> ProjectIndex<[T]> for core::ops::Range<usize> {
type Output = [T];
#[inline(always)]
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
let new_len = self.end.checked_sub(self.start)?;
if self.end > slice.len() {
return None;
}
Some(core::ptr::slice_from_raw_parts_mut(
slice.cast::<T>().wrapping_add(self.start),
new_len,
))
}
}
// SAFETY: Safety requirement guaranteed by the forwarded impl.
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeTo<usize> {
type Output = [T];
#[inline(always)]
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
(0..self.end).get(slice)
}
}
// SAFETY: Safety requirement guaranteed by the forwarded impl.
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFrom<usize> {
type Output = [T];
#[inline(always)]
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
(self.start..slice.len()).get(slice)
}
}
// SAFETY: `get` returned the pointer as is, so it always has the same provenance and offset of 0.
unsafe impl<T> ProjectIndex<[T]> for core::ops::RangeFull {
type Output = [T];
#[inline(always)]
fn get(self, slice: *mut [T]) -> Option<*mut [T]> {
Some(slice)
}
}
/// A helper trait to perform field projection.
///
/// This trait has a `DEREF` generic parameter so it can be implemented twice for types that
/// implement [`Deref`]. This will cause an ambiguity error and thus block [`Deref`] types being
/// used as base of projection, as they can inject unsoundness. Users therefore must not specify
/// `DEREF` and should always leave it to be inferred.
///
/// # Safety
///
/// `proj` may only invoke `f` with a valid allocation, as the documentation of [`Self::proj`]
/// describes.
#[doc(hidden)]
pub unsafe trait ProjectField<const DEREF: bool> {
/// Project a pointer to a type to a pointer of a field.
///
/// `f` may only be invoked with a valid allocation so it can safely obtain raw pointers to
/// fields using `&raw mut`.
///
/// This is needed because `base` might not point to a valid allocation, while `&raw mut`
/// requires pointers to be in bounds of a valid allocation.
///
/// # Safety
///
/// `f` must return a pointer in bounds of the provided pointer.
unsafe fn proj<F>(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F) -> *mut F;
}
// NOTE: in theory, this API should work for `T: ?Sized` and `F: ?Sized`, too. However, we cannot
// currently support that as we need to obtain a valid allocation that `&raw const` can operate on.
//
// SAFETY: `proj` invokes `f` with valid allocation.
unsafe impl<T> ProjectField<false> for T {
#[inline(always)]
unsafe fn proj<F>(base: *mut Self, f: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
// Create a valid allocation to start projection, as `base` is not necessarily so. The
// memory is never actually used so it will be optimized out, so it should work even for
// very large `T` (`memoffset` crate also relies on this). To be extra certain, we also
// annotate `f` closure with `#[inline(always)]` in the macro.
let mut place = MaybeUninit::uninit();
let place_base = place.as_mut_ptr();
let field = f(place_base);
// SAFETY: `field` is in bounds from `base` per safety requirement.
let offset = unsafe { field.byte_offset_from(place_base) };
// Use `wrapping_byte_offset` as `base` does not need to be of valid allocation.
base.wrapping_byte_offset(offset).cast()
}
}
// SAFETY: Vacuously satisfied.
unsafe impl<T: Deref> ProjectField<true> for T {
#[inline(always)]
unsafe fn proj<F>(_: *mut Self, _: impl FnOnce(*mut Self) -> *mut F) -> *mut F {
build_error!("this function is a guard against `Deref` impl and is never invoked");
}
}
/// Create a projection from a raw pointer.
///
/// The projected pointer is within the memory region marked by the input pointer. There is no
/// requirement that the input raw pointer needs to be valid, so this macro may be used for
/// projecting pointers outside normal address space, e.g. I/O pointers. However, if the input
/// pointer is valid, the projected pointer is also valid.
///
/// Supported projections include field projections and index projections.
/// It is not allowed to project into types that implement custom [`Deref`] or
/// [`Index`](core::ops::Index).
///
/// The macro has basic syntax of `kernel::ptr::project!(ptr, projection)`, where `ptr` is an
/// expression that evaluates to a raw pointer which serves as the base of projection. `projection`
/// can be a projection expression of form `.field` (normally identifier, or numeral in case of
/// tuple structs) or of form `[index]`.
///
/// If a mutable pointer is needed, the macro input can be prefixed with the `mut` keyword, i.e.
/// `kernel::ptr::project!(mut ptr, projection)`. By default, a const pointer is created.
///
/// `ptr::project!` macro can perform both fallible indexing and build-time checked indexing.
/// `[index]` form performs build-time bounds checking; if compiler fails to prove `[index]` is in
/// bounds, compilation will fail. `[index]?` can be used to perform runtime bounds checking;
/// `OutOfBound` error is raised via `?` if the index is out of bounds.
///
/// # Examples
///
/// Field projections are performed with `.field_name`:
///
/// ```
/// struct MyStruct { field: u32, }
/// let ptr: *const MyStruct = core::ptr::dangling();
/// let field_ptr: *const u32 = kernel::ptr::project!(ptr, .field);
///
/// struct MyTupleStruct(u32, u32);
///
/// fn proj(ptr: *const MyTupleStruct) {
/// let field_ptr: *const u32 = kernel::ptr::project!(ptr, .1);
/// }
/// ```
///
/// Index projections are performed with `[index]`:
///
/// ```
/// fn proj(ptr: *const [u8; 32]) -> Result {
/// let field_ptr: *const u8 = kernel::ptr::project!(ptr, [1]);
/// // The following invocation, if uncommented, would fail the build.
/// //
/// // kernel::ptr::project!(ptr, [128]);
///
/// // This will raise an `OutOfBound` error (which is convertible to `ERANGE`).
/// kernel::ptr::project!(ptr, [128]?);
/// Ok(())
/// }
/// ```
///
/// If you need to match on the error instead of propagate, put the invocation inside a closure:
///
/// ```
/// let ptr: *const [u8; 32] = core::ptr::dangling();
/// let field_ptr: Result<*const u8> = (|| -> Result<_> {
/// Ok(kernel::ptr::project!(ptr, [128]?))
/// })();
/// assert!(field_ptr.is_err());
/// ```
///
/// For mutable pointers, put `mut` as the first token in macro invocation.
///
/// ```
/// let ptr: *mut [(u8, u16); 32] = core::ptr::dangling_mut();
/// let field_ptr: *mut u16 = kernel::ptr::project!(mut ptr, [1].1);
/// ```
#[macro_export]
macro_rules! project_pointer {
(@gen $ptr:ident, ) => {};
// Field projection. `$field` needs to be `tt` to support tuple index like `.0`.
(@gen $ptr:ident, .$field:tt $($rest:tt)*) => {
// SAFETY: The provided closure always returns an in-bounds pointer.
let $ptr = unsafe {
$crate::ptr::projection::ProjectField::proj($ptr, #[inline(always)] |ptr| {
// Check unaligned field. Not all users (e.g. DMA) can handle unaligned
// projections.
if false {
let _ = &(*ptr).$field;
}
// SAFETY: `$field` is in bounds, and no implicit `Deref` is possible (if the
// type implements `Deref`, Rust cannot infer the generic parameter `DEREF`).
&raw mut (*ptr).$field
})
};
$crate::ptr::project!(@gen $ptr, $($rest)*)
};
// Fallible index projection.
(@gen $ptr:ident, [$index:expr]? $($rest:tt)*) => {
let $ptr = $crate::ptr::projection::ProjectIndex::get($index, $ptr)
.ok_or($crate::ptr::projection::OutOfBound)?;
$crate::ptr::project!(@gen $ptr, $($rest)*)
};
// Build-time checked index projection.
(@gen $ptr:ident, [$index:expr] $($rest:tt)*) => {
let $ptr = $crate::ptr::projection::ProjectIndex::index($index, $ptr);
$crate::ptr::project!(@gen $ptr, $($rest)*)
};
(mut $ptr:expr, $($proj:tt)*) => {{
let ptr: *mut _ = $ptr;
$crate::ptr::project!(@gen ptr, $($proj)*);
ptr
}};
($ptr:expr, $($proj:tt)*) => {{
let ptr = <*const _>::cast_mut($ptr);
// We currently always project using mutable pointer, as it is not decided whether `&raw
// const` allows the resulting pointer to be mutated (see documentation of `addr_of!`).
$crate::ptr::project!(@gen ptr, $($proj)*);
ptr.cast_const()
}};
}

View File

@@ -68,7 +68,7 @@ impl pci::Driver for DmaSampleDriver {
CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?;
for (i, value) in TEST_VALUES.into_iter().enumerate() { for (i, value) in TEST_VALUES.into_iter().enumerate() {
kernel::dma_write!(ca[i] = MyStruct::new(value.0, value.1))?; kernel::dma_write!(ca, [i]?, MyStruct::new(value.0, value.1));
} }
let size = 4 * page::PAGE_SIZE; let size = 4 * page::PAGE_SIZE;
@@ -85,24 +85,26 @@ impl pci::Driver for DmaSampleDriver {
} }
} }
impl DmaSampleDriver {
fn check_dma(&self) -> Result {
for (i, value) in TEST_VALUES.into_iter().enumerate() {
let val0 = kernel::dma_read!(self.ca, [i]?.h);
let val1 = kernel::dma_read!(self.ca, [i]?.b);
assert_eq!(val0, value.0);
assert_eq!(val1, value.1);
}
Ok(())
}
}
#[pinned_drop] #[pinned_drop]
impl PinnedDrop for DmaSampleDriver { impl PinnedDrop for DmaSampleDriver {
fn drop(self: Pin<&mut Self>) { fn drop(self: Pin<&mut Self>) {
dev_info!(self.pdev, "Unload DMA test driver.\n"); dev_info!(self.pdev, "Unload DMA test driver.\n");
for (i, value) in TEST_VALUES.into_iter().enumerate() { assert!(self.check_dma().is_ok());
let val0 = kernel::dma_read!(self.ca[i].h);
let val1 = kernel::dma_read!(self.ca[i].b);
assert!(val0.is_ok());
assert!(val1.is_ok());
if let Ok(val0) = val0 {
assert_eq!(val0, value.0);
}
if let Ok(val1) = val1 {
assert_eq!(val1, value.1);
}
}
for (i, entry) in self.sgt.iter().enumerate() { for (i, entry) in self.sgt.iter().enumerate() {
dev_info!( dev_info!(

View File

@@ -310,16 +310,18 @@ $(obj)/%.lst: $(obj)/%.c FORCE
# The features in this list are the ones allowed for non-`rust/` code. # The features in this list are the ones allowed for non-`rust/` code.
# #
# - Stable since Rust 1.79.0: `feature(slice_ptr_len)`.
# - Stable since Rust 1.81.0: `feature(lint_reasons)`. # - Stable since Rust 1.81.0: `feature(lint_reasons)`.
# - Stable since Rust 1.82.0: `feature(asm_const)`, # - Stable since Rust 1.82.0: `feature(asm_const)`,
# `feature(offset_of_nested)`, `feature(raw_ref_op)`. # `feature(offset_of_nested)`, `feature(raw_ref_op)`.
# - Stable since Rust 1.84.0: `feature(strict_provenance)`.
# - Stable since Rust 1.87.0: `feature(asm_goto)`. # - Stable since Rust 1.87.0: `feature(asm_goto)`.
# - Expected to become stable: `feature(arbitrary_self_types)`. # - Expected to become stable: `feature(arbitrary_self_types)`.
# - To be determined: `feature(used_with_arg)`. # - To be determined: `feature(used_with_arg)`.
# #
# Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
# the unstable features in use. # the unstable features in use.
rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,offset_of_nested,raw_ref_op,used_with_arg rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,offset_of_nested,raw_ref_op,slice_ptr_len,strict_provenance,used_with_arg
# `--out-dir` is required to avoid temporaries being created by `rustc` in the # `--out-dir` is required to avoid temporaries being created by `rustc` in the
# current working directory, which may be not accessible in the out-of-tree # current working directory, which may be not accessible in the out-of-tree