/* * Copyright 2017 Red Hat Inc. * * 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. * * Authors: Jérôme Glisse */ #include #include #include #include #include #include #include "compote.h" #include #include #include #include "nouveau_ttm.h" #include "nouveau_chan.h" #include "nouveau_usif.h" #include "nouveau_abi16.h" #include "nouveau_bo.h" int compote_channel_new(struct compote_channel **channelp, struct compote_file *cfile) { uint32_t fb_ctxdma_handle, tt_ctxdma_handle; struct compote_channel *channel; struct nvif_device *nvifdevice; struct nouveau_drm *nvdrm; int ret; *channelp = NULL; channel = kzalloc(sizeof(*channel), GFP_KERNEL); if (channel == NULL) return -ENOMEM; channel->cfile = cfile; kref_init(&channel->kref); /* create channel object and initialise dma and fence management */ tt_ctxdma_handle = 0; nvdrm = cfile->cdevice->nvdrm; nvifdevice = &cfile->nvclient.device; fb_ctxdma_handle = NVA06F_V0_ENGINE_GR; ret = nouveau_channel_new(nvdrm, nvifdevice, fb_ctxdma_handle, tt_ctxdma_handle, &channel->nvchan); if (ret) goto error; *channelp = channel; return 0; error: kfree(channel); return ret; } static void compote_channel_kref_put(struct kref *kref) { struct compote_channel *channel; struct compote_file *cfile; channel = container_of(kref, struct compote_channel, kref); cfile = channel->cfile; /* * Wait for all activity to stop before releasing notify object, which * may be still in use. */ nouveau_channel_idle(channel->nvchan); nouveau_channel_del(&channel->nvchan); down_write(&cfile->rwsem); list_del_init(&channel->list); up_write(&cfile->rwsem); kfree(channel); } void compote_channel_ref(struct compote_channel *channel) { kref_get(&channel->kref); } void compote_channel_unref(struct compote_channel *channel) { kref_put(&channel->kref, compote_channel_kref_put); } int compote_ioctl_channel_alloc(struct compote_file *cfile, void __user *uarg) { struct compote_ioctl_channel_alloc arg; struct compote_channel *channel; int ret; ret = compote_channel_new(&channel, cfile); if (ret) return ret; arg.channel = channel->nvchan->chid; ret = copy_to_user(uarg, &arg, sizeof(arg)); if (ret) { compote_channel_unref(channel); return ret; } down_write(&cfile->rwsem); list_add_tail(&channel->list, &cfile->channels); up_write(&cfile->rwsem); return 0; } int compote_ioctl_channel_free(struct compote_file *cfile, void __user *uarg) { struct compote_ioctl_channel_free arg; struct compote_channel *channel; int ret; ret = copy_from_user(&arg, uarg, sizeof(arg)); if (ret) return ret; down_write(&cfile->rwsem); list_for_each_entry (channel, &cfile->channels, list) { if (channel->nvchan->chid == arg.channel) { compote_channel_ref(channel); up_write(&cfile->rwsem); compote_channel_unref(channel); compote_channel_unref(channel); return 0; } } up_write(&cfile->rwsem); return -EINVAL; }