Commit d87342c1 authored by Inki Dae's avatar Inki Dae Committed by Inki Dae

drm/exynos: add iommu support for g2d

Chagelog v2:
removed unnecessary structure, struct g2d_gem_node.

Chagelog v1:
This patch adds iommu support for g2d driver. For this, it
adds subdrv_probe/remove callback to enable or disable
g2d iommu. And with this patch, in case of using g2d iommu,
we can get or put device address to a gem handle from user
through exynos_drm_gem_get/put_dma_addr(). Actually, these
functions take a reference to a gem handle so that the gem
object used by g2d dma is released properly.

And runqueue_node has a pointer to drm_file object of current
process to manage gem handles to owner.

This patch is based on the below patch set, "drm/exynos: add
iommu support for -next".
     http://www.spinics.net/lists/dri-devel/msg29041.htmlSigned-off-by: default avatarInki Dae <inki.dae@samsung.com>
Signed-off-by: default avatarKyungmin Park <kyungmin.park@samsung.com>
parent 1055b39f
...@@ -17,11 +17,14 @@ ...@@ -17,11 +17,14 @@
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/dma-mapping.h>
#include <linux/dma-attrs.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/exynos_drm.h> #include <drm/exynos_drm.h>
#include "exynos_drm_drv.h" #include "exynos_drm_drv.h"
#include "exynos_drm_gem.h" #include "exynos_drm_gem.h"
#include "exynos_drm_iommu.h"
#define G2D_HW_MAJOR_VER 4 #define G2D_HW_MAJOR_VER 4
#define G2D_HW_MINOR_VER 1 #define G2D_HW_MINOR_VER 1
...@@ -92,6 +95,8 @@ ...@@ -92,6 +95,8 @@
#define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM) #define G2D_CMDLIST_POOL_SIZE (G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM)
#define G2D_CMDLIST_DATA_NUM (G2D_CMDLIST_SIZE / sizeof(u32) - 2) #define G2D_CMDLIST_DATA_NUM (G2D_CMDLIST_SIZE / sizeof(u32) - 2)
#define MAX_BUF_ADDR_NR 6
/* cmdlist data structure */ /* cmdlist data structure */
struct g2d_cmdlist { struct g2d_cmdlist {
u32 head; u32 head;
...@@ -104,15 +109,11 @@ struct drm_exynos_pending_g2d_event { ...@@ -104,15 +109,11 @@ struct drm_exynos_pending_g2d_event {
struct drm_exynos_g2d_event event; struct drm_exynos_g2d_event event;
}; };
struct g2d_gem_node {
struct list_head list;
unsigned int handle;
};
struct g2d_cmdlist_node { struct g2d_cmdlist_node {
struct list_head list; struct list_head list;
struct g2d_cmdlist *cmdlist; struct g2d_cmdlist *cmdlist;
unsigned int gem_nr; unsigned int map_nr;
unsigned int handles[MAX_BUF_ADDR_NR];
dma_addr_t dma_addr; dma_addr_t dma_addr;
struct drm_exynos_pending_g2d_event *event; struct drm_exynos_pending_g2d_event *event;
...@@ -122,6 +123,7 @@ struct g2d_runqueue_node { ...@@ -122,6 +123,7 @@ struct g2d_runqueue_node {
struct list_head list; struct list_head list;
struct list_head run_cmdlist; struct list_head run_cmdlist;
struct list_head event_list; struct list_head event_list;
struct drm_file *filp;
pid_t pid; pid_t pid;
struct completion complete; struct completion complete;
int async; int async;
...@@ -143,6 +145,7 @@ struct g2d_data { ...@@ -143,6 +145,7 @@ struct g2d_data {
struct mutex cmdlist_mutex; struct mutex cmdlist_mutex;
dma_addr_t cmdlist_pool; dma_addr_t cmdlist_pool;
void *cmdlist_pool_virt; void *cmdlist_pool_virt;
struct dma_attrs cmdlist_dma_attrs;
/* runqueue*/ /* runqueue*/
struct g2d_runqueue_node *runqueue_node; struct g2d_runqueue_node *runqueue_node;
...@@ -155,11 +158,17 @@ static int g2d_init_cmdlist(struct g2d_data *g2d) ...@@ -155,11 +158,17 @@ static int g2d_init_cmdlist(struct g2d_data *g2d)
{ {
struct device *dev = g2d->dev; struct device *dev = g2d->dev;
struct g2d_cmdlist_node *node = g2d->cmdlist_node; struct g2d_cmdlist_node *node = g2d->cmdlist_node;
struct exynos_drm_subdrv *subdrv = &g2d->subdrv;
int nr; int nr;
int ret; int ret;
g2d->cmdlist_pool_virt = dma_alloc_coherent(dev, G2D_CMDLIST_POOL_SIZE, init_dma_attrs(&g2d->cmdlist_dma_attrs);
&g2d->cmdlist_pool, GFP_KERNEL); dma_set_attr(DMA_ATTR_WRITE_COMBINE, &g2d->cmdlist_dma_attrs);
g2d->cmdlist_pool_virt = dma_alloc_attrs(subdrv->drm_dev->dev,
G2D_CMDLIST_POOL_SIZE,
&g2d->cmdlist_pool, GFP_KERNEL,
&g2d->cmdlist_dma_attrs);
if (!g2d->cmdlist_pool_virt) { if (!g2d->cmdlist_pool_virt) {
dev_err(dev, "failed to allocate dma memory\n"); dev_err(dev, "failed to allocate dma memory\n");
return -ENOMEM; return -ENOMEM;
...@@ -184,18 +193,20 @@ static int g2d_init_cmdlist(struct g2d_data *g2d) ...@@ -184,18 +193,20 @@ static int g2d_init_cmdlist(struct g2d_data *g2d)
return 0; return 0;
err: err:
dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
g2d->cmdlist_pool); g2d->cmdlist_pool_virt,
g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
return ret; return ret;
} }
static void g2d_fini_cmdlist(struct g2d_data *g2d) static void g2d_fini_cmdlist(struct g2d_data *g2d)
{ {
struct device *dev = g2d->dev; struct exynos_drm_subdrv *subdrv = &g2d->subdrv;
kfree(g2d->cmdlist_node); kfree(g2d->cmdlist_node);
dma_free_coherent(dev, G2D_CMDLIST_POOL_SIZE, g2d->cmdlist_pool_virt, dma_free_attrs(subdrv->drm_dev->dev, G2D_CMDLIST_POOL_SIZE,
g2d->cmdlist_pool); g2d->cmdlist_pool_virt,
g2d->cmdlist_pool, &g2d->cmdlist_dma_attrs);
} }
static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d) static struct g2d_cmdlist_node *g2d_get_cmdlist(struct g2d_data *g2d)
...@@ -245,62 +256,51 @@ static void g2d_add_cmdlist_to_inuse(struct exynos_drm_g2d_private *g2d_priv, ...@@ -245,62 +256,51 @@ static void g2d_add_cmdlist_to_inuse(struct exynos_drm_g2d_private *g2d_priv,
list_add_tail(&node->event->base.link, &g2d_priv->event_list); list_add_tail(&node->event->base.link, &g2d_priv->event_list);
} }
static int g2d_get_cmdlist_gem(struct drm_device *drm_dev, static int g2d_map_cmdlist_gem(struct g2d_data *g2d,
struct drm_file *file, struct g2d_cmdlist_node *node,
struct g2d_cmdlist_node *node) struct drm_device *drm_dev,
struct drm_file *file)
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv;
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv;
struct g2d_cmdlist *cmdlist = node->cmdlist; struct g2d_cmdlist *cmdlist = node->cmdlist;
dma_addr_t *addr;
int offset; int offset;
int i; int i;
for (i = 0; i < node->gem_nr; i++) { for (i = 0; i < node->map_nr; i++) {
struct g2d_gem_node *gem_node; unsigned long handle;
dma_addr_t *addr;
gem_node = kzalloc(sizeof(*gem_node), GFP_KERNEL);
if (!gem_node) {
dev_err(g2d_priv->dev, "failed to allocate gem node\n");
return -ENOMEM;
}
offset = cmdlist->last - (i * 2 + 1); offset = cmdlist->last - (i * 2 + 1);
gem_node->handle = cmdlist->data[offset]; handle = cmdlist->data[offset];
addr = exynos_drm_gem_get_dma_addr(drm_dev, gem_node->handle, addr = exynos_drm_gem_get_dma_addr(drm_dev, handle, file);
file);
if (IS_ERR(addr)) { if (IS_ERR(addr)) {
node->gem_nr = i; node->map_nr = i;
kfree(gem_node); return -EFAULT;
return PTR_ERR(addr);
} }
cmdlist->data[offset] = *addr; cmdlist->data[offset] = *addr;
list_add_tail(&gem_node->list, &g2d_priv->gem_list); node->handles[i] = handle;
g2d_priv->gem_nr++;
} }
return 0; return 0;
} }
static void g2d_put_cmdlist_gem(struct drm_device *drm_dev, static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d,
struct drm_file *file, struct g2d_cmdlist_node *node,
unsigned int nr) struct drm_file *filp)
{ {
struct drm_exynos_file_private *file_priv = file->driver_priv; struct exynos_drm_subdrv *subdrv = &g2d->subdrv;
struct exynos_drm_g2d_private *g2d_priv = file_priv->g2d_priv; int i;
struct g2d_gem_node *node, *n;
list_for_each_entry_safe_reverse(node, n, &g2d_priv->gem_list, list) { for (i = 0; i < node->map_nr; i++) {
if (!nr) unsigned int handle = node->handles[i];
break;
exynos_drm_gem_put_dma_addr(drm_dev, node->handle, file); exynos_drm_gem_put_dma_addr(subdrv->drm_dev, handle, filp);
list_del_init(&node->list);
kfree(node); node->handles[i] = 0;
nr--;
} }
node->map_nr = 0;
} }
static void g2d_dma_start(struct g2d_data *g2d, static void g2d_dma_start(struct g2d_data *g2d,
...@@ -337,10 +337,18 @@ static struct g2d_runqueue_node *g2d_get_runqueue_node(struct g2d_data *g2d) ...@@ -337,10 +337,18 @@ static struct g2d_runqueue_node *g2d_get_runqueue_node(struct g2d_data *g2d)
static void g2d_free_runqueue_node(struct g2d_data *g2d, static void g2d_free_runqueue_node(struct g2d_data *g2d,
struct g2d_runqueue_node *runqueue_node) struct g2d_runqueue_node *runqueue_node)
{ {
struct g2d_cmdlist_node *node;
if (!runqueue_node) if (!runqueue_node)
return; return;
mutex_lock(&g2d->cmdlist_mutex); mutex_lock(&g2d->cmdlist_mutex);
/*
* commands in run_cmdlist have been completed so unmap all gem
* objects in each command node so that they are unreferenced.
*/
list_for_each_entry(node, &runqueue_node->run_cmdlist, list)
g2d_unmap_cmdlist_gem(g2d, node, runqueue_node->filp);
list_splice_tail_init(&runqueue_node->run_cmdlist, &g2d->free_cmdlist); list_splice_tail_init(&runqueue_node->run_cmdlist, &g2d->free_cmdlist);
mutex_unlock(&g2d->cmdlist_mutex); mutex_unlock(&g2d->cmdlist_mutex);
...@@ -587,7 +595,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, ...@@ -587,7 +595,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
if (ret < 0) if (ret < 0)
goto err_free_event; goto err_free_event;
node->gem_nr = req->cmd_gem_nr; node->map_nr = req->cmd_gem_nr;
if (req->cmd_gem_nr) { if (req->cmd_gem_nr) {
struct drm_exynos_g2d_cmd *cmd_gem; struct drm_exynos_g2d_cmd *cmd_gem;
...@@ -605,7 +613,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, ...@@ -605,7 +613,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
if (ret < 0) if (ret < 0)
goto err_free_event; goto err_free_event;
ret = g2d_get_cmdlist_gem(drm_dev, file, node); ret = g2d_map_cmdlist_gem(g2d, node, drm_dev, file);
if (ret < 0) if (ret < 0)
goto err_unmap; goto err_unmap;
} }
...@@ -624,7 +632,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data, ...@@ -624,7 +632,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
return 0; return 0;
err_unmap: err_unmap:
g2d_put_cmdlist_gem(drm_dev, file, node->gem_nr); g2d_unmap_cmdlist_gem(g2d, node, file);
err_free_event: err_free_event:
if (node->event) { if (node->event) {
spin_lock_irqsave(&drm_dev->event_lock, flags); spin_lock_irqsave(&drm_dev->event_lock, flags);
...@@ -680,6 +688,7 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, ...@@ -680,6 +688,7 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
mutex_lock(&g2d->runqueue_mutex); mutex_lock(&g2d->runqueue_mutex);
runqueue_node->pid = current->pid; runqueue_node->pid = current->pid;
runqueue_node->filp = file;
list_add_tail(&runqueue_node->list, &g2d->runqueue); list_add_tail(&runqueue_node->list, &g2d->runqueue);
if (!g2d->runqueue_node) if (!g2d->runqueue_node)
g2d_exec_runqueue(g2d); g2d_exec_runqueue(g2d);
...@@ -696,6 +705,43 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data, ...@@ -696,6 +705,43 @@ int exynos_g2d_exec_ioctl(struct drm_device *drm_dev, void *data,
} }
EXPORT_SYMBOL_GPL(exynos_g2d_exec_ioctl); EXPORT_SYMBOL_GPL(exynos_g2d_exec_ioctl);
static int g2d_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
{
struct g2d_data *g2d;
int ret;
g2d = dev_get_drvdata(dev);
if (!g2d)
return -EFAULT;
/* allocate dma-aware cmdlist buffer. */
ret = g2d_init_cmdlist(g2d);
if (ret < 0) {
dev_err(dev, "cmdlist init failed\n");
return ret;
}
if (!is_drm_iommu_supported(drm_dev))
return 0;
ret = drm_iommu_attach_device(drm_dev, dev);
if (ret < 0) {
dev_err(dev, "failed to enable iommu.\n");
g2d_fini_cmdlist(g2d);
}
return ret;
}
static void g2d_subdrv_remove(struct drm_device *drm_dev, struct device *dev)
{
if (!is_drm_iommu_supported(drm_dev))
return;
drm_iommu_detach_device(drm_dev, dev);
}
static int g2d_open(struct drm_device *drm_dev, struct device *dev, static int g2d_open(struct drm_device *drm_dev, struct device *dev,
struct drm_file *file) struct drm_file *file)
{ {
...@@ -734,12 +780,19 @@ static void g2d_close(struct drm_device *drm_dev, struct device *dev, ...@@ -734,12 +780,19 @@ static void g2d_close(struct drm_device *drm_dev, struct device *dev,
return; return;
mutex_lock(&g2d->cmdlist_mutex); mutex_lock(&g2d->cmdlist_mutex);
list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) list_for_each_entry_safe(node, n, &g2d_priv->inuse_cmdlist, list) {
/*
* unmap all gem objects not completed.
*
* P.S. if current process was terminated forcely then
* there may be some commands in inuse_cmdlist so unmap
* them.
*/
g2d_unmap_cmdlist_gem(g2d, node, file);
list_move_tail(&node->list, &g2d->free_cmdlist); list_move_tail(&node->list, &g2d->free_cmdlist);
}
mutex_unlock(&g2d->cmdlist_mutex); mutex_unlock(&g2d->cmdlist_mutex);
g2d_put_cmdlist_gem(drm_dev, file, g2d_priv->gem_nr);
kfree(file_priv->g2d_priv); kfree(file_priv->g2d_priv);
} }
...@@ -778,15 +831,11 @@ static int __devinit g2d_probe(struct platform_device *pdev) ...@@ -778,15 +831,11 @@ static int __devinit g2d_probe(struct platform_device *pdev)
mutex_init(&g2d->cmdlist_mutex); mutex_init(&g2d->cmdlist_mutex);
mutex_init(&g2d->runqueue_mutex); mutex_init(&g2d->runqueue_mutex);
ret = g2d_init_cmdlist(g2d);
if (ret < 0)
goto err_destroy_workqueue;
g2d->gate_clk = clk_get(dev, "fimg2d"); g2d->gate_clk = clk_get(dev, "fimg2d");
if (IS_ERR(g2d->gate_clk)) { if (IS_ERR(g2d->gate_clk)) {
dev_err(dev, "failed to get gate clock\n"); dev_err(dev, "failed to get gate clock\n");
ret = PTR_ERR(g2d->gate_clk); ret = PTR_ERR(g2d->gate_clk);
goto err_fini_cmdlist; goto err_destroy_workqueue;
} }
pm_runtime_enable(dev); pm_runtime_enable(dev);
...@@ -818,6 +867,8 @@ static int __devinit g2d_probe(struct platform_device *pdev) ...@@ -818,6 +867,8 @@ static int __devinit g2d_probe(struct platform_device *pdev)
subdrv = &g2d->subdrv; subdrv = &g2d->subdrv;
subdrv->dev = dev; subdrv->dev = dev;
subdrv->probe = g2d_subdrv_probe;
subdrv->remove = g2d_subdrv_remove;
subdrv->open = g2d_open; subdrv->open = g2d_open;
subdrv->close = g2d_close; subdrv->close = g2d_close;
...@@ -835,8 +886,6 @@ static int __devinit g2d_probe(struct platform_device *pdev) ...@@ -835,8 +886,6 @@ static int __devinit g2d_probe(struct platform_device *pdev)
err_put_clk: err_put_clk:
pm_runtime_disable(dev); pm_runtime_disable(dev);
clk_put(g2d->gate_clk); clk_put(g2d->gate_clk);
err_fini_cmdlist:
g2d_fini_cmdlist(g2d);
err_destroy_workqueue: err_destroy_workqueue:
destroy_workqueue(g2d->g2d_workq); destroy_workqueue(g2d->g2d_workq);
err_destroy_slab: err_destroy_slab:
......
...@@ -266,14 +266,14 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, ...@@ -266,14 +266,14 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
return 0; return 0;
} }
void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, dma_addr_t *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
unsigned int gem_handle, unsigned int gem_handle,
struct drm_file *file_priv) struct drm_file *filp)
{ {
struct exynos_drm_gem_obj *exynos_gem_obj; struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_gem_object *obj; struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, file_priv, gem_handle); obj = drm_gem_object_lookup(dev, filp, gem_handle);
if (!obj) { if (!obj) {
DRM_ERROR("failed to lookup gem object.\n"); DRM_ERROR("failed to lookup gem object.\n");
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
...@@ -294,12 +294,12 @@ void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, ...@@ -294,12 +294,12 @@ void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
void exynos_drm_gem_put_dma_addr(struct drm_device *dev, void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle, unsigned int gem_handle,
struct drm_file *file_priv) struct drm_file *filp)
{ {
struct exynos_drm_gem_obj *exynos_gem_obj; struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_gem_object *obj; struct drm_gem_object *obj;
obj = drm_gem_object_lookup(dev, file_priv, gem_handle); obj = drm_gem_object_lookup(dev, filp, gem_handle);
if (!obj) { if (!obj) {
DRM_ERROR("failed to lookup gem object.\n"); DRM_ERROR("failed to lookup gem object.\n");
return; return;
......
...@@ -105,9 +105,9 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, ...@@ -105,9 +105,9 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
* other drivers such as 2d/3d acceleration drivers. * other drivers such as 2d/3d acceleration drivers.
* with this function call, gem object reference count would be increased. * with this function call, gem object reference count would be increased.
*/ */
void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, dma_addr_t *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
unsigned int gem_handle, unsigned int gem_handle,
struct drm_file *file_priv); struct drm_file *filp);
/* /*
* put dma address from gem handle and this function could be used for * put dma address from gem handle and this function could be used for
...@@ -116,7 +116,7 @@ void *exynos_drm_gem_get_dma_addr(struct drm_device *dev, ...@@ -116,7 +116,7 @@ void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
*/ */
void exynos_drm_gem_put_dma_addr(struct drm_device *dev, void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
unsigned int gem_handle, unsigned int gem_handle,
struct drm_file *file_priv); struct drm_file *filp);
/* get buffer offset to map to user space. */ /* get buffer offset to map to user space. */
int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
......
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