Commit 70b5f09e authored by Dave Airlie's avatar Dave Airlie

Merge tag 'drm-msm-next-2019-04-21' of https://gitlab.freedesktop.org/drm/msm into drm-next

This time around it is a bunch of cleanup and fixes, expanding gpu
"zap" shader support (so we can take the GPU out of secure mode on
boot) to a6xx, and small UABI extension to support robustness (see
mesa MR 673).
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
From: Rob Clark <robdclark@gmail.com>
Link: https://patchwork.freedesktop.org/patch/msgid/CAF6AEGsHwsEfi4y2LYKSqeqDEYvffwVgKhiP8jHcHpxp13J5LQ@mail.gmail.com
parents 42f1a013 b02872df
...@@ -24,7 +24,10 @@ Required properties: ...@@ -24,7 +24,10 @@ Required properties:
* "cxo" * "cxo"
* "axi" * "axi"
* "mnoc" * "mnoc"
- power-domains: should be <&clock_gpucc GPU_CX_GDSC> - power-domains: should be:
<&clock_gpucc GPU_CX_GDSC>
<&clock_gpucc GPU_GX_GDSC>
- power-domain-names: Matching names for the power domains
- iommus: phandle to the adreno iommu - iommus: phandle to the adreno iommu
- operating-points-v2: phandle to the OPP operating points - operating-points-v2: phandle to the OPP operating points
...@@ -51,7 +54,10 @@ Example: ...@@ -51,7 +54,10 @@ Example:
<&gcc GCC_GPU_MEMNOC_GFX_CLK>; <&gcc GCC_GPU_MEMNOC_GFX_CLK>;
clock-names = "gmu", "cxo", "axi", "memnoc"; clock-names = "gmu", "cxo", "axi", "memnoc";
power-domains = <&gpucc GPU_CX_GDSC>; power-domains = <&gpucc GPU_CX_GDSC>,
<&gpucc GPU_GX_GDSC>;
power-domain-names = "cx", "gx";
iommus = <&adreno_smmu 5>; iommus = <&adreno_smmu 5>;
operating-points-v2 = <&gmu_opp_table>; operating-points-v2 = <&gmu_opp_table>;
......
...@@ -22,9 +22,14 @@ Required properties: ...@@ -22,9 +22,14 @@ Required properties:
- qcom,adreno-630.2 - qcom,adreno-630.2
- iommus: optional phandle to an adreno iommu instance - iommus: optional phandle to an adreno iommu instance
- operating-points-v2: optional phandle to the OPP operating points - operating-points-v2: optional phandle to the OPP operating points
- interconnects: optional phandle to an interconnect provider. See
../interconnect/interconnect.txt for details.
- qcom,gmu: For GMU attached devices a phandle to the GMU device that will - qcom,gmu: For GMU attached devices a phandle to the GMU device that will
control the power for the GPU. Applicable targets: control the power for the GPU. Applicable targets:
- qcom,adreno-630.2 - qcom,adreno-630.2
- zap-shader: For a5xx and a6xx devices this node contains a memory-region that
points to reserved memory to store the zap shader that can be used to help
bring the GPU out of secure mode.
Example 3xx/4xx/a5xx: Example 3xx/4xx/a5xx:
...@@ -70,6 +75,12 @@ Example a6xx (with GMU): ...@@ -70,6 +75,12 @@ Example a6xx (with GMU):
operating-points-v2 = <&gpu_opp_table>; operating-points-v2 = <&gpu_opp_table>;
interconnects = <&rsc_hlos MASTER_GFX3D &rsc_hlos SLAVE_EBI1>;
qcom,gmu = <&gmu>; qcom,gmu = <&gmu>;
zap-shader {
memory-region = <&zap_shader_region>;
};
}; };
}; };
...@@ -21,6 +21,11 @@ config DRM_MSM ...@@ -21,6 +21,11 @@ config DRM_MSM
help help
DRM/KMS driver for MSM/snapdragon. DRM/KMS driver for MSM/snapdragon.
config DRM_MSM_GPU_STATE
bool
depends on DRM_MSM && (DEBUG_FS || DEV_COREDUMP)
default y
config DRM_MSM_REGISTER_LOGGING config DRM_MSM_REGISTER_LOGGING
bool "MSM DRM register logging" bool "MSM DRM register logging"
depends on DRM_MSM depends on DRM_MSM
......
...@@ -15,7 +15,6 @@ msm-y := \ ...@@ -15,7 +15,6 @@ msm-y := \
adreno/a6xx_gpu.o \ adreno/a6xx_gpu.o \
adreno/a6xx_gmu.o \ adreno/a6xx_gmu.o \
adreno/a6xx_hfi.o \ adreno/a6xx_hfi.o \
adreno/a6xx_gpu_state.o \
hdmi/hdmi.o \ hdmi/hdmi.o \
hdmi/hdmi_audio.o \ hdmi/hdmi_audio.o \
hdmi/hdmi_bridge.o \ hdmi/hdmi_bridge.o \
...@@ -96,6 +95,8 @@ msm-y := \ ...@@ -96,6 +95,8 @@ msm-y := \
msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o
msm-$(CONFIG_DRM_MSM_GPU_STATE) += adreno/a6xx_gpu_state.o
msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
msm-$(CONFIG_COMMON_CLK) += disp/mdp4/mdp4_lvds_pll.o msm-$(CONFIG_COMMON_CLK) += disp/mdp4/mdp4_lvds_pll.o
msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o
......
...@@ -15,9 +15,6 @@ ...@@ -15,9 +15,6 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/cpumask.h> #include <linux/cpumask.h>
#include <linux/qcom_scm.h> #include <linux/qcom_scm.h>
#include <linux/dma-mapping.h>
#include <linux/of_address.h>
#include <linux/soc/qcom/mdt_loader.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/nvmem-consumer.h> #include <linux/nvmem-consumer.h>
#include <linux/slab.h> #include <linux/slab.h>
...@@ -30,94 +27,6 @@ static void a5xx_dump(struct msm_gpu *gpu); ...@@ -30,94 +27,6 @@ static void a5xx_dump(struct msm_gpu *gpu);
#define GPU_PAS_ID 13 #define GPU_PAS_ID 13
static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname)
{
struct device *dev = &gpu->pdev->dev;
const struct firmware *fw;
struct device_node *np;
struct resource r;
phys_addr_t mem_phys;
ssize_t mem_size;
void *mem_region = NULL;
int ret;
if (!IS_ENABLED(CONFIG_ARCH_QCOM))
return -EINVAL;
np = of_get_child_by_name(dev->of_node, "zap-shader");
if (!np)
return -ENODEV;
np = of_parse_phandle(np, "memory-region", 0);
if (!np)
return -EINVAL;
ret = of_address_to_resource(np, 0, &r);
if (ret)
return ret;
mem_phys = r.start;
mem_size = resource_size(&r);
/* Request the MDT file for the firmware */
fw = adreno_request_fw(to_adreno_gpu(gpu), fwname);
if (IS_ERR(fw)) {
DRM_DEV_ERROR(dev, "Unable to load %s\n", fwname);
return PTR_ERR(fw);
}
/* Figure out how much memory we need */
mem_size = qcom_mdt_get_size(fw);
if (mem_size < 0) {
ret = mem_size;
goto out;
}
/* Allocate memory for the firmware image */
mem_region = memremap(mem_phys, mem_size, MEMREMAP_WC);
if (!mem_region) {
ret = -ENOMEM;
goto out;
}
/*
* Load the rest of the MDT
*
* Note that we could be dealing with two different paths, since
* with upstream linux-firmware it would be in a qcom/ subdir..
* adreno_request_fw() handles this, but qcom_mdt_load() does
* not. But since we've already gotten thru adreno_request_fw()
* we know which of the two cases it is:
*/
if (to_adreno_gpu(gpu)->fwloc == FW_LOCATION_LEGACY) {
ret = qcom_mdt_load(dev, fw, fwname, GPU_PAS_ID,
mem_region, mem_phys, mem_size, NULL);
} else {
char *newname;
newname = kasprintf(GFP_KERNEL, "qcom/%s", fwname);
ret = qcom_mdt_load(dev, fw, newname, GPU_PAS_ID,
mem_region, mem_phys, mem_size, NULL);
kfree(newname);
}
if (ret)
goto out;
/* Send the image to the secure world */
ret = qcom_scm_pas_auth_and_reset(GPU_PAS_ID);
if (ret)
DRM_DEV_ERROR(dev, "Unable to authorize the image\n");
out:
if (mem_region)
memunmap(mem_region);
release_firmware(fw);
return ret;
}
static void a5xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring) static void a5xx_flush(struct msm_gpu *gpu, struct msm_ringbuffer *ring)
{ {
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
...@@ -563,8 +472,6 @@ static int a5xx_zap_shader_resume(struct msm_gpu *gpu) ...@@ -563,8 +472,6 @@ static int a5xx_zap_shader_resume(struct msm_gpu *gpu)
static int a5xx_zap_shader_init(struct msm_gpu *gpu) static int a5xx_zap_shader_init(struct msm_gpu *gpu)
{ {
static bool loaded; static bool loaded;
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct platform_device *pdev = gpu->pdev;
int ret; int ret;
/* /*
...@@ -574,23 +481,9 @@ static int a5xx_zap_shader_init(struct msm_gpu *gpu) ...@@ -574,23 +481,9 @@ static int a5xx_zap_shader_init(struct msm_gpu *gpu)
if (loaded) if (loaded)
return a5xx_zap_shader_resume(gpu); return a5xx_zap_shader_resume(gpu);
/* We need SCM to be able to load the firmware */ ret = adreno_zap_shader_load(gpu, GPU_PAS_ID);
if (!qcom_scm_is_available()) {
DRM_DEV_ERROR(&pdev->dev, "SCM is not available\n");
return -EPROBE_DEFER;
}
/* Each GPU has a target specific zap shader firmware name to use */
if (!adreno_gpu->info->zapfw) {
DRM_DEV_ERROR(&pdev->dev,
"Zap shader firmware file not specified for this target\n");
return -ENODEV;
}
ret = zap_shader_load_mdt(gpu, adreno_gpu->info->zapfw);
loaded = !ret; loaded = !ret;
return ret; return ret;
} }
......
...@@ -3,12 +3,31 @@ ...@@ -3,12 +3,31 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/interconnect.h> #include <linux/interconnect.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <soc/qcom/cmd-db.h> #include <soc/qcom/cmd-db.h>
#include "a6xx_gpu.h" #include "a6xx_gpu.h"
#include "a6xx_gmu.xml.h" #include "a6xx_gmu.xml.h"
static void a6xx_gmu_fault(struct a6xx_gmu *gmu)
{
struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct msm_gpu *gpu = &adreno_gpu->base;
struct drm_device *dev = gpu->dev;
struct msm_drm_private *priv = dev->dev_private;
/* FIXME: add a banner here */
gmu->hung = true;
/* Turn off the hangcheck timer while we are resetting */
del_timer(&gpu->hangcheck_timer);
/* Queue the GPU handler because we need to treat this as a recovery */
queue_work(priv->wq, &gpu->recover_work);
}
static irqreturn_t a6xx_gmu_irq(int irq, void *data) static irqreturn_t a6xx_gmu_irq(int irq, void *data)
{ {
struct a6xx_gmu *gmu = data; struct a6xx_gmu *gmu = data;
...@@ -20,8 +39,7 @@ static irqreturn_t a6xx_gmu_irq(int irq, void *data) ...@@ -20,8 +39,7 @@ static irqreturn_t a6xx_gmu_irq(int irq, void *data)
if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_WDOG_BITE) { if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_WDOG_BITE) {
dev_err_ratelimited(gmu->dev, "GMU watchdog expired\n"); dev_err_ratelimited(gmu->dev, "GMU watchdog expired\n");
/* Temporary until we can recover safely */ a6xx_gmu_fault(gmu);
BUG();
} }
if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_HOST_AHB_BUS_ERROR) if (status & A6XX_GMU_AO_HOST_INTERRUPT_STATUS_HOST_AHB_BUS_ERROR)
...@@ -45,8 +63,7 @@ static irqreturn_t a6xx_hfi_irq(int irq, void *data) ...@@ -45,8 +63,7 @@ static irqreturn_t a6xx_hfi_irq(int irq, void *data)
if (status & A6XX_GMU_GMU2HOST_INTR_INFO_CM3_FAULT) { if (status & A6XX_GMU_GMU2HOST_INTR_INFO_CM3_FAULT) {
dev_err_ratelimited(gmu->dev, "GMU firmware fault\n"); dev_err_ratelimited(gmu->dev, "GMU firmware fault\n");
/* Temporary until we can recover safely */ a6xx_gmu_fault(gmu);
BUG();
} }
return IRQ_HANDLED; return IRQ_HANDLED;
...@@ -165,10 +182,8 @@ static bool a6xx_gmu_check_idle_level(struct a6xx_gmu *gmu) ...@@ -165,10 +182,8 @@ static bool a6xx_gmu_check_idle_level(struct a6xx_gmu *gmu)
} }
/* Wait for the GMU to get to its most idle state */ /* Wait for the GMU to get to its most idle state */
int a6xx_gmu_wait_for_idle(struct a6xx_gpu *a6xx_gpu) int a6xx_gmu_wait_for_idle(struct a6xx_gmu *gmu)
{ {
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
return spin_until(a6xx_gmu_check_idle_level(gmu)); return spin_until(a6xx_gmu_check_idle_level(gmu));
} }
...@@ -567,7 +582,7 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state) ...@@ -567,7 +582,7 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
if (!rpmh_init) { if (!rpmh_init) {
a6xx_gmu_rpmh_init(gmu); a6xx_gmu_rpmh_init(gmu);
rpmh_init = true; rpmh_init = true;
} else if (state != GMU_RESET) { } else {
ret = a6xx_rpmh_start(gmu); ret = a6xx_rpmh_start(gmu);
if (ret) if (ret)
return ret; return ret;
...@@ -633,20 +648,6 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state) ...@@ -633,20 +648,6 @@ static int a6xx_gmu_fw_start(struct a6xx_gmu *gmu, unsigned int state)
A6XX_GMU_AO_HOST_INTERRUPT_STATUS_HOST_AHB_BUS_ERROR | \ A6XX_GMU_AO_HOST_INTERRUPT_STATUS_HOST_AHB_BUS_ERROR | \
A6XX_GMU_AO_HOST_INTERRUPT_STATUS_FENCE_ERR) A6XX_GMU_AO_HOST_INTERRUPT_STATUS_FENCE_ERR)
static void a6xx_gmu_irq_enable(struct a6xx_gmu *gmu)
{
gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_CLR, ~0);
gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR, ~0);
gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_MASK,
~A6XX_GMU_IRQ_MASK);
gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK,
~A6XX_HFI_IRQ_MASK);
enable_irq(gmu->gmu_irq);
enable_irq(gmu->hfi_irq);
}
static void a6xx_gmu_irq_disable(struct a6xx_gmu *gmu) static void a6xx_gmu_irq_disable(struct a6xx_gmu *gmu)
{ {
disable_irq(gmu->gmu_irq); disable_irq(gmu->gmu_irq);
...@@ -656,21 +657,10 @@ static void a6xx_gmu_irq_disable(struct a6xx_gmu *gmu) ...@@ -656,21 +657,10 @@ static void a6xx_gmu_irq_disable(struct a6xx_gmu *gmu)
gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, ~0); gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, ~0);
} }
int a6xx_gmu_reset(struct a6xx_gpu *a6xx_gpu) static void a6xx_gmu_rpmh_off(struct a6xx_gmu *gmu)
{ {
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
int ret;
u32 val; u32 val;
/* Flush all the queues */
a6xx_hfi_stop(gmu);
/* Stop the interrupts */
a6xx_gmu_irq_disable(gmu);
/* Force off SPTP in case the GMU is managing it */
a6xx_sptprac_disable(gmu);
/* Make sure there are no outstanding RPMh votes */ /* Make sure there are no outstanding RPMh votes */
gmu_poll_timeout(gmu, REG_A6XX_RSCC_TCS0_DRV0_STATUS, val, gmu_poll_timeout(gmu, REG_A6XX_RSCC_TCS0_DRV0_STATUS, val,
(val & 1), 100, 10000); (val & 1), 100, 10000);
...@@ -680,37 +670,22 @@ int a6xx_gmu_reset(struct a6xx_gpu *a6xx_gpu) ...@@ -680,37 +670,22 @@ int a6xx_gmu_reset(struct a6xx_gpu *a6xx_gpu)
(val & 1), 100, 10000); (val & 1), 100, 10000);
gmu_poll_timeout(gmu, REG_A6XX_RSCC_TCS3_DRV0_STATUS, val, gmu_poll_timeout(gmu, REG_A6XX_RSCC_TCS3_DRV0_STATUS, val,
(val & 1), 100, 1000); (val & 1), 100, 1000);
}
/* Force off the GX GSDC */ /* Force the GMU off in case it isn't responsive */
regulator_force_disable(gmu->gx); static void a6xx_gmu_force_off(struct a6xx_gmu *gmu)
{
/* Disable the resources */ /* Flush all the queues */
clk_bulk_disable_unprepare(gmu->nr_clocks, gmu->clocks); a6xx_hfi_stop(gmu);
pm_runtime_put_sync(gmu->dev);
/* Re-enable the resources */
pm_runtime_get_sync(gmu->dev);
/* Use a known rate to bring up the GMU */
clk_set_rate(gmu->core_clk, 200000000);
ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks);
if (ret)
goto out;
a6xx_gmu_irq_enable(gmu);
ret = a6xx_gmu_fw_start(gmu, GMU_RESET);
if (!ret)
ret = a6xx_hfi_start(gmu, GMU_COLD_BOOT);
/* Set the GPU back to the highest power frequency */ /* Stop the interrupts */
__a6xx_gmu_set_freq(gmu, gmu->nr_gpu_freqs - 1); a6xx_gmu_irq_disable(gmu);
out: /* Force off SPTP in case the GMU is managing it */
if (ret) a6xx_sptprac_disable(gmu);
a6xx_gmu_clear_oob(gmu, GMU_OOB_BOOT_SLUMBER);
return ret; /* Make sure there are no outstanding RPMh votes */
a6xx_gmu_rpmh_off(gmu);
} }
int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
...@@ -723,19 +698,26 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) ...@@ -723,19 +698,26 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
if (WARN(!gmu->mmio, "The GMU is not set up yet\n")) if (WARN(!gmu->mmio, "The GMU is not set up yet\n"))
return 0; return 0;
gmu->hung = false;
/* Turn on the resources */ /* Turn on the resources */
pm_runtime_get_sync(gmu->dev); pm_runtime_get_sync(gmu->dev);
/* Use a known rate to bring up the GMU */ /* Use a known rate to bring up the GMU */
clk_set_rate(gmu->core_clk, 200000000); clk_set_rate(gmu->core_clk, 200000000);
ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks); ret = clk_bulk_prepare_enable(gmu->nr_clocks, gmu->clocks);
if (ret) if (ret) {
goto out; pm_runtime_put(gmu->dev);
return ret;
}
/* Set the bus quota to a reasonable value for boot */ /* Set the bus quota to a reasonable value for boot */
icc_set_bw(gpu->icc_path, 0, MBps_to_icc(3072)); icc_set_bw(gpu->icc_path, 0, MBps_to_icc(3072));
a6xx_gmu_irq_enable(gmu); /* Enable the GMU interrupt */
gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_CLR, ~0);
gmu_write(gmu, REG_A6XX_GMU_AO_HOST_INTERRUPT_MASK, ~A6XX_GMU_IRQ_MASK);
enable_irq(gmu->gmu_irq);
/* Check to see if we are doing a cold or warm boot */ /* Check to see if we are doing a cold or warm boot */
status = gmu_read(gmu, REG_A6XX_GMU_GENERAL_7) == 1 ? status = gmu_read(gmu, REG_A6XX_GMU_GENERAL_7) == 1 ?
...@@ -746,14 +728,35 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu) ...@@ -746,14 +728,35 @@ int a6xx_gmu_resume(struct a6xx_gpu *a6xx_gpu)
goto out; goto out;
ret = a6xx_hfi_start(gmu, status); ret = a6xx_hfi_start(gmu, status);
if (ret)
goto out;
/*
* Turn on the GMU firmware fault interrupt after we know the boot
* sequence is successful
*/
gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_CLR, ~0);
gmu_write(gmu, REG_A6XX_GMU_GMU2HOST_INTR_MASK, ~A6XX_HFI_IRQ_MASK);
enable_irq(gmu->hfi_irq);
/* Set the GPU to the highest power frequency */ /* Set the GPU to the highest power frequency */
__a6xx_gmu_set_freq(gmu, gmu->nr_gpu_freqs - 1); __a6xx_gmu_set_freq(gmu, gmu->nr_gpu_freqs - 1);
/*
* "enable" the GX power domain which won't actually do anything but it
* will make sure that the refcounting is correct in case we need to
* bring down the GX after a GMU failure
*/
if (!IS_ERR(gmu->gxpd))
pm_runtime_get(gmu->gxpd);
out: out:
/* Make sure to turn off the boot OOB request on error */ /* On failure, shut down the GMU to leave it in a good state */
if (ret) if (ret) {
a6xx_gmu_clear_oob(gmu, GMU_OOB_BOOT_SLUMBER); disable_irq(gmu->gmu_irq);
a6xx_rpmh_stop(gmu);
pm_runtime_put(gmu->dev);
}
return ret; return ret;
} }
...@@ -773,11 +776,12 @@ bool a6xx_gmu_isidle(struct a6xx_gmu *gmu) ...@@ -773,11 +776,12 @@ bool a6xx_gmu_isidle(struct a6xx_gmu *gmu)
return true; return true;
} }
int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) /* Gracefully try to shut down the GMU and by extension the GPU */
static void a6xx_gmu_shutdown(struct a6xx_gmu *gmu)
{ {
struct a6xx_gpu *a6xx_gpu = container_of(gmu, struct a6xx_gpu, gmu);
struct adreno_gpu *adreno_gpu = &a6xx_gpu->base; struct adreno_gpu *adreno_gpu = &a6xx_gpu->base;
struct msm_gpu *gpu = &adreno_gpu->base; struct msm_gpu *gpu = &adreno_gpu->base;
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
u32 val; u32 val;
/* /*
...@@ -787,10 +791,19 @@ int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) ...@@ -787,10 +791,19 @@ int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu)
val = gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE); val = gmu_read(gmu, REG_A6XX_GPU_GMU_CX_GMU_RPMH_POWER_STATE);
if (val != 0xf) { if (val != 0xf) {
int ret = a6xx_gmu_wait_for_idle(a6xx_gpu); int ret = a6xx_gmu_wait_for_idle(gmu);
/* Temporary until we can recover safely */ /* If the GMU isn't responding assume it is hung */
BUG_ON(ret); if (ret) {
a6xx_gmu_force_off(gmu);
return;
}
/* Clear the VBIF pipe before shutting down */
gpu_write(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL0, 0xf);
spin_until((gpu_read(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL1) & 0xf)
== 0xf);
gpu_write(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL0, 0);
/* tell the GMU we want to slumber */ /* tell the GMU we want to slumber */
a6xx_gmu_notify_slumber(gmu); a6xx_gmu_notify_slumber(gmu);
...@@ -822,10 +835,37 @@ int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu) ...@@ -822,10 +835,37 @@ int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu)
/* Tell RPMh to power off the GPU */ /* Tell RPMh to power off the GPU */
a6xx_rpmh_stop(gmu); a6xx_rpmh_stop(gmu);
}
int a6xx_gmu_stop(struct a6xx_gpu *a6xx_gpu)
{
struct a6xx_gmu *gmu = &a6xx_gpu->gmu;
struct msm_gpu *gpu = &a6xx_gpu->base.base;
if (!pm_runtime_active(gmu->dev))
return 0;
/*
* Force the GMU off if we detected a hang, otherwise try to shut it
* down gracefully
*/
if (gmu->hung)
a6xx_gmu_force_off(gmu);
else
a6xx_gmu_shutdown(gmu);
/* Remove the bus vote */ /* Remove the bus vote */
icc_set_bw(gpu->icc_path, 0, 0); icc_set_bw(gpu->icc_path, 0, 0);
/*
* Make sure the GX domain is off before turning off the GMU (CX)
* domain. Usually the GMU does this but only if the shutdown sequence
* was successful
*/
if (!IS_ERR(gmu->gxpd))
pm_runtime_put_sync(gmu->gxpd);
clk_bulk_disable_unprepare(gmu->nr_clocks, gmu->clocks); clk_bulk_disable_unprepare(gmu->nr_clocks, gmu->clocks);
pm_runtime_put_sync(gmu->dev); pm_runtime_put_sync(gmu->dev);
...@@ -948,11 +988,11 @@ static int a6xx_gmu_memory_probe(struct a6xx_gmu *gmu) ...@@ -948,11 +988,11 @@ static int a6xx_gmu_memory_probe(struct a6xx_gmu *gmu)
} }
/* Return the 'arc-level' for the given frequency */ /* Return the 'arc-level' for the given frequency */
static u32 a6xx_gmu_get_arc_level(struct device *dev, unsigned long freq) static unsigned int a6xx_gmu_get_arc_level(struct device *dev,
unsigned long freq)
{ {
struct dev_pm_opp *opp; struct dev_pm_opp *opp;
struct device_node *np; unsigned int val;
u32 val = 0;
if (!freq) if (!freq)
return 0; return 0;
...@@ -961,12 +1001,7 @@ static u32 a6xx_gmu_get_arc_level(struct device *dev, unsigned long freq) ...@@ -961,12 +1001,7 @@ static u32 a6xx_gmu_get_arc_level(struct device *dev, unsigned long freq)
if (IS_ERR(opp)) if (IS_ERR(opp))
return 0; return 0;
np = dev_pm_opp_get_of_node(opp); val = dev_pm_opp_get_level(opp);
if (np) {
of_property_read_u32(np, "opp-level", &val);
of_node_put(np);
}
dev_pm_opp_put(opp); dev_pm_opp_put(opp);
...@@ -1002,7 +1037,7 @@ static int a6xx_gmu_rpmh_arc_votes_init(struct device *dev, u32 *votes, ...@@ -1002,7 +1037,7 @@ static int a6xx_gmu_rpmh_arc_votes_init(struct device *dev, u32 *votes,
/* Construct a vote for each frequency */ /* Construct a vote for each frequency */
for (i = 0; i < freqs_count; i++) { for (i = 0; i < freqs_count; i++) {
u8 pindex = 0, sindex = 0; u8 pindex = 0, sindex = 0;
u32 level = a6xx_gmu_get_arc_level(dev, freqs[i]); unsigned int level = a6xx_gmu_get_arc_level(dev, freqs[i]);
/* Get the primary index that matches the arc level */ /* Get the primary index that matches the arc level */
for (j = 0; j < pri_count; j++) { for (j = 0; j < pri_count; j++) {
...@@ -1195,9 +1230,15 @@ void a6xx_gmu_remove(struct a6xx_gpu *a6xx_gpu) ...@@ -1195,9 +1230,15 @@ void a6xx_gmu_remove(struct a6xx_gpu *a6xx_gpu)
if (IS_ERR_OR_NULL(gmu->mmio)) if (IS_ERR_OR_NULL(gmu->mmio))
return; return;
pm_runtime_disable(gmu->dev);
a6xx_gmu_stop(a6xx_gpu); a6xx_gmu_stop(a6xx_gpu);
pm_runtime_disable(gmu->dev);
if (!IS_ERR(gmu->gxpd)) {
pm_runtime_disable(gmu->gxpd);
dev_pm_domain_detach(gmu->gxpd, false);
}
a6xx_gmu_irq_disable(gmu); a6xx_gmu_irq_disable(gmu);
a6xx_gmu_memory_free(gmu, gmu->hfi); a6xx_gmu_memory_free(gmu, gmu->hfi);
...@@ -1223,7 +1264,6 @@ int a6xx_gmu_probe(struct a6xx_gpu *a6xx_gpu, struct device_node *node) ...@@ -1223,7 +1264,6 @@ int a6xx_gmu_probe(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
gmu->idle_level = GMU_IDLE_STATE_ACTIVE; gmu->idle_level = GMU_IDLE_STATE_ACTIVE;
pm_runtime_enable(gmu->dev); pm_runtime_enable(gmu->dev);
gmu->gx = devm_regulator_get(gmu->dev, "vdd");
/* Get the list of clocks */ /* Get the list of clocks */
ret = a6xx_gmu_clocks_probe(gmu); ret = a6xx_gmu_clocks_probe(gmu);
...@@ -1257,6 +1297,12 @@ int a6xx_gmu_probe(struct a6xx_gpu *a6xx_gpu, struct device_node *node) ...@@ -1257,6 +1297,12 @@ int a6xx_gmu_probe(struct a6xx_gpu *a6xx_gpu, struct device_node *node)
if (gmu->hfi_irq < 0 || gmu->gmu_irq < 0) if (gmu->hfi_irq < 0 || gmu->gmu_irq < 0)
goto err; goto err;
/*
* Get a link to the GX power domain to reset the GPU in case of GMU
* crash
*/
gmu->gxpd = dev_pm_domain_attach_by_name(gmu->dev, "gx");
/* Get the power levels for the GMU and GPU */ /* Get the power levels for the GMU and GPU */
a6xx_gmu_pwrlevels_probe(gmu); a6xx_gmu_pwrlevels_probe(gmu);
......
...@@ -27,9 +27,6 @@ struct a6xx_gmu_bo { ...@@ -27,9 +27,6 @@ struct a6xx_gmu_bo {
/* the GMU is coming up for the first time or back from a power collapse */ /* the GMU is coming up for the first time or back from a power collapse */
#define GMU_COLD_BOOT 1 #define GMU_COLD_BOOT 1
/* The GMU is being soft reset after a fault */
#define GMU_RESET 2
/* /*
* These define the level of control that the GMU has - the higher the number * These define the level of control that the GMU has - the higher the number
* the more things that the GMU hardware controls on its own. * the more things that the GMU hardware controls on its own.
...@@ -52,11 +49,11 @@ struct a6xx_gmu { ...@@ -52,11 +49,11 @@ struct a6xx_gmu {
int hfi_irq; int hfi_irq;
int gmu_irq; int gmu_irq;
struct regulator *gx;
struct iommu_domain *domain; struct iommu_domain *domain;
u64 uncached_iova_base; u64 uncached_iova_base;
struct device *gxpd;
int idle_level; int idle_level;
struct a6xx_gmu_bo *hfi; struct a6xx_gmu_bo *hfi;
...@@ -78,7 +75,7 @@ struct a6xx_gmu { ...@@ -78,7 +75,7 @@ struct a6xx_gmu {
struct a6xx_hfi_queue queues[2]; struct a6xx_hfi_queue queues[2];
struct tasklet_struct hfi_tasklet; bool hung;
}; };
static inline u32 gmu_read(struct a6xx_gmu *gmu, u32 offset) static inline u32 gmu_read(struct a6xx_gmu *gmu, u32 offset)
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include <linux/devfreq.h> #include <linux/devfreq.h>
#define GPU_PAS_ID 13
static inline bool _a6xx_check_idle(struct msm_gpu *gpu) static inline bool _a6xx_check_idle(struct msm_gpu *gpu)
{ {
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
...@@ -343,6 +345,20 @@ static int a6xx_ucode_init(struct msm_gpu *gpu) ...@@ -343,6 +345,20 @@ static int a6xx_ucode_init(struct msm_gpu *gpu)
return 0; return 0;
} }
static int a6xx_zap_shader_init(struct msm_gpu *gpu)
{
static bool loaded;
int ret;
if (loaded)
return 0;
ret = adreno_zap_shader_load(gpu, GPU_PAS_ID);
loaded = !ret;
return ret;
}
#define A6XX_INT_MASK (A6XX_RBBM_INT_0_MASK_CP_AHB_ERROR | \ #define A6XX_INT_MASK (A6XX_RBBM_INT_0_MASK_CP_AHB_ERROR | \
A6XX_RBBM_INT_0_MASK_RBBM_ATB_ASYNCFIFO_OVERFLOW | \ A6XX_RBBM_INT_0_MASK_RBBM_ATB_ASYNCFIFO_OVERFLOW | \
A6XX_RBBM_INT_0_MASK_CP_HW_ERROR | \ A6XX_RBBM_INT_0_MASK_CP_HW_ERROR | \
...@@ -491,7 +507,27 @@ static int a6xx_hw_init(struct msm_gpu *gpu) ...@@ -491,7 +507,27 @@ static int a6xx_hw_init(struct msm_gpu *gpu)
if (ret) if (ret)
goto out; goto out;
/*
* Try to load a zap shader into the secure world. If successful
* we can use the CP to switch out of secure mode. If not then we
* have no resource but to try to switch ourselves out manually. If we
* guessed wrong then access to the RBBM_SECVID_TRUST_CNTL register will
* be blocked and a permissions violation will soon follow.
*/
ret = a6xx_zap_shader_init(gpu);
if (!ret) {
OUT_PKT7(gpu->rb[0], CP_SET_SECURE_MODE, 1);
OUT_RING(gpu->rb[0], 0x00000000);
a6xx_flush(gpu, gpu->rb[0]);
if (!a6xx_idle(gpu, gpu->rb[0]))
return -EINVAL;
} else {
/* Print a warning so if we die, we know why */
dev_warn_once(gpu->dev->dev,
"Zap shader not enabled - using SECVID_TRUST_CNTL instead\n");
gpu_write(gpu, REG_A6XX_RBBM_SECVID_TRUST_CNTL, 0x0); gpu_write(gpu, REG_A6XX_RBBM_SECVID_TRUST_CNTL, 0x0);
}
out: out:
/* /*
...@@ -678,13 +714,15 @@ static int a6xx_pm_resume(struct msm_gpu *gpu) ...@@ -678,13 +714,15 @@ static int a6xx_pm_resume(struct msm_gpu *gpu)
struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu); struct a6xx_gpu *a6xx_gpu = to_a6xx_gpu(adreno_gpu);
int ret; int ret;
ret = a6xx_gmu_resume(a6xx_gpu);
gpu->needs_hw_init = true; gpu->needs_hw_init = true;
ret = a6xx_gmu_resume(a6xx_gpu);
if (ret)
return ret;
msm_gpu_resume_devfreq(gpu); msm_gpu_resume_devfreq(gpu);
return ret; return 0;
} }
static int a6xx_pm_suspend(struct msm_gpu *gpu) static int a6xx_pm_suspend(struct msm_gpu *gpu)
...@@ -694,18 +732,6 @@ static int a6xx_pm_suspend(struct msm_gpu *gpu) ...@@ -694,18 +732,6 @@ static int a6xx_pm_suspend(struct msm_gpu *gpu)
devfreq_suspend_device(gpu->devfreq.devfreq); devfreq_suspend_device(gpu->devfreq.devfreq);
/*
* Make sure the GMU is idle before continuing (because some transitions
* may use VBIF
*/
a6xx_gmu_wait_for_idle(a6xx_gpu);
/* Clear the VBIF pipe before shutting down */
/* FIXME: This accesses the GPU - do we need to make sure it is on? */
gpu_write(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL0, 0xf);
spin_until((gpu_read(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL1) & 0xf) == 0xf);
gpu_write(gpu, REG_A6XX_VBIF_XIN_HALT_CTRL0, 0);
return a6xx_gmu_stop(a6xx_gpu); return a6xx_gmu_stop(a6xx_gpu);
} }
...@@ -781,14 +807,16 @@ static const struct adreno_gpu_funcs funcs = { ...@@ -781,14 +807,16 @@ static const struct adreno_gpu_funcs funcs = {
.active_ring = a6xx_active_ring, .active_ring = a6xx_active_ring,
.irq = a6xx_irq, .irq = a6xx_irq,
.destroy = a6xx_destroy, .destroy = a6xx_destroy,
#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_DEV_COREDUMP) #if defined(CONFIG_DRM_MSM_GPU_STATE)
.show = a6xx_show, .show = a6xx_show,
#endif #endif
.gpu_busy = a6xx_gpu_busy, .gpu_busy = a6xx_gpu_busy,
.gpu_get_freq = a6xx_gmu_get_freq, .gpu_get_freq = a6xx_gmu_get_freq,
.gpu_set_freq = a6xx_gmu_set_freq, .gpu_set_freq = a6xx_gmu_set_freq,
#if defined(CONFIG_DRM_MSM_GPU_STATE)
.gpu_state_get = a6xx_gpu_state_get, .gpu_state_get = a6xx_gpu_state_get,
.gpu_state_put = a6xx_gpu_state_put, .gpu_state_put = a6xx_gpu_state_put,
#endif
}, },
.get_timestamp = a6xx_get_timestamp, .get_timestamp = a6xx_get_timestamp,
}; };
......
...@@ -46,9 +46,8 @@ struct a6xx_gpu { ...@@ -46,9 +46,8 @@ struct a6xx_gpu {
int a6xx_gmu_resume(struct a6xx_gpu *gpu); int a6xx_gmu_resume(struct a6xx_gpu *gpu);
int a6xx_gmu_stop(struct a6xx_gpu *gpu); int a6xx_gmu_stop(struct a6xx_gpu *gpu);
int a6xx_gmu_wait_for_idle(struct a6xx_gpu *gpu); int a6xx_gmu_wait_for_idle(struct a6xx_gmu *gmu);
int a6xx_gmu_reset(struct a6xx_gpu *a6xx_gpu);
bool a6xx_gmu_isidle(struct a6xx_gmu *gmu); bool a6xx_gmu_isidle(struct a6xx_gmu *gmu);
int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state); int a6xx_gmu_set_oob(struct a6xx_gmu *gmu, enum a6xx_gmu_oob_state state);
......
...@@ -155,6 +155,7 @@ static const struct adreno_info gpulist[] = { ...@@ -155,6 +155,7 @@ static const struct adreno_info gpulist[] = {
.gmem = SZ_1M, .gmem = SZ_1M,
.inactive_period = DRM_MSM_INACTIVE_PERIOD, .inactive_period = DRM_MSM_INACTIVE_PERIOD,
.init = a6xx_gpu_init, .init = a6xx_gpu_init,
.zapfw = "a630_zap.mdt",
}, },
}; };
...@@ -229,6 +230,7 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev) ...@@ -229,6 +230,7 @@ struct msm_gpu *adreno_load_gpu(struct drm_device *dev)
ret = pm_runtime_get_sync(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) { if (ret < 0) {
pm_runtime_put_sync(&pdev->dev);
DRM_DEV_ERROR(dev->dev, "Couldn't power up the GPU: %d\n", ret); DRM_DEV_ERROR(dev->dev, "Couldn't power up the GPU: %d\n", ret);
return NULL; return NULL;
} }
......
...@@ -19,13 +19,148 @@ ...@@ -19,13 +19,148 @@
#include <linux/ascii85.h> #include <linux/ascii85.h>
#include <linux/interconnect.h> #include <linux/interconnect.h>
#include <linux/qcom_scm.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/of_address.h>
#include <linux/pm_opp.h> #include <linux/pm_opp.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/soc/qcom/mdt_loader.h>
#include "adreno_gpu.h" #include "adreno_gpu.h"
#include "msm_gem.h" #include "msm_gem.h"
#include "msm_mmu.h" #include "msm_mmu.h"
static bool zap_available = true;
static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname,
u32 pasid)
{
struct device *dev = &gpu->pdev->dev;
const struct firmware *fw;
struct device_node *np, *mem_np;
struct resource r;
phys_addr_t mem_phys;
ssize_t mem_size;
void *mem_region = NULL;
int ret;
if (!IS_ENABLED(CONFIG_ARCH_QCOM)) {
zap_available = false;
return -EINVAL;
}
np = of_get_child_by_name(dev->of_node, "zap-shader");
if (!np) {
zap_available = false;
return -ENODEV;
}
mem_np = of_parse_phandle(np, "memory-region", 0);
of_node_put(np);
if (!mem_np) {
zap_available = false;
return -EINVAL;
}
ret = of_address_to_resource(mem_np, 0, &r);
of_node_put(mem_np);
if (ret)
return ret;
mem_phys = r.start;
mem_size = resource_size(&r);
/* Request the MDT file for the firmware */
fw = adreno_request_fw(to_adreno_gpu(gpu), fwname);
if (IS_ERR(fw)) {
DRM_DEV_ERROR(dev, "Unable to load %s\n", fwname);
return PTR_ERR(fw);
}
/* Figure out how much memory we need */
mem_size = qcom_mdt_get_size(fw);
if (mem_size < 0) {
ret = mem_size;
goto out;
}
/* Allocate memory for the firmware image */
mem_region = memremap(mem_phys, mem_size, MEMREMAP_WC);
if (!mem_region) {
ret = -ENOMEM;
goto out;
}
/*
* Load the rest of the MDT
*
* Note that we could be dealing with two different paths, since
* with upstream linux-firmware it would be in a qcom/ subdir..
* adreno_request_fw() handles this, but qcom_mdt_load() does
* not. But since we've already gotten through adreno_request_fw()
* we know which of the two cases it is:
*/
if (to_adreno_gpu(gpu)->fwloc == FW_LOCATION_LEGACY) {
ret = qcom_mdt_load(dev, fw, fwname, pasid,
mem_region, mem_phys, mem_size, NULL);
} else {
char *newname;
newname = kasprintf(GFP_KERNEL, "qcom/%s", fwname);
ret = qcom_mdt_load(dev, fw, newname, pasid,
mem_region, mem_phys, mem_size, NULL);
kfree(newname);
}
if (ret)
goto out;
/* Send the image to the secure world */
ret = qcom_scm_pas_auth_and_reset(pasid);
/*
* If the scm call returns -EOPNOTSUPP we assume that this target
* doesn't need/support the zap shader so quietly fail
*/
if (ret == -EOPNOTSUPP)
zap_available = false;
else if (ret)
DRM_DEV_ERROR(dev, "Unable to authorize the image\n");
out:
if (mem_region)
memunmap(mem_region);
release_firmware(fw);
return ret;
}
int adreno_zap_shader_load(struct msm_gpu *gpu, u32 pasid)
{
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
struct platform_device *pdev = gpu->pdev;
/* Short cut if we determine the zap shader isn't available/needed */
if (!zap_available)
return -ENODEV;
/* We need SCM to be able to load the firmware */
if (!qcom_scm_is_available()) {
DRM_DEV_ERROR(&pdev->dev, "SCM is not available\n");
return -EPROBE_DEFER;
}
/* Each GPU has a target specific zap shader firmware name to use */
if (!adreno_gpu->info->zapfw) {
zap_available = false;
DRM_DEV_ERROR(&pdev->dev,
"Zap shader firmware file not specified for this target\n");
return -ENODEV;
}
return zap_shader_load_mdt(gpu, adreno_gpu->info->zapfw, pasid);
}
int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)
{ {
struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu); struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
...@@ -63,6 +198,12 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value) ...@@ -63,6 +198,12 @@ int adreno_get_param(struct msm_gpu *gpu, uint32_t param, uint64_t *value)
case MSM_PARAM_NR_RINGS: case MSM_PARAM_NR_RINGS:
*value = gpu->nr_rings; *value = gpu->nr_rings;
return 0; return 0;
case MSM_PARAM_PP_PGTABLE:
*value = 0;
return 0;
case MSM_PARAM_FAULTS:
*value = gpu->global_faults;
return 0;
default: default:
DBG("%s: invalid param: %u", gpu->name, param); DBG("%s: invalid param: %u", gpu->name, param);
return -EINVAL; return -EINVAL;
......
...@@ -252,6 +252,12 @@ void adreno_gpu_state_destroy(struct msm_gpu_state *state); ...@@ -252,6 +252,12 @@ void adreno_gpu_state_destroy(struct msm_gpu_state *state);
int adreno_gpu_state_get(struct msm_gpu *gpu, struct msm_gpu_state *state); int adreno_gpu_state_get(struct msm_gpu *gpu, struct msm_gpu_state *state);
int adreno_gpu_state_put(struct msm_gpu_state *state); int adreno_gpu_state_put(struct msm_gpu_state *state);
/*
* For a5xx and a6xx targets load the zap shader that is used to pull the GPU
* out of secure mode
*/
int adreno_zap_shader_load(struct msm_gpu *gpu, u32 pasid);
/* ringbuffer helpers (the parts that are adreno specific) */ /* ringbuffer helpers (the parts that are adreno specific) */
static inline void static inline void
......
...@@ -46,6 +46,9 @@ ...@@ -46,6 +46,9 @@
#define LEFT_MIXER 0 #define LEFT_MIXER 0
#define RIGHT_MIXER 1 #define RIGHT_MIXER 1
/* timeout in ms waiting for frame done */
#define DPU_CRTC_FRAME_DONE_TIMEOUT_MS 60
static struct dpu_kms *_dpu_crtc_get_kms(struct drm_crtc *crtc) static struct dpu_kms *_dpu_crtc_get_kms(struct drm_crtc *crtc)
{ {
struct msm_drm_private *priv = crtc->dev->dev_private; struct msm_drm_private *priv = crtc->dev->dev_private;
...@@ -425,65 +428,6 @@ void dpu_crtc_complete_commit(struct drm_crtc *crtc, ...@@ -425,65 +428,6 @@ void dpu_crtc_complete_commit(struct drm_crtc *crtc,
trace_dpu_crtc_complete_commit(DRMID(crtc)); trace_dpu_crtc_complete_commit(DRMID(crtc));
} }
static void _dpu_crtc_setup_mixer_for_encoder(
struct drm_crtc *crtc,
struct drm_encoder *enc)
{
struct dpu_crtc_state *cstate = to_dpu_crtc_state(crtc->state);
struct dpu_kms *dpu_kms = _dpu_crtc_get_kms(crtc);
struct dpu_rm *rm = &dpu_kms->rm;
struct dpu_crtc_mixer *mixer;
struct dpu_hw_ctl *last_valid_ctl = NULL;
int i;
struct dpu_rm_hw_iter lm_iter, ctl_iter;
dpu_rm_init_hw_iter(&lm_iter, enc->base.id, DPU_HW_BLK_LM);
dpu_rm_init_hw_iter(&ctl_iter, enc->base.id, DPU_HW_BLK_CTL);
/* Set up all the mixers and ctls reserved by this encoder */
for (i = cstate->num_mixers; i < ARRAY_SIZE(cstate->mixers); i++) {
mixer = &cstate->mixers[i];
if (!dpu_rm_get_hw(rm, &lm_iter))
break;
mixer->hw_lm = (struct dpu_hw_mixer *)lm_iter.hw;
/* CTL may be <= LMs, if <, multiple LMs controlled by 1 CTL */
if (!dpu_rm_get_hw(rm, &ctl_iter)) {
DPU_DEBUG("no ctl assigned to lm %d, using previous\n",
mixer->hw_lm->idx - LM_0);
mixer->lm_ctl = last_valid_ctl;
} else {
mixer->lm_ctl = (struct dpu_hw_ctl *)ctl_iter.hw;
last_valid_ctl = mixer->lm_ctl;
}
/* Shouldn't happen, mixers are always >= ctls */
if (!mixer->lm_ctl) {
DPU_ERROR("no valid ctls found for lm %d\n",
mixer->hw_lm->idx - LM_0);
return;
}
cstate->num_mixers++;
DPU_DEBUG("setup mixer %d: lm %d\n",
i, mixer->hw_lm->idx - LM_0);
DPU_DEBUG("setup mixer %d: ctl %d\n",
i, mixer->lm_ctl->idx - CTL_0);
}
}
static void _dpu_crtc_setup_mixers(struct drm_crtc *crtc)
{
struct drm_encoder *enc;
WARN_ON(!drm_modeset_is_locked(&crtc->mutex));
/* Check for mixers on all encoders attached to this crtc */
drm_for_each_encoder_mask(enc, crtc->dev, crtc->state->encoder_mask)
_dpu_crtc_setup_mixer_for_encoder(crtc, enc);
}
static void _dpu_crtc_setup_lm_bounds(struct drm_crtc *crtc, static void _dpu_crtc_setup_lm_bounds(struct drm_crtc *crtc,
struct drm_crtc_state *state) struct drm_crtc_state *state)
{ {
...@@ -533,10 +477,7 @@ static void dpu_crtc_atomic_begin(struct drm_crtc *crtc, ...@@ -533,10 +477,7 @@ static void dpu_crtc_atomic_begin(struct drm_crtc *crtc,
dev = crtc->dev; dev = crtc->dev;
smmu_state = &dpu_crtc->smmu_state; smmu_state = &dpu_crtc->smmu_state;
if (!cstate->num_mixers) {
_dpu_crtc_setup_mixers(crtc);
_dpu_crtc_setup_lm_bounds(crtc, crtc->state); _dpu_crtc_setup_lm_bounds(crtc, crtc->state);
}
if (dpu_crtc->event) { if (dpu_crtc->event) {
WARN_ON(dpu_crtc->event); WARN_ON(dpu_crtc->event);
...@@ -683,7 +624,7 @@ static int _dpu_crtc_wait_for_frame_done(struct drm_crtc *crtc) ...@@ -683,7 +624,7 @@ static int _dpu_crtc_wait_for_frame_done(struct drm_crtc *crtc)
DPU_ATRACE_BEGIN("frame done completion wait"); DPU_ATRACE_BEGIN("frame done completion wait");
ret = wait_for_completion_timeout(&dpu_crtc->frame_done_comp, ret = wait_for_completion_timeout(&dpu_crtc->frame_done_comp,
msecs_to_jiffies(DPU_FRAME_DONE_TIMEOUT)); msecs_to_jiffies(DPU_CRTC_FRAME_DONE_TIMEOUT_MS));
if (!ret) { if (!ret) {
DRM_ERROR("frame done wait timed out, ret:%d\n", ret); DRM_ERROR("frame done wait timed out, ret:%d\n", ret);
rc = -ETIMEDOUT; rc = -ETIMEDOUT;
......
...@@ -69,6 +69,9 @@ ...@@ -69,6 +69,9 @@
#define MAX_VDISPLAY_SPLIT 1080 #define MAX_VDISPLAY_SPLIT 1080
/* timeout in frames waiting for frame done */
#define DPU_ENCODER_FRAME_DONE_TIMEOUT_FRAMES 5
/** /**
* enum dpu_enc_rc_events - events for resource control state machine * enum dpu_enc_rc_events - events for resource control state machine
* @DPU_ENC_RC_EVENT_KICKOFF: * @DPU_ENC_RC_EVENT_KICKOFF:
...@@ -158,7 +161,7 @@ enum dpu_enc_rc_states { ...@@ -158,7 +161,7 @@ enum dpu_enc_rc_states {
* Bit0 = phys_encs[0] etc. * Bit0 = phys_encs[0] etc.
* @crtc_frame_event_cb: callback handler for frame event * @crtc_frame_event_cb: callback handler for frame event
* @crtc_frame_event_cb_data: callback handler private data * @crtc_frame_event_cb_data: callback handler private data
* @frame_done_timeout: frame done timeout in Hz * @frame_done_timeout_ms: frame done timeout in ms
* @frame_done_timer: watchdog timer for frame done event * @frame_done_timer: watchdog timer for frame done event
* @vsync_event_timer: vsync timer * @vsync_event_timer: vsync timer
* @disp_info: local copy of msm_display_info struct * @disp_info: local copy of msm_display_info struct
...@@ -196,7 +199,7 @@ struct dpu_encoder_virt { ...@@ -196,7 +199,7 @@ struct dpu_encoder_virt {
void (*crtc_frame_event_cb)(void *, u32 event); void (*crtc_frame_event_cb)(void *, u32 event);
void *crtc_frame_event_cb_data; void *crtc_frame_event_cb_data;
atomic_t frame_done_timeout; atomic_t frame_done_timeout_ms;
struct timer_list frame_done_timer; struct timer_list frame_done_timer;
struct timer_list vsync_event_timer; struct timer_list vsync_event_timer;
...@@ -521,7 +524,7 @@ static void _dpu_encoder_adjust_mode(struct drm_connector *connector, ...@@ -521,7 +524,7 @@ static void _dpu_encoder_adjust_mode(struct drm_connector *connector,
list_for_each_entry(cur_mode, &connector->modes, head) { list_for_each_entry(cur_mode, &connector->modes, head) {
if (cur_mode->vdisplay == adj_mode->vdisplay && if (cur_mode->vdisplay == adj_mode->vdisplay &&
cur_mode->hdisplay == adj_mode->hdisplay && cur_mode->hdisplay == adj_mode->hdisplay &&
cur_mode->vrefresh == adj_mode->vrefresh) { drm_mode_vrefresh(cur_mode) == drm_mode_vrefresh(adj_mode)) {
adj_mode->private = cur_mode->private; adj_mode->private = cur_mode->private;
adj_mode->private_flags |= cur_mode->private_flags; adj_mode->private_flags |= cur_mode->private_flags;
} }
...@@ -959,10 +962,14 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, ...@@ -959,10 +962,14 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc,
struct dpu_kms *dpu_kms; struct dpu_kms *dpu_kms;
struct list_head *connector_list; struct list_head *connector_list;
struct drm_connector *conn = NULL, *conn_iter; struct drm_connector *conn = NULL, *conn_iter;
struct dpu_rm_hw_iter pp_iter, ctl_iter; struct drm_crtc *drm_crtc;
struct dpu_crtc_state *cstate;
struct dpu_rm_hw_iter hw_iter;
struct msm_display_topology topology; struct msm_display_topology topology;
struct dpu_hw_ctl *hw_ctl[MAX_CHANNELS_PER_ENC] = { NULL }; struct dpu_hw_ctl *hw_ctl[MAX_CHANNELS_PER_ENC] = { NULL };
int i = 0, ret; struct dpu_hw_mixer *hw_lm[MAX_CHANNELS_PER_ENC] = { NULL };
int num_lm = 0, num_ctl = 0;
int i, j, ret;
if (!drm_enc) { if (!drm_enc) {
DPU_ERROR("invalid encoder\n"); DPU_ERROR("invalid encoder\n");
...@@ -990,10 +997,14 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, ...@@ -990,10 +997,14 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc,
return; return;
} }
drm_for_each_crtc(drm_crtc, drm_enc->dev)
if (drm_crtc->state->encoder_mask & drm_encoder_mask(drm_enc))
break;
topology = dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode); topology = dpu_encoder_get_topology(dpu_enc, dpu_kms, adj_mode);
/* Reserve dynamic resources now. Indicating non-AtomicTest phase */ /* Reserve dynamic resources now. Indicating non-AtomicTest phase */
ret = dpu_rm_reserve(&dpu_kms->rm, drm_enc, drm_enc->crtc->state, ret = dpu_rm_reserve(&dpu_kms->rm, drm_enc, drm_crtc->state,
topology, false); topology, false);
if (ret) { if (ret) {
DPU_ERROR_ENC(dpu_enc, DPU_ERROR_ENC(dpu_enc,
...@@ -1001,21 +1012,41 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, ...@@ -1001,21 +1012,41 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc,
return; return;
} }
dpu_rm_init_hw_iter(&pp_iter, drm_enc->base.id, DPU_HW_BLK_PINGPONG); dpu_rm_init_hw_iter(&hw_iter, drm_enc->base.id, DPU_HW_BLK_PINGPONG);
for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
dpu_enc->hw_pp[i] = NULL; dpu_enc->hw_pp[i] = NULL;
if (!dpu_rm_get_hw(&dpu_kms->rm, &pp_iter)) if (!dpu_rm_get_hw(&dpu_kms->rm, &hw_iter))
break;
dpu_enc->hw_pp[i] = (struct dpu_hw_pingpong *) hw_iter.hw;
}
dpu_rm_init_hw_iter(&hw_iter, drm_enc->base.id, DPU_HW_BLK_CTL);
for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
if (!dpu_rm_get_hw(&dpu_kms->rm, &hw_iter))
break; break;
dpu_enc->hw_pp[i] = (struct dpu_hw_pingpong *) pp_iter.hw; hw_ctl[i] = (struct dpu_hw_ctl *)hw_iter.hw;
num_ctl++;
} }
dpu_rm_init_hw_iter(&ctl_iter, drm_enc->base.id, DPU_HW_BLK_CTL); dpu_rm_init_hw_iter(&hw_iter, drm_enc->base.id, DPU_HW_BLK_LM);
for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) { for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
if (!dpu_rm_get_hw(&dpu_kms->rm, &ctl_iter)) if (!dpu_rm_get_hw(&dpu_kms->rm, &hw_iter))
break; break;
hw_ctl[i] = (struct dpu_hw_ctl *)ctl_iter.hw; hw_lm[i] = (struct dpu_hw_mixer *)hw_iter.hw;
num_lm++;
}
cstate = to_dpu_crtc_state(drm_crtc->state);
for (i = 0; i < num_lm; i++) {
int ctl_idx = (i < num_ctl) ? i : (num_ctl-1);
cstate->mixers[i].hw_lm = hw_lm[i];
cstate->mixers[i].lm_ctl = hw_ctl[ctl_idx];
} }
cstate->num_mixers = num_lm;
for (i = 0; i < dpu_enc->num_phys_encs; i++) { for (i = 0; i < dpu_enc->num_phys_encs; i++) {
struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i]; struct dpu_encoder_phys *phys = dpu_enc->phys_encs[i];
...@@ -1023,18 +1054,38 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, ...@@ -1023,18 +1054,38 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc,
if (!dpu_enc->hw_pp[i]) { if (!dpu_enc->hw_pp[i]) {
DPU_ERROR_ENC(dpu_enc, "no pp block assigned" DPU_ERROR_ENC(dpu_enc, "no pp block assigned"
"at idx: %d\n", i); "at idx: %d\n", i);
return; goto error;
} }
if (!hw_ctl[i]) { if (!hw_ctl[i]) {
DPU_ERROR_ENC(dpu_enc, "no ctl block assigned" DPU_ERROR_ENC(dpu_enc, "no ctl block assigned"
"at idx: %d\n", i); "at idx: %d\n", i);
return; goto error;
} }
phys->hw_pp = dpu_enc->hw_pp[i]; phys->hw_pp = dpu_enc->hw_pp[i];
phys->hw_ctl = hw_ctl[i]; phys->hw_ctl = hw_ctl[i];
dpu_rm_init_hw_iter(&hw_iter, drm_enc->base.id,
DPU_HW_BLK_INTF);
for (j = 0; j < MAX_CHANNELS_PER_ENC; j++) {
struct dpu_hw_intf *hw_intf;
if (!dpu_rm_get_hw(&dpu_kms->rm, &hw_iter))
break;
hw_intf = (struct dpu_hw_intf *)hw_iter.hw;
if (hw_intf->idx == phys->intf_idx)
phys->hw_intf = hw_intf;
}
if (!phys->hw_intf) {
DPU_ERROR_ENC(dpu_enc,
"no intf block assigned at idx: %d\n",
i);
goto error;
}
phys->connector = conn->state->connector; phys->connector = conn->state->connector;
if (phys->ops.mode_set) if (phys->ops.mode_set)
phys->ops.mode_set(phys, mode, adj_mode); phys->ops.mode_set(phys, mode, adj_mode);
...@@ -1042,6 +1093,9 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc, ...@@ -1042,6 +1093,9 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc,
} }
dpu_enc->mode_set_complete = true; dpu_enc->mode_set_complete = true;
error:
dpu_rm_release(&dpu_kms->rm, drm_enc);
} }
static void _dpu_encoder_virt_enable_helper(struct drm_encoder *drm_enc) static void _dpu_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
...@@ -1182,7 +1236,7 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc) ...@@ -1182,7 +1236,7 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
} }
/* after phys waits for frame-done, should be no more frames pending */ /* after phys waits for frame-done, should be no more frames pending */
if (atomic_xchg(&dpu_enc->frame_done_timeout, 0)) { if (atomic_xchg(&dpu_enc->frame_done_timeout_ms, 0)) {
DPU_ERROR("enc%d timeout pending\n", drm_enc->base.id); DPU_ERROR("enc%d timeout pending\n", drm_enc->base.id);
del_timer_sync(&dpu_enc->frame_done_timer); del_timer_sync(&dpu_enc->frame_done_timer);
} }
...@@ -1339,7 +1393,7 @@ static void dpu_encoder_frame_done_callback( ...@@ -1339,7 +1393,7 @@ static void dpu_encoder_frame_done_callback(
} }
if (!dpu_enc->frame_busy_mask[0]) { if (!dpu_enc->frame_busy_mask[0]) {
atomic_set(&dpu_enc->frame_done_timeout, 0); atomic_set(&dpu_enc->frame_done_timeout_ms, 0);
del_timer(&dpu_enc->frame_done_timer); del_timer(&dpu_enc->frame_done_timer);
dpu_encoder_resource_control(drm_enc, dpu_encoder_resource_control(drm_enc,
...@@ -1547,8 +1601,14 @@ static void _dpu_encoder_kickoff_phys(struct dpu_encoder_virt *dpu_enc, ...@@ -1547,8 +1601,14 @@ static void _dpu_encoder_kickoff_phys(struct dpu_encoder_virt *dpu_enc,
if (!ctl) if (!ctl)
continue; continue;
if (phys->split_role != ENC_ROLE_SLAVE) /*
* This is cleared in frame_done worker, which isn't invoked
* for async commits. So don't set this for async, since it'll
* roll over to the next commit.
*/
if (!async && phys->split_role != ENC_ROLE_SLAVE)
set_bit(i, dpu_enc->frame_busy_mask); set_bit(i, dpu_enc->frame_busy_mask);
if (!phys->ops.needs_single_flush || if (!phys->ops.needs_single_flush ||
!phys->ops.needs_single_flush(phys)) !phys->ops.needs_single_flush(phys))
_dpu_encoder_trigger_flush(&dpu_enc->base, phys, 0x0, _dpu_encoder_trigger_flush(&dpu_enc->base, phys, 0x0,
...@@ -1800,11 +1860,20 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc, bool async) ...@@ -1800,11 +1860,20 @@ void dpu_encoder_kickoff(struct drm_encoder *drm_enc, bool async)
trace_dpu_enc_kickoff(DRMID(drm_enc)); trace_dpu_enc_kickoff(DRMID(drm_enc));
atomic_set(&dpu_enc->frame_done_timeout, /*
DPU_FRAME_DONE_TIMEOUT * 1000 / * Asynchronous frames don't handle FRAME_DONE events. As such, they
drm_enc->crtc->state->adjusted_mode.vrefresh); * shouldn't enable the frame_done watchdog since it will always time
mod_timer(&dpu_enc->frame_done_timer, jiffies + * out.
((atomic_read(&dpu_enc->frame_done_timeout) * HZ) / 1000)); */
if (!async) {
unsigned long timeout_ms;
timeout_ms = DPU_ENCODER_FRAME_DONE_TIMEOUT_FRAMES * 1000 /
drm_mode_vrefresh(&drm_enc->crtc->state->adjusted_mode);
atomic_set(&dpu_enc->frame_done_timeout_ms, timeout_ms);
mod_timer(&dpu_enc->frame_done_timer,
jiffies + msecs_to_jiffies(timeout_ms));
}
/* All phys encs are ready to go, trigger the kickoff */ /* All phys encs are ready to go, trigger the kickoff */
_dpu_encoder_kickoff_phys(dpu_enc, async); _dpu_encoder_kickoff_phys(dpu_enc, async);
...@@ -2124,7 +2193,7 @@ static void dpu_encoder_frame_done_timeout(struct timer_list *t) ...@@ -2124,7 +2193,7 @@ static void dpu_encoder_frame_done_timeout(struct timer_list *t)
DRM_DEBUG_KMS("id:%u invalid timeout frame_busy_mask=%lu\n", DRM_DEBUG_KMS("id:%u invalid timeout frame_busy_mask=%lu\n",
DRMID(drm_enc), dpu_enc->frame_busy_mask[0]); DRMID(drm_enc), dpu_enc->frame_busy_mask[0]);
return; return;
} else if (!atomic_xchg(&dpu_enc->frame_done_timeout, 0)) { } else if (!atomic_xchg(&dpu_enc->frame_done_timeout_ms, 0)) {
DRM_DEBUG_KMS("id:%u invalid timeout\n", DRMID(drm_enc)); DRM_DEBUG_KMS("id:%u invalid timeout\n", DRMID(drm_enc));
return; return;
} }
...@@ -2170,7 +2239,7 @@ int dpu_encoder_setup(struct drm_device *dev, struct drm_encoder *enc, ...@@ -2170,7 +2239,7 @@ int dpu_encoder_setup(struct drm_device *dev, struct drm_encoder *enc,
spin_lock_init(&dpu_enc->enc_spinlock); spin_lock_init(&dpu_enc->enc_spinlock);
atomic_set(&dpu_enc->frame_done_timeout, 0); atomic_set(&dpu_enc->frame_done_timeout_ms, 0);
timer_setup(&dpu_enc->frame_done_timer, timer_setup(&dpu_enc->frame_done_timer,
dpu_encoder_frame_done_timeout, 0); dpu_encoder_frame_done_timeout, 0);
......
...@@ -200,6 +200,7 @@ struct dpu_encoder_irq { ...@@ -200,6 +200,7 @@ struct dpu_encoder_irq {
* @hw_mdptop: Hardware interface to the top registers * @hw_mdptop: Hardware interface to the top registers
* @hw_ctl: Hardware interface to the ctl registers * @hw_ctl: Hardware interface to the ctl registers
* @hw_pp: Hardware interface to the ping pong registers * @hw_pp: Hardware interface to the ping pong registers
* @hw_intf: Hardware interface to the intf registers
* @dpu_kms: Pointer to the dpu_kms top level * @dpu_kms: Pointer to the dpu_kms top level
* @cached_mode: DRM mode cached at mode_set time, acted on in enable * @cached_mode: DRM mode cached at mode_set time, acted on in enable
* @enabled: Whether the encoder has enabled and running a mode * @enabled: Whether the encoder has enabled and running a mode
...@@ -228,6 +229,7 @@ struct dpu_encoder_phys { ...@@ -228,6 +229,7 @@ struct dpu_encoder_phys {
struct dpu_hw_mdp *hw_mdptop; struct dpu_hw_mdp *hw_mdptop;
struct dpu_hw_ctl *hw_ctl; struct dpu_hw_ctl *hw_ctl;
struct dpu_hw_pingpong *hw_pp; struct dpu_hw_pingpong *hw_pp;
struct dpu_hw_intf *hw_intf;
struct dpu_kms *dpu_kms; struct dpu_kms *dpu_kms;
struct drm_display_mode cached_mode; struct drm_display_mode cached_mode;
enum dpu_enc_split_role split_role; enum dpu_enc_split_role split_role;
...@@ -250,19 +252,6 @@ static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys) ...@@ -250,19 +252,6 @@ static inline int dpu_encoder_phys_inc_pending(struct dpu_encoder_phys *phys)
return atomic_inc_return(&phys->pending_kickoff_cnt); return atomic_inc_return(&phys->pending_kickoff_cnt);
} }
/**
* struct dpu_encoder_phys_vid - sub-class of dpu_encoder_phys to handle video
* mode specific operations
* @base: Baseclass physical encoder structure
* @hw_intf: Hardware interface to the intf registers
* @timing_params: Current timing parameter
*/
struct dpu_encoder_phys_vid {
struct dpu_encoder_phys base;
struct dpu_hw_intf *hw_intf;
struct intf_timing_params timing_params;
};
/** /**
* struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to handle command * struct dpu_encoder_phys_cmd - sub-class of dpu_encoder_phys to handle command
* mode specific operations * mode specific operations
......
...@@ -404,7 +404,8 @@ static void dpu_encoder_phys_cmd_tearcheck_config( ...@@ -404,7 +404,8 @@ static void dpu_encoder_phys_cmd_tearcheck_config(
return; return;
} }
tc_cfg.vsync_count = vsync_hz / (mode->vtotal * mode->vrefresh); tc_cfg.vsync_count = vsync_hz /
(mode->vtotal * drm_mode_vrefresh(mode));
/* enable external TE after kickoff to avoid premature autorefresh */ /* enable external TE after kickoff to avoid premature autorefresh */
tc_cfg.hw_vsync_mode = 0; tc_cfg.hw_vsync_mode = 0;
...@@ -424,7 +425,7 @@ static void dpu_encoder_phys_cmd_tearcheck_config( ...@@ -424,7 +425,7 @@ static void dpu_encoder_phys_cmd_tearcheck_config(
DPU_DEBUG_CMDENC(cmd_enc, DPU_DEBUG_CMDENC(cmd_enc,
"tc %d vsync_clk_speed_hz %u vtotal %u vrefresh %u\n", "tc %d vsync_clk_speed_hz %u vtotal %u vrefresh %u\n",
phys_enc->hw_pp->idx - PINGPONG_0, vsync_hz, phys_enc->hw_pp->idx - PINGPONG_0, vsync_hz,
mode->vtotal, mode->vrefresh); mode->vtotal, drm_mode_vrefresh(mode));
DPU_DEBUG_CMDENC(cmd_enc, DPU_DEBUG_CMDENC(cmd_enc,
"tc %d enable %u start_pos %u rd_ptr_irq %u\n", "tc %d enable %u start_pos %u rd_ptr_irq %u\n",
phys_enc->hw_pp->idx - PINGPONG_0, tc_enable, tc_cfg.start_pos, phys_enc->hw_pp->idx - PINGPONG_0, tc_enable, tc_cfg.start_pos,
......
...@@ -18,14 +18,14 @@ ...@@ -18,14 +18,14 @@
#include "dpu_trace.h" #include "dpu_trace.h"
#define DPU_DEBUG_VIDENC(e, fmt, ...) DPU_DEBUG("enc%d intf%d " fmt, \ #define DPU_DEBUG_VIDENC(e, fmt, ...) DPU_DEBUG("enc%d intf%d " fmt, \
(e) && (e)->base.parent ? \ (e) && (e)->parent ? \
(e)->base.parent->base.id : -1, \ (e)->parent->base.id : -1, \
(e) && (e)->hw_intf ? \ (e) && (e)->hw_intf ? \
(e)->hw_intf->idx - INTF_0 : -1, ##__VA_ARGS__) (e)->hw_intf->idx - INTF_0 : -1, ##__VA_ARGS__)
#define DPU_ERROR_VIDENC(e, fmt, ...) DPU_ERROR("enc%d intf%d " fmt, \ #define DPU_ERROR_VIDENC(e, fmt, ...) DPU_ERROR("enc%d intf%d " fmt, \
(e) && (e)->base.parent ? \ (e) && (e)->parent ? \
(e)->base.parent->base.id : -1, \ (e)->parent->base.id : -1, \
(e) && (e)->hw_intf ? \ (e) && (e)->hw_intf ? \
(e)->hw_intf->idx - INTF_0 : -1, ##__VA_ARGS__) (e)->hw_intf->idx - INTF_0 : -1, ##__VA_ARGS__)
...@@ -44,7 +44,7 @@ static bool dpu_encoder_phys_vid_is_master( ...@@ -44,7 +44,7 @@ static bool dpu_encoder_phys_vid_is_master(
} }
static void drm_mode_to_intf_timing_params( static void drm_mode_to_intf_timing_params(
const struct dpu_encoder_phys_vid *vid_enc, const struct dpu_encoder_phys *phys_enc,
const struct drm_display_mode *mode, const struct drm_display_mode *mode,
struct intf_timing_params *timing) struct intf_timing_params *timing)
{ {
...@@ -92,7 +92,7 @@ static void drm_mode_to_intf_timing_params( ...@@ -92,7 +92,7 @@ static void drm_mode_to_intf_timing_params(
timing->hsync_skew = mode->hskew; timing->hsync_skew = mode->hskew;
/* DSI controller cannot handle active-low sync signals. */ /* DSI controller cannot handle active-low sync signals. */
if (vid_enc->hw_intf->cap->type == INTF_DSI) { if (phys_enc->hw_intf->cap->type == INTF_DSI) {
timing->hsync_polarity = 0; timing->hsync_polarity = 0;
timing->vsync_polarity = 0; timing->vsync_polarity = 0;
} }
...@@ -143,11 +143,11 @@ static u32 get_vertical_total(const struct intf_timing_params *timing) ...@@ -143,11 +143,11 @@ static u32 get_vertical_total(const struct intf_timing_params *timing)
* lines based on the chip worst case latencies. * lines based on the chip worst case latencies.
*/ */
static u32 programmable_fetch_get_num_lines( static u32 programmable_fetch_get_num_lines(
struct dpu_encoder_phys_vid *vid_enc, struct dpu_encoder_phys *phys_enc,
const struct intf_timing_params *timing) const struct intf_timing_params *timing)
{ {
u32 worst_case_needed_lines = u32 worst_case_needed_lines =
vid_enc->hw_intf->cap->prog_fetch_lines_worst_case; phys_enc->hw_intf->cap->prog_fetch_lines_worst_case;
u32 start_of_frame_lines = u32 start_of_frame_lines =
timing->v_back_porch + timing->vsync_pulse_width; timing->v_back_porch + timing->vsync_pulse_width;
u32 needed_vfp_lines = worst_case_needed_lines - start_of_frame_lines; u32 needed_vfp_lines = worst_case_needed_lines - start_of_frame_lines;
...@@ -155,26 +155,26 @@ static u32 programmable_fetch_get_num_lines( ...@@ -155,26 +155,26 @@ static u32 programmable_fetch_get_num_lines(
/* Fetch must be outside active lines, otherwise undefined. */ /* Fetch must be outside active lines, otherwise undefined. */
if (start_of_frame_lines >= worst_case_needed_lines) { if (start_of_frame_lines >= worst_case_needed_lines) {
DPU_DEBUG_VIDENC(vid_enc, DPU_DEBUG_VIDENC(phys_enc,
"prog fetch is not needed, large vbp+vsw\n"); "prog fetch is not needed, large vbp+vsw\n");
actual_vfp_lines = 0; actual_vfp_lines = 0;
} else if (timing->v_front_porch < needed_vfp_lines) { } else if (timing->v_front_porch < needed_vfp_lines) {
/* Warn fetch needed, but not enough porch in panel config */ /* Warn fetch needed, but not enough porch in panel config */
pr_warn_once pr_warn_once
("low vbp+vfp may lead to perf issues in some cases\n"); ("low vbp+vfp may lead to perf issues in some cases\n");
DPU_DEBUG_VIDENC(vid_enc, DPU_DEBUG_VIDENC(phys_enc,
"less vfp than fetch req, using entire vfp\n"); "less vfp than fetch req, using entire vfp\n");
actual_vfp_lines = timing->v_front_porch; actual_vfp_lines = timing->v_front_porch;
} else { } else {
DPU_DEBUG_VIDENC(vid_enc, "room in vfp for needed prefetch\n"); DPU_DEBUG_VIDENC(phys_enc, "room in vfp for needed prefetch\n");
actual_vfp_lines = needed_vfp_lines; actual_vfp_lines = needed_vfp_lines;
} }
DPU_DEBUG_VIDENC(vid_enc, DPU_DEBUG_VIDENC(phys_enc,
"v_front_porch %u v_back_porch %u vsync_pulse_width %u\n", "v_front_porch %u v_back_porch %u vsync_pulse_width %u\n",
timing->v_front_porch, timing->v_back_porch, timing->v_front_porch, timing->v_back_porch,
timing->vsync_pulse_width); timing->vsync_pulse_width);
DPU_DEBUG_VIDENC(vid_enc, DPU_DEBUG_VIDENC(phys_enc,
"wc_lines %u needed_vfp_lines %u actual_vfp_lines %u\n", "wc_lines %u needed_vfp_lines %u actual_vfp_lines %u\n",
worst_case_needed_lines, needed_vfp_lines, actual_vfp_lines); worst_case_needed_lines, needed_vfp_lines, actual_vfp_lines);
...@@ -194,8 +194,6 @@ static u32 programmable_fetch_get_num_lines( ...@@ -194,8 +194,6 @@ static u32 programmable_fetch_get_num_lines(
static void programmable_fetch_config(struct dpu_encoder_phys *phys_enc, static void programmable_fetch_config(struct dpu_encoder_phys *phys_enc,
const struct intf_timing_params *timing) const struct intf_timing_params *timing)
{ {
struct dpu_encoder_phys_vid *vid_enc =
to_dpu_encoder_phys_vid(phys_enc);
struct intf_prog_fetch f = { 0 }; struct intf_prog_fetch f = { 0 };
u32 vfp_fetch_lines = 0; u32 vfp_fetch_lines = 0;
u32 horiz_total = 0; u32 horiz_total = 0;
...@@ -203,10 +201,10 @@ static void programmable_fetch_config(struct dpu_encoder_phys *phys_enc, ...@@ -203,10 +201,10 @@ static void programmable_fetch_config(struct dpu_encoder_phys *phys_enc,
u32 vfp_fetch_start_vsync_counter = 0; u32 vfp_fetch_start_vsync_counter = 0;
unsigned long lock_flags; unsigned long lock_flags;
if (WARN_ON_ONCE(!vid_enc->hw_intf->ops.setup_prg_fetch)) if (WARN_ON_ONCE(!phys_enc->hw_intf->ops.setup_prg_fetch))
return; return;
vfp_fetch_lines = programmable_fetch_get_num_lines(vid_enc, timing); vfp_fetch_lines = programmable_fetch_get_num_lines(phys_enc, timing);
if (vfp_fetch_lines) { if (vfp_fetch_lines) {
vert_total = get_vertical_total(timing); vert_total = get_vertical_total(timing);
horiz_total = get_horizontal_total(timing); horiz_total = get_horizontal_total(timing);
...@@ -216,12 +214,12 @@ static void programmable_fetch_config(struct dpu_encoder_phys *phys_enc, ...@@ -216,12 +214,12 @@ static void programmable_fetch_config(struct dpu_encoder_phys *phys_enc,
f.fetch_start = vfp_fetch_start_vsync_counter; f.fetch_start = vfp_fetch_start_vsync_counter;
} }
DPU_DEBUG_VIDENC(vid_enc, DPU_DEBUG_VIDENC(phys_enc,
"vfp_fetch_lines %u vfp_fetch_start_vsync_counter %u\n", "vfp_fetch_lines %u vfp_fetch_start_vsync_counter %u\n",
vfp_fetch_lines, vfp_fetch_start_vsync_counter); vfp_fetch_lines, vfp_fetch_start_vsync_counter);
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
vid_enc->hw_intf->ops.setup_prg_fetch(vid_enc->hw_intf, &f); phys_enc->hw_intf->ops.setup_prg_fetch(phys_enc->hw_intf, &f);
spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
} }
...@@ -231,7 +229,7 @@ static bool dpu_encoder_phys_vid_mode_fixup( ...@@ -231,7 +229,7 @@ static bool dpu_encoder_phys_vid_mode_fixup(
struct drm_display_mode *adj_mode) struct drm_display_mode *adj_mode)
{ {
if (phys_enc) if (phys_enc)
DPU_DEBUG_VIDENC(to_dpu_encoder_phys_vid(phys_enc), "\n"); DPU_DEBUG_VIDENC(phys_enc, "\n");
/* /*
* Modifying mode has consequences when the mode comes back to us * Modifying mode has consequences when the mode comes back to us
...@@ -242,7 +240,6 @@ static bool dpu_encoder_phys_vid_mode_fixup( ...@@ -242,7 +240,6 @@ static bool dpu_encoder_phys_vid_mode_fixup(
static void dpu_encoder_phys_vid_setup_timing_engine( static void dpu_encoder_phys_vid_setup_timing_engine(
struct dpu_encoder_phys *phys_enc) struct dpu_encoder_phys *phys_enc)
{ {
struct dpu_encoder_phys_vid *vid_enc;
struct drm_display_mode mode; struct drm_display_mode mode;
struct intf_timing_params timing_params = { 0 }; struct intf_timing_params timing_params = { 0 };
const struct dpu_format *fmt = NULL; const struct dpu_format *fmt = NULL;
...@@ -256,13 +253,12 @@ static void dpu_encoder_phys_vid_setup_timing_engine( ...@@ -256,13 +253,12 @@ static void dpu_encoder_phys_vid_setup_timing_engine(
} }
mode = phys_enc->cached_mode; mode = phys_enc->cached_mode;
vid_enc = to_dpu_encoder_phys_vid(phys_enc); if (!phys_enc->hw_intf->ops.setup_timing_gen) {
if (!vid_enc->hw_intf->ops.setup_timing_gen) {
DPU_ERROR("timing engine setup is not supported\n"); DPU_ERROR("timing engine setup is not supported\n");
return; return;
} }
DPU_DEBUG_VIDENC(vid_enc, "enabling mode:\n"); DPU_DEBUG_VIDENC(phys_enc, "enabling mode:\n");
drm_mode_debug_printmodeline(&mode); drm_mode_debug_printmodeline(&mode);
if (phys_enc->split_role != ENC_ROLE_SOLO) { if (phys_enc->split_role != ENC_ROLE_SOLO) {
...@@ -271,32 +267,30 @@ static void dpu_encoder_phys_vid_setup_timing_engine( ...@@ -271,32 +267,30 @@ static void dpu_encoder_phys_vid_setup_timing_engine(
mode.hsync_start >>= 1; mode.hsync_start >>= 1;
mode.hsync_end >>= 1; mode.hsync_end >>= 1;
DPU_DEBUG_VIDENC(vid_enc, DPU_DEBUG_VIDENC(phys_enc,
"split_role %d, halve horizontal %d %d %d %d\n", "split_role %d, halve horizontal %d %d %d %d\n",
phys_enc->split_role, phys_enc->split_role,
mode.hdisplay, mode.htotal, mode.hdisplay, mode.htotal,
mode.hsync_start, mode.hsync_end); mode.hsync_start, mode.hsync_end);
} }
drm_mode_to_intf_timing_params(vid_enc, &mode, &timing_params); drm_mode_to_intf_timing_params(phys_enc, &mode, &timing_params);
fmt = dpu_get_dpu_format(fmt_fourcc); fmt = dpu_get_dpu_format(fmt_fourcc);
DPU_DEBUG_VIDENC(vid_enc, "fmt_fourcc 0x%X\n", fmt_fourcc); DPU_DEBUG_VIDENC(phys_enc, "fmt_fourcc 0x%X\n", fmt_fourcc);
intf_cfg.intf = vid_enc->hw_intf->idx; intf_cfg.intf = phys_enc->hw_intf->idx;
intf_cfg.intf_mode_sel = DPU_CTL_MODE_SEL_VID; intf_cfg.intf_mode_sel = DPU_CTL_MODE_SEL_VID;
intf_cfg.stream_sel = 0; /* Don't care value for video mode */ intf_cfg.stream_sel = 0; /* Don't care value for video mode */
intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc); intf_cfg.mode_3d = dpu_encoder_helper_get_3d_blend_mode(phys_enc);
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
vid_enc->hw_intf->ops.setup_timing_gen(vid_enc->hw_intf, phys_enc->hw_intf->ops.setup_timing_gen(phys_enc->hw_intf,
&timing_params, fmt); &timing_params, fmt);
phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg); phys_enc->hw_ctl->ops.setup_intf_cfg(phys_enc->hw_ctl, &intf_cfg);
spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
programmable_fetch_config(phys_enc, &timing_params); programmable_fetch_config(phys_enc, &timing_params);
vid_enc->timing_params = timing_params;
} }
static void dpu_encoder_phys_vid_vblank_irq(void *arg, int irq_idx) static void dpu_encoder_phys_vid_vblank_irq(void *arg, int irq_idx)
...@@ -353,22 +347,10 @@ static void dpu_encoder_phys_vid_underrun_irq(void *arg, int irq_idx) ...@@ -353,22 +347,10 @@ static void dpu_encoder_phys_vid_underrun_irq(void *arg, int irq_idx)
phys_enc); phys_enc);
} }
static bool _dpu_encoder_phys_is_dual_ctl(struct dpu_encoder_phys *phys_enc)
{
struct dpu_crtc_state *dpu_cstate;
if (!phys_enc)
return false;
dpu_cstate = to_dpu_crtc_state(phys_enc->parent->crtc->state);
return dpu_cstate->num_ctls > 1;
}
static bool dpu_encoder_phys_vid_needs_single_flush( static bool dpu_encoder_phys_vid_needs_single_flush(
struct dpu_encoder_phys *phys_enc) struct dpu_encoder_phys *phys_enc)
{ {
return (phys_enc && _dpu_encoder_phys_is_dual_ctl(phys_enc)); return phys_enc->split_role != ENC_ROLE_SOLO;
} }
static void _dpu_encoder_phys_vid_setup_irq_hw_idx( static void _dpu_encoder_phys_vid_setup_irq_hw_idx(
...@@ -396,19 +378,15 @@ static void dpu_encoder_phys_vid_mode_set( ...@@ -396,19 +378,15 @@ static void dpu_encoder_phys_vid_mode_set(
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adj_mode) struct drm_display_mode *adj_mode)
{ {
struct dpu_encoder_phys_vid *vid_enc;
if (!phys_enc || !phys_enc->dpu_kms) { if (!phys_enc || !phys_enc->dpu_kms) {
DPU_ERROR("invalid encoder/kms\n"); DPU_ERROR("invalid encoder/kms\n");
return; return;
} }
vid_enc = to_dpu_encoder_phys_vid(phys_enc);
if (adj_mode) { if (adj_mode) {
phys_enc->cached_mode = *adj_mode; phys_enc->cached_mode = *adj_mode;
drm_mode_debug_printmodeline(adj_mode); drm_mode_debug_printmodeline(adj_mode);
DPU_DEBUG_VIDENC(vid_enc, "caching mode:\n"); DPU_DEBUG_VIDENC(phys_enc, "caching mode:\n");
} }
_dpu_encoder_phys_vid_setup_irq_hw_idx(phys_enc); _dpu_encoder_phys_vid_setup_irq_hw_idx(phys_enc);
...@@ -419,7 +397,6 @@ static int dpu_encoder_phys_vid_control_vblank_irq( ...@@ -419,7 +397,6 @@ static int dpu_encoder_phys_vid_control_vblank_irq(
bool enable) bool enable)
{ {
int ret = 0; int ret = 0;
struct dpu_encoder_phys_vid *vid_enc;
int refcount; int refcount;
if (!phys_enc) { if (!phys_enc) {
...@@ -428,7 +405,6 @@ static int dpu_encoder_phys_vid_control_vblank_irq( ...@@ -428,7 +405,6 @@ static int dpu_encoder_phys_vid_control_vblank_irq(
} }
refcount = atomic_read(&phys_enc->vblank_refcount); refcount = atomic_read(&phys_enc->vblank_refcount);
vid_enc = to_dpu_encoder_phys_vid(phys_enc);
/* Slave encoders don't report vblank */ /* Slave encoders don't report vblank */
if (!dpu_encoder_phys_vid_is_master(phys_enc)) if (!dpu_encoder_phys_vid_is_master(phys_enc))
...@@ -453,7 +429,7 @@ static int dpu_encoder_phys_vid_control_vblank_irq( ...@@ -453,7 +429,7 @@ static int dpu_encoder_phys_vid_control_vblank_irq(
if (ret) { if (ret) {
DRM_ERROR("failed: id:%u intf:%d ret:%d enable:%d refcnt:%d\n", DRM_ERROR("failed: id:%u intf:%d ret:%d enable:%d refcnt:%d\n",
DRMID(phys_enc->parent), DRMID(phys_enc->parent),
vid_enc->hw_intf->idx - INTF_0, ret, enable, phys_enc->hw_intf->idx - INTF_0, ret, enable,
refcount); refcount);
} }
return ret; return ret;
...@@ -461,43 +437,17 @@ static int dpu_encoder_phys_vid_control_vblank_irq( ...@@ -461,43 +437,17 @@ static int dpu_encoder_phys_vid_control_vblank_irq(
static void dpu_encoder_phys_vid_enable(struct dpu_encoder_phys *phys_enc) static void dpu_encoder_phys_vid_enable(struct dpu_encoder_phys *phys_enc)
{ {
struct msm_drm_private *priv;
struct dpu_encoder_phys_vid *vid_enc;
struct dpu_rm_hw_iter iter;
struct dpu_hw_ctl *ctl; struct dpu_hw_ctl *ctl;
u32 flush_mask = 0; u32 flush_mask = 0;
if (!phys_enc || !phys_enc->parent || !phys_enc->parent->dev ||
!phys_enc->parent->dev->dev_private) {
DPU_ERROR("invalid encoder/device\n");
return;
}
priv = phys_enc->parent->dev->dev_private;
vid_enc = to_dpu_encoder_phys_vid(phys_enc);
ctl = phys_enc->hw_ctl; ctl = phys_enc->hw_ctl;
dpu_rm_init_hw_iter(&iter, phys_enc->parent->base.id, DPU_HW_BLK_INTF); DPU_DEBUG_VIDENC(phys_enc, "\n");
while (dpu_rm_get_hw(&phys_enc->dpu_kms->rm, &iter)) {
struct dpu_hw_intf *hw_intf = (struct dpu_hw_intf *)iter.hw;
if (hw_intf->idx == phys_enc->intf_idx) {
vid_enc->hw_intf = hw_intf;
break;
}
}
if (!vid_enc->hw_intf) {
DPU_ERROR("hw_intf not assigned\n");
return;
}
DPU_DEBUG_VIDENC(vid_enc, "\n");
if (WARN_ON(!vid_enc->hw_intf->ops.enable_timing)) if (WARN_ON(!phys_enc->hw_intf->ops.enable_timing))
return; return;
dpu_encoder_helper_split_config(phys_enc, vid_enc->hw_intf->idx); dpu_encoder_helper_split_config(phys_enc, phys_enc->hw_intf->idx);
dpu_encoder_phys_vid_setup_timing_engine(phys_enc); dpu_encoder_phys_vid_setup_timing_engine(phys_enc);
...@@ -510,11 +460,12 @@ static void dpu_encoder_phys_vid_enable(struct dpu_encoder_phys *phys_enc) ...@@ -510,11 +460,12 @@ static void dpu_encoder_phys_vid_enable(struct dpu_encoder_phys *phys_enc)
!dpu_encoder_phys_vid_is_master(phys_enc)) !dpu_encoder_phys_vid_is_master(phys_enc))
goto skip_flush; goto skip_flush;
ctl->ops.get_bitmask_intf(ctl, &flush_mask, vid_enc->hw_intf->idx); ctl->ops.get_bitmask_intf(ctl, &flush_mask, phys_enc->hw_intf->idx);
ctl->ops.update_pending_flush(ctl, flush_mask); ctl->ops.update_pending_flush(ctl, flush_mask);
skip_flush: skip_flush:
DPU_DEBUG_VIDENC(vid_enc, "update pending flush ctl %d flush_mask %x\n", DPU_DEBUG_VIDENC(phys_enc,
"update pending flush ctl %d flush_mask %x\n",
ctl->idx - CTL_0, flush_mask); ctl->idx - CTL_0, flush_mask);
/* ctl_flush & timing engine enable will be triggered by framework */ /* ctl_flush & timing engine enable will be triggered by framework */
...@@ -524,16 +475,13 @@ static void dpu_encoder_phys_vid_enable(struct dpu_encoder_phys *phys_enc) ...@@ -524,16 +475,13 @@ static void dpu_encoder_phys_vid_enable(struct dpu_encoder_phys *phys_enc)
static void dpu_encoder_phys_vid_destroy(struct dpu_encoder_phys *phys_enc) static void dpu_encoder_phys_vid_destroy(struct dpu_encoder_phys *phys_enc)
{ {
struct dpu_encoder_phys_vid *vid_enc;
if (!phys_enc) { if (!phys_enc) {
DPU_ERROR("invalid encoder\n"); DPU_ERROR("invalid encoder\n");
return; return;
} }
vid_enc = to_dpu_encoder_phys_vid(phys_enc); DPU_DEBUG_VIDENC(phys_enc, "\n");
DPU_DEBUG_VIDENC(vid_enc, "\n"); kfree(phys_enc);
kfree(vid_enc);
} }
static void dpu_encoder_phys_vid_get_hw_resources( static void dpu_encoder_phys_vid_get_hw_resources(
...@@ -589,7 +537,6 @@ static int dpu_encoder_phys_vid_wait_for_vblank( ...@@ -589,7 +537,6 @@ static int dpu_encoder_phys_vid_wait_for_vblank(
static void dpu_encoder_phys_vid_prepare_for_kickoff( static void dpu_encoder_phys_vid_prepare_for_kickoff(
struct dpu_encoder_phys *phys_enc) struct dpu_encoder_phys *phys_enc)
{ {
struct dpu_encoder_phys_vid *vid_enc;
struct dpu_hw_ctl *ctl; struct dpu_hw_ctl *ctl;
int rc; int rc;
...@@ -597,7 +544,6 @@ static void dpu_encoder_phys_vid_prepare_for_kickoff( ...@@ -597,7 +544,6 @@ static void dpu_encoder_phys_vid_prepare_for_kickoff(
DPU_ERROR("invalid encoder/parameters\n"); DPU_ERROR("invalid encoder/parameters\n");
return; return;
} }
vid_enc = to_dpu_encoder_phys_vid(phys_enc);
ctl = phys_enc->hw_ctl; ctl = phys_enc->hw_ctl;
if (!ctl || !ctl->ops.wait_reset_status) if (!ctl || !ctl->ops.wait_reset_status)
...@@ -609,7 +555,7 @@ static void dpu_encoder_phys_vid_prepare_for_kickoff( ...@@ -609,7 +555,7 @@ static void dpu_encoder_phys_vid_prepare_for_kickoff(
*/ */
rc = ctl->ops.wait_reset_status(ctl); rc = ctl->ops.wait_reset_status(ctl);
if (rc) { if (rc) {
DPU_ERROR_VIDENC(vid_enc, "ctl %d reset failure: %d\n", DPU_ERROR_VIDENC(phys_enc, "ctl %d reset failure: %d\n",
ctl->idx, rc); ctl->idx, rc);
dpu_encoder_helper_unregister_irq(phys_enc, INTR_IDX_VSYNC); dpu_encoder_helper_unregister_irq(phys_enc, INTR_IDX_VSYNC);
} }
...@@ -618,7 +564,6 @@ static void dpu_encoder_phys_vid_prepare_for_kickoff( ...@@ -618,7 +564,6 @@ static void dpu_encoder_phys_vid_prepare_for_kickoff(
static void dpu_encoder_phys_vid_disable(struct dpu_encoder_phys *phys_enc) static void dpu_encoder_phys_vid_disable(struct dpu_encoder_phys *phys_enc)
{ {
struct msm_drm_private *priv; struct msm_drm_private *priv;
struct dpu_encoder_phys_vid *vid_enc;
unsigned long lock_flags; unsigned long lock_flags;
int ret; int ret;
...@@ -629,16 +574,13 @@ static void dpu_encoder_phys_vid_disable(struct dpu_encoder_phys *phys_enc) ...@@ -629,16 +574,13 @@ static void dpu_encoder_phys_vid_disable(struct dpu_encoder_phys *phys_enc)
} }
priv = phys_enc->parent->dev->dev_private; priv = phys_enc->parent->dev->dev_private;
vid_enc = to_dpu_encoder_phys_vid(phys_enc); if (!phys_enc->hw_intf || !phys_enc->hw_ctl) {
if (!vid_enc->hw_intf || !phys_enc->hw_ctl) {
DPU_ERROR("invalid hw_intf %d hw_ctl %d\n", DPU_ERROR("invalid hw_intf %d hw_ctl %d\n",
vid_enc->hw_intf != 0, phys_enc->hw_ctl != 0); phys_enc->hw_intf != 0, phys_enc->hw_ctl != 0);
return; return;
} }
DPU_DEBUG_VIDENC(vid_enc, "\n"); if (WARN_ON(!phys_enc->hw_intf->ops.enable_timing))
if (WARN_ON(!vid_enc->hw_intf->ops.enable_timing))
return; return;
if (phys_enc->enable_state == DPU_ENC_DISABLED) { if (phys_enc->enable_state == DPU_ENC_DISABLED) {
...@@ -647,7 +589,7 @@ static void dpu_encoder_phys_vid_disable(struct dpu_encoder_phys *phys_enc) ...@@ -647,7 +589,7 @@ static void dpu_encoder_phys_vid_disable(struct dpu_encoder_phys *phys_enc)
} }
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
vid_enc->hw_intf->ops.enable_timing(vid_enc->hw_intf, 0); phys_enc->hw_intf->ops.enable_timing(phys_enc->hw_intf, 0);
if (dpu_encoder_phys_vid_is_master(phys_enc)) if (dpu_encoder_phys_vid_is_master(phys_enc))
dpu_encoder_phys_inc_pending(phys_enc); dpu_encoder_phys_inc_pending(phys_enc);
spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
...@@ -666,7 +608,7 @@ static void dpu_encoder_phys_vid_disable(struct dpu_encoder_phys *phys_enc) ...@@ -666,7 +608,7 @@ static void dpu_encoder_phys_vid_disable(struct dpu_encoder_phys *phys_enc)
atomic_set(&phys_enc->pending_kickoff_cnt, 0); atomic_set(&phys_enc->pending_kickoff_cnt, 0);
DRM_ERROR("wait disable failed: id:%u intf:%d ret:%d\n", DRM_ERROR("wait disable failed: id:%u intf:%d ret:%d\n",
DRMID(phys_enc->parent), DRMID(phys_enc->parent),
vid_enc->hw_intf->idx - INTF_0, ret); phys_enc->hw_intf->idx - INTF_0, ret);
} }
} }
...@@ -677,25 +619,21 @@ static void dpu_encoder_phys_vid_handle_post_kickoff( ...@@ -677,25 +619,21 @@ static void dpu_encoder_phys_vid_handle_post_kickoff(
struct dpu_encoder_phys *phys_enc) struct dpu_encoder_phys *phys_enc)
{ {
unsigned long lock_flags; unsigned long lock_flags;
struct dpu_encoder_phys_vid *vid_enc;
if (!phys_enc) { if (!phys_enc) {
DPU_ERROR("invalid encoder\n"); DPU_ERROR("invalid encoder\n");
return; return;
} }
vid_enc = to_dpu_encoder_phys_vid(phys_enc);
DPU_DEBUG_VIDENC(vid_enc, "enable_state %d\n", phys_enc->enable_state);
/* /*
* Video mode must flush CTL before enabling timing engine * Video mode must flush CTL before enabling timing engine
* Video encoders need to turn on their interfaces now * Video encoders need to turn on their interfaces now
*/ */
if (phys_enc->enable_state == DPU_ENC_ENABLING) { if (phys_enc->enable_state == DPU_ENC_ENABLING) {
trace_dpu_enc_phys_vid_post_kickoff(DRMID(phys_enc->parent), trace_dpu_enc_phys_vid_post_kickoff(DRMID(phys_enc->parent),
vid_enc->hw_intf->idx - INTF_0); phys_enc->hw_intf->idx - INTF_0);
spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags); spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
vid_enc->hw_intf->ops.enable_timing(vid_enc->hw_intf, 1); phys_enc->hw_intf->ops.enable_timing(phys_enc->hw_intf, 1);
spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags); spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
phys_enc->enable_state = DPU_ENC_ENABLED; phys_enc->enable_state = DPU_ENC_ENABLED;
} }
...@@ -704,16 +642,13 @@ static void dpu_encoder_phys_vid_handle_post_kickoff( ...@@ -704,16 +642,13 @@ static void dpu_encoder_phys_vid_handle_post_kickoff(
static void dpu_encoder_phys_vid_irq_control(struct dpu_encoder_phys *phys_enc, static void dpu_encoder_phys_vid_irq_control(struct dpu_encoder_phys *phys_enc,
bool enable) bool enable)
{ {
struct dpu_encoder_phys_vid *vid_enc;
int ret; int ret;
if (!phys_enc) if (!phys_enc)
return; return;
vid_enc = to_dpu_encoder_phys_vid(phys_enc);
trace_dpu_enc_phys_vid_irq_ctrl(DRMID(phys_enc->parent), trace_dpu_enc_phys_vid_irq_ctrl(DRMID(phys_enc->parent),
vid_enc->hw_intf->idx - INTF_0, phys_enc->hw_intf->idx - INTF_0,
enable, enable,
atomic_read(&phys_enc->vblank_refcount)); atomic_read(&phys_enc->vblank_refcount));
...@@ -732,19 +667,16 @@ static void dpu_encoder_phys_vid_irq_control(struct dpu_encoder_phys *phys_enc, ...@@ -732,19 +667,16 @@ static void dpu_encoder_phys_vid_irq_control(struct dpu_encoder_phys *phys_enc,
static int dpu_encoder_phys_vid_get_line_count( static int dpu_encoder_phys_vid_get_line_count(
struct dpu_encoder_phys *phys_enc) struct dpu_encoder_phys *phys_enc)
{ {
struct dpu_encoder_phys_vid *vid_enc;
if (!phys_enc) if (!phys_enc)
return -EINVAL; return -EINVAL;
if (!dpu_encoder_phys_vid_is_master(phys_enc)) if (!dpu_encoder_phys_vid_is_master(phys_enc))
return -EINVAL; return -EINVAL;
vid_enc = to_dpu_encoder_phys_vid(phys_enc); if (!phys_enc->hw_intf || !phys_enc->hw_intf->ops.get_line_count)
if (!vid_enc->hw_intf || !vid_enc->hw_intf->ops.get_line_count)
return -EINVAL; return -EINVAL;
return vid_enc->hw_intf->ops.get_line_count(vid_enc->hw_intf); return phys_enc->hw_intf->ops.get_line_count(phys_enc->hw_intf);
} }
static void dpu_encoder_phys_vid_init_ops(struct dpu_encoder_phys_ops *ops) static void dpu_encoder_phys_vid_init_ops(struct dpu_encoder_phys_ops *ops)
...@@ -771,7 +703,6 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init( ...@@ -771,7 +703,6 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init(
struct dpu_enc_phys_init_params *p) struct dpu_enc_phys_init_params *p)
{ {
struct dpu_encoder_phys *phys_enc = NULL; struct dpu_encoder_phys *phys_enc = NULL;
struct dpu_encoder_phys_vid *vid_enc = NULL;
struct dpu_encoder_irq *irq; struct dpu_encoder_irq *irq;
int i, ret = 0; int i, ret = 0;
...@@ -780,18 +711,16 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init( ...@@ -780,18 +711,16 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init(
goto fail; goto fail;
} }
vid_enc = kzalloc(sizeof(*vid_enc), GFP_KERNEL); phys_enc = kzalloc(sizeof(*phys_enc), GFP_KERNEL);
if (!vid_enc) { if (!phys_enc) {
ret = -ENOMEM; ret = -ENOMEM;
goto fail; goto fail;
} }
phys_enc = &vid_enc->base;
phys_enc->hw_mdptop = p->dpu_kms->hw_mdp; phys_enc->hw_mdptop = p->dpu_kms->hw_mdp;
phys_enc->intf_idx = p->intf_idx; phys_enc->intf_idx = p->intf_idx;
DPU_DEBUG_VIDENC(vid_enc, "\n"); DPU_DEBUG_VIDENC(phys_enc, "\n");
dpu_encoder_phys_vid_init_ops(&phys_enc->ops); dpu_encoder_phys_vid_init_ops(&phys_enc->ops);
phys_enc->parent = p->parent; phys_enc->parent = p->parent;
...@@ -825,13 +754,13 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init( ...@@ -825,13 +754,13 @@ struct dpu_encoder_phys *dpu_encoder_phys_vid_init(
init_waitqueue_head(&phys_enc->pending_kickoff_wq); init_waitqueue_head(&phys_enc->pending_kickoff_wq);
phys_enc->enable_state = DPU_ENC_DISABLED; phys_enc->enable_state = DPU_ENC_DISABLED;
DPU_DEBUG_VIDENC(vid_enc, "created intf idx:%d\n", p->intf_idx); DPU_DEBUG_VIDENC(phys_enc, "created intf idx:%d\n", p->intf_idx);
return phys_enc; return phys_enc;
fail: fail:
DPU_ERROR("failed to create encoder\n"); DPU_ERROR("failed to create encoder\n");
if (vid_enc) if (phys_enc)
dpu_encoder_phys_vid_destroy(phys_enc); dpu_encoder_phys_vid_destroy(phys_enc);
return ERR_PTR(ret); return ERR_PTR(ret);
......
...@@ -73,9 +73,6 @@ ...@@ -73,9 +73,6 @@
#define DPU_NAME_SIZE 12 #define DPU_NAME_SIZE 12
/* timeout in frames waiting for frame done */
#define DPU_FRAME_DONE_TIMEOUT 60
/* /*
* struct dpu_irq_callback - IRQ callback handlers * struct dpu_irq_callback - IRQ callback handlers
* @list: list to callback * @list: list to callback
......
...@@ -387,7 +387,7 @@ static void _dpu_plane_set_ot_limit(struct drm_plane *plane, ...@@ -387,7 +387,7 @@ static void _dpu_plane_set_ot_limit(struct drm_plane *plane,
ot_params.width = drm_rect_width(&pdpu->pipe_cfg.src_rect); ot_params.width = drm_rect_width(&pdpu->pipe_cfg.src_rect);
ot_params.height = drm_rect_height(&pdpu->pipe_cfg.src_rect); ot_params.height = drm_rect_height(&pdpu->pipe_cfg.src_rect);
ot_params.is_wfd = !pdpu->is_rt_pipe; ot_params.is_wfd = !pdpu->is_rt_pipe;
ot_params.frame_rate = crtc->mode.vrefresh; ot_params.frame_rate = drm_mode_vrefresh(&crtc->mode);
ot_params.vbif_idx = VBIF_RT; ot_params.vbif_idx = VBIF_RT;
ot_params.clk_ctrl = pdpu->pipe_hw->cap->clk_ctrl; ot_params.clk_ctrl = pdpu->pipe_hw->cap->clk_ctrl;
ot_params.rd = true; ot_params.rd = true;
......
...@@ -59,10 +59,10 @@ static int pingpong_tearcheck_setup(struct drm_encoder *encoder, ...@@ -59,10 +59,10 @@ static int pingpong_tearcheck_setup(struct drm_encoder *encoder,
return -EINVAL; return -EINVAL;
} }
total_lines_x100 = mode->vtotal * mode->vrefresh; total_lines_x100 = mode->vtotal * drm_mode_vrefresh(mode);
if (!total_lines_x100) { if (!total_lines_x100) {
DRM_DEV_ERROR(dev, "%s: vtotal(%d) or vrefresh(%d) is 0\n", DRM_DEV_ERROR(dev, "%s: vtotal(%d) or vrefresh(%d) is 0\n",
__func__, mode->vtotal, mode->vrefresh); __func__, mode->vtotal, drm_mode_vrefresh(mode));
return -EINVAL; return -EINVAL;
} }
......
...@@ -75,7 +75,7 @@ static int msm_gpu_open(struct inode *inode, struct file *file) ...@@ -75,7 +75,7 @@ static int msm_gpu_open(struct inode *inode, struct file *file)
struct msm_gpu_show_priv *show_priv; struct msm_gpu_show_priv *show_priv;
int ret; int ret;
if (!gpu) if (!gpu || !gpu->funcs->gpu_state_get)
return -ENODEV; return -ENODEV;
show_priv = kmalloc(sizeof(*show_priv), GFP_KERNEL); show_priv = kmalloc(sizeof(*show_priv), GFP_KERNEL);
......
...@@ -39,9 +39,10 @@ ...@@ -39,9 +39,10 @@
* MSM_GEM_INFO ioctl. * MSM_GEM_INFO ioctl.
* - 1.4.0 - softpin, MSM_RELOC_BO_DUMP, and GEM_INFO support to set/get * - 1.4.0 - softpin, MSM_RELOC_BO_DUMP, and GEM_INFO support to set/get
* GEM object's debug name * GEM object's debug name
* - 1.5.0 - Add SUBMITQUERY_QUERY ioctl
*/ */
#define MSM_VERSION_MAJOR 1 #define MSM_VERSION_MAJOR 1
#define MSM_VERSION_MINOR 4 #define MSM_VERSION_MINOR 5
#define MSM_VERSION_PATCHLEVEL 0 #define MSM_VERSION_PATCHLEVEL 0
static const struct drm_mode_config_funcs mode_config_funcs = { static const struct drm_mode_config_funcs mode_config_funcs = {
...@@ -457,6 +458,9 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv) ...@@ -457,6 +458,9 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
priv->wq = alloc_ordered_workqueue("msm", 0); priv->wq = alloc_ordered_workqueue("msm", 0);
INIT_WORK(&priv->free_work, msm_gem_free_work);
init_llist_head(&priv->free_list);
INIT_LIST_HEAD(&priv->inactive_list); INIT_LIST_HEAD(&priv->inactive_list);
drm_mode_config_init(ddev); drm_mode_config_init(ddev);
...@@ -964,6 +968,11 @@ static int msm_ioctl_submitqueue_new(struct drm_device *dev, void *data, ...@@ -964,6 +968,11 @@ static int msm_ioctl_submitqueue_new(struct drm_device *dev, void *data,
args->flags, &args->id); args->flags, &args->id);
} }
static int msm_ioctl_submitqueue_query(struct drm_device *dev, void *data,
struct drm_file *file)
{
return msm_submitqueue_query(dev, file->driver_priv, data);
}
static int msm_ioctl_submitqueue_close(struct drm_device *dev, void *data, static int msm_ioctl_submitqueue_close(struct drm_device *dev, void *data,
struct drm_file *file) struct drm_file *file)
...@@ -984,6 +993,7 @@ static const struct drm_ioctl_desc msm_ioctls[] = { ...@@ -984,6 +993,7 @@ static const struct drm_ioctl_desc msm_ioctls[] = {
DRM_IOCTL_DEF_DRV(MSM_GEM_MADVISE, msm_ioctl_gem_madvise, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_GEM_MADVISE, msm_ioctl_gem_madvise, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_NEW, msm_ioctl_submitqueue_new, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_NEW, msm_ioctl_submitqueue_new, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_CLOSE, msm_ioctl_submitqueue_close, DRM_AUTH|DRM_RENDER_ALLOW), DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_CLOSE, msm_ioctl_submitqueue_close, DRM_AUTH|DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_QUERY, msm_ioctl_submitqueue_query, DRM_AUTH|DRM_RENDER_ALLOW),
}; };
static const struct vm_operations_struct vm_ops = { static const struct vm_operations_struct vm_ops = {
...@@ -1019,7 +1029,7 @@ static struct drm_driver msm_driver = { ...@@ -1019,7 +1029,7 @@ static struct drm_driver msm_driver = {
.irq_uninstall = msm_irq_uninstall, .irq_uninstall = msm_irq_uninstall,
.enable_vblank = msm_enable_vblank, .enable_vblank = msm_enable_vblank,
.disable_vblank = msm_disable_vblank, .disable_vblank = msm_disable_vblank,
.gem_free_object = msm_gem_free_object, .gem_free_object_unlocked = msm_gem_free_object,
.gem_vm_ops = &vm_ops, .gem_vm_ops = &vm_ops,
.dumb_create = msm_gem_dumb_create, .dumb_create = msm_gem_dumb_create,
.dumb_map_offset = msm_gem_dumb_map_offset, .dumb_map_offset = msm_gem_dumb_map_offset,
......
...@@ -185,6 +185,10 @@ struct msm_drm_private { ...@@ -185,6 +185,10 @@ struct msm_drm_private {
/* list of GEM objects: */ /* list of GEM objects: */
struct list_head inactive_list; struct list_head inactive_list;
/* worker for delayed free of objects: */
struct work_struct free_work;
struct llist_head free_list;
struct workqueue_struct *wq; struct workqueue_struct *wq;
unsigned int num_planes; unsigned int num_planes;
...@@ -324,6 +328,7 @@ void msm_gem_kernel_put(struct drm_gem_object *bo, ...@@ -324,6 +328,7 @@ void msm_gem_kernel_put(struct drm_gem_object *bo,
struct msm_gem_address_space *aspace, bool locked); struct msm_gem_address_space *aspace, bool locked);
struct drm_gem_object *msm_gem_import(struct drm_device *dev, struct drm_gem_object *msm_gem_import(struct drm_device *dev,
struct dma_buf *dmabuf, struct sg_table *sgt); struct dma_buf *dmabuf, struct sg_table *sgt);
void msm_gem_free_work(struct work_struct *work);
__printf(2, 3) __printf(2, 3)
void msm_gem_object_set_name(struct drm_gem_object *bo, const char *fmt, ...); void msm_gem_object_set_name(struct drm_gem_object *bo, const char *fmt, ...);
...@@ -419,6 +424,8 @@ struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx, ...@@ -419,6 +424,8 @@ struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
u32 id); u32 id);
int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx, int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
u32 prio, u32 flags, u32 *id); u32 prio, u32 flags, u32 *id);
int msm_submitqueue_query(struct drm_device *drm, struct msm_file_private *ctx,
struct drm_msm_submitqueue_query *args);
int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id); int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id);
void msm_submitqueue_close(struct msm_file_private *ctx); void msm_submitqueue_close(struct msm_file_private *ctx);
......
...@@ -851,8 +851,18 @@ void msm_gem_describe_objects(struct list_head *list, struct seq_file *m) ...@@ -851,8 +851,18 @@ void msm_gem_describe_objects(struct list_head *list, struct seq_file *m)
/* don't call directly! Use drm_gem_object_put() and friends */ /* don't call directly! Use drm_gem_object_put() and friends */
void msm_gem_free_object(struct drm_gem_object *obj) void msm_gem_free_object(struct drm_gem_object *obj)
{ {
struct drm_device *dev = obj->dev;
struct msm_gem_object *msm_obj = to_msm_bo(obj); struct msm_gem_object *msm_obj = to_msm_bo(obj);
struct drm_device *dev = obj->dev;
struct msm_drm_private *priv = dev->dev_private;
if (llist_add(&msm_obj->freed, &priv->free_list))
queue_work(priv->wq, &priv->free_work);
}
static void free_object(struct msm_gem_object *msm_obj)
{
struct drm_gem_object *obj = &msm_obj->base;
struct drm_device *dev = obj->dev;
WARN_ON(!mutex_is_locked(&dev->struct_mutex)); WARN_ON(!mutex_is_locked(&dev->struct_mutex));
...@@ -887,6 +897,29 @@ void msm_gem_free_object(struct drm_gem_object *obj) ...@@ -887,6 +897,29 @@ void msm_gem_free_object(struct drm_gem_object *obj)
kfree(msm_obj); kfree(msm_obj);
} }
void msm_gem_free_work(struct work_struct *work)
{
struct msm_drm_private *priv =
container_of(work, struct msm_drm_private, free_work);
struct drm_device *dev = priv->dev;
struct llist_node *freed;
struct msm_gem_object *msm_obj, *next;
while ((freed = llist_del_all(&priv->free_list))) {
mutex_lock(&dev->struct_mutex);
llist_for_each_entry_safe(msm_obj, next,
freed, freed)
free_object(msm_obj);
mutex_unlock(&dev->struct_mutex);
if (need_resched())
break;
}
}
/* convenience method to construct a GEM buffer object, and userspace handle */ /* convenience method to construct a GEM buffer object, and userspace handle */
int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file, int msm_gem_new_handle(struct drm_device *dev, struct drm_file *file,
uint32_t size, uint32_t flags, uint32_t *handle, uint32_t size, uint32_t flags, uint32_t *handle,
...@@ -1017,6 +1050,13 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev, ...@@ -1017,6 +1050,13 @@ static struct drm_gem_object *_msm_gem_new(struct drm_device *dev,
ret = drm_gem_object_init(dev, obj, size); ret = drm_gem_object_init(dev, obj, size);
if (ret) if (ret)
goto fail; goto fail;
/*
* Our buffers are kept pinned, so allocating them from the
* MOVABLE zone is a really bad idea, and conflicts with CMA.
* See comments above new_inode() why this is required _and_
* expected if you're going to pin these pages.
*/
mapping_set_gfp_mask(obj->filp->f_mapping, GFP_HIGHUSER);
} }
return obj; return obj;
......
...@@ -84,6 +84,8 @@ struct msm_gem_object { ...@@ -84,6 +84,8 @@ struct msm_gem_object {
struct list_head vmas; /* list of msm_gem_vma */ struct list_head vmas; /* list of msm_gem_vma */
struct llist_node freed;
/* normally (resv == &_resv) except for imported bo's */ /* normally (resv == &_resv) except for imported bo's */
struct reservation_object *resv; struct reservation_object *resv;
struct reservation_object _resv; struct reservation_object _resv;
...@@ -133,6 +135,7 @@ enum msm_gem_lock { ...@@ -133,6 +135,7 @@ enum msm_gem_lock {
void msm_gem_purge(struct drm_gem_object *obj, enum msm_gem_lock subclass); void msm_gem_purge(struct drm_gem_object *obj, enum msm_gem_lock subclass);
void msm_gem_vunmap(struct drm_gem_object *obj, enum msm_gem_lock subclass); void msm_gem_vunmap(struct drm_gem_object *obj, enum msm_gem_lock subclass);
void msm_gem_free_work(struct work_struct *work);
/* Created per submit-ioctl, to track bo's and cmdstream bufs, etc, /* Created per submit-ioctl, to track bo's and cmdstream bufs, etc,
* associated with the cmdstream submission for synchronization (and * associated with the cmdstream submission for synchronization (and
...@@ -163,7 +166,10 @@ struct msm_gem_submit { ...@@ -163,7 +166,10 @@ struct msm_gem_submit {
} *cmd; /* array of size nr_cmds */ } *cmd; /* array of size nr_cmds */
struct { struct {
uint32_t flags; uint32_t flags;
union {
struct msm_gem_object *obj; struct msm_gem_object *obj;
uint32_t handle;
};
uint64_t iova; uint64_t iova;
} bos[0]; } bos[0];
}; };
......
...@@ -74,27 +74,14 @@ void msm_gem_submit_free(struct msm_gem_submit *submit) ...@@ -74,27 +74,14 @@ void msm_gem_submit_free(struct msm_gem_submit *submit)
kfree(submit); kfree(submit);
} }
static inline unsigned long __must_check
copy_from_user_inatomic(void *to, const void __user *from, unsigned long n)
{
if (access_ok(from, n))
return __copy_from_user_inatomic(to, from, n);
return -EFAULT;
}
static int submit_lookup_objects(struct msm_gem_submit *submit, static int submit_lookup_objects(struct msm_gem_submit *submit,
struct drm_msm_gem_submit *args, struct drm_file *file) struct drm_msm_gem_submit *args, struct drm_file *file)
{ {
unsigned i; unsigned i;
int ret = 0; int ret = 0;
spin_lock(&file->table_lock);
pagefault_disable();
for (i = 0; i < args->nr_bos; i++) { for (i = 0; i < args->nr_bos; i++) {
struct drm_msm_gem_submit_bo submit_bo; struct drm_msm_gem_submit_bo submit_bo;
struct drm_gem_object *obj;
struct msm_gem_object *msm_obj;
void __user *userptr = void __user *userptr =
u64_to_user_ptr(args->bos + (i * sizeof(submit_bo))); u64_to_user_ptr(args->bos + (i * sizeof(submit_bo)));
...@@ -103,16 +90,11 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, ...@@ -103,16 +90,11 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,
*/ */
submit->bos[i].flags = 0; submit->bos[i].flags = 0;
if (copy_from_user_inatomic(&submit_bo, userptr, sizeof(submit_bo))) {
pagefault_enable();
spin_unlock(&file->table_lock);
if (copy_from_user(&submit_bo, userptr, sizeof(submit_bo))) { if (copy_from_user(&submit_bo, userptr, sizeof(submit_bo))) {
ret = -EFAULT; ret = -EFAULT;
i = 0;
goto out; goto out;
} }
spin_lock(&file->table_lock);
pagefault_disable();
}
/* at least one of READ and/or WRITE flags should be set: */ /* at least one of READ and/or WRITE flags should be set: */
#define MANDATORY_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE) #define MANDATORY_FLAGS (MSM_SUBMIT_BO_READ | MSM_SUBMIT_BO_WRITE)
...@@ -121,19 +103,28 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, ...@@ -121,19 +103,28 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,
!(submit_bo.flags & MANDATORY_FLAGS)) { !(submit_bo.flags & MANDATORY_FLAGS)) {
DRM_ERROR("invalid flags: %x\n", submit_bo.flags); DRM_ERROR("invalid flags: %x\n", submit_bo.flags);
ret = -EINVAL; ret = -EINVAL;
goto out_unlock; i = 0;
goto out;
} }
submit->bos[i].handle = submit_bo.handle;
submit->bos[i].flags = submit_bo.flags; submit->bos[i].flags = submit_bo.flags;
/* in validate_objects() we figure out if this is true: */ /* in validate_objects() we figure out if this is true: */
submit->bos[i].iova = submit_bo.presumed; submit->bos[i].iova = submit_bo.presumed;
}
spin_lock(&file->table_lock);
for (i = 0; i < args->nr_bos; i++) {
struct drm_gem_object *obj;
struct msm_gem_object *msm_obj;
/* normally use drm_gem_object_lookup(), but for bulk lookup /* normally use drm_gem_object_lookup(), but for bulk lookup
* all under single table_lock just hit object_idr directly: * all under single table_lock just hit object_idr directly:
*/ */
obj = idr_find(&file->object_idr, submit_bo.handle); obj = idr_find(&file->object_idr, submit->bos[i].handle);
if (!obj) { if (!obj) {
DRM_ERROR("invalid handle %u at index %u\n", submit_bo.handle, i); DRM_ERROR("invalid handle %u at index %u\n", submit->bos[i].handle, i);
ret = -EINVAL; ret = -EINVAL;
goto out_unlock; goto out_unlock;
} }
...@@ -142,7 +133,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, ...@@ -142,7 +133,7 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,
if (!list_empty(&msm_obj->submit_entry)) { if (!list_empty(&msm_obj->submit_entry)) {
DRM_ERROR("handle %u at index %u already on submit list\n", DRM_ERROR("handle %u at index %u already on submit list\n",
submit_bo.handle, i); submit->bos[i].handle, i);
ret = -EINVAL; ret = -EINVAL;
goto out_unlock; goto out_unlock;
} }
...@@ -155,7 +146,6 @@ static int submit_lookup_objects(struct msm_gem_submit *submit, ...@@ -155,7 +146,6 @@ static int submit_lookup_objects(struct msm_gem_submit *submit,
} }
out_unlock: out_unlock:
pagefault_enable();
spin_unlock(&file->table_lock); spin_unlock(&file->table_lock);
out: out:
......
...@@ -85,7 +85,7 @@ msm_gem_map_vma(struct msm_gem_address_space *aspace, ...@@ -85,7 +85,7 @@ msm_gem_map_vma(struct msm_gem_address_space *aspace,
vma->mapped = true; vma->mapped = true;
if (aspace->mmu) if (aspace && aspace->mmu)
ret = aspace->mmu->funcs->map(aspace->mmu, vma->iova, sgt, ret = aspace->mmu->funcs->map(aspace->mmu, vma->iova, sgt,
size, prot); size, prot);
......
...@@ -443,24 +443,15 @@ static void recover_worker(struct work_struct *work) ...@@ -443,24 +443,15 @@ static void recover_worker(struct work_struct *work)
if (submit) { if (submit) {
struct task_struct *task; struct task_struct *task;
/* Increment the fault counts */
gpu->global_faults++;
submit->queue->faults++;
task = get_pid_task(submit->pid, PIDTYPE_PID); task = get_pid_task(submit->pid, PIDTYPE_PID);
if (task) { if (task) {
comm = kstrdup(task->comm, GFP_KERNEL); comm = kstrdup(task->comm, GFP_KERNEL);
/*
* So slightly annoying, in other paths like
* mmap'ing gem buffers, mmap_sem is acquired
* before struct_mutex, which means we can't
* hold struct_mutex across the call to
* get_cmdline(). But submits are retired
* from the same in-order workqueue, so we can
* safely drop the lock here without worrying
* about the submit going away.
*/
mutex_unlock(&dev->struct_mutex);
cmd = kstrdup_quotable_cmdline(task, GFP_KERNEL); cmd = kstrdup_quotable_cmdline(task, GFP_KERNEL);
put_task_struct(task); put_task_struct(task);
mutex_lock(&dev->struct_mutex);
} }
if (comm && cmd) { if (comm && cmd) {
......
...@@ -104,6 +104,9 @@ struct msm_gpu { ...@@ -104,6 +104,9 @@ struct msm_gpu {
/* does gpu need hw_init? */ /* does gpu need hw_init? */
bool needs_hw_init; bool needs_hw_init;
/* number of GPU hangs (for all contexts) */
int global_faults;
/* worker for handling active-list retiring: */ /* worker for handling active-list retiring: */
struct work_struct retire_work; struct work_struct retire_work;
......
...@@ -38,13 +38,8 @@ static int msm_iommu_attach(struct msm_mmu *mmu, const char * const *names, ...@@ -38,13 +38,8 @@ static int msm_iommu_attach(struct msm_mmu *mmu, const char * const *names,
int cnt) int cnt)
{ {
struct msm_iommu *iommu = to_msm_iommu(mmu); struct msm_iommu *iommu = to_msm_iommu(mmu);
int ret;
pm_runtime_get_sync(mmu->dev); return iommu_attach_device(iommu->domain, mmu->dev);
ret = iommu_attach_device(iommu->domain, mmu->dev);
pm_runtime_put_sync(mmu->dev);
return ret;
} }
static void msm_iommu_detach(struct msm_mmu *mmu, const char * const *names, static void msm_iommu_detach(struct msm_mmu *mmu, const char * const *names,
...@@ -52,9 +47,7 @@ static void msm_iommu_detach(struct msm_mmu *mmu, const char * const *names, ...@@ -52,9 +47,7 @@ static void msm_iommu_detach(struct msm_mmu *mmu, const char * const *names,
{ {
struct msm_iommu *iommu = to_msm_iommu(mmu); struct msm_iommu *iommu = to_msm_iommu(mmu);
pm_runtime_get_sync(mmu->dev);
iommu_detach_device(iommu->domain, mmu->dev); iommu_detach_device(iommu->domain, mmu->dev);
pm_runtime_put_sync(mmu->dev);
} }
static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova,
...@@ -63,9 +56,7 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova, ...@@ -63,9 +56,7 @@ static int msm_iommu_map(struct msm_mmu *mmu, uint64_t iova,
struct msm_iommu *iommu = to_msm_iommu(mmu); struct msm_iommu *iommu = to_msm_iommu(mmu);
size_t ret; size_t ret;
// pm_runtime_get_sync(mmu->dev);
ret = iommu_map_sg(iommu->domain, iova, sgt->sgl, sgt->nents, prot); ret = iommu_map_sg(iommu->domain, iova, sgt->sgl, sgt->nents, prot);
// pm_runtime_put_sync(mmu->dev);
WARN_ON(!ret); WARN_ON(!ret);
return (ret == len) ? 0 : -EINVAL; return (ret == len) ? 0 : -EINVAL;
...@@ -75,9 +66,7 @@ static int msm_iommu_unmap(struct msm_mmu *mmu, uint64_t iova, unsigned len) ...@@ -75,9 +66,7 @@ static int msm_iommu_unmap(struct msm_mmu *mmu, uint64_t iova, unsigned len)
{ {
struct msm_iommu *iommu = to_msm_iommu(mmu); struct msm_iommu *iommu = to_msm_iommu(mmu);
pm_runtime_get_sync(mmu->dev);
iommu_unmap(iommu->domain, iova, len); iommu_unmap(iommu->domain, iova, len);
pm_runtime_put_sync(mmu->dev);
return 0; return 0;
} }
......
...@@ -120,6 +120,47 @@ int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx) ...@@ -120,6 +120,47 @@ int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx)
return msm_submitqueue_create(drm, ctx, default_prio, 0, NULL); return msm_submitqueue_create(drm, ctx, default_prio, 0, NULL);
} }
static int msm_submitqueue_query_faults(struct msm_gpu_submitqueue *queue,
struct drm_msm_submitqueue_query *args)
{
size_t size = min_t(size_t, args->len, sizeof(queue->faults));
int ret;
/* If a zero length was passed in, return the data size we expect */
if (!args->len) {
args->len = sizeof(queue->faults);
return 0;
}
/* Set the length to the actual size of the data */
args->len = size;
ret = copy_to_user(u64_to_user_ptr(args->data), &queue->faults, size);
return ret ? -EFAULT : 0;
}
int msm_submitqueue_query(struct drm_device *drm, struct msm_file_private *ctx,
struct drm_msm_submitqueue_query *args)
{
struct msm_gpu_submitqueue *queue;
int ret = -EINVAL;
if (args->pad)
return -EINVAL;
queue = msm_submitqueue_get(ctx, args->id);
if (!queue)
return -ENOENT;
if (args->param == MSM_SUBMITQUEUE_PARAM_FAULTS)
ret = msm_submitqueue_query_faults(queue, args);
msm_submitqueue_put(queue);
return ret;
}
int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id) int msm_submitqueue_remove(struct msm_file_private *ctx, u32 id)
{ {
struct msm_gpu_submitqueue *entry; struct msm_gpu_submitqueue *entry;
......
...@@ -74,6 +74,8 @@ struct drm_msm_timespec { ...@@ -74,6 +74,8 @@ struct drm_msm_timespec {
#define MSM_PARAM_TIMESTAMP 0x05 #define MSM_PARAM_TIMESTAMP 0x05
#define MSM_PARAM_GMEM_BASE 0x06 #define MSM_PARAM_GMEM_BASE 0x06
#define MSM_PARAM_NR_RINGS 0x07 #define MSM_PARAM_NR_RINGS 0x07
#define MSM_PARAM_PP_PGTABLE 0x08 /* => 1 for per-process pagetables, else 0 */
#define MSM_PARAM_FAULTS 0x09
struct drm_msm_param { struct drm_msm_param {
__u32 pipe; /* in, MSM_PIPE_x */ __u32 pipe; /* in, MSM_PIPE_x */
...@@ -286,6 +288,16 @@ struct drm_msm_submitqueue { ...@@ -286,6 +288,16 @@ struct drm_msm_submitqueue {
__u32 id; /* out, identifier */ __u32 id; /* out, identifier */
}; };
#define MSM_SUBMITQUEUE_PARAM_FAULTS 0
struct drm_msm_submitqueue_query {
__u64 data;
__u32 id;
__u32 param;
__u32 len;
__u32 pad;
};
#define DRM_MSM_GET_PARAM 0x00 #define DRM_MSM_GET_PARAM 0x00
/* placeholder: /* placeholder:
#define DRM_MSM_SET_PARAM 0x01 #define DRM_MSM_SET_PARAM 0x01
...@@ -302,6 +314,7 @@ struct drm_msm_submitqueue { ...@@ -302,6 +314,7 @@ struct drm_msm_submitqueue {
*/ */
#define DRM_MSM_SUBMITQUEUE_NEW 0x0A #define DRM_MSM_SUBMITQUEUE_NEW 0x0A
#define DRM_MSM_SUBMITQUEUE_CLOSE 0x0B #define DRM_MSM_SUBMITQUEUE_CLOSE 0x0B
#define DRM_MSM_SUBMITQUEUE_QUERY 0x0C
#define DRM_IOCTL_MSM_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param) #define DRM_IOCTL_MSM_GET_PARAM DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GET_PARAM, struct drm_msm_param)
#define DRM_IOCTL_MSM_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new) #define DRM_IOCTL_MSM_GEM_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_NEW, struct drm_msm_gem_new)
...@@ -313,6 +326,7 @@ struct drm_msm_submitqueue { ...@@ -313,6 +326,7 @@ struct drm_msm_submitqueue {
#define DRM_IOCTL_MSM_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_MADVISE, struct drm_msm_gem_madvise) #define DRM_IOCTL_MSM_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_GEM_MADVISE, struct drm_msm_gem_madvise)
#define DRM_IOCTL_MSM_SUBMITQUEUE_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_NEW, struct drm_msm_submitqueue) #define DRM_IOCTL_MSM_SUBMITQUEUE_NEW DRM_IOWR(DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_NEW, struct drm_msm_submitqueue)
#define DRM_IOCTL_MSM_SUBMITQUEUE_CLOSE DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_CLOSE, __u32) #define DRM_IOCTL_MSM_SUBMITQUEUE_CLOSE DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_CLOSE, __u32)
#define DRM_IOCTL_MSM_SUBMITQUEUE_QUERY DRM_IOW (DRM_COMMAND_BASE + DRM_MSM_SUBMITQUEUE_QUERY, struct drm_msm_submitqueue_query)
#if defined(__cplusplus) #if defined(__cplusplus)
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment