mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
Merge tag 'drm-fixes-2026-03-14' of https://gitlab.freedesktop.org/drm/kernel
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:
@@ -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:
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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#
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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(),\
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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),\
|
||||||
|
|||||||
@@ -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) \
|
||||||
|
|||||||
@@ -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),\
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
crtc_state->psr2_su_area.y1 -= crtc_state->psr2_su_area.y1 % y_alignment;
|
if (crtc_state->psr2_su_area.y1 % y_alignment) {
|
||||||
if (crtc_state->psr2_su_area.y2 % y_alignment)
|
crtc_state->psr2_su_area.y1 -= crtc_state->psr2_su_area.y1 % 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 {
|
||||||
* Adjust su area to cover cursor fully as necessary (early
|
bool cursor_in_su_area;
|
||||||
* transport). This needs to be done after
|
|
||||||
* drm_atomic_add_affected_planes to ensure visible 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_pipe_alignment(crtc_state);
|
/*
|
||||||
|
* Adjust su area to cover cursor fully as necessary
|
||||||
|
* (early transport). This needs to be done after
|
||||||
|
* drm_atomic_add_affected_planes to ensure visible
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|||||||
@@ -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) ?
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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++) {
|
||||||
|
|||||||
@@ -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]] =
|
||||||
|
|||||||
@@ -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;
|
||||||
dsc->bits_per_component * 3);
|
|
||||||
|
|
||||||
int new_htotal = mode->htotal - mode->hdisplay + new_hdisplay;
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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));
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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))?;
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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(())
|
|
||||||
})()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)]
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -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>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
305
rust/kernel/ptr/projection.rs
Normal file
305
rust/kernel/ptr/projection.rs
Normal 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()
|
||||||
|
}};
|
||||||
|
}
|
||||||
@@ -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!(
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user