blob: e8511bbe4e4efa3ad9ae819f06d2a86ea5c8487e [file] [log] [blame]
/*
* Copyright 2017 NXP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
#include <drm/drmP.h>
#include <drm/imx_drm.h>
#include <linux/component.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <video/dpu.h>
#include "imx-drm.h"
struct imx_drm_dpu_bliteng {
struct dpu_bliteng *dpu_be;
struct list_head list;
};
static DEFINE_MUTEX(imx_drm_dpu_bliteng_lock);
static LIST_HEAD(imx_drm_dpu_bliteng_list);
static int imx_dpu_num;
static struct imx_drm_dpu_bliteng *imx_drm_dpu_bliteng_find_by_id(s32 id)
{
struct imx_drm_dpu_bliteng *bliteng;
mutex_lock(&imx_drm_dpu_bliteng_lock);
list_for_each_entry(bliteng, &imx_drm_dpu_bliteng_list, list) {
if (id == dpu_bliteng_get_id(bliteng->dpu_be)) {
mutex_unlock(&imx_drm_dpu_bliteng_lock);
return bliteng;
}
}
mutex_unlock(&imx_drm_dpu_bliteng_lock);
return NULL;
}
static int imx_drm_dpu_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file)
{
struct drm_imx_dpu_set_cmdlist *req;
struct imx_drm_dpu_bliteng *bliteng;
struct dpu_bliteng *dpu_be;
u32 cmd_nr, *cmd, *cmd_list;
void *user_data;
s32 id = 0;
struct drm_imx_dpu_frame_info frame_info;
int ret;
req = data;
user_data = (void *)(unsigned long)req->user_data;
if (copy_from_user(&id, (void __user *)user_data,
sizeof(id))) {
return -EFAULT;
}
if (id != 0 && id != 1)
return -EINVAL;
user_data += sizeof(id);
if (copy_from_user(&frame_info, (void __user *)user_data,
sizeof(frame_info))) {
return -EFAULT;
}
bliteng = imx_drm_dpu_bliteng_find_by_id(id);
if (!bliteng) {
DRM_ERROR("Failed to get dpu_bliteng\n");
return -ENODEV;
}
dpu_be = bliteng->dpu_be;
retry:
ret = dpu_be_get(dpu_be);
if (ret == -EBUSY)
goto retry;
cmd_nr = req->cmd_nr;
cmd = (u32 *)(unsigned long)req->cmd;
cmd_list = dpu_bliteng_get_cmd_list(dpu_be);
if (copy_from_user(cmd_list, (void __user *)cmd,
sizeof(*cmd) * cmd_nr)) {
ret = -EFAULT;
goto err;
}
dpu_be_configure_prefetch(dpu_be, frame_info.width, frame_info.height,
frame_info.x_offset, frame_info.y_offset,
frame_info.stride, frame_info.format,
frame_info.modifier, frame_info.baddr,
frame_info.uv_addr);
ret = dpu_be_blit(dpu_be, cmd_list, cmd_nr);
err:
dpu_be_put(dpu_be);
return ret;
}
static int imx_drm_dpu_wait_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file)
{
struct drm_imx_dpu_wait *wait;
struct imx_drm_dpu_bliteng *bliteng;
struct dpu_bliteng *dpu_be;
void *user_data;
s32 id = 0;
int ret;
wait = data;
user_data = (void *)(unsigned long)wait->user_data;
if (copy_from_user(&id, (void __user *)user_data,
sizeof(id))) {
return -EFAULT;
}
if (id != 0 && id != 1)
return -EINVAL;
bliteng = imx_drm_dpu_bliteng_find_by_id(id);
if (!bliteng) {
DRM_ERROR("Failed to get dpu_bliteng\n");
return -ENODEV;
}
dpu_be = bliteng->dpu_be;
retry:
ret = dpu_be_get(dpu_be);
if (ret == -EBUSY)
goto retry;
dpu_be_wait(dpu_be);
dpu_be_put(dpu_be);
return ret;
}
static int imx_drm_dpu_get_param_ioctl(struct drm_device *drm_dev, void *data,
struct drm_file *file)
{
enum drm_imx_dpu_param *param = data;
int ret;
switch (*param) {
case (DRM_IMX_MAX_DPUS):
ret = imx_dpu_num;
break;
default:
ret = -EINVAL;
DRM_ERROR("Unknown param![%d]\n", *param);
break;
}
return ret;
}
static struct drm_ioctl_desc imx_drm_dpu_ioctls[] = {
DRM_IOCTL_DEF_DRV(IMX_DPU_SET_CMDLIST, imx_drm_dpu_set_cmdlist_ioctl,
DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(IMX_DPU_WAIT, imx_drm_dpu_wait_ioctl,
DRM_RENDER_ALLOW),
DRM_IOCTL_DEF_DRV(IMX_DPU_GET_PARAM, imx_drm_dpu_get_param_ioctl,
DRM_RENDER_ALLOW),
};
static int dpu_bliteng_bind(struct device *dev, struct device *master,
void *data)
{
struct drm_device *drm = (struct drm_device *)data;
struct imx_drm_dpu_bliteng *bliteng;
struct dpu_bliteng *dpu_bliteng = NULL;
int ret;
bliteng = devm_kzalloc(dev, sizeof(*bliteng), GFP_KERNEL);
if (!bliteng)
return -ENOMEM;
INIT_LIST_HEAD(&bliteng->list);
ret = dpu_bliteng_get_empty_instance(&dpu_bliteng, dev);
if (ret)
return ret;
dpu_bliteng_set_id(dpu_bliteng, imx_dpu_num);
dpu_bliteng_set_dev(dpu_bliteng, dev);
ret = dpu_bliteng_init(dpu_bliteng);
if (ret)
return ret;
mutex_lock(&imx_drm_dpu_bliteng_lock);
bliteng->dpu_be = dpu_bliteng;
list_add_tail(&bliteng->list, &imx_drm_dpu_bliteng_list);
mutex_unlock(&imx_drm_dpu_bliteng_lock);
dev_set_drvdata(dev, dpu_bliteng);
imx_dpu_num++;
if (drm->driver->num_ioctls == 0) {
drm->driver->ioctls = imx_drm_dpu_ioctls;
drm->driver->num_ioctls = ARRAY_SIZE(imx_drm_dpu_ioctls);
}
return 0;
}
static void dpu_bliteng_unbind(struct device *dev, struct device *master,
void *data)
{
struct drm_device *drm = (struct drm_device *)data;
struct imx_drm_dpu_bliteng *bliteng;
struct dpu_bliteng *dpu_bliteng = dev_get_drvdata(dev);
s32 id = dpu_bliteng_get_id(dpu_bliteng);
bliteng = imx_drm_dpu_bliteng_find_by_id(id);
list_del(&bliteng->list);
dpu_bliteng_fini(dpu_bliteng);
dev_set_drvdata(dev, NULL);
imx_dpu_num--;
if (drm->driver->num_ioctls != 0) {
drm->driver->ioctls = NULL;
drm->driver->num_ioctls = 0;
}
}
static const struct component_ops dpu_bliteng_ops = {
.bind = dpu_bliteng_bind,
.unbind = dpu_bliteng_unbind,
};
static int dpu_bliteng_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
if (!dev->platform_data)
return -EINVAL;
return component_add(dev, &dpu_bliteng_ops);
}
static int dpu_bliteng_remove(struct platform_device *pdev)
{
component_del(&pdev->dev, &dpu_bliteng_ops);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int dpu_bliteng_suspend(struct device *dev)
{
struct dpu_bliteng *dpu_bliteng = dev_get_drvdata(dev);
int ret;
if (dpu_bliteng == NULL)
return 0;
retry:
ret = dpu_be_get(dpu_bliteng);
if (ret == -EBUSY)
goto retry;
dpu_be_wait(dpu_bliteng);
dpu_be_put(dpu_bliteng);
dpu_bliteng_fini(dpu_bliteng);
return 0;
}
static int dpu_bliteng_resume(struct device *dev)
{
struct dpu_bliteng *dpu_bliteng = dev_get_drvdata(dev);
if (dpu_bliteng != NULL)
dpu_bliteng_init(dpu_bliteng);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(dpu_bliteng_pm_ops,
dpu_bliteng_suspend, dpu_bliteng_resume);
struct platform_driver dpu_bliteng_driver = {
.driver = {
.name = "imx-drm-dpu-bliteng",
.pm = &dpu_bliteng_pm_ops,
},
.probe = dpu_bliteng_probe,
.remove = dpu_bliteng_remove,
};
module_platform_driver(dpu_bliteng_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("NXP Semiconductor");
MODULE_DESCRIPTION("i.MX DRM DPU BLITENG");