| /* |
| * 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; |
| |
| ret = dpu_be_get(dpu_be); |
| |
| 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; |
| |
| ret = dpu_be_get(dpu_be); |
| |
| 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; |
| |
| ret = dpu_be_get(dpu_bliteng); |
| |
| 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"); |