On 12/31/2024 3:18 PM, neil.armstrong@xxxxxxxxxx wrote: > On 30/12/2024 22:11, Akhil P Oommen wrote: >> ACD a.k.a Adaptive Clock Distribution is a feature which helps to reduce >> the power consumption. In some chipsets, it is also a requirement to >> support higher GPU frequencies. This patch adds support for GPU ACD by >> sending necessary data to GMU and AOSS. The feature support for the >> chipset is detected based on devicetree data. >> >> Signed-off-by: Akhil P Oommen <quic_akhilpo@xxxxxxxxxxx> >> --- >> drivers/gpu/drm/msm/adreno/a6xx_gmu.c | 84 +++++++++++++++++++++++++ >> +++++----- >> drivers/gpu/drm/msm/adreno/a6xx_gmu.h | 1 + >> drivers/gpu/drm/msm/adreno/a6xx_hfi.c | 36 +++++++++++++++ >> drivers/gpu/drm/msm/adreno/a6xx_hfi.h | 21 +++++++++ >> 4 files changed, 132 insertions(+), 10 deletions(-) >> >> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c b/drivers/gpu/drm/ >> msm/adreno/a6xx_gmu.c >> index 14db7376c712..2689e79aefa5 100644 >> --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.c >> +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.c >> @@ -1021,14 +1021,6 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) >> gmu->hung = false; >> - /* Notify AOSS about the ACD state (unimplemented for now => >> disable it) */ >> - if (!IS_ERR(gmu->qmp)) { >> - ret = qmp_send(gmu->qmp, "{class: gpu, res: acd, val: %d}", >> - 0 /* Hardcode ACD to be disabled for now */); >> - if (ret) >> - dev_err(gmu->dev, "failed to send GPU ACD state\n"); >> - } >> - >> /* Turn on the resources */ >> pm_runtime_get_sync(gmu->dev); >> @@ -1476,6 +1468,68 @@ static int a6xx_gmu_pwrlevels_probe(struct >> a6xx_gmu *gmu) >> return a6xx_gmu_rpmh_votes_init(gmu); >> } >> +static int a6xx_gmu_acd_probe(struct a6xx_gmu *gmu) >> +{ >> + struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu); >> + struct a6xx_hfi_acd_table *cmd = &gmu->acd_table; >> + struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; >> + struct msm_gpu *gpu = &adreno_gpu->base; >> + int ret, i, cmd_idx = 0; >> + >> + cmd->version = 1; >> + cmd->stride = 1; >> + cmd->enable_by_level = 0; >> + >> + /* Skip freq = 0 and parse acd-level for rest of the OPPs */ >> + for (i = 1; i < gmu->nr_gpu_freqs; i++) { >> + struct dev_pm_opp *opp; >> + struct device_node *np; >> + unsigned long freq; >> + u32 val; >> + >> + freq = gmu->gpu_freqs[i]; >> + opp = dev_pm_opp_find_freq_exact(&gpu->pdev->dev, freq, true); >> + np = dev_pm_opp_get_of_node(opp); >> + >> + ret = of_property_read_u32(np, "qcom,opp-acd-level", &val); >> + of_node_put(np); >> + dev_pm_opp_put(opp); >> + if (ret == -EINVAL) >> + continue; >> + else if (ret) { >> + DRM_DEV_ERROR(gmu->dev, "Unable to read acd level for >> freq %lu\n", freq); >> + return ret; >> + } >> + >> + cmd->enable_by_level |= BIT(i); >> + cmd->data[cmd_idx++] = val; >> + } >> + >> + cmd->num_levels = cmd_idx; >> + >> + /* It is a problem if qmp node is unavailable when ACD is >> required */ >> + if (cmd->enable_by_level && IS_ERR_OR_NULL(gmu->qmp)) { >> + DRM_DEV_ERROR(gmu->dev, "Unable to send ACD state to AOSS\n"); >> + return -EINVAL; >> + } >> + >> + /* Otherwise, nothing to do if qmp is unavailable */ >> + if (IS_ERR_OR_NULL(gmu->qmp)) >> + return 0; >> + >> + /* >> + * Notify AOSS about the ACD state. AOSS is supposed to assume >> that ACD is disabled on >> + * system reset. So it is harmless if we couldn't notify 'OFF' state >> + */ >> + ret = qmp_send(gmu->qmp, "{class: gpu, res: acd, val: %d}", !! >> cmd->enable_by_level); >> + if (ret && cmd->enable_by_level) { >> + DRM_DEV_ERROR(gmu->dev, "Failed to send ACD state to AOSS\n"); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> static int a6xx_gmu_clocks_probe(struct a6xx_gmu *gmu) >> { >> int ret = devm_clk_bulk_get_all(gmu->dev, &gmu->clocks); >> @@ -1793,7 +1847,7 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, >> struct device_node *node) >> gmu->qmp = qmp_get(gmu->dev); >> if (IS_ERR(gmu->qmp) && adreno_is_a7xx(adreno_gpu)) { >> ret = PTR_ERR(gmu->qmp); >> - goto remove_device_link; >> + goto detach_gxpd; >> } >> init_completion(&gmu->pd_gate); >> @@ -1809,6 +1863,10 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, >> struct device_node *node) >> /* Get the power levels for the GMU and GPU */ >> a6xx_gmu_pwrlevels_probe(gmu); >> + ret = a6xx_gmu_acd_probe(gmu); >> + if (ret) >> + goto detach_gxpd; >> + >> /* Set up the HFI queues */ >> a6xx_hfi_init(gmu); >> @@ -1819,7 +1877,13 @@ int a6xx_gmu_init(struct a6xx_gpu *a6xx_gpu, >> struct device_node *node) >> return 0; >> -remove_device_link: >> +detach_gxpd: >> + if (!IS_ERR_OR_NULL(gmu->gxpd)) >> + dev_pm_domain_detach(gmu->gxpd, false); >> + >> + if (!IS_ERR_OR_NULL(gmu->qmp)) >> + qmp_put(gmu->qmp); >> + >> device_link_del(link); >> detach_cxpd: >> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h b/drivers/gpu/drm/ >> msm/adreno/a6xx_gmu.h >> index b4a79f88ccf4..87d225b08e9b 100644 >> --- a/drivers/gpu/drm/msm/adreno/a6xx_gmu.h >> +++ b/drivers/gpu/drm/msm/adreno/a6xx_gmu.h >> @@ -81,6 +81,7 @@ struct a6xx_gmu { >> int nr_gpu_freqs; >> unsigned long gpu_freqs[16]; >> u32 gx_arc_votes[16]; >> + struct a6xx_hfi_acd_table acd_table; >> int nr_gmu_freqs; >> unsigned long gmu_freqs[4]; >> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c b/drivers/gpu/drm/ >> msm/adreno/a6xx_hfi.c >> index cb8844ed46b2..3c183c1c6266 100644 >> --- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.c >> +++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.c >> @@ -702,6 +702,38 @@ static int a6xx_hfi_send_bw_table(struct a6xx_gmu >> *gmu) >> NULL, 0); >> } >> +#define HFI_FEATURE_ACD 12 >> + >> +static int a6xx_hfi_enable_acd(struct a6xx_gmu *gmu) >> +{ >> + struct a6xx_hfi_acd_table *acd_table = &gmu->acd_table; >> + struct a6xx_hfi_msg_feature_ctrl msg = { >> + .feature = HFI_FEATURE_ACD, >> + .enable = 1, >> + .data = 0, >> + }; >> + int ret; >> + >> + if (!acd_table->enable_by_level) >> + return 0; >> + >> + /* Enable ACD feature at GMU */ >> + ret = a6xx_hfi_send_msg(gmu, HFI_H2F_FEATURE_CTRL, &msg, >> sizeof(msg), NULL, 0); >> + if (ret) { >> + DRM_DEV_ERROR(gmu->dev, "Unable to enable ACD (%d)\n", ret); >> + return ret; >> + } >> + >> + /* Send ACD table to GMU */ >> + ret = a6xx_hfi_send_msg(gmu, HFI_H2F_MSG_ACD, &msg, sizeof(msg), >> NULL, 0); > > > Seems you still don't send the proper acd_table Aah! I forgot this one. Usually the end-to-end validation is done by HW folks during Bringups. But I think I can do some additional validation on the gmu fw side. Will check that and post Rev-4. -Akhil. > > Neil > >> + if (ret) { >> + DRM_DEV_ERROR(gmu->dev, "Unable to ACD table (%d)\n", ret); >> + return ret; >> + } >> + >> + return 0; >> +} >> + >> static int a6xx_hfi_send_test(struct a6xx_gmu *gmu) >> { >> struct a6xx_hfi_msg_test msg = { 0 }; >> @@ -799,6 +831,10 @@ int a6xx_hfi_start(struct a6xx_gmu *gmu, int >> boot_state) >> if (ret) >> return ret; >> + ret = a6xx_hfi_enable_acd(gmu); >> + if (ret) >> + return ret; >> + >> ret = a6xx_hfi_send_core_fw_start(gmu); >> if (ret) >> return ret; >> diff --git a/drivers/gpu/drm/msm/adreno/a6xx_hfi.h b/drivers/gpu/drm/ >> msm/adreno/a6xx_hfi.h >> index 528110169398..51864c8ad0e6 100644 >> --- a/drivers/gpu/drm/msm/adreno/a6xx_hfi.h >> +++ b/drivers/gpu/drm/msm/adreno/a6xx_hfi.h >> @@ -151,12 +151,33 @@ struct a6xx_hfi_msg_test { >> u32 header; >> }; >> +#define HFI_H2F_MSG_ACD 7 >> +#define MAX_ACD_STRIDE 2 >> + >> +struct a6xx_hfi_acd_table { >> + u32 header; >> + u32 version; >> + u32 enable_by_level; >> + u32 stride; >> + u32 num_levels; >> + u32 data[16 * MAX_ACD_STRIDE]; >> +}; >> + >> #define HFI_H2F_MSG_START 10 >> struct a6xx_hfi_msg_start { >> u32 header; >> }; >> +#define HFI_H2F_FEATURE_CTRL 11 >> + >> +struct a6xx_hfi_msg_feature_ctrl { >> + u32 header; >> + u32 feature; >> + u32 enable; >> + u32 data; >> +}; >> + >> #define HFI_H2F_MSG_CORE_FW_START 14 >> struct a6xx_hfi_msg_core_fw_start { >> >