@@ -8,6 +8,7 @@
#include <linux/firmware/qcom/qcom_scm.h>
#include <linux/pm_opp.h>
#include <linux/nvmem-consumer.h>
+#include <linux/pm_domain.h>
#include <linux/slab.h>
#include "msm_gem.h"
#include "msm_mmu.h"
@@ -1053,6 +1054,13 @@ static void a5xx_destroy(struct msm_gpu *gpu)
}
adreno_gpu_cleanup(adreno_gpu);
+
+ if (a5xx_gpu->mx_link)
+ device_link_del(a5xx_gpu->mx_link);
+
+ if (a5xx_gpu->gxpd)
+ dev_pm_domain_detach(a5xx_gpu->gxpd, true);
+
kfree(a5xx_gpu);
}
@@ -1339,8 +1347,15 @@ static void a5xx_dump(struct msm_gpu *gpu)
static int a5xx_pm_resume(struct msm_gpu *gpu)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+ struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
int ret;
+ if (a5xx_gpu->gxpd) {
+ ret = pm_runtime_resume_and_get(a5xx_gpu->gxpd);
+ if (ret < 0)
+ return ret;
+ }
+
/* Turn on the core power */
ret = msm_gpu_pm_resume(gpu);
if (ret)
@@ -1414,6 +1429,9 @@ static int a5xx_pm_suspend(struct msm_gpu *gpu)
if (ret)
return ret;
+ if (a5xx_gpu->gxpd)
+ pm_runtime_put(a5xx_gpu->gxpd);
+
if (a5xx_gpu->has_whereami)
for (i = 0; i < gpu->nr_rings; i++)
a5xx_gpu->shadow[i] = 0;
@@ -1762,6 +1780,40 @@ struct msm_gpu *a5xx_gpu_init(struct drm_device *dev)
a5xx_gpu->lm_leakage = 0x4E001A;
+ /*
+ * If the device has several power domain (gx and mx), none are attached by the core.
+ */
+ if (!pdev->dev.pm_domain) {
+ struct device **opp_virt_dev;
+ struct device *pd;
+
+ /* FIXME: add cpr once it is supported */
+ static const char *genpd_names[] = { "mx", NULL };
+
+ pd = dev_pm_domain_attach_by_name(&pdev->dev, "gx");
+ if (IS_ERR(pd))
+ return ERR_CAST(pd);
+
+ /* GX is required for GPU to function */
+ if (pd == NULL)
+ return ERR_PTR(-EINVAL);
+
+ a5xx_gpu->gxpd = pd;
+
+ ret = devm_pm_opp_attach_genpd(&pdev->dev, genpd_names, &opp_virt_dev);
+ if (ret) {
+ dev_pm_domain_detach(a5xx_gpu->gxpd, true);
+ return ERR_PTR(ret);
+ }
+
+ a5xx_gpu->mx_link = device_link_add(&pdev->dev, opp_virt_dev[0],
+ DL_FLAG_RPM_ACTIVE |
+ DL_FLAG_PM_RUNTIME |
+ DL_FLAG_STATELESS);
+ if (!a5xx_gpu->mx_link)
+ return ERR_PTR(-ENODEV);
+ }
+
check_speed_bin(&pdev->dev);
nr_rings = 4;
@@ -44,6 +44,9 @@ struct a5xx_gpu {
/* True if the microcode supports the WHERE_AM_I opcode */
bool has_whereami;
+
+ struct device *gxpd;
+ struct device_link *mx_link;
};
#define to_a5xx_gpu(x) container_of(x, struct a5xx_gpu, base)
For some a5xx Adrenos we have to specify both GX and MX power domains. GX is used to power up the GPU clocks and logic. MX is used for scaling voltage of memory cells. In case the DT specifies several (GX, MX) power domains, none will be bound by the core. We have to manage GX manually. Also make sure that the MX domain is resumed and scaled to the proper performance state following the desired frequency. Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@linaro.org> --- drivers/gpu/drm/msm/adreno/a5xx_gpu.c | 52 +++++++++++++++++++++++++++ drivers/gpu/drm/msm/adreno/a5xx_gpu.h | 3 ++ 2 files changed, 55 insertions(+)