media: venus: firmware: add no TZ boot and shutdown routine
authorVikash Garodia <vgarodia@codeaurora.org>
Wed, 17 Oct 2018 13:18:22 +0000 (09:18 -0400)
committerMauro Carvalho Chehab <mchehab+samsung@kernel.org>
Fri, 23 Nov 2018 11:34:12 +0000 (06:34 -0500)
Video hardware is mainly comprised of vcodec subsystem and video
control subsystem. Video control has ARM9 which executes the video
firmware instructions whereas vcodec does the video frame processing.
This change adds support to load the video firmware and bring ARM9
out of reset for platforms which does not have trustzone.
An iommu domain is associated and managed with the firmware device.

Signed-off-by: Vikash Garodia <vgarodia@codeaurora.org>
Acked-by: Stanimir Varbanov <stanimir.varbanov@linaro.org>
Reviewed-by: Alexandre Courbot <acourbot@chromium.org>
Tested-by: Alexandre Courbot <acourbot@chromium.org>
Signed-off-by: Hans Verkuil <hverkuil-cisco@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
drivers/media/platform/qcom/venus/core.c
drivers/media/platform/qcom/venus/core.h
drivers/media/platform/qcom/venus/firmware.c
drivers/media/platform/qcom/venus/firmware.h
drivers/media/platform/qcom/venus/hfi_venus_io.h

index 440f25f2d515ef0d411d1528dc08a01625dde2e2..3bd3b8ab1f82011f112c53eb1156a3d4c72e2f09 100644 (file)
@@ -76,7 +76,7 @@ static void venus_sys_error_handler(struct work_struct *work)
        hfi_core_deinit(core, true);
        hfi_destroy(core);
        mutex_lock(&core->lock);
-       venus_shutdown(core->dev);
+       venus_shutdown(core);
 
        pm_runtime_put_sync(core->dev);
 
@@ -327,7 +327,7 @@ err_dev_unregister:
 err_core_deinit:
        hfi_core_deinit(core, false);
 err_venus_shutdown:
-       venus_shutdown(dev);
+       venus_shutdown(core);
 err_runtime_disable:
        pm_runtime_set_suspended(dev);
        pm_runtime_disable(dev);
@@ -348,7 +348,7 @@ static int venus_remove(struct platform_device *pdev)
        WARN_ON(ret);
 
        hfi_destroy(core);
-       venus_shutdown(dev);
+       venus_shutdown(core);
        of_platform_depopulate(dev);
 
        venus_firmware_deinit(core);
index c629666df2344c36f83dfebba89912871a577a9a..6382cea2918582af545818a3cce4c4d47245aa25 100644 (file)
@@ -133,6 +133,7 @@ struct venus_core {
        unsigned int use_tz;
        struct video_firmware {
                struct device *dev;
+               struct iommu_domain *iommu_domain;
        } fw;
        struct mutex lock;
        struct list_head instances;
index 98b3b166aa1ba3724e8d231c8d2e5a8e9c8c8257..c29acfd70c1b152cd58a5783e052792582692dce 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/device.h>
 #include <linux/firmware.h>
 #include <linux/kernel.h>
+#include <linux/iommu.h>
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
@@ -122,6 +123,56 @@ err_unmap:
        return ret;
 }
 
+static int venus_boot_no_tz(struct venus_core *core, phys_addr_t mem_phys,
+                           size_t mem_size)
+{
+       struct iommu_domain *iommu;
+       struct device *dev;
+       int ret;
+
+       dev = core->fw.dev;
+       if (!dev)
+               return -EPROBE_DEFER;
+
+       iommu = core->fw.iommu_domain;
+
+       ret = iommu_map(iommu, VENUS_FW_START_ADDR, mem_phys, mem_size,
+                       IOMMU_READ | IOMMU_WRITE | IOMMU_PRIV);
+       if (ret) {
+               dev_err(dev, "could not map video firmware region\n");
+               return ret;
+       }
+
+       venus_reset_cpu(core);
+
+       return 0;
+}
+
+static int venus_shutdown_no_tz(struct venus_core *core)
+{
+       struct iommu_domain *iommu;
+       size_t unmapped;
+       u32 reg;
+       struct device *dev = core->fw.dev;
+       void __iomem *base = core->base;
+
+       /* Assert the reset to ARM9 */
+       reg = readl_relaxed(base + WRAPPER_A9SS_SW_RESET);
+       reg |= WRAPPER_A9SS_SW_RESET_BIT;
+       writel_relaxed(reg, base + WRAPPER_A9SS_SW_RESET);
+
+       /* Make sure reset is asserted before the mapping is removed */
+       mb();
+
+       iommu = core->fw.iommu_domain;
+
+       unmapped = iommu_unmap(iommu, VENUS_FW_START_ADDR, VENUS_FW_MEM_SIZE);
+       if (unmapped != VENUS_FW_MEM_SIZE)
+               dev_err(dev, "failed to unmap firmware\n");
+
+       return 0;
+}
+
 int venus_boot(struct venus_core *core)
 {
        struct device *dev = core->dev;
@@ -139,17 +190,30 @@ int venus_boot(struct venus_core *core)
                return -EINVAL;
        }
 
-       return qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
+       if (core->use_tz)
+               ret = qcom_scm_pas_auth_and_reset(VENUS_PAS_ID);
+       else
+               ret = venus_boot_no_tz(core, mem_phys, mem_size);
+
+       return ret;
 }
 
-int venus_shutdown(struct device *dev)
+int venus_shutdown(struct venus_core *core)
 {
-       return qcom_scm_pas_shutdown(VENUS_PAS_ID);
+       int ret;
+
+       if (core->use_tz)
+               ret = qcom_scm_pas_shutdown(VENUS_PAS_ID);
+       else
+               ret = venus_shutdown_no_tz(core);
+
+       return ret;
 }
 
 int venus_firmware_init(struct venus_core *core)
 {
        struct platform_device_info info;
+       struct iommu_domain *iommu_dom;
        struct platform_device *pdev;
        struct device_node *np;
        int ret;
@@ -182,10 +246,27 @@ int venus_firmware_init(struct venus_core *core)
 
        core->fw.dev = &pdev->dev;
 
+       iommu_dom = iommu_domain_alloc(&platform_bus_type);
+       if (!iommu_dom) {
+               dev_err(core->fw.dev, "Failed to allocate iommu domain\n");
+               ret = -ENOMEM;
+               goto err_unregister;
+       }
+
+       ret = iommu_attach_device(iommu_dom, core->fw.dev);
+       if (ret) {
+               dev_err(core->fw.dev, "could not attach device\n");
+               goto err_iommu_free;
+       }
+
+       core->fw.iommu_domain = iommu_dom;
+
        of_node_put(np);
 
        return 0;
 
+err_iommu_free:
+       iommu_domain_free(iommu_dom);
 err_unregister:
        platform_device_unregister(pdev);
        of_node_put(np);
@@ -194,8 +275,15 @@ err_unregister:
 
 void venus_firmware_deinit(struct venus_core *core)
 {
+       struct iommu_domain *iommu;
+
        if (!core->fw.dev)
                return;
 
+       iommu = core->fw.iommu_domain;
+
+       iommu_detach_device(iommu, core->fw.dev);
+       iommu_domain_free(iommu);
+
        platform_device_unregister(to_platform_device(core->fw.dev));
 }
index fd7edf0c7b754ada15a95383dfa09eaf502d667f..119a9a4fc1a2b24ada3d05cf31c18dfa019d539b 100644 (file)
@@ -19,7 +19,7 @@ struct device;
 int venus_firmware_init(struct venus_core *core);
 void venus_firmware_deinit(struct venus_core *core);
 int venus_boot(struct venus_core *core);
-int venus_shutdown(struct device *dev);
+int venus_shutdown(struct venus_core *core);
 int venus_set_hw_state(struct venus_core *core, bool suspend);
 
 static inline int venus_set_hw_state_suspend(struct venus_core *core)
index d69f51b70ad2df546032f517700c04c7c7e4c88a..ef0c72a0c8929aa385d422a520cd2e8ddab6ee7b 100644 (file)
 #define WRAPPER_NONPIX_START_ADDR              (WRAPPER_BASE + 0x1030)
 #define WRAPPER_NONPIX_END_ADDR                        (WRAPPER_BASE + 0x1034)
 #define WRAPPER_A9SS_SW_RESET                  (WRAPPER_BASE + 0x3000)
+#define WRAPPER_A9SS_SW_RESET_BIT              BIT(4)
 
 /* Venus 4xx */
 #define WRAPPER_VCODEC0_MMCC_POWER_STATUS      (WRAPPER_BASE + 0x90)