rk3288: vcodec service, support iommu for vpu and hevc
authorljf <ljf@rock-chips.com>
Tue, 25 Mar 2014 06:07:25 +0000 (14:07 +0800)
committerljf <ljf@rock-chips.com>
Tue, 25 Mar 2014 06:07:25 +0000 (14:07 +0800)
arch/arm/mach-rockchip/vcodec_service.c

index 56cac6c852c6a66278ab47e00ab20f61833bc5dd..56bafde434d2e8bf733fe40672211adeb6e23c7d 100755 (executable)
 #include <linux/rockchip_ion.h>\r
 #endif\r
 \r
+//#define CONFIG_VCODEC_MMU\r
+\r
+#ifdef CONFIG_VCODEC_MMU\r
+#include <linux/rockchip/iovmm.h>\r
+#include <linux/rockchip/sysmmu.h>\r
+#include <linux/dma-buf.h>\r
+#endif\r
+\r
 #ifdef CONFIG_DEBUG_FS\r
 #include <linux/debugfs.h>\r
 #endif\r
@@ -201,11 +209,14 @@ typedef struct vpu_session {
  */\r
 typedef struct vpu_reg {\r
        VPU_CLIENT_TYPE         type;\r
-       VPU_FREQ                freq;\r
+       VPU_FREQ                    freq;\r
        vpu_session             *session;\r
        struct list_head        session_link;           /* link to vpu service session */\r
        struct list_head        status_link;            /* link to register set list */\r
        unsigned long           size;\r
+#if defined(CONFIG_VCODEC_MMU)    \r
+    struct list_head    mem_region_list;\r
+#endif    \r
        unsigned long           *reg;\r
 } vpu_reg;\r
 \r
@@ -222,6 +233,17 @@ enum vcodec_device_id {
        VCODEC_DEVICE_ID_HEVC\r
 };\r
 \r
+struct vcodec_mem_region {\r
+    struct list_head srv_lnk;\r
+    struct list_head reg_lnk;\r
+    struct list_head session_lnk;\r
+    dma_addr_t       iova;              /* virtual address for iommu */\r
+    struct dma_buf   *buf;\r
+    struct dma_buf_attachment *attachment;\r
+    struct sg_table *sg_table;\r
+    struct ion_handle *hdl;\r
+};\r
+\r
 typedef struct vpu_service_info {\r
        struct wake_lock        wake_lock;\r
        struct delayed_work     power_off_work;\r
@@ -269,6 +291,10 @@ typedef struct vpu_service_info {
        struct ion_client * ion_client;\r
 #endif \r
 \r
+#if defined(CONFIG_VCODEC_MMU)\r
+    struct list_head mem_region_list;\r
+#endif\r
+\r
        enum vcodec_device_id dev_id;\r
 \r
     struct delayed_work simulate_work;\r
@@ -543,6 +569,53 @@ static inline bool reg_check_interlace(vpu_reg *reg)
 }\r
 \r
 #if defined(CONFIG_VCODEC_MMU)\r
+\r
+static unsigned int vcodec_map_ion_handle(vpu_service_info *pservice, \r
+                                          vpu_reg *reg,\r
+                                          struct ion_handle *ion_handle,\r
+                                          struct dma_buf *buf, int offset)\r
+{\r
+    struct vcodec_mem_region *mem_region = kzalloc(sizeof(struct vcodec_mem_region), GFP_KERNEL);\r
+    if (mem_region == NULL) {\r
+        dev_err(pservice->dev, "allocate memory for iommu memory region failed\n");\r
+        return -1;\r
+    }\r
+    \r
+    mem_region->buf = buf;\r
+    mem_region->hdl = ion_handle;\r
+    \r
+    mem_region->attachment = dma_buf_attach(buf, pservice->dev);\r
+    if (IS_ERR_OR_NULL(mem_region->attachment)) {\r
+        dev_err(pservice->dev, "dma_buf_attach() failed: %ld\n", PTR_ERR(mem_region->attachment));\r
+        goto err_buf_map_attach;\r
+    }\r
+    \r
+    mem_region->sg_table = dma_buf_map_attachment(mem_region->attachment, DMA_BIDIRECTIONAL);\r
+    if (IS_ERR_OR_NULL(mem_region->sg_table)) {\r
+        dev_err(pservice->dev, "dma_buf_map_attachment() failed: %ld\n", PTR_ERR(mem_region->sg_table));\r
+        goto err_buf_map_attachment;\r
+    }\r
+    \r
+    mem_region->iova = iovmm_map(pservice->dev, mem_region->sg_table->sgl, offset, buf->size);\r
+    if (mem_region->iova == 0 || IS_ERR_VALUE(mem_region->iova)) {\r
+        dev_err(pservice->dev, "iovmm_map() failed: %d\n", mem_region->iova);\r
+        goto err_iovmm_map;\r
+    }\r
+    \r
+    INIT_LIST_HEAD(&mem_region->reg_lnk);\r
+    list_add_tail(&mem_region->reg_lnk, &reg->mem_region_list);\r
+    \r
+    return mem_region->iova;\r
+    \r
+err_iovmm_map:\r
+       dma_buf_unmap_attachment(mem_region->attachment, mem_region->sg_table, DMA_BIDIRECTIONAL);\r
+err_buf_map_attachment:\r
+       dma_buf_detach(buf, mem_region->attachment);\r
+err_buf_map_attach:\r
+    kfree(mem_region);\r
+       return 0;\r
+}\r
+\r
 static u8 table_vpu_dec[] = {\r
        12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 40, 41\r
 };\r
@@ -555,7 +628,7 @@ static u8 table_hevc_dec[1] = {
 \r
 };\r
 \r
-static int reg_address_translate(struct vpu_service_info *pservice, vpu_reg *reg)\r
+static int vcodec_reg_address_translate(struct vpu_service_info *pservice, vpu_reg *reg)\r
 {\r
        VPU_HW_ID hw_id;\r
        int i;\r
@@ -569,8 +642,9 @@ static int reg_address_translate(struct vpu_service_info *pservice, vpu_reg *reg
             for (i=0; i<sizeof(table_vpu_dec); i++) {\r
                                int usr_fd;\r
                                struct ion_handle *hdl;\r
-                               ion_phys_addr_t phy_addr;\r
-                               size_t len;\r
+                               //ion_phys_addr_t phy_addr;\r
+                struct dma_buf *buf;\r
+                               //size_t len;\r
                                int offset;\r
 \r
 #if 0\r
@@ -585,14 +659,26 @@ static int reg_address_translate(struct vpu_service_info *pservice, vpu_reg *reg
                                        hdl = ion_import_dma_buf(pservice->ion_client, usr_fd);\r
                     if (IS_ERR(hdl)) {\r
                                                pr_err("import dma-buf from fd %d failed\n", usr_fd);\r
-                                               return ERR_PTR(hdl);\r
+                                               return PTR_ERR(hdl);\r
                     }\r
 \r
+#if 0\r
                                        ion_phys(pservice->ion_client, hdl, &phy_addr, &len);\r
 \r
                                        reg->reg[table_vpu_dec[i]] = phy_addr + offset;\r
-\r
-                                       ion_free(pservice->ion_client, hdl);\r
+                    \r
+                    ion_free(pservice->ion_client, hdl);\r
+#else \r
+                    buf = ion_share_dma_buf(pservice->ion_client, hdl);\r
+                    if (IS_ERR_OR_NULL(buf)) {\r
+                        dev_err(pservice->dev, "ion_share_dma_buf() failed\n");\r
+                        ion_free(pservice->ion_client, hdl);\r
+                        return PTR_ERR(buf);\r
+                    }\r
+                    \r
+                    reg->reg[table_vpu_dec[i]] = vcodec_map_ion_handle(pservice, reg, hdl, buf, offset);\r
+#endif\r
+                                       \r
                 }\r
             }\r
         } else if (reg->type == VPU_ENC) {\r
@@ -624,6 +710,10 @@ static vpu_reg *reg_init(struct vpu_service_info *pservice, vpu_session *session
        INIT_LIST_HEAD(&reg->session_link);\r
        INIT_LIST_HEAD(&reg->status_link);\r
 \r
+#if defined(CONFIG_VCODEC_MMU)    \r
+    INIT_LIST_HEAD(&reg->mem_region_list);\r
+#endif    \r
+\r
        if (copy_from_user(&reg->reg[0], (void __user *)src, size)) {\r
                pr_err("error: copy_from_user failed in reg_init\n");\r
                kfree(reg);\r
@@ -631,7 +721,7 @@ static vpu_reg *reg_init(struct vpu_service_info *pservice, vpu_session *session
        }\r
 \r
 #if defined(CONFIG_VCODEC_MMU)\r
-    if (0 > reg_address_translate(pservice, reg)) {\r
+    if (0 > vcodec_reg_address_translate(pservice, reg)) {\r
                pr_err("error: translate reg address failed\n");\r
                kfree(reg);\r
                return NULL;\r
@@ -665,10 +755,32 @@ static vpu_reg *reg_init(struct vpu_service_info *pservice, vpu_session *session
 \r
 static void reg_deinit(struct vpu_service_info *pservice, vpu_reg *reg)\r
 {\r
+#if defined(CONFIG_VCODEC_MMU)    \r
+    struct vcodec_mem_region *mem_region = NULL, *n;\r
+#endif\r
+    \r
        list_del_init(&reg->session_link);\r
        list_del_init(&reg->status_link);\r
        if (reg == pservice->reg_codec) pservice->reg_codec = NULL;\r
        if (reg == pservice->reg_pproc) pservice->reg_pproc = NULL;\r
+    \r
+#if defined(CONFIG_VCODEC_MMU)\r
+    // release memory region attach to this registers table.\r
+    list_for_each_entry_safe(mem_region, n, &reg->mem_region_list, reg_lnk) {\r
+        iovmm_unmap(pservice->dev, mem_region->iova);\r
+        \r
+        dma_buf_unmap_attachment(mem_region->attachment, mem_region->sg_table, DMA_BIDIRECTIONAL);\r
+        dma_buf_detach(mem_region->buf, mem_region->attachment);\r
+        \r
+        dma_buf_put(mem_region->buf);\r
+        ion_free(pservice->ion_client, mem_region->hdl);\r
+        \r
+        list_del_init(&mem_region->reg_lnk);\r
+        \r
+        kfree(mem_region);\r
+    }\r
+#endif    \r
+    \r
        kfree(reg);\r
 }\r
 \r
@@ -966,7 +1078,7 @@ static int return_reg(struct vpu_service_info *pservice, vpu_reg *reg, u32 __use
        }\r
        case VPU_DEC : {\r
         int reg_len = pservice->hw_info->hw_id == HEVC_ID ? REG_NUM_HEVC_DEC : REG_NUM_9190_DEC;\r
-               if (copy_to_user(dst, &reg->reg[0], reg_len))\r
+               if (copy_to_user(dst, &reg->reg[0], SIZE_REG(reg_len)))\r
                        ret = -EFAULT;\r
                break;\r
        }\r
@@ -1231,6 +1343,10 @@ static int vcodec_probe(struct platform_device *pdev)
     struct device_node *np = pdev->dev.of_node;\r
     struct vpu_service_info *pservice = devm_kzalloc(dev, sizeof(struct vpu_service_info), GFP_KERNEL);\r
     char *prop = (char*)dev_name(dev);\r
+#if defined(CONFIG_VCODEC_MMU)\r
+    struct device *mmu_dev = NULL;\r
+    char mmu_dev_dts_name[40];\r
+#endif\r
 \r
     pr_info("probe device %s\n", dev_name(dev));\r
 \r
@@ -1385,6 +1501,13 @@ static int vcodec_probe(struct platform_device *pdev)
        } else {\r
                dev_info(&pdev->dev, "vcodec ion client create success!\n");\r
        }\r
+    \r
+    sprintf(mmu_dev_dts_name, "iommu,%s", dev_name(dev));\r
+    \r
+    mmu_dev = rockchip_get_sysmmu_device_by_compatible(mmu_dev_dts_name);\r
+    platform_set_sysmmu(mmu_dev, pservice->dev);\r
+    \r
+    iovmm_activate(pservice->dev);\r
 #endif\r
 \r
 #if HEVC_SIM_ENABLE\r