| /**************************************************************************** |
| * |
| * The MIT License (MIT) |
| * |
| * Copyright (c) 2014 - 2018 Vivante Corporation |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a |
| * copy of this software and associated documentation files (the "Software"), |
| * to deal in the Software without restriction, including without limitation |
| * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
| * and/or sell copies of the Software, and to permit persons to whom the |
| * Software is furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| * DEALINGS IN THE SOFTWARE. |
| * |
| ***************************************************************************** |
| * |
| * The GPL License (GPL) |
| * |
| * Copyright (C) 2014 - 2018 Vivante Corporation |
| * |
| * 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. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software Foundation, |
| * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| * |
| ***************************************************************************** |
| * |
| * Note: This software is released under dual MIT and GPL licenses. A |
| * recipient may use this file under the terms of either the MIT license or |
| * GPL License. If you wish to use only one license not the other, you can |
| * indicate your decision by deleting one of the above license notices in your |
| * version of this file. |
| * |
| *****************************************************************************/ |
| |
| |
| #if gcdENABLE_DRM |
| |
| #include <drm/drmP.h> |
| #include <drm/drm_gem.h> |
| #include <linux/dma-buf.h> |
| #include "gc_hal_kernel_linux.h" |
| #include "gc_hal_drm.h" |
| |
| #define _GC_OBJ_ZONE gcvZONE_KERNEL |
| |
| /******************************************************************************\ |
| ******************************* gckKERNEL DRM Code ****************************** |
| \******************************************************************************/ |
| |
| struct viv_gem_object { |
| struct drm_gem_object base; |
| |
| uint32_t node_handle; |
| gckVIDMEM_NODE node_object; |
| }; |
| |
| struct dma_buf *viv_gem_prime_export(struct drm_device *drm, |
| struct drm_gem_object *gem_obj, |
| int flags) |
| { |
| struct viv_gem_object *viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| struct dma_buf *dmabuf = gcvNULL; |
| gckGALDEVICE gal_dev = (gckGALDEVICE)drm->dev_private; |
| |
| if (gal_dev) |
| { |
| gckKERNEL kernel = gal_dev->device->map[gal_dev->device->defaultHwType].kernels[0]; |
| gcmkVERIFY_OK(gckVIDMEM_NODE_Export(kernel, viv_obj->node_handle, flags, |
| (gctPOINTER*)&dmabuf, gcvNULL)); |
| } |
| |
| return dmabuf; |
| } |
| |
| struct drm_gem_object *viv_gem_prime_import(struct drm_device *drm, |
| struct dma_buf *dmabuf) |
| { |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj; |
| |
| gcsHAL_INTERFACE iface; |
| gckGALDEVICE gal_dev; |
| gckKERNEL kernel; |
| gctUINT32 processID; |
| gckVIDMEM_NODE nodeObject; |
| gceSTATUS status = gcvSTATUS_OK; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| gckOS_ZeroMemory(&iface, sizeof(iface)); |
| iface.command = gcvHAL_WRAP_USER_MEMORY; |
| iface.hardwareType = gal_dev->device->defaultHwType; |
| iface.u.WrapUserMemory.desc.flag = gcvALLOC_FLAG_DMABUF; |
| iface.u.WrapUserMemory.desc.handle = -1; |
| iface.u.WrapUserMemory.desc.dmabuf = gcmPTR_TO_UINT64(dmabuf); |
| gcmkONERROR(gckDEVICE_Dispatch(gal_dev->device, &iface)); |
| |
| kernel = gal_dev->device->map[gal_dev->device->defaultHwType].kernels[0]; |
| gcmkONERROR(gckOS_GetProcessID(&processID)); |
| gcmkONERROR(gckVIDMEM_HANDLE_Lookup(kernel, processID, iface.u.WrapUserMemory.node, &nodeObject)); |
| |
| /* ioctl output */ |
| gem_obj = kzalloc(sizeof(struct viv_gem_object), GFP_KERNEL); |
| drm_gem_private_object_init(drm, gem_obj, dmabuf->size); |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| viv_obj->node_handle = iface.u.WrapUserMemory.node; |
| viv_obj->node_object = nodeObject; |
| |
| OnError: |
| return gem_obj; |
| } |
| |
| void viv_gem_free_object(struct drm_gem_object *gem_obj) |
| { |
| struct viv_gem_object *viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| struct drm_device *drm = gem_obj->dev; |
| |
| gcsHAL_INTERFACE iface; |
| gckGALDEVICE gal_dev = (gckGALDEVICE)drm->dev_private; |
| |
| gckOS_ZeroMemory(&iface, sizeof(iface)); |
| iface.command = gcvHAL_RELEASE_VIDEO_MEMORY; |
| iface.hardwareType = gal_dev->device->defaultHwType; |
| iface.u.ReleaseVideoMemory.node = viv_obj->node_handle; |
| gcmkVERIFY_OK(gckDEVICE_Dispatch(gal_dev->device, &iface)); |
| |
| drm_gem_object_release(gem_obj); |
| kfree(gem_obj); |
| } |
| |
| static int viv_ioctl_gem_create(struct drm_device *drm, void *data, |
| struct drm_file *file) |
| { |
| int ret = 0; |
| struct drm_viv_gem_create *args = (struct drm_viv_gem_create*)data; |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj = gcvNULL; |
| |
| gcsHAL_INTERFACE iface; |
| gckGALDEVICE gal_dev; |
| gckKERNEL kernel; |
| gctUINT32 processID; |
| gckVIDMEM_NODE nodeObject; |
| gctUINT32 flags = gcvALLOC_FLAG_DMABUF_EXPORTABLE; |
| gceSTATUS status = gcvSTATUS_OK; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| if (args->flags & DRM_VIV_GEM_CONTIGUOUS) |
| { |
| flags |= gcvALLOC_FLAG_CONTIGUOUS; |
| } |
| if (args->flags & DRM_VIV_GEM_CACHED) |
| { |
| flags |= gcvALLOC_FLAG_CACHEABLE; |
| } |
| if (args->flags & DRM_VIV_GEM_SECURE) |
| { |
| flags |= gcvALLOC_FLAG_SECURITY; |
| } |
| if (args->flags & DRM_VIV_GEM_CMA_LIMIT) |
| { |
| flags |= gcvALLOC_FLAG_CMA_LIMIT; |
| } |
| |
| gckOS_ZeroMemory(&iface, sizeof(iface)); |
| iface.command = gcvHAL_ALLOCATE_LINEAR_VIDEO_MEMORY; |
| iface.hardwareType = gal_dev->device->defaultHwType; |
| iface.u.AllocateLinearVideoMemory.bytes = PAGE_ALIGN(args->size); |
| iface.u.AllocateLinearVideoMemory.alignment = 256; |
| iface.u.AllocateLinearVideoMemory.type = gcvSURF_RENDER_TARGET; /* should be general */ |
| iface.u.AllocateLinearVideoMemory.flag = flags; |
| iface.u.AllocateLinearVideoMemory.pool = gcvPOOL_DEFAULT; |
| gcmkONERROR(gckDEVICE_Dispatch(gal_dev->device, &iface)); |
| |
| kernel = gal_dev->device->map[gal_dev->device->defaultHwType].kernels[0]; |
| gcmkONERROR(gckOS_GetProcessID(&processID)); |
| gcmkONERROR(gckVIDMEM_HANDLE_Lookup(kernel, processID, iface.u.AllocateLinearVideoMemory.node, &nodeObject)); |
| |
| /* ioctl output */ |
| gem_obj = kzalloc(sizeof(struct viv_gem_object), GFP_KERNEL); |
| drm_gem_private_object_init(drm, gem_obj, iface.u.AllocateLinearVideoMemory.bytes); |
| ret = drm_gem_handle_create(file, gem_obj, &args->handle); |
| |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| viv_obj->node_handle = iface.u.AllocateLinearVideoMemory.node; |
| viv_obj->node_object = nodeObject; |
| |
| /* drop reference from allocate - handle holds it now */ |
| drm_gem_object_unreference_unlocked(gem_obj); |
| |
| OnError: |
| return gcmIS_ERROR(status) ? -ENOTTY : 0; |
| } |
| |
| static int viv_ioctl_gem_lock(struct drm_device *drm, void *data, |
| struct drm_file *file) |
| { |
| struct drm_viv_gem_lock *args = (struct drm_viv_gem_lock*)data; |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj = gcvNULL; |
| |
| gcsHAL_INTERFACE iface; |
| gceSTATUS status = gcvSTATUS_OK; |
| gckGALDEVICE gal_dev = gcvNULL; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| gem_obj = drm_gem_object_lookup(file, args->handle); |
| if (!gem_obj) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| |
| gckOS_ZeroMemory(&iface, sizeof(iface)); |
| iface.command = gcvHAL_LOCK_VIDEO_MEMORY; |
| iface.hardwareType = gal_dev->device->defaultHwType; |
| iface.u.LockVideoMemory.node = viv_obj->node_handle; |
| iface.u.LockVideoMemory.cacheable = args->cacheable; |
| gcmkONERROR(gckDEVICE_Dispatch(gal_dev->device, &iface)); |
| |
| args->logical = iface.u.LockVideoMemory.memory; |
| |
| OnError: |
| if (gem_obj) |
| { |
| drm_gem_object_unreference_unlocked(gem_obj); |
| } |
| return gcmIS_ERROR(status) ? -ENOTTY : 0; |
| } |
| |
| static int viv_ioctl_gem_unlock(struct drm_device *drm, void *data, |
| struct drm_file *file) |
| { |
| struct drm_viv_gem_unlock *args = (struct drm_viv_gem_unlock*)data; |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj = gcvNULL; |
| |
| gcsHAL_INTERFACE iface; |
| gceSTATUS status = gcvSTATUS_OK; |
| gckGALDEVICE gal_dev = gcvNULL; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| gem_obj = drm_gem_object_lookup(file, args->handle); |
| if (!gem_obj) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| |
| memset(&iface, 0, sizeof(iface)); |
| iface.command = gcvHAL_UNLOCK_VIDEO_MEMORY; |
| iface.hardwareType = gal_dev->device->defaultHwType; |
| iface.u.UnlockVideoMemory.node = (gctUINT64)viv_obj->node_handle; |
| iface.u.UnlockVideoMemory.type = gcvSURF_TYPE_UNKNOWN; |
| gcmkONERROR(gckDEVICE_Dispatch(gal_dev->device, &iface)); |
| |
| memset(&iface, 0, sizeof(iface)); |
| iface.command = gcvHAL_BOTTOM_HALF_UNLOCK_VIDEO_MEMORY; |
| iface.hardwareType = gal_dev->device->defaultHwType; |
| iface.u.BottomHalfUnlockVideoMemory.node = (gctUINT64)viv_obj->node_handle; |
| iface.u.BottomHalfUnlockVideoMemory.type = gcvSURF_TYPE_UNKNOWN; |
| gcmkONERROR(gckDEVICE_Dispatch(gal_dev->device, &iface)); |
| |
| OnError: |
| if (gem_obj) |
| { |
| drm_gem_object_unreference_unlocked(gem_obj); |
| } |
| return gcmIS_ERROR(status) ? -ENOTTY : 0; |
| } |
| |
| static int viv_ioctl_gem_cache(struct drm_device *drm, void *data, |
| struct drm_file *file) |
| { |
| struct drm_viv_gem_cache *args = (struct drm_viv_gem_cache*)data; |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj = gcvNULL; |
| |
| gcsHAL_INTERFACE iface; |
| gceSTATUS status = gcvSTATUS_OK; |
| gckGALDEVICE gal_dev = gcvNULL; |
| gceCACHEOPERATION cache_op = 0; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| gem_obj = drm_gem_object_lookup(file, args->handle); |
| if (!gem_obj) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| |
| switch (args->op) |
| { |
| case DRM_VIV_GEM_CLEAN_CACHE: |
| cache_op = gcvCACHE_CLEAN; |
| break; |
| case DRM_VIV_GEM_INVALIDATE_CACHE: |
| cache_op = gcvCACHE_INVALIDATE; |
| break; |
| case DRM_VIV_GEM_FLUSH_CACHE: |
| cache_op = gcvCACHE_FLUSH; |
| break; |
| case DRM_VIV_GEM_MEMORY_BARRIER: |
| cache_op = gcvCACHE_MEMORY_BARRIER; |
| break; |
| default: |
| break; |
| } |
| |
| gckOS_ZeroMemory(&iface, sizeof(iface)); |
| iface.command = gcvHAL_CACHE; |
| iface.hardwareType = gal_dev->device->defaultHwType; |
| iface.u.Cache.node = viv_obj->node_handle; |
| iface.u.Cache.operation = cache_op; |
| iface.u.Cache.logical = args->logical; |
| iface.u.Cache.bytes = args->bytes; |
| gcmkONERROR(gckDEVICE_Dispatch(gal_dev->device, &iface)); |
| |
| OnError: |
| if (gem_obj) |
| { |
| drm_gem_object_unreference_unlocked(gem_obj); |
| } |
| return gcmIS_ERROR(status) ? -ENOTTY : 0; |
| } |
| |
| static int viv_ioctl_gem_query(struct drm_device *drm, void *data, |
| struct drm_file *file) |
| { |
| struct drm_viv_gem_query *args = (struct drm_viv_gem_query*)data; |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj = gcvNULL; |
| |
| gceSTATUS status = gcvSTATUS_OK; |
| gckGALDEVICE gal_dev = gcvNULL; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| gem_obj = drm_gem_object_lookup(file, args->handle); |
| if (!gem_obj) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| |
| switch (args->param) |
| { |
| case DRM_VIV_GEM_PARAM_POOL: |
| args->value = (__u64)viv_obj->node_object->pool; |
| break; |
| case DRM_VIV_GEM_PARAM_SIZE: |
| args->value = (__u64)gem_obj->size; |
| break; |
| default: |
| gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); |
| } |
| |
| OnError: |
| if (gem_obj) |
| { |
| drm_gem_object_unreference_unlocked(gem_obj); |
| } |
| return gcmIS_ERROR(status) ? -ENOTTY : 0; |
| } |
| |
| static int viv_ioctl_gem_timestamp(struct drm_device *drm, void *data, |
| struct drm_file *file) |
| { |
| struct drm_viv_gem_timestamp *args = (struct drm_viv_gem_timestamp *)data; |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj = gcvNULL; |
| |
| gceSTATUS status = gcvSTATUS_OK; |
| gckGALDEVICE gal_dev = gcvNULL; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| gem_obj = drm_gem_object_lookup(file, args->handle); |
| if (!gem_obj) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| |
| viv_obj->node_object->timeStamp += args->inc; |
| args->timestamp = viv_obj->node_object->timeStamp; |
| |
| OnError: |
| if (gem_obj) |
| { |
| drm_gem_object_unreference_unlocked(gem_obj); |
| } |
| return gcmIS_ERROR(status) ? -ENOTTY : 0; |
| } |
| |
| static int viv_ioctl_gem_set_tiling(struct drm_device *drm, void *data, |
| struct drm_file *file) |
| { |
| struct drm_viv_gem_set_tiling *args = (struct drm_viv_gem_set_tiling*)data; |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj = gcvNULL; |
| |
| gceSTATUS status = gcvSTATUS_OK; |
| gckGALDEVICE gal_dev = gcvNULL; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| gem_obj = drm_gem_object_lookup(file, args->handle); |
| if (!gem_obj) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| |
| viv_obj->node_object->tilingMode = args->tiling_mode; |
| viv_obj->node_object->tsMode = args->ts_mode; |
| viv_obj->node_object->clearValue = args->clear_value; |
| |
| OnError: |
| if (gem_obj) |
| { |
| drm_gem_object_unreference_unlocked(gem_obj); |
| } |
| return gcmIS_ERROR(status) ? -ENOTTY : 0; |
| } |
| |
| static int viv_ioctl_gem_get_tiling(struct drm_device *drm, void *data, |
| struct drm_file *file) |
| { |
| struct drm_viv_gem_get_tiling *args = (struct drm_viv_gem_get_tiling*)data; |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj = gcvNULL; |
| |
| gceSTATUS status = gcvSTATUS_OK; |
| gckGALDEVICE gal_dev = gcvNULL; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| gem_obj = drm_gem_object_lookup(file, args->handle); |
| if (!gem_obj) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| |
| args->tiling_mode = viv_obj->node_object->tilingMode; |
| args->ts_mode = viv_obj->node_object->tsMode; |
| args->clear_value = viv_obj->node_object->clearValue; |
| |
| OnError: |
| if (gem_obj) |
| { |
| drm_gem_object_unreference_unlocked(gem_obj); |
| } |
| return gcmIS_ERROR(status) ? -ENOTTY : 0; |
| } |
| |
| static int viv_ioctl_gem_attach_aux(struct drm_device *drm, void *data, |
| struct drm_file *file) |
| { |
| struct drm_viv_gem_attach_aux *args = (struct drm_viv_gem_attach_aux*)data; |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj = gcvNULL; |
| struct drm_gem_object *gem_ts_obj = gcvNULL; |
| |
| gceSTATUS status = gcvSTATUS_OK; |
| gckGALDEVICE gal_dev = gcvNULL; |
| gckVIDMEM_NODE nodeObj = gcvNULL; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| |
| gem_obj = drm_gem_object_lookup(file, args->handle); |
| if (!gem_obj) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| nodeObj = viv_obj->node_object; |
| |
| /* do not support re-attach */ |
| if (nodeObj->tsNode) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_SUPPORTED); |
| } |
| |
| if (args->ts_handle) |
| { |
| struct viv_gem_object *viv_ts_obj; |
| gckKERNEL kernel = gal_dev->device->map[gal_dev->device->defaultHwType].kernels[0]; |
| |
| gem_ts_obj = drm_gem_object_lookup(file, args->ts_handle); |
| if (!gem_ts_obj) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| viv_ts_obj = container_of(gem_ts_obj, struct viv_gem_object, base); |
| |
| gcmkONERROR(gckVIDMEM_NODE_Reference(kernel, viv_ts_obj->node_object)); |
| nodeObj->tsNode = viv_ts_obj->node_object; |
| } |
| |
| OnError: |
| if (gem_obj) |
| { |
| drm_gem_object_unreference_unlocked(gem_obj); |
| |
| if (gem_ts_obj) |
| { |
| drm_gem_object_unreference_unlocked(gem_ts_obj); |
| } |
| } |
| return gcmIS_ERROR(status) ? -ENOTTY : 0; |
| } |
| |
| static int viv_ioctl_gem_ref_node(struct drm_device *drm, void *data, |
| struct drm_file *file) |
| { |
| struct drm_viv_gem_ref_node *args = (struct drm_viv_gem_ref_node*)data; |
| struct drm_gem_object *gem_obj = gcvNULL; |
| struct viv_gem_object *viv_obj = gcvNULL; |
| |
| gceSTATUS status = gcvSTATUS_OK; |
| gckGALDEVICE gal_dev = gcvNULL; |
| gckKERNEL kernel = gcvNULL; |
| gctUINT32 processID; |
| gckVIDMEM_NODE nodeObj; |
| gctUINT32 nodeHandle = 0, tsNodeHandle = 0; |
| gctBOOL refered = gcvFALSE; |
| int ret = 0; |
| |
| gal_dev = (gckGALDEVICE)drm->dev_private; |
| if (!gal_dev) |
| { |
| gcmkONERROR(gcvSTATUS_INVALID_ARGUMENT); |
| } |
| kernel = gal_dev->device->map[gal_dev->device->defaultHwType].kernels[0]; |
| |
| gem_obj = drm_gem_object_lookup(file, args->handle); |
| if (!gem_obj) |
| { |
| gcmkONERROR(gcvSTATUS_NOT_FOUND); |
| } |
| viv_obj = container_of(gem_obj, struct viv_gem_object, base); |
| nodeObj = viv_obj->node_object; |
| |
| gcmkONERROR(gckOS_GetProcessID(&processID)); |
| gcmkONERROR(gckVIDMEM_HANDLE_Allocate(kernel, nodeObj, &nodeHandle)); |
| gcmkONERROR( |
| gckKERNEL_AddProcessDB(kernel, |
| processID, gcvDB_VIDEO_MEMORY, |
| gcmINT2PTR(nodeHandle), |
| gcvNULL, |
| 0)); |
| gcmkONERROR(gckVIDMEM_NODE_Reference(kernel, nodeObj)); |
| refered = gcvTRUE; |
| |
| if (nodeObj->tsNode) |
| { |
| gcmkONERROR(gckVIDMEM_HANDLE_Allocate(kernel, nodeObj->tsNode, &tsNodeHandle)); |
| gcmkONERROR( |
| gckKERNEL_AddProcessDB(kernel, |
| processID, gcvDB_VIDEO_MEMORY, |
| gcmINT2PTR(tsNodeHandle), |
| gcvNULL, |
| 0)); |
| gcmkONERROR(gckVIDMEM_NODE_Reference(kernel, nodeObj->tsNode)); |
| } |
| args->node = nodeHandle; |
| args->ts_node = tsNodeHandle; |
| |
| OnError: |
| if (gcmIS_ERROR(status) && kernel) |
| { |
| gctUINT32 processID; |
| |
| gcmkVERIFY_OK(gckOS_GetProcessID(&processID)); |
| |
| if (tsNodeHandle) |
| { |
| gckVIDMEM_HANDLE_Dereference(kernel, processID, tsNodeHandle); |
| } |
| |
| if (nodeHandle) |
| { |
| gckVIDMEM_HANDLE_Dereference(kernel, processID, nodeHandle); |
| } |
| |
| if (refered) |
| { |
| gcmkONERROR(gckVIDMEM_NODE_Dereference(kernel, nodeObj)); |
| } |
| |
| args->node = 0; |
| args->ts_node = 0; |
| |
| ret = -ENOTTY; |
| } |
| |
| if (gem_obj) |
| { |
| drm_gem_object_unreference_unlocked(gem_obj); |
| } |
| |
| return ret; |
| } |
| |
| static const struct drm_ioctl_desc viv_ioctls[] = |
| { |
| DRM_IOCTL_DEF_DRV(VIV_GEM_CREATE, viv_ioctl_gem_create, DRM_AUTH | DRM_RENDER_ALLOW), |
| DRM_IOCTL_DEF_DRV(VIV_GEM_LOCK, viv_ioctl_gem_lock, DRM_AUTH | DRM_RENDER_ALLOW), |
| DRM_IOCTL_DEF_DRV(VIV_GEM_UNLOCK, viv_ioctl_gem_unlock, DRM_AUTH | DRM_RENDER_ALLOW), |
| DRM_IOCTL_DEF_DRV(VIV_GEM_CACHE, viv_ioctl_gem_cache, DRM_AUTH | DRM_RENDER_ALLOW), |
| DRM_IOCTL_DEF_DRV(VIV_GEM_QUERY, viv_ioctl_gem_query, DRM_AUTH | DRM_RENDER_ALLOW), |
| DRM_IOCTL_DEF_DRV(VIV_GEM_TIMESTAMP, viv_ioctl_gem_timestamp, DRM_AUTH | DRM_RENDER_ALLOW), |
| DRM_IOCTL_DEF_DRV(VIV_GEM_SET_TILING, viv_ioctl_gem_set_tiling, DRM_AUTH | DRM_RENDER_ALLOW), |
| DRM_IOCTL_DEF_DRV(VIV_GEM_GET_TILING, viv_ioctl_gem_get_tiling, DRM_AUTH | DRM_RENDER_ALLOW), |
| DRM_IOCTL_DEF_DRV(VIV_GEM_ATTACH_AUX, viv_ioctl_gem_attach_aux, DRM_AUTH | DRM_RENDER_ALLOW), |
| DRM_IOCTL_DEF_DRV(VIV_GEM_REF_NODE, viv_ioctl_gem_ref_node, DRM_AUTH | DRM_RENDER_ALLOW), |
| }; |
| |
| int viv_drm_open(struct drm_device *drm, struct drm_file *file) |
| { |
| gctINT i; |
| gctUINT32 pid = _GetProcessID(); |
| gckGALDEVICE gal_dev = (gckGALDEVICE)drm->dev_private; |
| gceSTATUS status = gcvSTATUS_OK; |
| |
| for (i = 0; i < gcdMAX_GPU_COUNT; ++i) |
| { |
| if (gal_dev->kernels[i]) |
| { |
| gcmkONERROR(gckKERNEL_AttachProcessEx(gal_dev->kernels[i], gcvTRUE, pid)); |
| } |
| } |
| file->driver_priv = gcmINT2PTR(pid); |
| |
| OnError: |
| return gcmIS_ERROR(status) ? -ENODEV : 0; |
| } |
| |
| void viv_drm_postclose(struct drm_device *drm, struct drm_file *file) |
| { |
| gctINT i; |
| gctUINT32 pid = gcmPTR2INT(file->driver_priv); |
| gckGALDEVICE gal_dev = (gckGALDEVICE)drm->dev_private; |
| |
| for (i = 0; i < gcdMAX_GPU_COUNT; ++i) |
| { |
| if (gal_dev->kernels[i]) |
| { |
| gcmkVERIFY_OK(gckKERNEL_AttachProcessEx(gal_dev->kernels[i], gcvFALSE, pid)); |
| } |
| } |
| } |
| |
| static const struct file_operations viv_drm_fops = { |
| .owner = THIS_MODULE, |
| .open = drm_open, |
| .release = drm_release, |
| .unlocked_ioctl = drm_ioctl, |
| #ifdef CONFIG_COMPAT |
| .compat_ioctl = drm_compat_ioctl, |
| #endif |
| .poll = drm_poll, |
| .read = drm_read, |
| .llseek = no_llseek, |
| }; |
| |
| static struct drm_driver viv_drm_driver = { |
| .driver_features = DRIVER_GEM | DRIVER_PRIME | DRIVER_RENDER, |
| .open = viv_drm_open, |
| .postclose = viv_drm_postclose, |
| #if LINUX_VERSION_CODE >= KERNEL_VERSION(4,7,0) |
| .gem_free_object_unlocked = viv_gem_free_object, |
| #else |
| .gem_free_object = viv_gem_free_object, |
| #endif |
| .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
| .prime_fd_to_handle = drm_gem_prime_fd_to_handle, |
| .gem_prime_export = viv_gem_prime_export, |
| .gem_prime_import = viv_gem_prime_import, |
| .ioctls = viv_ioctls, |
| .num_ioctls = DRM_VIV_NUM_IOCTLS, |
| .fops = &viv_drm_fops, |
| .name = "vivante", |
| .desc = "vivante DRM", |
| .date = "20170808", |
| .major = 1, |
| .minor = 0, |
| }; |
| |
| int viv_drm_probe(struct device *dev) |
| { |
| int ret = 0; |
| gceSTATUS status = gcvSTATUS_OK; |
| gckGALDEVICE gal_dev = gcvNULL; |
| struct drm_device *drm = gcvNULL; |
| |
| gal_dev = (gckGALDEVICE)dev_get_drvdata(dev); |
| if (!gal_dev) |
| { |
| ret = -ENODEV; |
| gcmkONERROR(gcvSTATUS_INVALID_OBJECT); |
| } |
| |
| drm = drm_dev_alloc(&viv_drm_driver, dev); |
| if (IS_ERR(drm)) |
| { |
| ret = PTR_ERR(drm); |
| gcmkONERROR(gcvSTATUS_OUT_OF_RESOURCES); |
| } |
| drm->dev_private = (void*)gal_dev; |
| |
| ret = drm_dev_register(drm, 0); |
| if (ret) |
| { |
| gcmkONERROR(gcvSTATUS_GENERIC_IO); |
| } |
| |
| gal_dev->drm = (void*)drm; |
| |
| OnError: |
| if (gcmIS_ERROR(status)) |
| { |
| if (drm) |
| { |
| drm_dev_unref(drm); |
| } |
| printk(KERN_ERR "galcore: Failed to setup drm device.\n"); |
| } |
| return ret; |
| } |
| |
| int viv_drm_remove(struct device *dev) |
| { |
| gckGALDEVICE gal_dev = (gckGALDEVICE)dev_get_drvdata(dev); |
| |
| if (gal_dev) |
| { |
| struct drm_device *drm = (struct drm_device*)gal_dev->drm; |
| |
| drm_dev_unregister(drm); |
| drm_dev_unref(drm); |
| } |
| |
| return 0; |
| } |
| |
| #endif |