*
  */
 
+#include <linux/types.h>
+#include <linux/cpumask.h>
+#include <linux/qcom_scm.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_reserved_mem.h>
+#include <linux/soc/qcom/mdt_loader.h>
 #include "msm_gem.h"
 #include "msm_mmu.h"
 #include "a5xx_gpu.h"
 extern bool hang_debug;
 static void a5xx_dump(struct msm_gpu *gpu);
 
+#define GPU_PAS_ID 13
+
+#if IS_ENABLED(CONFIG_QCOM_MDT_LOADER)
+
+static int zap_shader_load_mdt(struct device *dev, const char *fwname)
+{
+       const struct firmware *fw;
+       phys_addr_t mem_phys;
+       ssize_t mem_size;
+       void *mem_region = NULL;
+       int ret;
+
+       /* Request the MDT file for the firmware */
+       ret = request_firmware(&fw, fwname, dev);
+       if (ret) {
+               DRM_DEV_ERROR(dev, "Unable to load %s\n", fwname);
+               return ret;
+       }
+
+       /* 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 = dmam_alloc_coherent(dev, mem_size, &mem_phys, GFP_KERNEL);
+       if (!mem_region) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       /* Load the rest of the MDT */
+       ret = qcom_mdt_load(dev, fw, fwname, GPU_PAS_ID, mem_region, mem_phys,
+               mem_size);
+       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:
+       release_firmware(fw);
+
+       return ret;
+}
+#else
+static int zap_shader_load_mdt(struct device *dev, const char *fwname)
+{
+       return -ENODEV;
+}
+#endif
+
 static void a5xx_submit(struct msm_gpu *gpu, struct msm_gem_submit *submit,
        struct msm_file_private *ctx)
 {
        return 0;
 }
 
+#define SCM_GPU_ZAP_SHADER_RESUME 0
+
+static int a5xx_zap_shader_resume(struct msm_gpu *gpu)
+{
+       int ret;
+
+       ret = qcom_scm_set_remote_state(SCM_GPU_ZAP_SHADER_RESUME, GPU_PAS_ID);
+       if (ret)
+               DRM_ERROR("%s: zap-shader resume failed: %d\n",
+                       gpu->name, ret);
+
+       return ret;
+}
+
+/* Set up a child device to "own" the zap shader */
+static int a5xx_zap_shader_dev_init(struct device *parent, struct device *dev)
+{
+       struct device_node *node;
+       int ret;
+
+       if (dev->parent)
+               return 0;
+
+       /* Find the sub-node for the zap shader */
+       node = of_get_child_by_name(parent->of_node, "zap-shader");
+       if (!node) {
+               DRM_DEV_ERROR(parent, "zap-shader not found in device tree\n");
+               return -ENODEV;
+       }
+
+       dev->parent = parent;
+       dev->of_node = node;
+       dev_set_name(dev, "adreno_zap_shader");
+
+       ret = device_register(dev);
+       if (ret) {
+               DRM_DEV_ERROR(parent, "Couldn't register zap shader device\n");
+               goto out;
+       }
+
+       ret = of_reserved_mem_device_init(dev);
+       if (ret) {
+               DRM_DEV_ERROR(parent, "Unable to set up the reserved memory\n");
+               device_unregister(dev);
+       }
+
+out:
+       if (ret)
+               dev->parent = NULL;
+
+       return ret;
+}
+
+static int a5xx_zap_shader_init(struct msm_gpu *gpu)
+{
+       static bool loaded;
+       struct adreno_gpu *adreno_gpu = to_adreno_gpu(gpu);
+       struct a5xx_gpu *a5xx_gpu = to_a5xx_gpu(adreno_gpu);
+       struct platform_device *pdev = a5xx_gpu->pdev;
+       int ret;
+
+       /*
+        * If the zap shader is already loaded into memory we just need to kick
+        * the remote processor to reinitialize it
+        */
+       if (loaded)
+               return a5xx_zap_shader_resume(gpu);
+
+       /* 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) {
+               DRM_DEV_ERROR(&pdev->dev,
+                       "Zap shader firmware file not specified for this target\n");
+               return -ENODEV;
+       }
+
+       ret = a5xx_zap_shader_dev_init(&pdev->dev, &a5xx_gpu->zap_dev);
+
+       if (!ret)
+               ret = zap_shader_load_mdt(&a5xx_gpu->zap_dev,
+                       adreno_gpu->info->zapfw);
+
+       loaded = !ret;
+
+       return ret;
+}
+
 #define A5XX_INT_MASK (A5XX_RBBM_INT_0_MASK_RBBM_AHB_ERROR | \
          A5XX_RBBM_INT_0_MASK_RBBM_TRANSFER_TIMEOUT | \
          A5XX_RBBM_INT_0_MASK_RBBM_ME_MS_TIMEOUT | \
                        return -EINVAL;
        }
 
-       /* Put the GPU into unsecure mode */
-       gpu_write(gpu, REG_A5XX_RBBM_SECVID_TRUST_CNTL, 0x0);
+       /*
+        * 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 = a5xx_zap_shader_init(gpu);
+       if (!ret) {
+               OUT_PKT7(gpu->rb, CP_SET_SECURE_MODE, 1);
+               OUT_RING(gpu->rb, 0x00000000);
+
+               gpu->funcs->flush(gpu);
+               if (!gpu->funcs->idle(gpu))
+                       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_A5XX_RBBM_SECVID_TRUST_CNTL, 0x0);
+       }
 
        return 0;
 }
 
        DBG("%s", gpu->name);
 
+       if (a5xx_gpu->zap_dev.parent)
+               device_unregister(&a5xx_gpu->zap_dev);
+
        if (a5xx_gpu->pm4_bo) {
                if (a5xx_gpu->pm4_iova)
                        msm_gem_put_iova(a5xx_gpu->pm4_bo, gpu->id);