X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fgpu%2Fdrm%2Frockchip%2Frockchip_drm_drv.c;h=d0cb8b62afea7376b7451b18130629c5e12b086d;hb=17f3b4e9bb831d79922a986d62c058f175b42e32;hp=f22e1e1ee64aae52a1e3efb657a78790bd6ba573;hpb=be4773e6a11a0cc1e63c9c32f000b870e51b8c01;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c index f22e1e1ee64a..d0cb8b62afea 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c @@ -14,21 +14,25 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include +#include +#include #include #include #include #include #include +#include #include "rockchip_drm_drv.h" #include "rockchip_drm_fb.h" #include "rockchip_drm_fbdev.h" #include "rockchip_drm_gem.h" +#include "rockchip_drm_rga.h" #define DRIVER_NAME "rockchip" #define DRIVER_DESC "RockChip Soc DRM" @@ -36,6 +40,9 @@ #define DRIVER_MAJOR 1 #define DRIVER_MINOR 0 +static LIST_HEAD(rockchip_drm_subdrv_list); +static DEFINE_MUTEX(subdrv_list_mutex); + /* * Attach a (component) device to the shared drm dma mapping from master drm * device. This is used by the VOPs to map GEM buffers to a common DMA @@ -44,7 +51,8 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, struct device *dev) { - struct dma_iommu_mapping *mapping = drm_dev->dev->archdata.mapping; + struct rockchip_drm_private *private = drm_dev->dev_private; + struct iommu_domain *domain = private->domain; int ret; ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); @@ -52,23 +60,35 @@ int rockchip_drm_dma_attach_device(struct drm_device *drm_dev, return ret; dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + ret = iommu_attach_device(domain, dev); + if (ret) { + dev_err(dev, "Failed to attach iommu device\n"); + return ret; + } - return arm_iommu_attach_device(dev, mapping); + if (!common_iommu_setup_dma_ops(dev, 0x10000000, SZ_2G, domain->ops)) { + dev_err(dev, "Failed to set dma_ops\n"); + iommu_detach_device(domain, dev); + ret = -ENODEV; + } + + return ret; } -EXPORT_SYMBOL_GPL(rockchip_drm_dma_attach_device); void rockchip_drm_dma_detach_device(struct drm_device *drm_dev, struct device *dev) { - arm_iommu_detach_device(dev); + struct rockchip_drm_private *private = drm_dev->dev_private; + struct iommu_domain *domain = private->domain; + + iommu_detach_device(domain, dev); } -EXPORT_SYMBOL_GPL(rockchip_drm_dma_detach_device); -int rockchip_register_crtc_funcs(struct drm_device *dev, - const struct rockchip_crtc_funcs *crtc_funcs, - int pipe) +int rockchip_register_crtc_funcs(struct drm_crtc *crtc, + const struct rockchip_crtc_funcs *crtc_funcs) { - struct rockchip_drm_private *priv = dev->dev_private; + int pipe = drm_crtc_index(crtc); + struct rockchip_drm_private *priv = crtc->dev->dev_private; if (pipe > ROCKCHIP_MAX_CRTC) return -EINVAL; @@ -77,18 +97,17 @@ int rockchip_register_crtc_funcs(struct drm_device *dev, return 0; } -EXPORT_SYMBOL_GPL(rockchip_register_crtc_funcs); -void rockchip_unregister_crtc_funcs(struct drm_device *dev, int pipe) +void rockchip_unregister_crtc_funcs(struct drm_crtc *crtc) { - struct rockchip_drm_private *priv = dev->dev_private; + int pipe = drm_crtc_index(crtc); + struct rockchip_drm_private *priv = crtc->dev->dev_private; if (pipe > ROCKCHIP_MAX_CRTC) return; priv->crtc_funcs[pipe] = NULL; } -EXPORT_SYMBOL_GPL(rockchip_unregister_crtc_funcs); static struct drm_crtc *rockchip_crtc_from_pipe(struct drm_device *drm, int pipe) @@ -130,17 +149,25 @@ static void rockchip_drm_crtc_disable_vblank(struct drm_device *dev, static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) { struct rockchip_drm_private *private; - struct dma_iommu_mapping *mapping; struct device *dev = drm_dev->dev; struct drm_connector *connector; + struct iommu_group *group; int ret; private = devm_kzalloc(drm_dev->dev, sizeof(*private), GFP_KERNEL); if (!private) return -ENOMEM; + mutex_init(&private->commit.lock); + INIT_WORK(&private->commit.work, rockchip_drm_atomic_work); + drm_dev->dev_private = private; +#ifdef CONFIG_DRM_DMA_SYNC + private->cpu_fence_context = fence_context_alloc(1); + atomic_set(&private->cpu_fence_seqno, 0); +#endif + drm_mode_config_init(drm_dev); rockchip_drm_mode_config_init(drm_dev); @@ -152,23 +179,36 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) goto err_config_cleanup; } - /* TODO(djkurtz): fetch the mapping start/size from somewhere */ - mapping = arm_iommu_create_mapping(&platform_bus_type, 0x00000000, - SZ_2G); - if (IS_ERR(mapping)) { - ret = PTR_ERR(mapping); - goto err_config_cleanup; - } + private->domain = iommu_domain_alloc(&platform_bus_type); + if (!private->domain) + return -ENOMEM; - ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + ret = iommu_get_dma_cookie(private->domain); if (ret) - goto err_release_mapping; - - dma_set_max_seg_size(dev, DMA_BIT_MASK(32)); + goto err_free_domain; + + group = iommu_group_get(dev); + if (!group) { + group = iommu_group_alloc(); + if (IS_ERR(group)) { + dev_err(dev, "Failed to allocate IOMMU group\n"); + goto err_put_cookie; + } - ret = arm_iommu_attach_device(dev, mapping); + ret = iommu_group_add_device(group, dev); + iommu_group_put(group); + if (ret) { + dev_err(dev, "failed to add device to IOMMU group\n"); + goto err_put_cookie; + } + } + /* + * Attach virtual iommu device, sub iommu device can share the same + * mapping with it. + */ + ret = rockchip_drm_dma_attach_device(drm_dev, dev); if (ret) - goto err_release_mapping; + goto err_group_remove_device; /* Try to bind all sub drivers. */ ret = component_bind_all(dev, drm_dev); @@ -212,6 +252,8 @@ static int rockchip_drm_load(struct drm_device *drm_dev, unsigned long flags) */ drm_dev->vblank_disable_allowed = true; + drm_mode_config_reset(drm_dev); + ret = rockchip_drm_fbdev_init(drm_dev); if (ret) goto err_vblank_cleanup; @@ -224,9 +266,13 @@ err_kms_helper_poll_fini: err_unbind: component_unbind_all(dev, drm_dev); err_detach_device: - arm_iommu_detach_device(dev); -err_release_mapping: - arm_iommu_release_mapping(dev->archdata.mapping); + rockchip_drm_dma_detach_device(drm_dev, dev); +err_group_remove_device: + iommu_group_remove_device(dev); +err_put_cookie: + iommu_put_dma_cookie(private->domain); +err_free_domain: + iommu_domain_free(private->domain); err_config_cleanup: drm_mode_config_cleanup(drm_dev); drm_dev->dev_private = NULL; @@ -236,19 +282,131 @@ err_config_cleanup: static int rockchip_drm_unload(struct drm_device *drm_dev) { struct device *dev = drm_dev->dev; + struct rockchip_drm_private *private = drm_dev->dev_private; rockchip_drm_fbdev_fini(drm_dev); drm_vblank_cleanup(drm_dev); drm_kms_helper_poll_fini(drm_dev); component_unbind_all(dev, drm_dev); - arm_iommu_detach_device(dev); - arm_iommu_release_mapping(dev->archdata.mapping); + rockchip_drm_dma_detach_device(drm_dev, dev); + iommu_group_remove_device(dev); + iommu_put_dma_cookie(private->domain); + iommu_domain_free(private->domain); drm_mode_config_cleanup(drm_dev); drm_dev->dev_private = NULL; return 0; } +static void rockchip_drm_crtc_cancel_pending_vblank(struct drm_crtc *crtc, + struct drm_file *file_priv) +{ + struct rockchip_drm_private *priv = crtc->dev->dev_private; + int pipe = drm_crtc_index(crtc); + + if (pipe < ROCKCHIP_MAX_CRTC && + priv->crtc_funcs[pipe] && + priv->crtc_funcs[pipe]->cancel_pending_vblank) + priv->crtc_funcs[pipe]->cancel_pending_vblank(crtc, file_priv); +} + +int rockchip_drm_register_subdrv(struct drm_rockchip_subdrv *subdrv) +{ + if (!subdrv) + return -EINVAL; + + mutex_lock(&subdrv_list_mutex); + list_add_tail(&subdrv->list, &rockchip_drm_subdrv_list); + mutex_unlock(&subdrv_list_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(rockchip_drm_register_subdrv); + +int rockchip_drm_unregister_subdrv(struct drm_rockchip_subdrv *subdrv) +{ + if (!subdrv) + return -EINVAL; + + mutex_lock(&subdrv_list_mutex); + list_del(&subdrv->list); + mutex_unlock(&subdrv_list_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(rockchip_drm_unregister_subdrv); + +static int rockchip_drm_open(struct drm_device *dev, struct drm_file *file) +{ + struct rockchip_drm_file_private *file_priv; + struct drm_rockchip_subdrv *subdrv; + int ret = 0; + + file_priv = kzalloc(sizeof(*file_priv), GFP_KERNEL); + if (!file_priv) + return -ENOMEM; + INIT_LIST_HEAD(&file_priv->gem_cpu_acquire_list); + + file->driver_priv = file_priv; + + mutex_lock(&subdrv_list_mutex); + list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) { + ret = subdrv->open(dev, subdrv->dev, file); + if (ret) { + mutex_unlock(&subdrv_list_mutex); + goto err_free_file_priv; + } + } + mutex_unlock(&subdrv_list_mutex); + + return 0; + +err_free_file_priv: + kfree(file_priv); + file_priv = NULL; + + return ret; +} + +static void rockchip_drm_preclose(struct drm_device *dev, + struct drm_file *file_priv) +{ + struct rockchip_drm_file_private *file_private = file_priv->driver_priv; + struct rockchip_gem_object_node *cur, *d; + struct drm_rockchip_subdrv *subdrv; + struct drm_crtc *crtc; + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) + rockchip_drm_crtc_cancel_pending_vblank(crtc, file_priv); + + mutex_lock(&dev->struct_mutex); + list_for_each_entry_safe(cur, d, + &file_private->gem_cpu_acquire_list, list) { +#ifdef CONFIG_DRM_DMA_SYNC + BUG_ON(!cur->rockchip_gem_obj->acquire_fence); + drm_fence_signal_and_put(&cur->rockchip_gem_obj->acquire_fence); +#endif + drm_gem_object_unreference(&cur->rockchip_gem_obj->base); + kfree(cur); + } + /* since we are deleting the whole list, just initialize the header + * instead of calling list_del for every element + */ + INIT_LIST_HEAD(&file_private->gem_cpu_acquire_list); + mutex_unlock(&dev->struct_mutex); + + mutex_lock(&subdrv_list_mutex); + list_for_each_entry(subdrv, &rockchip_drm_subdrv_list, list) + subdrv->close(dev, subdrv->dev, file_priv); + mutex_unlock(&subdrv_list_mutex); +} + +static void rockchip_drm_postclose(struct drm_device *dev, struct drm_file *file) +{ + kfree(file->driver_priv); + file->driver_priv = NULL; +} + void rockchip_drm_lastclose(struct drm_device *dev) { struct rockchip_drm_private *priv = dev->dev_private; @@ -256,6 +414,27 @@ void rockchip_drm_lastclose(struct drm_device *dev) drm_fb_helper_restore_fbdev_mode_unlocked(&priv->fbdev_helper); } +static const struct drm_ioctl_desc rockchip_ioctls[] = { + DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CREATE, rockchip_gem_create_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_MAP_OFFSET, + rockchip_gem_map_offset_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CPU_ACQUIRE, + rockchip_gem_cpu_acquire_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(ROCKCHIP_GEM_CPU_RELEASE, + rockchip_gem_cpu_release_ioctl, + DRM_UNLOCKED | DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_GET_VER, rockchip_rga_get_ver_ioctl, + DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_SET_CMDLIST, + rockchip_rga_set_cmdlist_ioctl, + DRM_AUTH | DRM_RENDER_ALLOW), + DRM_IOCTL_DEF_DRV(ROCKCHIP_RGA_EXEC, rockchip_rga_exec_ioctl, + DRM_AUTH | DRM_RENDER_ALLOW), +}; + static const struct file_operations rockchip_drm_driver_fops = { .owner = THIS_MODULE, .open = drm_open, @@ -275,11 +454,16 @@ const struct vm_operations_struct rockchip_drm_vm_ops = { }; static struct drm_driver rockchip_drm_driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, + .driver_features = DRIVER_MODESET | DRIVER_GEM | + DRIVER_PRIME | DRIVER_ATOMIC | + DRIVER_RENDER, .load = rockchip_drm_load, .unload = rockchip_drm_unload, + .preclose = rockchip_drm_preclose, .lastclose = rockchip_drm_lastclose, .get_vblank_counter = drm_vblank_no_hw_counter, + .open = rockchip_drm_open, + .postclose = rockchip_drm_postclose, .enable_vblank = rockchip_drm_crtc_enable_vblank, .disable_vblank = rockchip_drm_crtc_disable_vblank, .gem_vm_ops = &rockchip_drm_vm_ops, @@ -290,11 +474,14 @@ static struct drm_driver rockchip_drm_driver = { .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = drm_gem_prime_import, + .gem_prime_import_sg_table = rockchip_gem_prime_import_sg_table, .gem_prime_export = drm_gem_prime_export, .gem_prime_get_sg_table = rockchip_gem_prime_get_sg_table, .gem_prime_vmap = rockchip_gem_prime_vmap, .gem_prime_vunmap = rockchip_gem_prime_vunmap, .gem_prime_mmap = rockchip_gem_mmap_buf, + .ioctls = rockchip_ioctls, + .num_ioctls = ARRAY_SIZE(rockchip_ioctls), .fops = &rockchip_drm_driver_fops, .name = DRIVER_NAME, .desc = DRIVER_DESC, @@ -381,36 +568,6 @@ static const struct dev_pm_ops rockchip_drm_pm_ops = { rockchip_drm_sys_resume) }; -/* - * @node: device tree node containing encoder input ports - * @encoder: drm_encoder - */ -int rockchip_drm_encoder_get_mux_id(struct device_node *node, - struct drm_encoder *encoder) -{ - struct device_node *ep; - struct drm_crtc *crtc = encoder->crtc; - struct of_endpoint endpoint; - struct device_node *port; - int ret; - - if (!node || !crtc) - return -EINVAL; - - for_each_endpoint_of_node(node, ep) { - port = of_graph_get_remote_port(ep); - of_node_put(port); - if (port == crtc->port) { - ret = of_graph_parse_endpoint(ep, &endpoint); - of_node_put(ep); - return ret ?: endpoint.id; - } - } - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(rockchip_drm_encoder_get_mux_id); - static int compare_of(struct device *dev, void *data) { struct device_node *np = data;