From patchwork Fri Apr 13 16:53:48 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Noralf_Tr=C3=B8nnes?= X-Patchwork-Id: 10340569 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E7CAD60153 for ; Fri, 13 Apr 2018 16:56:56 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D543D28917 for ; Fri, 13 Apr 2018 16:56:56 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C760A28926; Fri, 13 Apr 2018 16:56:56 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-5.2 required=2.0 tests=BAYES_00, MAILING_LIST_MULTI, RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 6886E28919 for ; Fri, 13 Apr 2018 16:56:55 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id B02416EB45; Fri, 13 Apr 2018 16:56:29 +0000 (UTC) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from smtp.domeneshop.no (smtp.domeneshop.no [IPv6:2a01:5b40:0:3005::1]) by gabe.freedesktop.org (Postfix) with ESMTPS id 372F389186; Fri, 13 Apr 2018 16:56:26 +0000 (UTC) Received: from 211.81-166-168.customer.lyse.net ([81.166.168.211]:55982 helo=localhost.localdomain) by smtp.domeneshop.no with esmtpa (Exim 4.84_2) (envelope-from ) id 1f71zs-000155-7e; Fri, 13 Apr 2018 18:56:24 +0200 From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= To: dri-devel@lists.freedesktop.org Date: Fri, 13 Apr 2018 18:53:48 +0200 Message-Id: <20180413165354.8331-20-noralf@tronnes.org> X-Mailer: git-send-email 2.15.1 In-Reply-To: <20180413165354.8331-1-noralf@tronnes.org> References: <20180413165354.8331-1-noralf@tronnes.org> MIME-Version: 1.0 Subject: [Intel-gfx] [RFC v4 19/25] drm/client: Finish the in-kernel client API X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: daniel.vetter@ffwll.ch, intel-gfx@lists.freedesktop.org, =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= , laurent.pinchart@ideasonboard.com, mstaudt@suse.de, dh.herrmann@gmail.com Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Virus-Scanned: ClamAV using ClamSMTP The modesetting code is already present, this adds the rest of the API. Signed-off-by: Noralf Trønnes --- drivers/gpu/drm/drm_client.c | 573 +++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_debugfs.c | 7 + drivers/gpu/drm/drm_drv.c | 11 + drivers/gpu/drm/drm_file.c | 3 + drivers/gpu/drm/drm_probe_helper.c | 3 + drivers/gpu/drm/drm_sysfs.c | 20 ++ include/drm/drm_client.h | 103 +++++++ include/drm/drm_device.h | 4 + 8 files changed, 724 insertions(+) diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index bce1630a0db2..760f1795f812 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -8,7 +8,9 @@ * Copyright (c) 2007 Dave Airlie */ +#include #include +#include #include #include @@ -17,14 +19,280 @@ #include #include #include +#include #include +#include "drm_crtc_internal.h" #include "drm_internal.h" struct drm_client_display_offset { int x, y; }; +static int drm_client_alloc_file(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + struct drm_file *file; + + file = drm_file_alloc(dev->primary); + if (IS_ERR(file)) + return PTR_ERR(file); + + drm_dev_get(dev); + + mutex_lock(&dev->filelist_mutex); + list_add(&file->lhead, &dev->filelist_internal); + mutex_unlock(&dev->filelist_mutex); + + client->file = file; + + return 0; +} + +static void drm_client_free_file(struct drm_client_dev *client) +{ + struct drm_device *dev = client->dev; + + mutex_lock(&dev->filelist_mutex); + list_del(&client->file->lhead); + mutex_unlock(&dev->filelist_mutex); + + drm_file_free(client->file); + drm_dev_put(dev); +} + +struct drm_client_dev * +drm_client_new(struct drm_device *dev, const struct drm_client_funcs *funcs) +{ + struct drm_client_dev *client; + int ret; + + if (WARN_ON(!funcs->name)) + return ERR_PTR(-EINVAL); + + client = kzalloc(sizeof(*client), GFP_KERNEL); + if (!client) + return ERR_PTR(-ENOMEM); + + client->dev = dev; + client->funcs = funcs; + + ret = drm_client_alloc_file(client); + if (ret) { + kfree(client); + return ERR_PTR(ret); + } + + mutex_lock(&dev->clientlist_mutex); + list_add(&client->list, &dev->clientlist); + mutex_unlock(&dev->clientlist_mutex); + + return client; +} +EXPORT_SYMBOL(drm_client_new); + +struct drm_client_dev * +drm_client_new_from_id(unsigned int dev_id, const struct drm_client_funcs *funcs) +{ + struct drm_client_dev *client; + struct drm_minor *minor; + + minor = drm_minor_acquire(dev_id); + if (IS_ERR(minor)) + return ERR_CAST(minor); + + client = drm_client_new(minor->dev, funcs); + + drm_minor_release(minor); + + return client; +} +EXPORT_SYMBOL(drm_client_new_from_id); + +/** + * drm_client_free - Free DRM client resources + * @client: DRM client + * + * This is called automatically on client removal unless the client returns + * non-zero in the &drm_client_funcs->remove callback. The fbdev client does + * this when it can't close &drm_file because userspace has an open fd. + * + * Note: + * If the client can't release it's resources on remove, it needs to hold a + * reference on the driver module to prevent the code from going away. + */ +void drm_client_free(struct drm_client_dev *client) +{ + DRM_DEV_DEBUG_KMS(client->dev->dev, "%s\n", client->funcs->name); + drm_client_free_file(client); + kfree(client); +} +EXPORT_SYMBOL(drm_client_free); + +static void drm_client_remove_locked(struct drm_client_dev *client) +{ + list_del(&client->list); + + if (!client->funcs->remove || !client->funcs->remove(client)) + drm_client_free(client); +} + +static void drm_client_remove_safe(struct drm_device *dev, + struct drm_client_dev *client) +{ + struct drm_client_dev *iter; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(iter, &dev->clientlist, list) { + if (iter == client) { + drm_client_remove_locked(client); + break; + } + } + mutex_unlock(&dev->clientlist_mutex); +} + +/** + * drm_client_remove - Remove client + * @client: Client + * + * Remove a client. + */ +void drm_client_remove(struct drm_client_dev *client) +{ + struct drm_device *dev; + + if (!client) + return; + + dev = client->dev; + drm_dev_get(dev); + drm_client_remove_safe(dev, client); + drm_dev_put(dev); +} +EXPORT_SYMBOL(drm_client_remove); + +struct drm_client_remove_defer { + struct list_head list; + struct drm_device *dev; + struct drm_client_dev *client; +}; + +static LIST_HEAD(drm_client_remove_defer_list); +static DEFINE_MUTEX(drm_client_remove_defer_list_lock); + +static void drm_client_remove_defer_work_fn(struct work_struct *work) +{ + struct drm_client_remove_defer *defer, *tmp; + + mutex_lock(&drm_client_remove_defer_list_lock); + list_for_each_entry_safe(defer, tmp, &drm_client_remove_defer_list, list) { + drm_client_remove_safe(defer->dev, defer->client); + drm_dev_put(defer->dev); + list_del(&defer->list); + kfree(defer); + } + mutex_unlock(&drm_client_remove_defer_list_lock); +} + +static DECLARE_WORK(drm_client_remove_defer_work, drm_client_remove_defer_work_fn); + +/** + * drm_client_remove_defer - Deferred client removal + * @client: Client + * + * Defer client removal to a worker. This makes it possible for a client running + * in a worker to remove itself. + * + * Returns: + * Zero on success, or -ENOMEM on allocation failure. + */ +int drm_client_remove_defer(struct drm_client_dev *client) +{ + struct drm_client_remove_defer *defer; + + if (!client) + return 0; + + defer = kzalloc(sizeof(*defer), GFP_KERNEL); + if (!defer) + return -ENOMEM; + + defer->dev = client->dev; + defer->client = client; + drm_dev_get(client->dev); + + mutex_lock(&drm_client_remove_defer_list_lock); + list_add(&defer->list, &drm_client_remove_defer_list); + mutex_unlock(&drm_client_remove_defer_list_lock); + + schedule_work(&drm_client_remove_defer_work); + + return 0; +} +EXPORT_SYMBOL(drm_client_remove_defer); + +void drm_client_init(void) +{ +} + +void drm_client_exit(void) +{ + flush_work(&drm_client_remove_defer_work); +} + +void drm_client_dev_unregister(struct drm_device *dev) +{ + struct drm_client_dev *client, *tmp; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry_safe(client, tmp, &dev->clientlist, list) + drm_client_remove_locked(client); + mutex_unlock(&dev->clientlist_mutex); +} + +void drm_client_dev_hotplug(struct drm_device *dev) +{ + struct drm_client_dev *client; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) { + if (!client->funcs->hotplug) + continue; + + ret = client->funcs->hotplug(client); + DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->funcs->name, ret); + } + mutex_unlock(&dev->clientlist_mutex); +} +EXPORT_SYMBOL(drm_client_dev_hotplug); + +void drm_client_dev_lastclose(struct drm_device *dev) +{ + struct drm_client_dev *client; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) { + if (!client->funcs->lastclose) + continue; + + ret = client->funcs->lastclose(client); + DRM_DEV_DEBUG_KMS(dev->dev, "%s: ret=%d\n", client->funcs->name, ret); + } + mutex_unlock(&dev->clientlist_mutex); +} + /** * drm_client_display_create() - Create display structure * @dev: DRM device @@ -348,6 +616,90 @@ int drm_client_display_restore(struct drm_client_display *display, bool force) } EXPORT_SYMBOL(drm_client_display_restore); +/** + * drm_client_display_commit_mode - Commit a mode/fb to the CRTC(s) + * @display: Client display + * @fb: Framebuffer (if NULL the current fb is used) + * @mode: Display mode (if NULL the current mode is used) + * + * Returns: + * Zero on success, negative error code on failure. + */ +int drm_client_display_commit(struct drm_client_display *display, + struct drm_framebuffer *fb, struct drm_display_mode *mode) +{ + struct drm_display_mode *use_mode = NULL; + struct drm_mode_set *modeset; + unsigned int count = 0; + + if (mode) { + struct drm_display_mode *iter; + + drm_client_display_for_each_mode(iter, display) { + if (!use_mode && drm_mode_equal(iter, mode)) + use_mode = iter; + count++; + } + + if (!use_mode) + return -EINVAL; + + /* + * Don't actually set the mode in the single mode case since it + * might be a tiled display which consists of multiple modes. + * Just keep the current mode. + */ + if (count == 1) + use_mode = NULL; + } + + count = 0; + drm_client_display_for_each_modeset(modeset, display) { + if (!modeset->num_connectors) + continue; + + if (fb) + modeset->fb = fb; + + if (use_mode) { + if (WARN_ON(++count > 1)) + return -EINVAL; + + if (modeset->mode) + drm_mode_destroy(display->dev, modeset->mode); + modeset->mode = drm_mode_duplicate(display->dev, use_mode); + if (!modeset->mode) + return -ENOMEM; + } + } + + return drm_client_display_restore(display, false); +} +EXPORT_SYMBOL(drm_client_display_commit); + +struct drm_framebuffer *drm_client_display_current_fb(struct drm_client_display *display) +{ + struct drm_mode_set *modeset; + + drm_client_display_for_each_modeset(modeset, display) { + struct drm_crtc *crtc = modeset->crtc; + struct drm_framebuffer *fb = NULL; + + drm_modeset_lock(&crtc->primary->mutex, NULL); + if (crtc->primary->state && crtc->primary->state->fb) + fb = crtc->primary->state->fb; + else if (!crtc->primary->state && crtc->primary->fb) + fb = crtc->primary->fb; + drm_modeset_unlock(&crtc->primary->mutex); + + if (fb) + return fb; + } + + return NULL; +} +EXPORT_SYMBOL(drm_client_display_current_fb); + static void drm_client_display_dpms_legacy(struct drm_client_display *display, int dpms_mode) { struct drm_device *dev = display->dev; @@ -869,3 +1221,224 @@ drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int return display; } EXPORT_SYMBOL(drm_client_find_display); + +static void drm_client_buffer_delete(struct drm_client_buffer *buffer) +{ + if (!buffer) + return; + + if (buffer->vaddr) + dma_buf_vunmap(buffer->dma_buf, buffer->vaddr); + + if (buffer->dma_buf) + dma_buf_put(buffer->dma_buf); + + drm_mode_destroy_dumb(buffer->client->dev, buffer->handle, buffer->client->file); + kfree(buffer); +} + +/* For testing __close_fd() */ +#include + +static struct drm_client_buffer * +drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format) +{ + struct drm_mode_create_dumb dumb_args = { }; + struct drm_prime_handle prime_args = { }; + struct drm_client_buffer *buffer; + struct dma_buf *dma_buf; + void *vaddr; + int ret; + + buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); + if (!buffer) + return ERR_PTR(-ENOMEM); + + buffer->client = client; + buffer->width = width; + buffer->height = height; + buffer->format = format; + + dumb_args.width = buffer->width; + dumb_args.height = buffer->height; + dumb_args.bpp = drm_format_plane_cpp(format, 0) * 8; + ret = drm_mode_create_dumb(client->dev, &dumb_args, client->file); + if (ret) + goto err_free; + + buffer->handle = dumb_args.handle; + buffer->pitch = dumb_args.pitch; + buffer->size = dumb_args.size; + + prime_args.handle = dumb_args.handle; + ret = drm_prime_handle_to_fd(client->dev, &prime_args, client->file); + if (ret) + goto err_delete; + + dma_buf = dma_buf_get(prime_args.fd); + if (IS_ERR(dma_buf)) { + ret = PTR_ERR(dma_buf); + goto err_delete; + } + + /* + * If called from a worker the dmabuf fd isn't closed and the ref + * doesn't drop to zero on free. + * If I use __close_fd() it's all fine, but that function is not exported. + * + * How do I get rid of this fd when in a worker/kernel thread? + * The fd isn't used beyond this function. + */ +// WARN_ON(__close_fd(current->files, prime_args.fd)); + + pr_info("%s: PF_KTHREAD=%u\n", __func__, !!(current->flags & PF_KTHREAD)); + + buffer->dma_buf = dma_buf; + + vaddr = dma_buf_vmap(dma_buf); + if (!vaddr) { + ret = -ENOMEM; + goto err_delete; + } + + buffer->vaddr = vaddr; + + return buffer; + +err_delete: + drm_client_buffer_delete(buffer); +err_free: + kfree(buffer); + + return ERR_PTR(ret); +} + +static int drm_client_buffer_rmfb(struct drm_client_buffer *buffer) +{ + int ret; + + if (!buffer || !buffer->fb) + return 0; + + ret = drm_mode_rmfb(buffer->client->dev, buffer->fb->base.id, buffer->client->file); + if (ret) + DRM_DEV_ERROR(buffer->client->dev->dev, + "Error removing FB:%u (%d)\n", buffer->fb->base.id, ret); + + buffer->fb = NULL; + + return 0; +} + +static int drm_client_buffer_addfb(struct drm_client_buffer *buffer, + struct drm_display_mode *mode) +{ + struct drm_client_dev *client = buffer->client; + struct drm_mode_fb_cmd2 fb_req = { }; + int ret; + + if (mode->hdisplay > buffer->width || mode->vdisplay > buffer->height) + return -EINVAL; + + fb_req.width = mode->hdisplay; + fb_req.height = mode->vdisplay; + fb_req.pixel_format = buffer->format; + fb_req.handles[0] = buffer->handle; + fb_req.pitches[0] = buffer->pitch; + + ret = drm_mode_addfb2(client->dev, &fb_req, client->file, client->funcs->name); + if (ret) + return ret; + + buffer->fb = drm_framebuffer_lookup(client->dev, buffer->client->file, fb_req.fb_id); + if (WARN_ON(!buffer->fb)) + return -ENOENT; + + /* drop the reference we picked up in framebuffer lookup */ + drm_framebuffer_put(buffer->fb); + + return 0; +} + +/** + * drm_client_framebuffer_create - Create a client framebuffer + * @client: DRM client + * @mode: Display mode to create a buffer for + * @format: Buffer format + * + * This function creates a &drm_client_buffer which consists of a + * &drm_framebuffer backed by a dumb buffer. The dumb buffer is &dma_buf + * exported to aquire a virtual address which is stored in + * &drm_client_buffer->vaddr. + * Call drm_client_framebuffer_delete() to free the buffer. + * + * Returns: + * Pointer to a client buffer or an error pointer on failure. + */ +struct drm_client_buffer * +drm_client_framebuffer_create(struct drm_client_dev *client, + struct drm_display_mode *mode, u32 format) +{ + struct drm_client_buffer *buffer; + int ret; + + buffer = drm_client_buffer_create(client, mode->hdisplay, + mode->vdisplay, format); + if (IS_ERR(buffer)) + return buffer; + + ret = drm_client_buffer_addfb(buffer, mode); + if (ret) { + drm_client_buffer_delete(buffer); + return ERR_PTR(ret); + } + + return buffer; +} +EXPORT_SYMBOL(drm_client_framebuffer_create); + +void drm_client_framebuffer_delete(struct drm_client_buffer *buffer) +{ + drm_client_buffer_rmfb(buffer); + drm_client_buffer_delete(buffer); +} +EXPORT_SYMBOL(drm_client_framebuffer_delete); + +int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, + struct drm_clip_rect *rect) +{ + if (!buffer->fb || !buffer->fb->funcs->dirty) + return 0; + + return buffer->fb->funcs->dirty(buffer->fb, buffer->client->file, + 0, 0, rect, rect ? 1 : 0); +} +EXPORT_SYMBOL(drm_client_framebuffer_flush); + +#ifdef CONFIG_DEBUG_FS +static int drm_client_debugfs_internal_clients(struct seq_file *m, void *data) +{ + struct drm_info_node *node = m->private; + struct drm_device *dev = node->minor->dev; + struct drm_printer p = drm_seq_file_printer(m); + struct drm_client_dev *client; + + mutex_lock(&dev->clientlist_mutex); + list_for_each_entry(client, &dev->clientlist, list) + drm_printf(&p, "%s\n", client->funcs->name); + mutex_unlock(&dev->clientlist_mutex); + + return 0; +} + +static const struct drm_info_list drm_client_debugfs_list[] = { + { "internal_clients", drm_client_debugfs_internal_clients, 0 }, +}; + +int drm_client_debugfs_init(struct drm_minor *minor) +{ + return drm_debugfs_create_files(drm_client_debugfs_list, + ARRAY_SIZE(drm_client_debugfs_list), + minor->debugfs_root, minor); +} +#endif diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index b2482818fee8..50a20bfc07ea 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -164,6 +165,12 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id, DRM_ERROR("Failed to create framebuffer debugfs file\n"); return ret; } + + ret = drm_client_debugfs_init(minor); + if (ret) { + DRM_ERROR("Failed to create client debugfs file\n"); + return ret; + } } if (dev->driver->debugfs_init) { diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 32a83b41ab61..6f21bafb29be 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -507,6 +508,8 @@ int drm_dev_init(struct drm_device *dev, dev->driver = driver; INIT_LIST_HEAD(&dev->filelist); + INIT_LIST_HEAD(&dev->filelist_internal); + INIT_LIST_HEAD(&dev->clientlist); INIT_LIST_HEAD(&dev->ctxlist); INIT_LIST_HEAD(&dev->vmalist); INIT_LIST_HEAD(&dev->maplist); @@ -516,6 +519,7 @@ int drm_dev_init(struct drm_device *dev, spin_lock_init(&dev->event_lock); mutex_init(&dev->struct_mutex); mutex_init(&dev->filelist_mutex); + mutex_init(&dev->clientlist_mutex); mutex_init(&dev->ctxlist_mutex); mutex_init(&dev->master_mutex); @@ -572,6 +576,7 @@ int drm_dev_init(struct drm_device *dev, err_free: mutex_destroy(&dev->master_mutex); mutex_destroy(&dev->ctxlist_mutex); + mutex_destroy(&dev->clientlist_mutex); mutex_destroy(&dev->filelist_mutex); mutex_destroy(&dev->struct_mutex); return ret; @@ -607,6 +612,7 @@ void drm_dev_fini(struct drm_device *dev) mutex_destroy(&dev->master_mutex); mutex_destroy(&dev->ctxlist_mutex); + mutex_destroy(&dev->clientlist_mutex); mutex_destroy(&dev->filelist_mutex); mutex_destroy(&dev->struct_mutex); kfree(dev->unique); @@ -862,6 +868,8 @@ void drm_dev_unregister(struct drm_device *dev) { struct drm_map_list *r_list, *list_temp; + drm_client_dev_unregister(dev); + if (drm_core_check_feature(dev, DRIVER_LEGACY)) drm_lastclose(dev); @@ -968,6 +976,7 @@ static const struct file_operations drm_stub_fops = { static void drm_core_exit(void) { + drm_client_exit(); unregister_chrdev(DRM_MAJOR, "drm"); debugfs_remove(drm_debugfs_root); drm_sysfs_destroy(); @@ -1001,6 +1010,8 @@ static int __init drm_core_init(void) if (ret < 0) goto error; + drm_client_init(); + drm_core_init_complete = true; DRM_DEBUG("Initialized\n"); diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 55505378df47..bcc688e58776 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -35,6 +35,7 @@ #include #include +#include #include #include @@ -443,6 +444,8 @@ void drm_lastclose(struct drm_device * dev) if (drm_core_check_feature(dev, DRIVER_LEGACY)) drm_legacy_dev_reinit(dev); + + drm_client_dev_lastclose(dev); } /** diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c index 527743394150..26be57e28a9d 100644 --- a/drivers/gpu/drm/drm_probe_helper.c +++ b/drivers/gpu/drm/drm_probe_helper.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -563,6 +564,8 @@ void drm_kms_helper_hotplug_event(struct drm_device *dev) drm_sysfs_hotplug_event(dev); if (dev->mode_config.funcs->output_poll_changed) dev->mode_config.funcs->output_poll_changed(dev); + + drm_client_dev_hotplug(dev); } EXPORT_SYMBOL(drm_kms_helper_hotplug_event); diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 1c5b5ce1fd7f..1fc066c41861 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include "drm_internal.h" @@ -320,6 +321,24 @@ void drm_sysfs_hotplug_event(struct drm_device *dev) } EXPORT_SYMBOL(drm_sysfs_hotplug_event); +static ssize_t remove_internal_clients_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct drm_minor *minor = dev_get_drvdata(dev); + + drm_client_dev_unregister(minor->dev); + + return len; +} +static DEVICE_ATTR_WO(remove_internal_clients); + +static struct attribute *minor_attrs[] = { + &dev_attr_remove_internal_clients.attr, + NULL, +}; +ATTRIBUTE_GROUPS(minor); + static void drm_sysfs_release(struct device *dev) { kfree(dev); @@ -347,6 +366,7 @@ struct device *drm_sysfs_minor_alloc(struct drm_minor *minor) kdev->class = drm_class; kdev->type = &drm_sysfs_device_minor; kdev->parent = minor->dev->dev; + kdev->groups = minor_groups; kdev->release = drm_sysfs_release; dev_set_drvdata(kdev, minor); diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h index 524f793d6e7b..6fd2fcaae826 100644 --- a/include/drm/drm_client.h +++ b/include/drm/drm_client.h @@ -5,13 +5,91 @@ #include +struct drm_clip_rect; struct drm_connector; struct drm_crtc; struct drm_device; struct drm_display_mode; +struct drm_framebuffer; +struct drm_minor; struct drm_mode_set; struct drm_plane; +struct drm_client_dev; + +/** + * struct drm_client_funcs - DRM client callbacks + */ +struct drm_client_funcs { + /** + * @name: + * + * Name of the client. Mandatory. + */ + const char *name; + + /** + * @remove: + * + * Called when a &drm_device is unregistered or the client is + * unregistered. If zero is returned drm_client_free() is called + * automatically. If the client can't drop it's resources it should + * return non-zero and call drm_client_free() later. + * + * This callback is optional. + */ + int (*remove)(struct drm_client_dev *client); + + /** + * @lastclose: + * + * Called on drm_lastclose(). The first client instance in the list + * that returns zero gets the privilege to restore and no more clients + * are called. + * + * This callback is optional. + */ + int (*lastclose)(struct drm_client_dev *client); + + /** + * @hotplug: + * + * Called on drm_kms_helper_hotplug_event(). + * + * This callback is optional. + */ + int (*hotplug)(struct drm_client_dev *client); +}; + +/** + * struct drm_client_dev - DRM client instance + */ +struct drm_client_dev { + struct list_head list; + struct drm_device *dev; + const struct drm_client_funcs *funcs; + struct drm_file *file; + unsigned int file_ref_count; + void *private; +}; + +struct drm_client_dev * +drm_client_new(struct drm_device *dev, const struct drm_client_funcs *funcs); +struct drm_client_dev * +drm_client_new_from_id(unsigned int dev_id, const struct drm_client_funcs *funcs); +void drm_client_remove(struct drm_client_dev *client); +int drm_client_remove_defer(struct drm_client_dev *client); +void drm_client_free(struct drm_client_dev *client); + +void drm_client_dev_unregister(struct drm_device *dev); +void drm_client_dev_hotplug(struct drm_device *dev); +void drm_client_dev_lastclose(struct drm_device *dev); + +void drm_client_init(void); +void drm_client_exit(void); + +int drm_client_debugfs_init(struct drm_minor *minor); + /** * struct drm_client_display - DRM client display */ @@ -76,4 +154,29 @@ drm_client_find_display(struct drm_device *dev, unsigned int width, unsigned int #define drm_client_display_for_each_mode(mode, display) \ list_for_each_entry(mode, &display->modes, head) +int drm_client_display_commit(struct drm_client_display *display, + struct drm_framebuffer *fb, struct drm_display_mode *mode); +struct drm_framebuffer *drm_client_display_current_fb(struct drm_client_display *display); + +struct drm_client_buffer { + struct drm_client_dev *client; + u32 width; + u32 height; + u32 format; + u32 handle; + u32 pitch; + u64 size; + struct dma_buf *dma_buf; + void *vaddr; + struct drm_framebuffer *fb; +}; + +struct drm_client_buffer * +drm_client_framebuffer_create(struct drm_client_dev *client, + struct drm_display_mode *mode, u32 format); +void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); + +int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, + struct drm_clip_rect *rect); + #endif diff --git a/include/drm/drm_device.h b/include/drm/drm_device.h index 3a0eac2885b7..17edadf8b691 100644 --- a/include/drm/drm_device.h +++ b/include/drm/drm_device.h @@ -74,6 +74,10 @@ struct drm_device { struct mutex filelist_mutex; struct list_head filelist; + struct list_head filelist_internal; + + struct mutex clientlist_mutex; + struct list_head clientlist; /** \name Memory management */ /*@{ */