From fc36679737b795a4ba0db82cd1897b799340d7c6 Mon Sep 17 00:00:00 2001 From: Dave Airlie Date: Mon, 10 Oct 2011 20:05:21 +0100 Subject: [PATCH] --- yaml --- r: 269743 b: refs/heads/master c: 62addcb8c188eaa28968e27a02518404939851d7 h: refs/heads/master i: 269741: 6f7bacfe1cb77a80913f1b43db2d427549536011 269739: 9c0d8a96926989665bffb26bfc39d97bcf5518b4 269735: 3f8882d80279d5743df48df85c5db111672ad18a 269727: 85f4f5835f30a148176202bd75a190866d751506 v: v3 --- [refs] | 2 +- trunk/drivers/gpu/drm/Kconfig | 2 + trunk/drivers/gpu/drm/Makefile | 1 + trunk/drivers/gpu/drm/exynos/Kconfig | 20 + trunk/drivers/gpu/drm/exynos/Makefile | 11 + trunk/drivers/gpu/drm/exynos/exynos_drm_buf.c | 110 ++ trunk/drivers/gpu/drm/exynos/exynos_drm_buf.h | 50 + .../gpu/drm/exynos/exynos_drm_connector.c | 293 +++++ .../gpu/drm/exynos/exynos_drm_connector.h | 34 + .../drivers/gpu/drm/exynos/exynos_drm_core.c | 272 +++++ .../drivers/gpu/drm/exynos/exynos_drm_crtc.c | 344 ++++++ .../drivers/gpu/drm/exynos/exynos_drm_crtc.h | 39 + trunk/drivers/gpu/drm/exynos/exynos_drm_drv.c | 230 ++++ trunk/drivers/gpu/drm/exynos/exynos_drm_drv.h | 242 ++++ .../gpu/drm/exynos/exynos_drm_encoder.c | 271 +++++ .../gpu/drm/exynos/exynos_drm_encoder.h | 45 + trunk/drivers/gpu/drm/exynos/exynos_drm_fb.c | 271 +++++ trunk/drivers/gpu/drm/exynos/exynos_drm_fb.h | 47 + .../drivers/gpu/drm/exynos/exynos_drm_fbdev.c | 441 +++++++ .../drivers/gpu/drm/exynos/exynos_drm_fbdev.h | 37 + .../drivers/gpu/drm/exynos/exynos_drm_fimd.c | 796 +++++++++++++ trunk/drivers/gpu/drm/exynos/exynos_drm_gem.c | 415 +++++++ trunk/drivers/gpu/drm/exynos/exynos_drm_gem.h | 107 ++ trunk/drivers/gpu/drm/radeon/radeon.h | 2 +- trunk/drivers/gpu/drm/radeon/radeon_combios.c | 4 +- .../gpu/drm/radeon/radeon_connectors.c | 28 +- trunk/drivers/gpu/drm/radeon/radeon_device.c | 20 +- trunk/drivers/gpu/drm/radeon/radeon_mode.h | 1 + trunk/drivers/gpu/drm/ttm/ttm_bo.c | 1 + trunk/drivers/gpu/drm/vmwgfx/Makefile | 2 +- trunk/drivers/gpu/drm/vmwgfx/svga3d_reg.h | 259 +++-- trunk/drivers/gpu/drm/vmwgfx/svga_escape.h | 2 +- trunk/drivers/gpu/drm/vmwgfx/svga_overlay.h | 22 +- trunk/drivers/gpu/drm/vmwgfx/svga_reg.h | 220 +++- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c | 47 + trunk/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c | 322 ++++++ trunk/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c | 166 ++- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h | 129 ++- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c | 758 +++++++++--- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c | 56 +- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c | 542 ++++++++- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h | 12 +- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c | 83 +- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c | 215 ++++ trunk/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c | 67 +- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c | 995 ++++++++++++---- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h | 44 +- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 273 +---- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c | 188 ++- .../drivers/gpu/drm/vmwgfx/vmwgfx_resource.c | 1015 +++++++++++++++-- trunk/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 566 +++++++++ trunk/include/drm/exynos_drm.h | 104 ++ trunk/include/drm/vmwgfx_drm.h | 106 ++ 53 files changed, 9249 insertions(+), 1080 deletions(-) create mode 100644 trunk/drivers/gpu/drm/exynos/Kconfig create mode 100644 trunk/drivers/gpu/drm/exynos/Makefile create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_buf.c create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_buf.h create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_connector.c create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_connector.h create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_core.c create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_crtc.c create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_crtc.h create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_drv.c create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_drv.h create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_encoder.c create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_encoder.h create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_fb.c create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_fb.h create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_fbdev.c create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_fbdev.h create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_fimd.c create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_gem.c create mode 100644 trunk/drivers/gpu/drm/exynos/exynos_drm_gem.h create mode 100644 trunk/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c create mode 100644 trunk/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c create mode 100644 trunk/include/drm/exynos_drm.h diff --git a/[refs] b/[refs] index ace584c05d0a..88e2e7bf34ed 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 64a742fac3a22f57303d8f1b7e347350a1c48254 +refs/heads/master: 62addcb8c188eaa28968e27a02518404939851d7 diff --git a/trunk/drivers/gpu/drm/Kconfig b/trunk/drivers/gpu/drm/Kconfig index b493663c7ba7..e405e61c27e2 100644 --- a/trunk/drivers/gpu/drm/Kconfig +++ b/trunk/drivers/gpu/drm/Kconfig @@ -158,3 +158,5 @@ config DRM_SAVAGE help Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister chipset. If M is selected the module will be called savage. + +source "drivers/gpu/drm/exynos/Kconfig" \ No newline at end of file diff --git a/trunk/drivers/gpu/drm/Makefile b/trunk/drivers/gpu/drm/Makefile index 89cf05a72d1c..c0496f660707 100644 --- a/trunk/drivers/gpu/drm/Makefile +++ b/trunk/drivers/gpu/drm/Makefile @@ -35,4 +35,5 @@ obj-$(CONFIG_DRM_SAVAGE)+= savage/ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ obj-$(CONFIG_DRM_VIA) +=via/ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ +obj-$(CONFIG_DRM_EXYNOS) +=exynos/ obj-y += i2c/ diff --git a/trunk/drivers/gpu/drm/exynos/Kconfig b/trunk/drivers/gpu/drm/exynos/Kconfig new file mode 100644 index 000000000000..847466aab435 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/Kconfig @@ -0,0 +1,20 @@ +config DRM_EXYNOS + tristate "DRM Support for Samsung SoC EXYNOS Series" + depends on DRM && PLAT_SAMSUNG + default n + select DRM_KMS_HELPER + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE + help + Choose this option if you have a Samsung SoC EXYNOS chipset. + If M is selected the module will be called exynosdrm. + +config DRM_EXYNOS_FIMD + tristate "Exynos DRM FIMD" + depends on DRM_EXYNOS + default n + help + Choose this option if you want to use Exynos FIMD for DRM. + If M is selected, the module will be called exynos_drm_fimd diff --git a/trunk/drivers/gpu/drm/exynos/Makefile b/trunk/drivers/gpu/drm/exynos/Makefile new file mode 100644 index 000000000000..0496d3ff2683 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the drm device driver. This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. + +ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos +exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \ + exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \ + exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o + +obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o +obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_buf.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_buf.c new file mode 100644 index 000000000000..6f8afea94fc9 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_buf.c @@ -0,0 +1,110 @@ +/* exynos_drm_buf.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Author: Inki Dae + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "drm.h" + +#include "exynos_drm_drv.h" +#include "exynos_drm_buf.h" + +static DEFINE_MUTEX(exynos_drm_buf_lock); + +static int lowlevel_buffer_allocate(struct drm_device *dev, + struct exynos_drm_buf_entry *entry) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + entry->vaddr = dma_alloc_writecombine(dev->dev, entry->size, + (dma_addr_t *)&entry->paddr, GFP_KERNEL); + if (!entry->paddr) { + DRM_ERROR("failed to allocate buffer.\n"); + return -ENOMEM; + } + + DRM_DEBUG_KMS("allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n", + (unsigned int)entry->vaddr, entry->paddr, entry->size); + + return 0; +} + +static void lowlevel_buffer_deallocate(struct drm_device *dev, + struct exynos_drm_buf_entry *entry) +{ + DRM_DEBUG_KMS("%s.\n", __FILE__); + + if (entry->paddr && entry->vaddr && entry->size) + dma_free_writecombine(dev->dev, entry->size, entry->vaddr, + entry->paddr); + else + DRM_DEBUG_KMS("entry data is null.\n"); +} + +struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev, + unsigned int size) +{ + struct exynos_drm_buf_entry *entry; + + DRM_DEBUG_KMS("%s.\n", __FILE__); + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + DRM_ERROR("failed to allocate exynos_drm_buf_entry.\n"); + return ERR_PTR(-ENOMEM); + } + + entry->size = size; + + /* + * allocate memory region with size and set the memory information + * to vaddr and paddr of a entry object. + */ + if (lowlevel_buffer_allocate(dev, entry) < 0) { + kfree(entry); + entry = NULL; + return ERR_PTR(-ENOMEM); + } + + return entry; +} + +void exynos_drm_buf_destroy(struct drm_device *dev, + struct exynos_drm_buf_entry *entry) +{ + DRM_DEBUG_KMS("%s.\n", __FILE__); + + if (!entry) { + DRM_DEBUG_KMS("entry is null.\n"); + return; + } + + lowlevel_buffer_deallocate(dev, entry); + + kfree(entry); + entry = NULL; +} + +MODULE_AUTHOR("Inki Dae "); +MODULE_DESCRIPTION("Samsung SoC DRM Buffer Management Module"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_buf.h b/trunk/drivers/gpu/drm/exynos/exynos_drm_buf.h new file mode 100644 index 000000000000..9b1f0fb8d3a5 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_buf.h @@ -0,0 +1,50 @@ +/* exynos_drm_buf.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Author: Inki Dae + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _EXYNOS_DRM_BUF_H_ +#define _EXYNOS_DRM_BUF_H_ + +/* + * exynos drm buffer entry structure. + * + * @paddr: physical address of allocated memory. + * @vaddr: kernel virtual address of allocated memory. + * @size: size of allocated memory. + */ +struct exynos_drm_buf_entry { + dma_addr_t paddr; + void __iomem *vaddr; + unsigned int size; +}; + +/* allocate physical memory. */ +struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev, + unsigned int size); + +/* remove allocated physical memory. */ +void exynos_drm_buf_destroy(struct drm_device *dev, + struct exynos_drm_buf_entry *entry); + +#endif diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_connector.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_connector.c new file mode 100644 index 000000000000..985d9e768728 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_connector.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "drm_crtc_helper.h" + +#include "exynos_drm_drv.h" +#include "exynos_drm_encoder.h" + +#define MAX_EDID 256 +#define to_exynos_connector(x) container_of(x, struct exynos_drm_connector,\ + drm_connector) + +struct exynos_drm_connector { + struct drm_connector drm_connector; +}; + +/* convert exynos_video_timings to drm_display_mode */ +static inline void +convert_to_display_mode(struct drm_display_mode *mode, + struct fb_videomode *timing) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + mode->clock = timing->pixclock / 1000; + + mode->hdisplay = timing->xres; + mode->hsync_start = mode->hdisplay + timing->left_margin; + mode->hsync_end = mode->hsync_start + timing->hsync_len; + mode->htotal = mode->hsync_end + timing->right_margin; + + mode->vdisplay = timing->yres; + mode->vsync_start = mode->vdisplay + timing->upper_margin; + mode->vsync_end = mode->vsync_start + timing->vsync_len; + mode->vtotal = mode->vsync_end + timing->lower_margin; +} + +/* convert drm_display_mode to exynos_video_timings */ +static inline void +convert_to_video_timing(struct fb_videomode *timing, + struct drm_display_mode *mode) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + memset(timing, 0, sizeof(*timing)); + + timing->pixclock = mode->clock * 1000; + timing->refresh = mode->vrefresh; + + timing->xres = mode->hdisplay; + timing->left_margin = mode->hsync_start - mode->hdisplay; + timing->hsync_len = mode->hsync_end - mode->hsync_start; + timing->right_margin = mode->htotal - mode->hsync_end; + + timing->yres = mode->vdisplay; + timing->upper_margin = mode->vsync_start - mode->vdisplay; + timing->vsync_len = mode->vsync_end - mode->vsync_start; + timing->lower_margin = mode->vtotal - mode->vsync_end; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + timing->vmode = FB_VMODE_INTERLACED; + else + timing->vmode = FB_VMODE_NONINTERLACED; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) + timing->vmode |= FB_VMODE_DOUBLE; +} + +static int exynos_drm_connector_get_modes(struct drm_connector *connector) +{ + struct exynos_drm_manager *manager = + exynos_drm_get_manager(connector->encoder); + struct exynos_drm_display *display = manager->display; + unsigned int count; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (!display) { + DRM_DEBUG_KMS("display is null.\n"); + return 0; + } + + /* + * if get_edid() exists then get_edid() callback of hdmi side + * is called to get edid data through i2c interface else + * get timing from the FIMD driver(display controller). + * + * P.S. in case of lcd panel, count is always 1 if success + * because lcd panel has only one mode. + */ + if (display->get_edid) { + int ret; + void *edid; + + edid = kzalloc(MAX_EDID, GFP_KERNEL); + if (!edid) { + DRM_ERROR("failed to allocate edid\n"); + return 0; + } + + ret = display->get_edid(manager->dev, connector, + edid, MAX_EDID); + if (ret < 0) { + DRM_ERROR("failed to get edid data.\n"); + kfree(edid); + edid = NULL; + return 0; + } + + drm_mode_connector_update_edid_property(connector, edid); + count = drm_add_edid_modes(connector, edid); + + kfree(connector->display_info.raw_edid); + connector->display_info.raw_edid = edid; + } else { + struct drm_display_mode *mode = drm_mode_create(connector->dev); + struct fb_videomode *timing; + + if (display->get_timing) + timing = display->get_timing(manager->dev); + else { + drm_mode_destroy(connector->dev, mode); + return 0; + } + + convert_to_display_mode(mode, timing); + + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_set_name(mode); + drm_mode_probed_add(connector, mode); + + count = 1; + } + + return count; +} + +static int exynos_drm_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct exynos_drm_manager *manager = + exynos_drm_get_manager(connector->encoder); + struct exynos_drm_display *display = manager->display; + struct fb_videomode timing; + int ret = MODE_BAD; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + convert_to_video_timing(&timing, mode); + + if (display && display->check_timing) + if (!display->check_timing(manager->dev, (void *)&timing)) + ret = MODE_OK; + + return ret; +} + +struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + return connector->encoder; +} + +static struct drm_connector_helper_funcs exynos_connector_helper_funcs = { + .get_modes = exynos_drm_connector_get_modes, + .mode_valid = exynos_drm_connector_mode_valid, + .best_encoder = exynos_drm_best_encoder, +}; + +/* get detection status of display device. */ +static enum drm_connector_status +exynos_drm_connector_detect(struct drm_connector *connector, bool force) +{ + struct exynos_drm_manager *manager = + exynos_drm_get_manager(connector->encoder); + struct exynos_drm_display *display = manager->display; + enum drm_connector_status status = connector_status_disconnected; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (display && display->is_connected) { + if (display->is_connected(manager->dev)) + status = connector_status_connected; + else + status = connector_status_disconnected; + } + + return status; +} + +static void exynos_drm_connector_destroy(struct drm_connector *connector) +{ + struct exynos_drm_connector *exynos_connector = + to_exynos_connector(connector); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(exynos_connector); +} + +static struct drm_connector_funcs exynos_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = exynos_drm_connector_detect, + .destroy = exynos_drm_connector_destroy, +}; + +struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, + struct drm_encoder *encoder) +{ + struct exynos_drm_connector *exynos_connector; + struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); + struct drm_connector *connector; + int type; + int err; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL); + if (!exynos_connector) { + DRM_ERROR("failed to allocate connector\n"); + return NULL; + } + + connector = &exynos_connector->drm_connector; + + switch (manager->display->type) { + case EXYNOS_DISPLAY_TYPE_HDMI: + type = DRM_MODE_CONNECTOR_HDMIA; + break; + default: + type = DRM_MODE_CONNECTOR_Unknown; + break; + } + + drm_connector_init(dev, connector, &exynos_connector_funcs, type); + drm_connector_helper_add(connector, &exynos_connector_helper_funcs); + + err = drm_sysfs_connector_add(connector); + if (err) + goto err_connector; + + connector->encoder = encoder; + err = drm_mode_connector_attach_encoder(connector, encoder); + if (err) { + DRM_ERROR("failed to attach a connector to a encoder\n"); + goto err_sysfs; + } + + DRM_DEBUG_KMS("connector has been created\n"); + + return connector; + +err_sysfs: + drm_sysfs_connector_remove(connector); +err_connector: + drm_connector_cleanup(connector); + kfree(exynos_connector); + return NULL; +} + +MODULE_AUTHOR("Inki Dae "); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("Seung-Woo Kim "); +MODULE_DESCRIPTION("Samsung SoC DRM Connector Driver"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_connector.h b/trunk/drivers/gpu/drm/exynos/exynos_drm_connector.h new file mode 100644 index 000000000000..1c7b2b5b579c --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_connector.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _EXYNOS_DRM_CONNECTOR_H_ +#define _EXYNOS_DRM_CONNECTOR_H_ + +struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, + struct drm_encoder *encoder); + +#endif diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_core.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_core.c new file mode 100644 index 000000000000..edb0ee13cffc --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_core.c @@ -0,0 +1,272 @@ +/* exynos_drm_core.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Author: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "exynos_drm_drv.h" +#include "exynos_drm_encoder.h" +#include "exynos_drm_connector.h" +#include "exynos_drm_fbdev.h" + +static DEFINE_MUTEX(exynos_drm_mutex); +static LIST_HEAD(exynos_drm_subdrv_list); +static struct drm_device *drm_dev; + +static int exynos_drm_subdrv_probe(struct drm_device *dev, + struct exynos_drm_subdrv *subdrv) +{ + struct drm_encoder *encoder; + struct drm_connector *connector; + + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + if (subdrv->probe) { + int ret; + + /* + * this probe callback would be called by sub driver + * after setting of all resources to this sub driver, + * such as clock, irq and register map are done or by load() + * of exynos drm driver. + * + * P.S. note that this driver is considered for modularization. + */ + ret = subdrv->probe(dev); + if (ret) + return ret; + } + + /* create and initialize a encoder for this sub driver. */ + encoder = exynos_drm_encoder_create(dev, &subdrv->manager, + (1 << MAX_CRTC) - 1); + if (!encoder) { + DRM_ERROR("failed to create encoder\n"); + return -EFAULT; + } + + /* + * create and initialize a connector for this sub driver and + * attach the encoder created above to the connector. + */ + connector = exynos_drm_connector_create(dev, encoder); + if (!connector) { + DRM_ERROR("failed to create connector\n"); + encoder->funcs->destroy(encoder); + return -EFAULT; + } + + subdrv->encoder = encoder; + subdrv->connector = connector; + + return 0; +} + +static void exynos_drm_subdrv_remove(struct drm_device *dev, + struct exynos_drm_subdrv *subdrv) +{ + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + if (subdrv->remove) + subdrv->remove(dev); + + if (subdrv->encoder) { + struct drm_encoder *encoder = subdrv->encoder; + encoder->funcs->destroy(encoder); + subdrv->encoder = NULL; + } + + if (subdrv->connector) { + struct drm_connector *connector = subdrv->connector; + connector->funcs->destroy(connector); + subdrv->connector = NULL; + } +} + +int exynos_drm_device_register(struct drm_device *dev) +{ + struct exynos_drm_subdrv *subdrv, *n; + int err; + + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + if (!dev) + return -EINVAL; + + if (drm_dev) { + DRM_ERROR("Already drm device were registered\n"); + return -EBUSY; + } + + mutex_lock(&exynos_drm_mutex); + list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) { + err = exynos_drm_subdrv_probe(dev, subdrv); + if (err) { + DRM_DEBUG("exynos drm subdrv probe failed.\n"); + list_del(&subdrv->list); + } + } + + drm_dev = dev; + mutex_unlock(&exynos_drm_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(exynos_drm_device_register); + +int exynos_drm_device_unregister(struct drm_device *dev) +{ + struct exynos_drm_subdrv *subdrv; + + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + if (!dev || dev != drm_dev) { + WARN(1, "Unexpected drm device unregister!\n"); + return -EINVAL; + } + + mutex_lock(&exynos_drm_mutex); + list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) + exynos_drm_subdrv_remove(dev, subdrv); + + drm_dev = NULL; + mutex_unlock(&exynos_drm_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(exynos_drm_device_unregister); + +static int exynos_drm_mode_group_reinit(struct drm_device *dev) +{ + struct drm_mode_group *group = &dev->primary->mode_group; + uint32_t *id_list = group->id_list; + int ret; + + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + ret = drm_mode_group_init_legacy_group(dev, group); + if (ret < 0) + return ret; + + kfree(id_list); + return 0; +} + +int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv) +{ + int err; + + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + if (!subdrv) + return -EINVAL; + + mutex_lock(&exynos_drm_mutex); + if (drm_dev) { + err = exynos_drm_subdrv_probe(drm_dev, subdrv); + if (err) { + DRM_ERROR("failed to probe exynos drm subdrv\n"); + mutex_unlock(&exynos_drm_mutex); + return err; + } + + /* + * if any specific driver such as fimd or hdmi driver called + * exynos_drm_subdrv_register() later than drm_load(), + * the fb helper should be re-initialized and re-configured. + */ + err = exynos_drm_fbdev_reinit(drm_dev); + if (err) { + DRM_ERROR("failed to reinitialize exynos drm fbdev\n"); + exynos_drm_subdrv_remove(drm_dev, subdrv); + mutex_unlock(&exynos_drm_mutex); + return err; + } + + err = exynos_drm_mode_group_reinit(drm_dev); + if (err) { + DRM_ERROR("failed to reinitialize mode group\n"); + exynos_drm_fbdev_fini(drm_dev); + exynos_drm_subdrv_remove(drm_dev, subdrv); + mutex_unlock(&exynos_drm_mutex); + return err; + } + } + + subdrv->drm_dev = drm_dev; + + list_add_tail(&subdrv->list, &exynos_drm_subdrv_list); + mutex_unlock(&exynos_drm_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register); + +int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv) +{ + int ret = -EFAULT; + + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + if (!subdrv) { + DRM_DEBUG("Unexpected exynos drm subdrv unregister!\n"); + return ret; + } + + mutex_lock(&exynos_drm_mutex); + if (drm_dev) { + exynos_drm_subdrv_remove(drm_dev, subdrv); + list_del(&subdrv->list); + + /* + * fb helper should be updated once a sub driver is released + * to re-configure crtc and connector and also to re-setup + * drm framebuffer. + */ + ret = exynos_drm_fbdev_reinit(drm_dev); + if (ret < 0) { + DRM_ERROR("failed fb helper reinit.\n"); + goto fail; + } + + ret = exynos_drm_mode_group_reinit(drm_dev); + if (ret < 0) { + DRM_ERROR("failed drm mode group reinit.\n"); + goto fail; + } + } + +fail: + mutex_unlock(&exynos_drm_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister); + +MODULE_AUTHOR("Inki Dae "); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("Seung-Woo Kim "); +MODULE_DESCRIPTION("Samsung SoC DRM Core Driver"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_crtc.c new file mode 100644 index 000000000000..683ceb0f5277 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_crtc.c @@ -0,0 +1,344 @@ +/* exynos_drm_crtc.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "drm_crtc_helper.h" + +#include "exynos_drm_drv.h" +#include "exynos_drm_fb.h" +#include "exynos_drm_encoder.h" + +#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ + drm_crtc) + +/* + * @fb_x: horizontal position from framebuffer base + * @fb_y: vertical position from framebuffer base + * @base_x: horizontal position from screen base + * @base_y: vertical position from screen base + * @crtc_w: width of crtc + * @crtc_h: height of crtc + */ +struct exynos_drm_crtc_pos { + unsigned int fb_x; + unsigned int fb_y; + unsigned int base_x; + unsigned int base_y; + unsigned int crtc_w; + unsigned int crtc_h; +}; + +/* + * Exynos specific crtc structure. + * + * @drm_crtc: crtc object. + * @overlay: contain information common to display controller and hdmi and + * contents of this overlay object would be copied to sub driver size. + * @pipe: a crtc index created at load() with a new crtc object creation + * and the crtc object would be set to private->crtc array + * to get a crtc object corresponding to this pipe from private->crtc + * array when irq interrupt occured. the reason of using this pipe is that + * drm framework doesn't support multiple irq yet. + * we can refer to the crtc to current hardware interrupt occured through + * this pipe value. + */ +struct exynos_drm_crtc { + struct drm_crtc drm_crtc; + struct exynos_drm_overlay overlay; + unsigned int pipe; +}; + +void exynos_drm_crtc_apply(struct drm_crtc *crtc) +{ + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct exynos_drm_overlay *overlay = &exynos_crtc->overlay; + + exynos_drm_fn_encoder(crtc, overlay, + exynos_drm_encoder_crtc_mode_set); + exynos_drm_fn_encoder(crtc, NULL, exynos_drm_encoder_crtc_commit); +} + +static void exynos_drm_overlay_update(struct exynos_drm_overlay *overlay, + struct drm_framebuffer *fb, + struct drm_display_mode *mode, + struct exynos_drm_crtc_pos *pos) +{ + struct exynos_drm_buffer_info buffer_info; + unsigned int actual_w = pos->crtc_w; + unsigned int actual_h = pos->crtc_h; + unsigned int hw_w; + unsigned int hw_h; + + /* update buffer address of framebuffer. */ + exynos_drm_fb_update_buf_off(fb, pos->fb_x, pos->fb_y, &buffer_info); + overlay->paddr = buffer_info.paddr; + overlay->vaddr = buffer_info.vaddr; + + hw_w = mode->hdisplay - pos->base_x; + hw_h = mode->vdisplay - pos->base_y; + + if (actual_w > hw_w) + actual_w = hw_w; + if (actual_h > hw_h) + actual_h = hw_h; + + overlay->offset_x = pos->base_x; + overlay->offset_y = pos->base_y; + overlay->width = actual_w; + overlay->height = actual_h; + overlay->bpp = fb->bits_per_pixel; + + DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)", + overlay->offset_x, overlay->offset_y, + overlay->width, overlay->height); + + overlay->buf_offsize = fb->width - actual_w; + overlay->line_size = actual_w; +} + +static int exynos_drm_crtc_update(struct drm_crtc *crtc) +{ + struct exynos_drm_crtc *exynos_crtc; + struct exynos_drm_overlay *overlay; + struct exynos_drm_crtc_pos pos; + struct drm_display_mode *mode = &crtc->mode; + struct drm_framebuffer *fb = crtc->fb; + + if (!mode || !fb) + return -EINVAL; + + exynos_crtc = to_exynos_crtc(crtc); + overlay = &exynos_crtc->overlay; + + memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos)); + pos.fb_x = crtc->x; + pos.fb_y = crtc->y; + pos.crtc_w = fb->width - crtc->x; + pos.crtc_h = fb->height - crtc->y; + + exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos); + + return 0; +} + +static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO */ +} + +static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* drm framework doesn't check NULL. */ +} + +static void exynos_drm_crtc_commit(struct drm_crtc *crtc) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* drm framework doesn't check NULL. */ +} + +static bool +exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* drm framework doesn't check NULL */ + return true; +} + +static int +exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, int x, int y, + struct drm_framebuffer *old_fb) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + mode = adjusted_mode; + + return exynos_drm_crtc_update(crtc); +} + +static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + int ret; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + ret = exynos_drm_crtc_update(crtc); + if (ret) + return ret; + + exynos_drm_crtc_apply(crtc); + + return ret; +} + +static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + /* drm framework doesn't check NULL */ +} + +static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { + .dpms = exynos_drm_crtc_dpms, + .prepare = exynos_drm_crtc_prepare, + .commit = exynos_drm_crtc_commit, + .mode_fixup = exynos_drm_crtc_mode_fixup, + .mode_set = exynos_drm_crtc_mode_set, + .mode_set_base = exynos_drm_crtc_mode_set_base, + .load_lut = exynos_drm_crtc_load_lut, +}; + +static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, + struct drm_framebuffer *fb, + struct drm_pending_vblank_event *event) +{ + struct drm_device *dev = crtc->dev; + struct exynos_drm_private *dev_priv = dev->dev_private; + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct drm_framebuffer *old_fb = crtc->fb; + int ret = -EINVAL; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + mutex_lock(&dev->struct_mutex); + + if (event && !dev_priv->pageflip_event) { + list_add_tail(&event->base.link, + &dev_priv->pageflip_event_list); + + ret = drm_vblank_get(dev, exynos_crtc->pipe); + if (ret) { + DRM_DEBUG("failed to acquire vblank counter\n"); + goto out; + } + + crtc->fb = fb; + ret = exynos_drm_crtc_update(crtc); + if (ret) { + crtc->fb = old_fb; + drm_vblank_put(dev, exynos_crtc->pipe); + dev_priv->pageflip_event = false; + + goto out; + } + + dev_priv->pageflip_event = true; + } +out: + mutex_unlock(&dev->struct_mutex); + return ret; +} + +static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) +{ + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + struct exynos_drm_private *private = crtc->dev->dev_private; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + private->crtc[exynos_crtc->pipe] = NULL; + + drm_crtc_cleanup(crtc); + kfree(exynos_crtc); +} + +static struct drm_crtc_funcs exynos_crtc_funcs = { + .set_config = drm_crtc_helper_set_config, + .page_flip = exynos_drm_crtc_page_flip, + .destroy = exynos_drm_crtc_destroy, +}; + +struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, + struct drm_crtc *crtc) +{ + struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); + + return &exynos_crtc->overlay; +} + +int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) +{ + struct exynos_drm_crtc *exynos_crtc; + struct exynos_drm_private *private = dev->dev_private; + struct drm_crtc *crtc; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); + if (!exynos_crtc) { + DRM_ERROR("failed to allocate exynos crtc\n"); + return -ENOMEM; + } + + exynos_crtc->pipe = nr; + crtc = &exynos_crtc->drm_crtc; + + private->crtc[nr] = crtc; + + drm_crtc_init(dev, crtc, &exynos_crtc_funcs); + drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); + + return 0; +} + +int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) +{ + struct exynos_drm_private *private = dev->dev_private; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_drm_fn_encoder(private->crtc[crtc], &crtc, + exynos_drm_enable_vblank); + + return 0; +} + +void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) +{ + struct exynos_drm_private *private = dev->dev_private; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_drm_fn_encoder(private->crtc[crtc], &crtc, + exynos_drm_disable_vblank); +} + +MODULE_AUTHOR("Inki Dae "); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("Seung-Woo Kim "); +MODULE_DESCRIPTION("Samsung SoC DRM CRTC Driver"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_crtc.h b/trunk/drivers/gpu/drm/exynos/exynos_drm_crtc.h new file mode 100644 index 000000000000..452b62b23853 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_crtc.h @@ -0,0 +1,39 @@ +/* exynos_drm_crtc.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _EXYNOS_DRM_CRTC_H_ +#define _EXYNOS_DRM_CRTC_H_ + +struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev, + struct drm_crtc *crtc); +void exynos_drm_crtc_apply(struct drm_crtc *crtc); +int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr); +int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc); +void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc); + +#endif diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_drv.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_drv.c new file mode 100644 index 000000000000..a190348ed9bd --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_drv.c @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "drm.h" + +#include + +#include "exynos_drm_drv.h" +#include "exynos_drm_crtc.h" +#include "exynos_drm_fbdev.h" +#include "exynos_drm_fb.h" +#include "exynos_drm_gem.h" + +#define DRIVER_NAME "exynos-drm" +#define DRIVER_DESC "Samsung SoC DRM" +#define DRIVER_DATE "20110530" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +static int exynos_drm_load(struct drm_device *dev, unsigned long flags) +{ + struct exynos_drm_private *private; + int ret; + int nr; + + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL); + if (!private) { + DRM_ERROR("failed to allocate private\n"); + return -ENOMEM; + } + + INIT_LIST_HEAD(&private->pageflip_event_list); + dev->dev_private = (void *)private; + + drm_mode_config_init(dev); + + exynos_drm_mode_config_init(dev); + + /* + * EXYNOS4 is enough to have two CRTCs and each crtc would be used + * without dependency of hardware. + */ + for (nr = 0; nr < MAX_CRTC; nr++) { + ret = exynos_drm_crtc_create(dev, nr); + if (ret) + goto err_crtc; + } + + ret = drm_vblank_init(dev, MAX_CRTC); + if (ret) + goto err_crtc; + + /* + * probe sub drivers such as display controller and hdmi driver, + * that were registered at probe() of platform driver + * to the sub driver and create encoder and connector for them. + */ + ret = exynos_drm_device_register(dev); + if (ret) + goto err_vblank; + + /* + * create and configure fb helper and also exynos specific + * fbdev object. + */ + ret = exynos_drm_fbdev_init(dev); + if (ret) { + DRM_ERROR("failed to initialize drm fbdev\n"); + goto err_drm_device; + } + + return 0; + +err_drm_device: + exynos_drm_device_unregister(dev); +err_vblank: + drm_vblank_cleanup(dev); +err_crtc: + drm_mode_config_cleanup(dev); + kfree(private); + + return ret; +} + +static int exynos_drm_unload(struct drm_device *dev) +{ + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + exynos_drm_fbdev_fini(dev); + exynos_drm_device_unregister(dev); + drm_vblank_cleanup(dev); + drm_mode_config_cleanup(dev); + kfree(dev->dev_private); + + dev->dev_private = NULL; + + return 0; +} + +static void exynos_drm_lastclose(struct drm_device *dev) +{ + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + exynos_drm_fbdev_restore_mode(dev); +} + +static struct vm_operations_struct exynos_drm_gem_vm_ops = { + .fault = exynos_drm_gem_fault, + .open = drm_gem_vm_open, + .close = drm_gem_vm_close, +}; + +static struct drm_ioctl_desc exynos_ioctls[] = { + DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl, + DRM_UNLOCKED | DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP_OFFSET, + exynos_drm_gem_map_offset_ioctl, DRM_UNLOCKED | + DRM_AUTH), + DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP, + exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH), +}; + +static struct drm_driver exynos_drm_driver = { + .driver_features = DRIVER_HAVE_IRQ | DRIVER_BUS_PLATFORM | + DRIVER_MODESET | DRIVER_GEM, + .load = exynos_drm_load, + .unload = exynos_drm_unload, + .lastclose = exynos_drm_lastclose, + .get_vblank_counter = drm_vblank_count, + .enable_vblank = exynos_drm_crtc_enable_vblank, + .disable_vblank = exynos_drm_crtc_disable_vblank, + .gem_init_object = exynos_drm_gem_init_object, + .gem_free_object = exynos_drm_gem_free_object, + .gem_vm_ops = &exynos_drm_gem_vm_ops, + .dumb_create = exynos_drm_gem_dumb_create, + .dumb_map_offset = exynos_drm_gem_dumb_map_offset, + .dumb_destroy = exynos_drm_gem_dumb_destroy, + .ioctls = exynos_ioctls, + .fops = { + .owner = THIS_MODULE, + .open = drm_open, + .mmap = exynos_drm_gem_mmap, + .poll = drm_poll, + .read = drm_read, + .unlocked_ioctl = drm_ioctl, + .release = drm_release, + }, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, +}; + +static int exynos_drm_platform_probe(struct platform_device *pdev) +{ + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls); + + return drm_platform_init(&exynos_drm_driver, pdev); +} + +static int exynos_drm_platform_remove(struct platform_device *pdev) +{ + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + drm_platform_exit(&exynos_drm_driver, pdev); + + return 0; +} + +static struct platform_driver exynos_drm_platform_driver = { + .probe = exynos_drm_platform_probe, + .remove = __devexit_p(exynos_drm_platform_remove), + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, +}; + +static int __init exynos_drm_init(void) +{ + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + return platform_driver_register(&exynos_drm_platform_driver); +} + +static void __exit exynos_drm_exit(void) +{ + DRM_DEBUG_DRIVER("%s\n", __FILE__); + + platform_driver_unregister(&exynos_drm_platform_driver); +} + +module_init(exynos_drm_init); +module_exit(exynos_drm_exit); + +MODULE_AUTHOR("Inki Dae "); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("Seung-Woo Kim "); +MODULE_DESCRIPTION("Samsung SoC DRM Driver"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_drv.h b/trunk/drivers/gpu/drm/exynos/exynos_drm_drv.h new file mode 100644 index 000000000000..832b6508adbd --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_drv.h @@ -0,0 +1,242 @@ +/* exynos_drm_drv.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _EXYNOS_DRM_DRV_H_ +#define _EXYNOS_DRM_DRV_H_ + +#include "drm.h" + +#define MAX_CRTC 2 + +struct drm_device; +struct exynos_drm_overlay; +struct drm_connector; + +/* this enumerates display type. */ +enum exynos_drm_output_type { + EXYNOS_DISPLAY_TYPE_NONE, + /* RGB or CPU Interface. */ + EXYNOS_DISPLAY_TYPE_LCD, + /* HDMI Interface. */ + EXYNOS_DISPLAY_TYPE_HDMI, +}; + +/* + * Exynos drm overlay ops structure. + * + * @mode_set: copy drm overlay info to hw specific overlay info. + * @commit: apply hardware specific overlay data to registers. + * @disable: disable hardware specific overlay. + */ +struct exynos_drm_overlay_ops { + void (*mode_set)(struct device *subdrv_dev, + struct exynos_drm_overlay *overlay); + void (*commit)(struct device *subdrv_dev); + void (*disable)(struct device *subdrv_dev); +}; + +/* + * Exynos drm common overlay structure. + * + * @offset_x: offset to x position. + * @offset_y: offset to y position. + * @width: window width. + * @height: window height. + * @bpp: pixel size.(in bit) + * @paddr: bus(accessed by dma) physical memory address to this overlay + * and this is physically continuous. + * @vaddr: virtual memory addresss to this overlay. + * @buf_off: start offset of framebuffer to be displayed. + * @buf_offsize: this value has result from + * (framebuffer width - display width) * bpp. + * @line_size: line size to this overlay memory in bytes. + * @default_win: a window to be enabled. + * @color_key: color key on or off. + * @index_color: if using color key feature then this value would be used + * as index color. + * @local_path: in case of lcd type, local path mode on or off. + * @transparency: transparency on or off. + * @activated: activated or not. + * + * this structure is common to exynos SoC and its contents would be copied + * to hardware specific overlay info. + */ +struct exynos_drm_overlay { + unsigned int offset_x; + unsigned int offset_y; + unsigned int width; + unsigned int height; + unsigned int bpp; + dma_addr_t paddr; + void __iomem *vaddr; + unsigned int buf_off; + unsigned int buf_offsize; + unsigned int line_size; + + bool default_win; + bool color_key; + unsigned int index_color; + bool local_path; + bool transparency; + bool activated; +}; + +/* + * Exynos DRM Display Structure. + * - this structure is common to analog tv, digital tv and lcd panel. + * + * @type: one of exynos_DISPLAY_TYPE_LCD and HDMI. + * @is_connected: check for that display is connected or not. + * @get_edid: get edid modes from display driver. + * @get_timing: get timing object from display driver. + * @check_timing: check if timing is valid or not. + * @power_on: display device on or off. + */ +struct exynos_drm_display { + enum exynos_drm_output_type type; + bool (*is_connected)(struct device *dev); + int (*get_edid)(struct device *dev, struct drm_connector *connector, + u8 *edid, int len); + void *(*get_timing)(struct device *dev); + int (*check_timing)(struct device *dev, void *timing); + int (*power_on)(struct device *dev, int mode); +}; + +/* + * Exynos drm manager ops + * + * @mode_set: convert drm_display_mode to hw specific display mode and + * would be called by encoder->mode_set(). + * @commit: set current hw specific display mode to hw. + * @enable_vblank: specific driver callback for enabling vblank interrupt. + * @disable_vblank: specific driver callback for disabling vblank interrupt. + */ +struct exynos_drm_manager_ops { + void (*mode_set)(struct device *subdrv_dev, void *mode); + void (*commit)(struct device *subdrv_dev); + int (*enable_vblank)(struct device *subdrv_dev); + void (*disable_vblank)(struct device *subdrv_dev); +}; + +/* + * Exynos drm common manager structure. + * + * @dev: pointer to device object for subdrv device driver. + * sub drivers such as display controller or hdmi driver, + * have their own device object. + * @ops: pointer to callbacks for exynos drm specific framebuffer. + * these callbacks should be set by specific drivers such fimd + * or hdmi driver and are used to control hardware global registers. + * @overlay_ops: pointer to callbacks for exynos drm specific framebuffer. + * these callbacks should be set by specific drivers such fimd + * or hdmi driver and are used to control hardware overlay reigsters. + * @display: pointer to callbacks for exynos drm specific framebuffer. + * these callbacks should be set by specific drivers such fimd + * or hdmi driver and are used to control display devices such as + * analog tv, digital tv and lcd panel and also get timing data for them. + */ +struct exynos_drm_manager { + struct device *dev; + int pipe; + struct exynos_drm_manager_ops *ops; + struct exynos_drm_overlay_ops *overlay_ops; + struct exynos_drm_display *display; +}; + +/* + * Exynos drm private structure. + */ +struct exynos_drm_private { + struct drm_fb_helper *fb_helper; + + /* for pageflip */ + struct list_head pageflip_event_list; + bool pageflip_event; + + /* + * created crtc object would be contained at this array and + * this array is used to be aware of which crtc did it request vblank. + */ + struct drm_crtc *crtc[MAX_CRTC]; +}; + +/* + * Exynos drm sub driver structure. + * + * @list: sub driver has its own list object to register to exynos drm driver. + * @drm_dev: pointer to drm_device and this pointer would be set + * when sub driver calls exynos_drm_subdrv_register(). + * @probe: this callback would be called by exynos drm driver after + * subdrv is registered to it. + * @remove: this callback is used to release resources created + * by probe callback. + * @manager: subdrv has its own manager to control a hardware appropriately + * and we can access a hardware drawing on this manager. + * @encoder: encoder object owned by this sub driver. + * @connector: connector object owned by this sub driver. + */ +struct exynos_drm_subdrv { + struct list_head list; + struct drm_device *drm_dev; + + int (*probe)(struct drm_device *dev); + void (*remove)(struct drm_device *dev); + + struct exynos_drm_manager manager; + struct drm_encoder *encoder; + struct drm_connector *connector; +}; + +/* + * this function calls a probe callback registered to sub driver list and + * create its own encoder and connector and then set drm_device object + * to global one. + */ +int exynos_drm_device_register(struct drm_device *dev); +/* + * this function calls a remove callback registered to sub driver list and + * destroy its own encoder and connetor. + */ +int exynos_drm_device_unregister(struct drm_device *dev); + +/* + * this function would be called by sub drivers such as display controller + * or hdmi driver to register this sub driver object to exynos drm driver + * and when a sub driver is registered to exynos drm driver a probe callback + * of the sub driver is called and creates its own encoder and connector + * and then fb helper and drm mode group would be re-initialized. + */ +int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv); + +/* + * this function removes subdrv list from exynos drm driver and fb helper + * and drm mode group would be re-initialized. + */ +int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv); + +#endif diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_encoder.c new file mode 100644 index 000000000000..7cf6fa86a67e --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_encoder.c @@ -0,0 +1,271 @@ +/* exynos_drm_encoder.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "drm_crtc_helper.h" + +#include "exynos_drm_drv.h" +#include "exynos_drm_crtc.h" +#include "exynos_drm_encoder.h" + +#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\ + drm_encoder) + +/* + * exynos specific encoder structure. + * + * @drm_encoder: encoder object. + * @manager: specific encoder has its own manager to control a hardware + * appropriately and we can access a hardware drawing on this manager. + */ +struct exynos_drm_encoder { + struct drm_encoder drm_encoder; + struct exynos_drm_manager *manager; +}; + +static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_connector *connector; + struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); + + DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder == encoder) { + struct exynos_drm_display *display = manager->display; + + if (display && display->power_on) + display->power_on(manager->dev, mode); + } + } +} + +static bool +exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* drm framework doesn't check NULL. */ + + return true; +} + +static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct drm_device *dev = encoder->dev; + struct drm_connector *connector; + struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); + struct exynos_drm_manager_ops *manager_ops = manager->ops; + struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev, + encoder->crtc); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + mode = adjusted_mode; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder == encoder) { + if (manager_ops && manager_ops->mode_set) + manager_ops->mode_set(manager->dev, mode); + + if (overlay_ops && overlay_ops->mode_set) + overlay_ops->mode_set(manager->dev, overlay); + } + } +} + +static void exynos_drm_encoder_prepare(struct drm_encoder *encoder) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* drm framework doesn't check NULL. */ +} + +static void exynos_drm_encoder_commit(struct drm_encoder *encoder) +{ + struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder); + struct exynos_drm_manager_ops *manager_ops = manager->ops; + struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (manager_ops && manager_ops->commit) + manager_ops->commit(manager->dev); + + if (overlay_ops && overlay_ops->commit) + overlay_ops->commit(manager->dev); +} + +static struct drm_crtc * +exynos_drm_encoder_get_crtc(struct drm_encoder *encoder) +{ + return encoder->crtc; +} + +static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = { + .dpms = exynos_drm_encoder_dpms, + .mode_fixup = exynos_drm_encoder_mode_fixup, + .mode_set = exynos_drm_encoder_mode_set, + .prepare = exynos_drm_encoder_prepare, + .commit = exynos_drm_encoder_commit, + .get_crtc = exynos_drm_encoder_get_crtc, +}; + +static void exynos_drm_encoder_destroy(struct drm_encoder *encoder) +{ + struct exynos_drm_encoder *exynos_encoder = + to_exynos_encoder(encoder); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_encoder->manager->pipe = -1; + + drm_encoder_cleanup(encoder); + encoder->dev->mode_config.num_encoder--; + kfree(exynos_encoder); +} + +static struct drm_encoder_funcs exynos_encoder_funcs = { + .destroy = exynos_drm_encoder_destroy, +}; + +struct drm_encoder * +exynos_drm_encoder_create(struct drm_device *dev, + struct exynos_drm_manager *manager, + unsigned int possible_crtcs) +{ + struct drm_encoder *encoder; + struct exynos_drm_encoder *exynos_encoder; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (!manager || !possible_crtcs) + return NULL; + + if (!manager->dev) + return NULL; + + exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL); + if (!exynos_encoder) { + DRM_ERROR("failed to allocate encoder\n"); + return NULL; + } + + exynos_encoder->manager = manager; + encoder = &exynos_encoder->drm_encoder; + encoder->possible_crtcs = possible_crtcs; + + DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs); + + drm_encoder_init(dev, encoder, &exynos_encoder_funcs, + DRM_MODE_ENCODER_TMDS); + + drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs); + + DRM_DEBUG_KMS("encoder has been created\n"); + + return encoder; +} + +struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder) +{ + return to_exynos_encoder(encoder)->manager; +} + +void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, + void (*fn)(struct drm_encoder *, void *)) +{ + struct drm_device *dev = crtc->dev; + struct drm_encoder *encoder; + + list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { + if (encoder->crtc != crtc) + continue; + + fn(encoder, data); + } +} + +void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data) +{ + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; + struct exynos_drm_manager_ops *manager_ops = manager->ops; + int crtc = *(int *)data; + + if (manager->pipe == -1) + manager->pipe = crtc; + + if (manager_ops->enable_vblank) + manager_ops->enable_vblank(manager->dev); +} + +void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data) +{ + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; + struct exynos_drm_manager_ops *manager_ops = manager->ops; + int crtc = *(int *)data; + + if (manager->pipe == -1) + manager->pipe = crtc; + + if (manager_ops->disable_vblank) + manager_ops->disable_vblank(manager->dev); +} + +void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data) +{ + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; + struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + + overlay_ops->commit(manager->dev); +} + +void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data) +{ + struct exynos_drm_manager *manager = + to_exynos_encoder(encoder)->manager; + struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops; + struct exynos_drm_overlay *overlay = data; + + overlay_ops->mode_set(manager->dev, overlay); +} + +MODULE_AUTHOR("Inki Dae "); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("Seung-Woo Kim "); +MODULE_DESCRIPTION("Samsung SoC DRM Encoder Driver"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/trunk/drivers/gpu/drm/exynos/exynos_drm_encoder.h new file mode 100644 index 000000000000..5ecd645d06a9 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_encoder.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _EXYNOS_DRM_ENCODER_H_ +#define _EXYNOS_DRM_ENCODER_H_ + +struct exynos_drm_manager; + +struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev, + struct exynos_drm_manager *mgr, + unsigned int possible_crtcs); +struct exynos_drm_manager * +exynos_drm_get_manager(struct drm_encoder *encoder); +void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data, + void (*fn)(struct drm_encoder *, void *)); +void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data); +void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data); +void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data); + +#endif diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_fb.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_fb.c new file mode 100644 index 000000000000..4505d90d657a --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_fb.c @@ -0,0 +1,271 @@ +/* exynos_drm_fb.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +#include "exynos_drm_fb.h" +#include "exynos_drm_buf.h" +#include "exynos_drm_gem.h" + +#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb) + +/* + * exynos specific framebuffer structure. + * + * @fb: drm framebuffer obejct. + * @exynos_gem_obj: exynos specific gem object containing a gem object. + * @entry: pointer to exynos drm buffer entry object. + * - containing only the information to physically continuous memory + * region allocated at default framebuffer creation. + */ +struct exynos_drm_fb { + struct drm_framebuffer fb; + struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_buf_entry *entry; +}; + +static void exynos_drm_fb_destroy(struct drm_framebuffer *fb) +{ + struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + drm_framebuffer_cleanup(fb); + + /* + * default framebuffer has no gem object so + * a buffer of the default framebuffer should be released at here. + */ + if (!exynos_fb->exynos_gem_obj && exynos_fb->entry) + exynos_drm_buf_destroy(fb->dev, exynos_fb->entry); + + kfree(exynos_fb); + exynos_fb = NULL; +} + +static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + return drm_gem_handle_create(file_priv, + &exynos_fb->exynos_gem_obj->base, handle); +} + +static int exynos_drm_fb_dirty(struct drm_framebuffer *fb, + struct drm_file *file_priv, unsigned flags, + unsigned color, struct drm_clip_rect *clips, + unsigned num_clips) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO */ + + return 0; +} + +static struct drm_framebuffer_funcs exynos_drm_fb_funcs = { + .destroy = exynos_drm_fb_destroy, + .create_handle = exynos_drm_fb_create_handle, + .dirty = exynos_drm_fb_dirty, +}; + +static struct drm_framebuffer * +exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev, + struct drm_mode_fb_cmd *mode_cmd) +{ + struct exynos_drm_fb *exynos_fb; + struct drm_framebuffer *fb; + struct exynos_drm_gem_obj *exynos_gem_obj = NULL; + struct drm_gem_object *obj; + unsigned int size; + int ret; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + mode_cmd->pitch = max(mode_cmd->pitch, + mode_cmd->width * (mode_cmd->bpp >> 3)); + + DRM_LOG_KMS("drm fb create(%dx%d)\n", + mode_cmd->width, mode_cmd->height); + + exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL); + if (!exynos_fb) { + DRM_ERROR("failed to allocate exynos drm framebuffer.\n"); + return ERR_PTR(-ENOMEM); + } + + fb = &exynos_fb->fb; + ret = drm_framebuffer_init(dev, fb, &exynos_drm_fb_funcs); + if (ret) { + DRM_ERROR("failed to initialize framebuffer.\n"); + goto err_init; + } + + DRM_LOG_KMS("create: fb id: %d\n", fb->base.id); + + size = mode_cmd->pitch * mode_cmd->height; + + /* + * mode_cmd->handle could be NULL at booting time or + * with user request. if NULL, a new buffer or a gem object + * would be allocated. + */ + if (!mode_cmd->handle) { + if (!file_priv) { + struct exynos_drm_buf_entry *entry; + + /* + * in case that file_priv is NULL, it allocates + * only buffer and this buffer would be used + * for default framebuffer. + */ + entry = exynos_drm_buf_create(dev, size); + if (IS_ERR(entry)) { + ret = PTR_ERR(entry); + goto err_buffer; + } + + exynos_fb->entry = entry; + + DRM_LOG_KMS("default fb: paddr = 0x%lx, size = 0x%x\n", + (unsigned long)entry->paddr, size); + + goto out; + } else { + exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, + size, + &mode_cmd->handle); + if (IS_ERR(exynos_gem_obj)) { + ret = PTR_ERR(exynos_gem_obj); + goto err_buffer; + } + } + } else { + obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + goto err_buffer; + } + + exynos_gem_obj = to_exynos_gem_obj(obj); + + drm_gem_object_unreference_unlocked(obj); + } + + /* + * if got a exynos_gem_obj from either a handle or + * a new creation then exynos_fb->exynos_gem_obj is NULL + * so that default framebuffer has no its own gem object, + * only its own buffer object. + */ + exynos_fb->entry = exynos_gem_obj->entry; + + DRM_LOG_KMS("paddr = 0x%lx, size = 0x%x, gem object = 0x%x\n", + (unsigned long)exynos_fb->entry->paddr, size, + (unsigned int)&exynos_gem_obj->base); + +out: + exynos_fb->exynos_gem_obj = exynos_gem_obj; + + drm_helper_mode_fill_fb_struct(fb, mode_cmd); + + return fb; + +err_buffer: + drm_framebuffer_cleanup(fb); + +err_init: + kfree(exynos_fb); + + return ERR_PTR(ret); +} + +struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, + struct drm_file *file_priv, + struct drm_mode_fb_cmd *mode_cmd) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + return exynos_drm_fb_init(file_priv, dev, mode_cmd); +} + +void exynos_drm_fb_update_buf_off(struct drm_framebuffer *fb, + unsigned int x, unsigned int y, + struct exynos_drm_buffer_info *info) +{ + struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb); + struct exynos_drm_buf_entry *entry; + unsigned long offset; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + offset = x * (fb->bits_per_pixel >> 3); + offset += y * fb->pitch; + + entry = exynos_fb->entry; + + info->base_addr = entry->paddr; + info->vaddr = entry->vaddr + offset; + info->paddr = entry->paddr + offset; + + DRM_DEBUG_KMS("updated vaddr = 0x%lx, paddr = 0x%lx, offset = 0x%x\n", + (unsigned long)info->vaddr, (unsigned long)info->paddr, + (unsigned int)offset); +} + +static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = { + .fb_create = exynos_drm_fb_create, +}; + +void exynos_drm_mode_config_init(struct drm_device *dev) +{ + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + + /* + * set max width and height as default value(4096x4096). + * this value would be used to check framebuffer size limitation + * at drm_mode_addfb(). + */ + dev->mode_config.max_width = 4096; + dev->mode_config.max_height = 4096; + + dev->mode_config.funcs = &exynos_drm_mode_config_funcs; +} + +MODULE_AUTHOR("Inki Dae "); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("Seung-Woo Kim "); +MODULE_DESCRIPTION("Samsung SoC DRM FB Driver"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_fb.h b/trunk/drivers/gpu/drm/exynos/exynos_drm_fb.h new file mode 100644 index 000000000000..eaa478abb6d1 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_fb.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _EXYNOS_DRM_FB_H_ +#define _EXYNOS_DRM_FB_H + +struct exynos_drm_buffer_info { + unsigned long base_addr; + dma_addr_t paddr; + void __iomem *vaddr; +}; + +void exynos_drm_fb_update_buf_off(struct drm_framebuffer *fb, + unsigned int x, unsigned int y, + struct exynos_drm_buffer_info *info); + +struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev, + struct drm_file *filp, + struct drm_mode_fb_cmd *mode_cmd); + +void exynos_drm_mode_config_init(struct drm_device *dev); + +#endif diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_fbdev.c new file mode 100644 index 000000000000..ac43bfc9e1f0 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_fbdev.c @@ -0,0 +1,441 @@ +/* exynos_drm_fbdev.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "drm_crtc.h" +#include "drm_fb_helper.h" +#include "drm_crtc_helper.h" + +#include "exynos_drm_drv.h" +#include "exynos_drm_fb.h" + +#define MAX_CONNECTOR 4 +#define PREFERRED_BPP 32 + +#define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\ + drm_fb_helper) + +struct exynos_drm_fbdev { + struct drm_fb_helper drm_fb_helper; + struct drm_framebuffer *fb; +}; + +static int exynos_drm_fbdev_set_par(struct fb_info *info) +{ + struct fb_var_screeninfo *var = &info->var; + + switch (var->bits_per_pixel) { + case 32: + case 24: + case 18: + case 16: + case 12: + info->fix.visual = FB_VISUAL_TRUECOLOR; + break; + case 1: + info->fix.visual = FB_VISUAL_MONO01; + break; + default: + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + break; + } + + info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8; + + return drm_fb_helper_set_par(info); +} + + +static struct fb_ops exynos_drm_fb_ops = { + .owner = THIS_MODULE, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_check_var = drm_fb_helper_check_var, + .fb_set_par = exynos_drm_fbdev_set_par, + .fb_blank = drm_fb_helper_blank, + .fb_pan_display = drm_fb_helper_pan_display, + .fb_setcmap = drm_fb_helper_setcmap, +}; + +static void exynos_drm_fbdev_update(struct drm_fb_helper *helper, + struct drm_framebuffer *fb, + unsigned int fb_width, + unsigned int fb_height) +{ + struct fb_info *fbi = helper->fbdev; + struct drm_device *dev = helper->dev; + struct exynos_drm_fbdev *exynos_fb = to_exynos_fbdev(helper); + struct exynos_drm_buffer_info buffer_info; + unsigned int size = fb_width * fb_height * (fb->bits_per_pixel >> 3); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_fb->fb = fb; + + drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth); + drm_fb_helper_fill_var(fbi, helper, fb_width, fb_height); + + exynos_drm_fb_update_buf_off(fb, fbi->var.xoffset, fbi->var.yoffset, + &buffer_info); + + dev->mode_config.fb_base = buffer_info.base_addr; + + fbi->screen_base = buffer_info.vaddr; + fbi->screen_size = size; + fbi->fix.smem_start = buffer_info.paddr; + fbi->fix.smem_len = size; +} + +static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); + struct drm_device *dev = helper->dev; + struct fb_info *fbi; + struct drm_mode_fb_cmd mode_cmd = { 0 }; + struct platform_device *pdev = dev->platformdev; + int ret; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n", + sizes->surface_width, sizes->surface_height, + sizes->surface_bpp); + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.bpp = sizes->surface_bpp; + mode_cmd.depth = sizes->surface_depth; + + mutex_lock(&dev->struct_mutex); + + fbi = framebuffer_alloc(0, &pdev->dev); + if (!fbi) { + DRM_ERROR("failed to allocate fb info.\n"); + ret = -ENOMEM; + goto out; + } + + exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd); + if (IS_ERR_OR_NULL(exynos_fbdev->fb)) { + DRM_ERROR("failed to create drm framebuffer.\n"); + ret = PTR_ERR(exynos_fbdev->fb); + goto out; + } + + helper->fb = exynos_fbdev->fb; + helper->fbdev = fbi; + + fbi->par = helper; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->fbops = &exynos_drm_fb_ops; + + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (ret) { + DRM_ERROR("failed to allocate cmap.\n"); + goto out; + } + + exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width, + sizes->fb_height); + +/* + * if failed, all resources allocated above would be released by + * drm_mode_config_cleanup() when drm_load() had been called prior + * to any specific driver such as fimd or hdmi driver. + */ +out: + mutex_unlock(&dev->struct_mutex); + return ret; +} + +static bool +exynos_drm_fbdev_is_samefb(struct drm_framebuffer *fb, + struct drm_fb_helper_surface_size *sizes) +{ + if (fb->width != sizes->surface_width) + return false; + if (fb->height != sizes->surface_height) + return false; + if (fb->bits_per_pixel != sizes->surface_bpp) + return false; + if (fb->depth != sizes->surface_depth) + return false; + + return true; +} + +static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_device *dev = helper->dev; + struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper); + struct drm_framebuffer *fb = exynos_fbdev->fb; + struct drm_mode_fb_cmd mode_cmd = { 0 }; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (helper->fb != fb) { + DRM_ERROR("drm framebuffer is different\n"); + return -EINVAL; + } + + if (exynos_drm_fbdev_is_samefb(fb, sizes)) + return 0; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + mode_cmd.bpp = sizes->surface_bpp; + mode_cmd.depth = sizes->surface_depth; + + if (fb->funcs->destroy) + fb->funcs->destroy(fb); + + exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd); + if (IS_ERR(exynos_fbdev->fb)) { + DRM_ERROR("failed to allocate fb.\n"); + return PTR_ERR(exynos_fbdev->fb); + } + + helper->fb = exynos_fbdev->fb; + exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width, + sizes->fb_height); + + return 0; +} + +static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + int ret = 0; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (!helper->fb) { + ret = exynos_drm_fbdev_create(helper, sizes); + if (ret < 0) { + DRM_ERROR("failed to create fbdev.\n"); + return ret; + } + + /* + * fb_helper expects a value more than 1 if succeed + * because register_framebuffer() should be called. + */ + ret = 1; + } else { + ret = exynos_drm_fbdev_recreate(helper, sizes); + if (ret < 0) { + DRM_ERROR("failed to reconfigure fbdev\n"); + return ret; + } + } + + return ret; +} + +static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { + .fb_probe = exynos_drm_fbdev_probe, +}; + +int exynos_drm_fbdev_init(struct drm_device *dev) +{ + struct exynos_drm_fbdev *fbdev; + struct exynos_drm_private *private = dev->dev_private; + struct drm_fb_helper *helper; + unsigned int num_crtc; + int ret; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector) + return 0; + + fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); + if (!fbdev) { + DRM_ERROR("failed to allocate drm fbdev.\n"); + return -ENOMEM; + } + + private->fb_helper = helper = &fbdev->drm_fb_helper; + helper->funcs = &exynos_drm_fb_helper_funcs; + + num_crtc = dev->mode_config.num_crtc; + + ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR); + if (ret < 0) { + DRM_ERROR("failed to initialize drm fb helper.\n"); + goto err_init; + } + + ret = drm_fb_helper_single_add_all_connectors(helper); + if (ret < 0) { + DRM_ERROR("failed to register drm_fb_helper_connector.\n"); + goto err_setup; + + } + + ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP); + if (ret < 0) { + DRM_ERROR("failed to set up hw configuration.\n"); + goto err_setup; + } + + return 0; + +err_setup: + drm_fb_helper_fini(helper); + +err_init: + private->fb_helper = NULL; + kfree(fbdev); + + return ret; +} + +static void exynos_drm_fbdev_destroy(struct drm_device *dev, + struct drm_fb_helper *fb_helper) +{ + struct drm_framebuffer *fb; + + /* release drm framebuffer and real buffer */ + if (fb_helper->fb && fb_helper->fb->funcs) { + fb = fb_helper->fb; + if (fb && fb->funcs->destroy) + fb->funcs->destroy(fb); + } + + /* release linux framebuffer */ + if (fb_helper->fbdev) { + struct fb_info *info; + int ret; + + info = fb_helper->fbdev; + ret = unregister_framebuffer(info); + if (ret < 0) + DRM_DEBUG_KMS("failed unregister_framebuffer()\n"); + + if (info->cmap.len) + fb_dealloc_cmap(&info->cmap); + + framebuffer_release(info); + } + + drm_fb_helper_fini(fb_helper); +} + +void exynos_drm_fbdev_fini(struct drm_device *dev) +{ + struct exynos_drm_private *private = dev->dev_private; + struct exynos_drm_fbdev *fbdev; + + if (!private || !private->fb_helper) + return; + + fbdev = to_exynos_fbdev(private->fb_helper); + + exynos_drm_fbdev_destroy(dev, private->fb_helper); + kfree(fbdev); + private->fb_helper = NULL; +} + +void exynos_drm_fbdev_restore_mode(struct drm_device *dev) +{ + struct exynos_drm_private *private = dev->dev_private; + + if (!private || !private->fb_helper) + return; + + drm_fb_helper_restore_fbdev_mode(private->fb_helper); +} + +int exynos_drm_fbdev_reinit(struct drm_device *dev) +{ + struct exynos_drm_private *private = dev->dev_private; + struct drm_fb_helper *fb_helper; + int ret; + + if (!private) + return -EINVAL; + + if (!dev->mode_config.num_connector) { + exynos_drm_fbdev_fini(dev); + return 0; + } + + fb_helper = private->fb_helper; + + if (fb_helper) { + drm_fb_helper_fini(fb_helper); + + ret = drm_fb_helper_init(dev, fb_helper, + dev->mode_config.num_crtc, MAX_CONNECTOR); + if (ret < 0) { + DRM_ERROR("failed to initialize drm fb helper\n"); + return ret; + } + + ret = drm_fb_helper_single_add_all_connectors(fb_helper); + if (ret < 0) { + DRM_ERROR("failed to add fb helper to connectors\n"); + goto err; + } + + ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP); + if (ret < 0) { + DRM_ERROR("failed to set up hw configuration.\n"); + goto err; + } + } else { + /* + * if drm_load() failed whem drm load() was called prior + * to specific drivers, fb_helper must be NULL and so + * this fuction should be called again to re-initialize and + * re-configure the fb helper. it means that this function + * has been called by the specific drivers. + */ + return exynos_drm_fbdev_init(dev); + } + +err: + /* + * if drm_load() failed when drm load() was called prior + * to specific drivers, the fb_helper must be NULL and so check it. + */ + if (fb_helper) + drm_fb_helper_fini(fb_helper); + + return ret; +} + +MODULE_AUTHOR("Inki Dae "); +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("Seung-Woo Kim "); +MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_fbdev.h b/trunk/drivers/gpu/drm/exynos/exynos_drm_fbdev.h new file mode 100644 index 000000000000..ccfce8a1a451 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_fbdev.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _EXYNOS_DRM_FBDEV_H_ +#define _EXYNOS_DRM_FBDEV_H_ + +int exynos_drm_fbdev_init(struct drm_device *dev); +int exynos_drm_fbdev_reinit(struct drm_device *dev); +void exynos_drm_fbdev_fini(struct drm_device *dev); +void exynos_drm_fbdev_restore_mode(struct drm_device *dev); + +#endif diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_fimd.c new file mode 100644 index 000000000000..620ad2d51368 --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -0,0 +1,796 @@ +/* exynos_drm_fimd.c + * + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Authors: + * Joonyoung Shim + * Inki Dae + * + * 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. + * + */ +#include "drmP.h" + +#include +#include +#include +#include + +#include +#include + +#include "exynos_drm_drv.h" +#include "exynos_drm_fbdev.h" +#include "exynos_drm_crtc.h" + +/* + * FIMD is stand for Fully Interactive Mobile Display and + * as a display controller, it transfers contents drawn on memory + * to a LCD Panel through Display Interfaces such as RGB or + * CPU Interface. + */ + +/* position control register for hardware window 0, 2 ~ 4.*/ +#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16) +#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16) +/* size control register for hardware window 0. */ +#define VIDOSD_C_SIZE_W0 (VIDOSD_BASE + 0x08) +/* alpha control register for hardware window 1 ~ 4. */ +#define VIDOSD_C(win) (VIDOSD_BASE + 0x18 + (win) * 16) +/* size control register for hardware window 1 ~ 4. */ +#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16) + +#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8) +#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8) +#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4) + +/* color key control register for hardware window 1 ~ 4. */ +#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + (x * 8)) +/* color key value register for hardware window 1 ~ 4. */ +#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + (x * 8)) + +/* FIMD has totally five hardware windows. */ +#define WINDOWS_NR 5 + +#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev)) + +struct fimd_win_data { + unsigned int offset_x; + unsigned int offset_y; + unsigned int width; + unsigned int height; + unsigned int bpp; + dma_addr_t paddr; + void __iomem *vaddr; + unsigned int buf_offsize; + unsigned int line_size; /* bytes */ +}; + +struct fimd_context { + struct exynos_drm_subdrv subdrv; + int irq; + struct drm_crtc *crtc; + struct clk *bus_clk; + struct clk *lcd_clk; + struct resource *regs_res; + void __iomem *regs; + struct fimd_win_data win_data[WINDOWS_NR]; + unsigned int clkdiv; + unsigned int default_win; + unsigned long irq_flags; + u32 vidcon0; + u32 vidcon1; + + struct fb_videomode *timing; +}; + +static bool fimd_display_is_connected(struct device *dev) +{ + struct fimd_context *ctx = get_fimd_context(dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO. */ + + return true; +} + +static void *fimd_get_timing(struct device *dev) +{ + struct fimd_context *ctx = get_fimd_context(dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + return ctx->timing; +} + +static int fimd_check_timing(struct device *dev, void *timing) +{ + struct fimd_context *ctx = get_fimd_context(dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO. */ + + return 0; +} + +static int fimd_display_power_on(struct device *dev, int mode) +{ + struct fimd_context *ctx = get_fimd_context(dev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO. */ + + return 0; +} + +static struct exynos_drm_display fimd_display = { + .type = EXYNOS_DISPLAY_TYPE_LCD, + .is_connected = fimd_display_is_connected, + .get_timing = fimd_get_timing, + .check_timing = fimd_check_timing, + .power_on = fimd_display_power_on, +}; + +static void fimd_commit(struct device *dev) +{ + struct fimd_context *ctx = get_fimd_context(dev); + struct fb_videomode *timing = ctx->timing; + u32 val; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* setup polarity values from machine code. */ + writel(ctx->vidcon1, ctx->regs + VIDCON1); + + /* setup vertical timing values. */ + val = VIDTCON0_VBPD(timing->upper_margin - 1) | + VIDTCON0_VFPD(timing->lower_margin - 1) | + VIDTCON0_VSPW(timing->vsync_len - 1); + writel(val, ctx->regs + VIDTCON0); + + /* setup horizontal timing values. */ + val = VIDTCON1_HBPD(timing->left_margin - 1) | + VIDTCON1_HFPD(timing->right_margin - 1) | + VIDTCON1_HSPW(timing->hsync_len - 1); + writel(val, ctx->regs + VIDTCON1); + + /* setup horizontal and vertical display size. */ + val = VIDTCON2_LINEVAL(timing->yres - 1) | + VIDTCON2_HOZVAL(timing->xres - 1); + writel(val, ctx->regs + VIDTCON2); + + /* setup clock source, clock divider, enable dma. */ + val = ctx->vidcon0; + val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); + + if (ctx->clkdiv > 1) + val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR; + else + val &= ~VIDCON0_CLKDIR; /* 1:1 clock */ + + /* + * fields of register with prefix '_F' would be updated + * at vsync(same as dma start) + */ + val |= VIDCON0_ENVID | VIDCON0_ENVID_F; + writel(val, ctx->regs + VIDCON0); +} + +static int fimd_enable_vblank(struct device *dev) +{ + struct fimd_context *ctx = get_fimd_context(dev); + u32 val; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (!test_and_set_bit(0, &ctx->irq_flags)) { + val = readl(ctx->regs + VIDINTCON0); + + val |= VIDINTCON0_INT_ENABLE; + val |= VIDINTCON0_INT_FRAME; + + val &= ~VIDINTCON0_FRAMESEL0_MASK; + val |= VIDINTCON0_FRAMESEL0_VSYNC; + val &= ~VIDINTCON0_FRAMESEL1_MASK; + val |= VIDINTCON0_FRAMESEL1_NONE; + + writel(val, ctx->regs + VIDINTCON0); + } + + return 0; +} + +static void fimd_disable_vblank(struct device *dev) +{ + struct fimd_context *ctx = get_fimd_context(dev); + u32 val; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (test_and_clear_bit(0, &ctx->irq_flags)) { + val = readl(ctx->regs + VIDINTCON0); + + val &= ~VIDINTCON0_INT_FRAME; + val &= ~VIDINTCON0_INT_ENABLE; + + writel(val, ctx->regs + VIDINTCON0); + } +} + +static struct exynos_drm_manager_ops fimd_manager_ops = { + .commit = fimd_commit, + .enable_vblank = fimd_enable_vblank, + .disable_vblank = fimd_disable_vblank, +}; + +static void fimd_win_mode_set(struct device *dev, + struct exynos_drm_overlay *overlay) +{ + struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_win_data *win_data; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (!overlay) { + dev_err(dev, "overlay is NULL\n"); + return; + } + + win_data = &ctx->win_data[ctx->default_win]; + + win_data->offset_x = overlay->offset_x; + win_data->offset_y = overlay->offset_y; + win_data->width = overlay->width; + win_data->height = overlay->height; + win_data->paddr = overlay->paddr; + win_data->vaddr = overlay->vaddr; + win_data->bpp = overlay->bpp; + win_data->buf_offsize = overlay->buf_offsize * (overlay->bpp >> 3); + win_data->line_size = overlay->line_size * (overlay->bpp >> 3); +} + +static void fimd_win_set_pixfmt(struct device *dev, unsigned int win) +{ + struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_win_data *win_data = &ctx->win_data[win]; + unsigned long val; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + val = WINCONx_ENWIN; + + switch (win_data->bpp) { + case 1: + val |= WINCON0_BPPMODE_1BPP; + val |= WINCONx_BITSWP; + val |= WINCONx_BURSTLEN_4WORD; + break; + case 2: + val |= WINCON0_BPPMODE_2BPP; + val |= WINCONx_BITSWP; + val |= WINCONx_BURSTLEN_8WORD; + break; + case 4: + val |= WINCON0_BPPMODE_4BPP; + val |= WINCONx_BITSWP; + val |= WINCONx_BURSTLEN_8WORD; + break; + case 8: + val |= WINCON0_BPPMODE_8BPP_PALETTE; + val |= WINCONx_BURSTLEN_8WORD; + val |= WINCONx_BYTSWP; + break; + case 16: + val |= WINCON0_BPPMODE_16BPP_565; + val |= WINCONx_HAWSWP; + val |= WINCONx_BURSTLEN_16WORD; + break; + case 24: + val |= WINCON0_BPPMODE_24BPP_888; + val |= WINCONx_WSWP; + val |= WINCONx_BURSTLEN_16WORD; + break; + case 32: + val |= WINCON1_BPPMODE_28BPP_A4888 + | WINCON1_BLD_PIX | WINCON1_ALPHA_SEL; + val |= WINCONx_WSWP; + val |= WINCONx_BURSTLEN_16WORD; + break; + default: + DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n"); + + val |= WINCON0_BPPMODE_24BPP_888; + val |= WINCONx_WSWP; + val |= WINCONx_BURSTLEN_16WORD; + break; + } + + DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp); + + writel(val, ctx->regs + WINCON(win)); +} + +static void fimd_win_set_colkey(struct device *dev, unsigned int win) +{ + struct fimd_context *ctx = get_fimd_context(dev); + unsigned int keycon0 = 0, keycon1 = 0; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F | + WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0); + + keycon1 = WxKEYCON1_COLVAL(0xffffffff); + + writel(keycon0, ctx->regs + WKEYCON0_BASE(win)); + writel(keycon1, ctx->regs + WKEYCON1_BASE(win)); +} + +static void fimd_win_commit(struct device *dev) +{ + struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_win_data *win_data; + int win = ctx->default_win; + unsigned long val, alpha, size; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (win < 0 || win > WINDOWS_NR) + return; + + win_data = &ctx->win_data[win]; + + /* + * SHADOWCON register is used for enabling timing. + * + * for example, once only width value of a register is set, + * if the dma is started then fimd hardware could malfunction so + * with protect window setting, the register fields with prefix '_F' + * wouldn't be updated at vsync also but updated once unprotect window + * is set. + */ + + /* protect windows */ + val = readl(ctx->regs + SHADOWCON); + val |= SHADOWCON_WINx_PROTECT(win); + writel(val, ctx->regs + SHADOWCON); + + /* buffer start address */ + val = win_data->paddr; + writel(val, ctx->regs + VIDWx_BUF_START(win, 0)); + + /* buffer end address */ + size = win_data->width * win_data->height * (win_data->bpp >> 3); + val = win_data->paddr + size; + writel(val, ctx->regs + VIDWx_BUF_END(win, 0)); + + DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n", + (unsigned long)win_data->paddr, val, size); + + /* buffer size */ + val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) | + VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size); + writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0)); + + /* OSD position */ + val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) | + VIDOSDxA_TOPLEFT_Y(win_data->offset_y); + writel(val, ctx->regs + VIDOSD_A(win)); + + val = VIDOSDxB_BOTRIGHT_X(win_data->offset_x + win_data->width - 1) | + VIDOSDxB_BOTRIGHT_Y(win_data->offset_y + win_data->height - 1); + writel(val, ctx->regs + VIDOSD_B(win)); + + DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %x\n", + win_data->offset_x, win_data->offset_y, + win_data->offset_x + win_data->width - 1, + win_data->offset_y + win_data->height - 1); + + /* hardware window 0 doesn't support alpha channel. */ + if (win != 0) { + /* OSD alpha */ + alpha = VIDISD14C_ALPHA1_R(0xf) | + VIDISD14C_ALPHA1_G(0xf) | + VIDISD14C_ALPHA1_B(0xf); + + writel(alpha, ctx->regs + VIDOSD_C(win)); + } + + /* OSD size */ + if (win != 3 && win != 4) { + u32 offset = VIDOSD_D(win); + if (win == 0) + offset = VIDOSD_C_SIZE_W0; + val = win_data->width * win_data->height; + writel(val, ctx->regs + offset); + + DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val); + } + + fimd_win_set_pixfmt(dev, win); + + /* hardware window 0 doesn't support color key. */ + if (win != 0) + fimd_win_set_colkey(dev, win); + + /* Enable DMA channel and unprotect windows */ + val = readl(ctx->regs + SHADOWCON); + val |= SHADOWCON_CHx_ENABLE(win); + val &= ~SHADOWCON_WINx_PROTECT(win); + writel(val, ctx->regs + SHADOWCON); +} + +static void fimd_win_disable(struct device *dev) +{ + struct fimd_context *ctx = get_fimd_context(dev); + struct fimd_win_data *win_data; + int win = ctx->default_win; + u32 val; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (win < 0 || win > WINDOWS_NR) + return; + + win_data = &ctx->win_data[win]; + + /* protect windows */ + val = readl(ctx->regs + SHADOWCON); + val |= SHADOWCON_WINx_PROTECT(win); + writel(val, ctx->regs + SHADOWCON); + + /* wincon */ + val = readl(ctx->regs + WINCON(win)); + val &= ~WINCONx_ENWIN; + writel(val, ctx->regs + WINCON(win)); + + /* unprotect windows */ + val = readl(ctx->regs + SHADOWCON); + val &= ~SHADOWCON_CHx_ENABLE(win); + val &= ~SHADOWCON_WINx_PROTECT(win); + writel(val, ctx->regs + SHADOWCON); +} + +static struct exynos_drm_overlay_ops fimd_overlay_ops = { + .mode_set = fimd_win_mode_set, + .commit = fimd_win_commit, + .disable = fimd_win_disable, +}; + +/* for pageflip event */ +static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc) +{ + struct exynos_drm_private *dev_priv = drm_dev->dev_private; + struct drm_pending_vblank_event *e, *t; + struct timeval now; + unsigned long flags; + + if (!dev_priv->pageflip_event) + return; + + spin_lock_irqsave(&drm_dev->event_lock, flags); + + exynos_drm_crtc_apply(dev_priv->crtc[crtc]); + + list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list, + base.link) { + do_gettimeofday(&now); + e->event.sequence = 0; + e->event.tv_sec = now.tv_sec; + e->event.tv_usec = now.tv_usec; + + list_move_tail(&e->base.link, &e->base.file_priv->event_list); + wake_up_interruptible(&e->base.file_priv->event_wait); + } + + drm_vblank_put(drm_dev, crtc); + dev_priv->pageflip_event = false; + + spin_unlock_irqrestore(&drm_dev->event_lock, flags); +} + +static irqreturn_t fimd_irq_handler(int irq, void *dev_id) +{ + struct fimd_context *ctx = (struct fimd_context *)dev_id; + struct exynos_drm_subdrv *subdrv = &ctx->subdrv; + struct drm_device *drm_dev = subdrv->drm_dev; + struct device *dev = subdrv->manager.dev; + struct exynos_drm_manager *manager = &subdrv->manager; + u32 val; + + val = readl(ctx->regs + VIDINTCON1); + + if (val & VIDINTCON1_INT_FRAME) + /* VSYNC interrupt */ + writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1); + + drm_handle_vblank(drm_dev, manager->pipe); + fimd_finish_pageflip(drm_dev, manager->pipe); + + return IRQ_HANDLED; +} + +static int fimd_subdrv_probe(struct drm_device *drm_dev) +{ + struct drm_driver *drm_driver = drm_dev->driver; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* + * enable drm irq mode. + * - with irq_enabled = 1, we can use the vblank feature. + * + * P.S. note that we wouldn't use drm irq handler but + * just specific driver own one instead because + * drm framework supports only one irq handler. + */ + drm_dev->irq_enabled = 1; + + /* + * with vblank_disable_allowed = 1, vblank interrupt will be disabled + * by drm timer once a current process gives up ownership of + * vblank event.(drm_vblank_put function was called) + */ + drm_dev->vblank_disable_allowed = 1; + + return 0; +} + +static void fimd_subdrv_remove(struct drm_device *drm_dev) +{ + struct drm_driver *drm_driver = drm_dev->driver; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* TODO. */ +} + +static int fimd_calc_clkdiv(struct fimd_context *ctx, + struct fb_videomode *timing) +{ + unsigned long clk = clk_get_rate(ctx->lcd_clk); + u32 retrace; + u32 clkdiv; + u32 best_framerate = 0; + u32 framerate; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + retrace = timing->left_margin + timing->hsync_len + + timing->right_margin + timing->xres; + retrace *= timing->upper_margin + timing->vsync_len + + timing->lower_margin + timing->yres; + + /* default framerate is 60Hz */ + if (!timing->refresh) + timing->refresh = 60; + + clk /= retrace; + + for (clkdiv = 1; clkdiv < 0x100; clkdiv++) { + int tmp; + + /* get best framerate */ + framerate = clk / clkdiv; + tmp = timing->refresh - framerate; + if (tmp < 0) { + best_framerate = framerate; + continue; + } else { + if (!best_framerate) + best_framerate = framerate; + else if (tmp < (best_framerate - framerate)) + best_framerate = framerate; + break; + } + } + + return clkdiv; +} + +static void fimd_clear_win(struct fimd_context *ctx, int win) +{ + u32 val; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + writel(0, ctx->regs + WINCON(win)); + writel(0, ctx->regs + VIDOSD_A(win)); + writel(0, ctx->regs + VIDOSD_B(win)); + writel(0, ctx->regs + VIDOSD_C(win)); + + if (win == 1 || win == 2) + writel(0, ctx->regs + VIDOSD_D(win)); + + val = readl(ctx->regs + SHADOWCON); + val &= ~SHADOWCON_WINx_PROTECT(win); + writel(val, ctx->regs + SHADOWCON); +} + +static int __devinit fimd_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fimd_context *ctx; + struct exynos_drm_subdrv *subdrv; + struct exynos_drm_fimd_pdata *pdata; + struct fb_videomode *timing; + struct resource *res; + int win; + int ret = -EINVAL; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + pdata = pdev->dev.platform_data; + if (!pdata) { + dev_err(dev, "no platform data specified\n"); + return -EINVAL; + } + + timing = &pdata->timing; + if (!timing) { + dev_err(dev, "timing is null.\n"); + return -EINVAL; + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->bus_clk = clk_get(dev, "fimd"); + if (IS_ERR(ctx->bus_clk)) { + dev_err(dev, "failed to get bus clock\n"); + ret = PTR_ERR(ctx->bus_clk); + goto err_clk_get; + } + + clk_enable(ctx->bus_clk); + + ctx->lcd_clk = clk_get(dev, "sclk_fimd"); + if (IS_ERR(ctx->lcd_clk)) { + dev_err(dev, "failed to get lcd clock\n"); + ret = PTR_ERR(ctx->lcd_clk); + goto err_bus_clk; + } + + clk_enable(ctx->lcd_clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "failed to find registers\n"); + ret = -ENOENT; + goto err_clk; + } + + ctx->regs_res = request_mem_region(res->start, resource_size(res), + dev_name(dev)); + if (!ctx->regs_res) { + dev_err(dev, "failed to claim register region\n"); + ret = -ENOENT; + goto err_clk; + } + + ctx->regs = ioremap(res->start, resource_size(res)); + if (!ctx->regs) { + dev_err(dev, "failed to map registers\n"); + ret = -ENXIO; + goto err_req_region_io; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(dev, "irq request failed.\n"); + goto err_req_region_irq; + } + + ctx->irq = res->start; + + for (win = 0; win < WINDOWS_NR; win++) + fimd_clear_win(ctx, win); + + ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx); + if (ret < 0) { + dev_err(dev, "irq request failed.\n"); + goto err_req_irq; + } + + ctx->clkdiv = fimd_calc_clkdiv(ctx, timing); + ctx->vidcon0 = pdata->vidcon0; + ctx->vidcon1 = pdata->vidcon1; + ctx->default_win = pdata->default_win; + ctx->timing = timing; + + timing->pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv; + + DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n", + timing->pixclock, ctx->clkdiv); + + subdrv = &ctx->subdrv; + + subdrv->probe = fimd_subdrv_probe; + subdrv->remove = fimd_subdrv_remove; + subdrv->manager.pipe = -1; + subdrv->manager.ops = &fimd_manager_ops; + subdrv->manager.overlay_ops = &fimd_overlay_ops; + subdrv->manager.display = &fimd_display; + subdrv->manager.dev = dev; + + platform_set_drvdata(pdev, ctx); + exynos_drm_subdrv_register(subdrv); + + return 0; + +err_req_irq: +err_req_region_irq: + iounmap(ctx->regs); + +err_req_region_io: + release_resource(ctx->regs_res); + kfree(ctx->regs_res); + +err_clk: + clk_disable(ctx->lcd_clk); + clk_put(ctx->lcd_clk); + +err_bus_clk: + clk_disable(ctx->bus_clk); + clk_put(ctx->bus_clk); + +err_clk_get: + kfree(ctx); + return ret; +} + +static int __devexit fimd_remove(struct platform_device *pdev) +{ + struct fimd_context *ctx = platform_get_drvdata(pdev); + + DRM_DEBUG_KMS("%s\n", __FILE__); + + exynos_drm_subdrv_unregister(&ctx->subdrv); + + clk_disable(ctx->lcd_clk); + clk_disable(ctx->bus_clk); + clk_put(ctx->lcd_clk); + clk_put(ctx->bus_clk); + + iounmap(ctx->regs); + release_resource(ctx->regs_res); + kfree(ctx->regs_res); + free_irq(ctx->irq, ctx); + + kfree(ctx); + + return 0; +} + +static struct platform_driver fimd_driver = { + .probe = fimd_probe, + .remove = __devexit_p(fimd_remove), + .driver = { + .name = "exynos4-fb", + .owner = THIS_MODULE, + }, +}; + +static int __init fimd_init(void) +{ + return platform_driver_register(&fimd_driver); +} + +static void __exit fimd_exit(void) +{ + platform_driver_unregister(&fimd_driver); +} + +module_init(fimd_init); +module_exit(fimd_exit); + +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_AUTHOR("Inki Dae "); +MODULE_DESCRIPTION("Samsung DRM FIMD Driver"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_gem.c b/trunk/drivers/gpu/drm/exynos/exynos_drm_gem.c new file mode 100644 index 000000000000..a8e7a88906ed --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -0,0 +1,415 @@ +/* exynos_drm_gem.c + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Author: Inki Dae + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#include "drmP.h" +#include "drm.h" + +#include + +#include "exynos_drm_drv.h" +#include "exynos_drm_gem.h" +#include "exynos_drm_buf.h" + +static unsigned int convert_to_vm_err_msg(int msg) +{ + unsigned int out_msg; + + switch (msg) { + case 0: + case -ERESTARTSYS: + case -EINTR: + out_msg = VM_FAULT_NOPAGE; + break; + + case -ENOMEM: + out_msg = VM_FAULT_OOM; + break; + + default: + out_msg = VM_FAULT_SIGBUS; + break; + } + + return out_msg; +} + +static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT; +} + +struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv, + struct drm_device *dev, unsigned int size, + unsigned int *handle) +{ + struct exynos_drm_gem_obj *exynos_gem_obj; + struct exynos_drm_buf_entry *entry; + struct drm_gem_object *obj; + int ret; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + size = roundup(size, PAGE_SIZE); + + exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL); + if (!exynos_gem_obj) { + DRM_ERROR("failed to allocate exynos gem object.\n"); + return ERR_PTR(-ENOMEM); + } + + /* allocate the new buffer object and memory region. */ + entry = exynos_drm_buf_create(dev, size); + if (!entry) { + kfree(exynos_gem_obj); + return ERR_PTR(-ENOMEM); + } + + exynos_gem_obj->entry = entry; + + obj = &exynos_gem_obj->base; + + ret = drm_gem_object_init(dev, obj, size); + if (ret < 0) { + DRM_ERROR("failed to initailize gem object.\n"); + goto err_obj_init; + } + + DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); + + ret = drm_gem_create_mmap_offset(obj); + if (ret < 0) { + DRM_ERROR("failed to allocate mmap offset.\n"); + goto err_create_mmap_offset; + } + + /* + * allocate a id of idr table where the obj is registered + * and handle has the id what user can see. + */ + ret = drm_gem_handle_create(file_priv, obj, handle); + if (ret) + goto err_handle_create; + + DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle); + + /* drop reference from allocate - handle holds it now. */ + drm_gem_object_unreference_unlocked(obj); + + return exynos_gem_obj; + +err_handle_create: + drm_gem_free_mmap_offset(obj); + +err_create_mmap_offset: + drm_gem_object_release(obj); + +err_obj_init: + exynos_drm_buf_destroy(dev, exynos_gem_obj->entry); + + kfree(exynos_gem_obj); + + return ERR_PTR(ret); +} + +int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_exynos_gem_create *args = data; + struct exynos_drm_gem_obj *exynos_gem_obj; + + DRM_DEBUG_KMS("%s : size = 0x%x\n", __FILE__, args->size); + + exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size, + &args->handle); + if (IS_ERR(exynos_gem_obj)) + return PTR_ERR(exynos_gem_obj); + + return 0; +} + +int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_exynos_gem_map_off *args = data; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n", + args->handle, (unsigned long)args->offset); + + if (!(dev->driver->driver_features & DRIVER_GEM)) { + DRM_ERROR("does not support GEM.\n"); + return -ENODEV; + } + + return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle, + &args->offset); +} + +static int exynos_drm_gem_mmap_buffer(struct file *filp, + struct vm_area_struct *vma) +{ + struct drm_gem_object *obj = filp->private_data; + struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); + struct exynos_drm_buf_entry *entry; + unsigned long pfn, vm_size; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + vma->vm_flags |= (VM_IO | VM_RESERVED); + + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + vma->vm_file = filp; + + vm_size = vma->vm_end - vma->vm_start; + /* + * a entry contains information to physically continuous memory + * allocated by user request or at framebuffer creation. + */ + entry = exynos_gem_obj->entry; + + /* check if user-requested size is valid. */ + if (vm_size > entry->size) + return -EINVAL; + + /* + * get page frame number to physical memory to be mapped + * to user space. + */ + pfn = exynos_gem_obj->entry->paddr >> PAGE_SHIFT; + + DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn); + + if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size, + vma->vm_page_prot)) { + DRM_ERROR("failed to remap pfn range.\n"); + return -EAGAIN; + } + + return 0; +} + +static const struct file_operations exynos_drm_gem_fops = { + .mmap = exynos_drm_gem_mmap_buffer, +}; + +int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_exynos_gem_mmap *args = data; + struct drm_gem_object *obj; + unsigned int addr; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + if (!(dev->driver->driver_features & DRIVER_GEM)) { + DRM_ERROR("does not support GEM.\n"); + return -ENODEV; + } + + obj = drm_gem_object_lookup(dev, file_priv, args->handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + return -EINVAL; + } + + obj->filp->f_op = &exynos_drm_gem_fops; + obj->filp->private_data = obj; + + down_write(¤t->mm->mmap_sem); + addr = do_mmap(obj->filp, 0, args->size, + PROT_READ | PROT_WRITE, MAP_SHARED, 0); + up_write(¤t->mm->mmap_sem); + + drm_gem_object_unreference_unlocked(obj); + + if (IS_ERR((void *)addr)) + return PTR_ERR((void *)addr); + + args->mapped = addr; + + DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped); + + return 0; +} + +int exynos_drm_gem_init_object(struct drm_gem_object *obj) +{ + DRM_DEBUG_KMS("%s\n", __FILE__); + + return 0; +} + +void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj) +{ + struct exynos_drm_gem_obj *exynos_gem_obj; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + DRM_DEBUG_KMS("handle count = %d\n", + atomic_read(&gem_obj->handle_count)); + + if (gem_obj->map_list.map) + drm_gem_free_mmap_offset(gem_obj); + + /* release file pointer to gem object. */ + drm_gem_object_release(gem_obj); + + exynos_gem_obj = to_exynos_gem_obj(gem_obj); + + exynos_drm_buf_destroy(gem_obj->dev, exynos_gem_obj->entry); + + kfree(exynos_gem_obj); +} + +int exynos_drm_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, struct drm_mode_create_dumb *args) +{ + struct exynos_drm_gem_obj *exynos_gem_obj; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* + * alocate memory to be used for framebuffer. + * - this callback would be called by user application + * with DRM_IOCTL_MODE_CREATE_DUMB command. + */ + + args->pitch = args->width * args->bpp >> 3; + args->size = args->pitch * args->height; + + exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size, + &args->handle); + if (IS_ERR(exynos_gem_obj)) + return PTR_ERR(exynos_gem_obj); + + return 0; +} + +int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *dev, uint32_t handle, uint64_t *offset) +{ + struct exynos_drm_gem_obj *exynos_gem_obj; + struct drm_gem_object *obj; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + mutex_lock(&dev->struct_mutex); + + /* + * get offset of memory allocated for drm framebuffer. + * - this callback would be called by user application + * with DRM_IOCTL_MODE_MAP_DUMB command. + */ + + obj = drm_gem_object_lookup(dev, file_priv, handle); + if (!obj) { + DRM_ERROR("failed to lookup gem object.\n"); + mutex_unlock(&dev->struct_mutex); + return -EINVAL; + } + + exynos_gem_obj = to_exynos_gem_obj(obj); + + *offset = get_gem_mmap_offset(&exynos_gem_obj->base); + + drm_gem_object_unreference(obj); + + DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); + + mutex_unlock(&dev->struct_mutex); + + return 0; +} + +int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct drm_gem_object *obj = vma->vm_private_data; + struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); + struct drm_device *dev = obj->dev; + unsigned long pfn; + pgoff_t page_offset; + int ret; + + page_offset = ((unsigned long)vmf->virtual_address - + vma->vm_start) >> PAGE_SHIFT; + + mutex_lock(&dev->struct_mutex); + + pfn = (exynos_gem_obj->entry->paddr >> PAGE_SHIFT) + page_offset; + + ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); + + mutex_unlock(&dev->struct_mutex); + + return convert_to_vm_err_msg(ret); +} + +int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* set vm_area_struct. */ + ret = drm_gem_mmap(filp, vma); + if (ret < 0) { + DRM_ERROR("failed to mmap.\n"); + return ret; + } + + vma->vm_flags &= ~VM_PFNMAP; + vma->vm_flags |= VM_MIXEDMAP; + + return ret; +} + + +int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, unsigned int handle) +{ + int ret; + + DRM_DEBUG_KMS("%s\n", __FILE__); + + /* + * obj->refcount and obj->handle_count are decreased and + * if both them are 0 then exynos_drm_gem_free_object() + * would be called by callback to release resources. + */ + ret = drm_gem_handle_delete(file_priv, handle); + if (ret < 0) { + DRM_ERROR("failed to delete drm_gem_handle.\n"); + return ret; + } + + return 0; +} + +MODULE_AUTHOR("Inki Dae "); +MODULE_DESCRIPTION("Samsung SoC DRM GEM Module"); +MODULE_LICENSE("GPL"); diff --git a/trunk/drivers/gpu/drm/exynos/exynos_drm_gem.h b/trunk/drivers/gpu/drm/exynos/exynos_drm_gem.h new file mode 100644 index 000000000000..e5fc0148277b --- /dev/null +++ b/trunk/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -0,0 +1,107 @@ +/* exynos_drm_gem.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authoer: Inki Dae + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _EXYNOS_DRM_GEM_H_ +#define _EXYNOS_DRM_GEM_H_ + +#define to_exynos_gem_obj(x) container_of(x,\ + struct exynos_drm_gem_obj, base) + +/* + * exynos drm buffer structure. + * + * @base: a gem object. + * - a new handle to this gem object would be created + * by drm_gem_handle_create(). + * @entry: pointer to exynos drm buffer entry object. + * - containing the information to physically + * continuous memory region allocated by user request + * or at framebuffer creation. + * + * P.S. this object would be transfered to user as kms_bo.handle so + * user can access the buffer through kms_bo.handle. + */ +struct exynos_drm_gem_obj { + struct drm_gem_object base; + struct exynos_drm_buf_entry *entry; +}; + +/* create a new buffer and get a new gem handle. */ +struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv, + struct drm_device *dev, unsigned int size, + unsigned int *handle); + +/* + * request gem object creation and buffer allocation as the size + * that it is calculated with framebuffer information such as width, + * height and bpp. + */ +int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* get buffer offset to map to user space. */ +int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* unmap a buffer from user space. */ +int exynos_drm_gem_munmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* initialize gem object. */ +int exynos_drm_gem_init_object(struct drm_gem_object *obj); + +/* free gem object. */ +void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj); + +/* create memory region for drm framebuffer. */ +int exynos_drm_gem_dumb_create(struct drm_file *file_priv, + struct drm_device *dev, struct drm_mode_create_dumb *args); + +/* map memory region for drm framebuffer to user space. */ +int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, + struct drm_device *dev, uint32_t handle, uint64_t *offset); + +/* page fault handler and mmap fault address(virtual) to physical memory. */ +int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); + +/* + * mmap the physically continuous memory that a gem object contains + * to user space. + */ +int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + +/* set vm_flags and we can change the vm attribute to other one at here. */ +int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); + +/* + * destroy memory region allocated. + * - a gem handle and physical memory region pointed by a gem object + * would be released by drm_gem_handle_delete(). + */ +int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, + struct drm_device *dev, unsigned int handle); + +#endif diff --git a/trunk/drivers/gpu/drm/radeon/radeon.h b/trunk/drivers/gpu/drm/radeon/radeon.h index 0040d28816f1..ff5424e43d1b 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon.h +++ b/trunk/drivers/gpu/drm/radeon/radeon.h @@ -102,7 +102,7 @@ extern int radeon_pcie_gen2; #define RADEON_FENCE_JIFFIES_TIMEOUT (HZ / 2) /* RADEON_IB_POOL_SIZE must be a power of 2 */ #define RADEON_IB_POOL_SIZE 16 -#define RADEON_DEBUGFS_MAX_NUM_FILES 32 +#define RADEON_DEBUGFS_MAX_COMPONENTS 32 #define RADEONFB_CONN_LIMIT 4 #define RADEON_BIOS_NUM_SCRATCH 8 diff --git a/trunk/drivers/gpu/drm/radeon/radeon_combios.c b/trunk/drivers/gpu/drm/radeon/radeon_combios.c index 63675241c7ff..8bf83c4b4147 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_combios.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_combios.c @@ -620,8 +620,8 @@ static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rde i2c.y_data_mask = 0x80; } else { /* default masks for ddc pads */ - i2c.mask_clk_mask = RADEON_GPIO_EN_1; - i2c.mask_data_mask = RADEON_GPIO_EN_0; + i2c.mask_clk_mask = RADEON_GPIO_MASK_1; + i2c.mask_data_mask = RADEON_GPIO_MASK_0; i2c.a_clk_mask = RADEON_GPIO_A_1; i2c.a_data_mask = RADEON_GPIO_A_0; i2c.en_clk_mask = RADEON_GPIO_EN_1; diff --git a/trunk/drivers/gpu/drm/radeon/radeon_connectors.c b/trunk/drivers/gpu/drm/radeon/radeon_connectors.c index c4b8741dbf58..9b5b3e4d2386 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_connectors.c @@ -724,6 +724,7 @@ radeon_vga_detect(struct drm_connector *connector, bool force) dret = radeon_ddc_probe(radeon_connector, radeon_connector->requires_extended_probe); if (dret) { + radeon_connector->detected_by_load = false; if (radeon_connector->edid) { kfree(radeon_connector->edid); radeon_connector->edid = NULL; @@ -750,12 +751,21 @@ radeon_vga_detect(struct drm_connector *connector, bool force) } else { /* if we aren't forcing don't do destructive polling */ - if (!force) - return connector->status; + if (!force) { + /* only return the previous status if we last + * detected a monitor via load. + */ + if (radeon_connector->detected_by_load) + return connector->status; + else + return ret; + } if (radeon_connector->dac_load_detect && encoder) { encoder_funcs = encoder->helper_private; ret = encoder_funcs->detect(encoder, connector); + if (ret == connector_status_connected) + radeon_connector->detected_by_load = true; } } @@ -897,6 +907,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) dret = radeon_ddc_probe(radeon_connector, radeon_connector->requires_extended_probe); if (dret) { + radeon_connector->detected_by_load = false; if (radeon_connector->edid) { kfree(radeon_connector->edid); radeon_connector->edid = NULL; @@ -959,8 +970,18 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) if ((ret == connector_status_connected) && (radeon_connector->use_digital == true)) goto out; + /* DVI-D and HDMI-A are digital only */ + if ((connector->connector_type == DRM_MODE_CONNECTOR_DVID) || + (connector->connector_type == DRM_MODE_CONNECTOR_HDMIA)) + goto out; + + /* if we aren't forcing don't do destructive polling */ if (!force) { - ret = connector->status; + /* only return the previous status if we last + * detected a monitor via load. + */ + if (radeon_connector->detected_by_load) + ret = connector->status; goto out; } @@ -984,6 +1005,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) ret = encoder_funcs->detect(encoder, connector); if (ret == connector_status_connected) { radeon_connector->use_digital = false; + radeon_connector->detected_by_load = true; } } break; diff --git a/trunk/drivers/gpu/drm/radeon/radeon_device.c b/trunk/drivers/gpu/drm/radeon/radeon_device.c index b51e15725c6e..cc695d05bd2b 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_device.c +++ b/trunk/drivers/gpu/drm/radeon/radeon_device.c @@ -750,14 +750,15 @@ int radeon_device_init(struct radeon_device *rdev, /* set DMA mask + need_dma32 flags. * PCIE - can handle 40-bits. - * IGP - can handle 40-bits (in theory) + * IGP - can handle 40-bits * AGP - generally dma32 is safest - * PCI - only dma32 + * PCI - dma32 for legacy pci gart, 40 bits on newer asics */ rdev->need_dma32 = false; if (rdev->flags & RADEON_IS_AGP) rdev->need_dma32 = true; - if (rdev->flags & RADEON_IS_PCI) + if ((rdev->flags & RADEON_IS_PCI) && + (rdev->family < CHIP_RS400)) rdev->need_dma32 = true; dma_bits = rdev->need_dma32 ? 32 : 40; @@ -981,7 +982,7 @@ struct radeon_debugfs { struct drm_info_list *files; unsigned num_files; }; -static struct radeon_debugfs _radeon_debugfs[RADEON_DEBUGFS_MAX_NUM_FILES]; +static struct radeon_debugfs _radeon_debugfs[RADEON_DEBUGFS_MAX_COMPONENTS]; static unsigned _radeon_debugfs_count = 0; int radeon_debugfs_add_files(struct radeon_device *rdev, @@ -996,14 +997,17 @@ int radeon_debugfs_add_files(struct radeon_device *rdev, return 0; } } - if ((_radeon_debugfs_count + nfiles) > RADEON_DEBUGFS_MAX_NUM_FILES) { - DRM_ERROR("Reached maximum number of debugfs files.\n"); - DRM_ERROR("Report so we increase RADEON_DEBUGFS_MAX_NUM_FILES.\n"); + + i = _radeon_debugfs_count + 1; + if (i > RADEON_DEBUGFS_MAX_COMPONENTS) { + DRM_ERROR("Reached maximum number of debugfs components.\n"); + DRM_ERROR("Report so we increase " + "RADEON_DEBUGFS_MAX_COMPONENTS.\n"); return -EINVAL; } _radeon_debugfs[_radeon_debugfs_count].files = files; _radeon_debugfs[_radeon_debugfs_count].num_files = nfiles; - _radeon_debugfs_count++; + _radeon_debugfs_count = i; #if defined(CONFIG_DEBUG_FS) drm_debugfs_create_files(files, nfiles, rdev->ddev->control->debugfs_root, diff --git a/trunk/drivers/gpu/drm/radeon/radeon_mode.h b/trunk/drivers/gpu/drm/radeon/radeon_mode.h index 68820f5f6303..ed0178f03235 100644 --- a/trunk/drivers/gpu/drm/radeon/radeon_mode.h +++ b/trunk/drivers/gpu/drm/radeon/radeon_mode.h @@ -447,6 +447,7 @@ struct radeon_connector { struct edid *edid; void *con_priv; bool dac_load_detect; + bool detected_by_load; /* if the connection status was determined by load */ uint16_t connector_object_id; struct radeon_hpd hpd; struct radeon_router router; diff --git a/trunk/drivers/gpu/drm/ttm/ttm_bo.c b/trunk/drivers/gpu/drm/ttm/ttm_bo.c index b824d9bdd87c..6e96c85b70da 100644 --- a/trunk/drivers/gpu/drm/ttm/ttm_bo.c +++ b/trunk/drivers/gpu/drm/ttm/ttm_bo.c @@ -1295,6 +1295,7 @@ int ttm_bo_create(struct ttm_bo_device *bdev, return ret; } +EXPORT_SYMBOL(ttm_bo_create); static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev, unsigned mem_type, bool allow_errors) diff --git a/trunk/drivers/gpu/drm/vmwgfx/Makefile b/trunk/drivers/gpu/drm/vmwgfx/Makefile index 7d8e9d5d498c..586869c8c11f 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/Makefile +++ b/trunk/drivers/gpu/drm/vmwgfx/Makefile @@ -5,6 +5,6 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_fb.o vmwgfx_ioctl.o vmwgfx_resource.o vmwgfx_buffer.o \ vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \ vmwgfx_overlay.o vmwgfx_marker.o vmwgfx_gmrid_manager.o \ - vmwgfx_fence.o + vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/trunk/drivers/gpu/drm/vmwgfx/svga3d_reg.h b/trunk/drivers/gpu/drm/vmwgfx/svga3d_reg.h index 77cb45331000..d0e085ee8249 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/svga3d_reg.h +++ b/trunk/drivers/gpu/drm/vmwgfx/svga3d_reg.h @@ -57,7 +57,8 @@ typedef enum { SVGA3D_HWVERSION_WS6_B1 = SVGA3D_MAKE_HWVERSION(1, 1), SVGA3D_HWVERSION_FUSION_11 = SVGA3D_MAKE_HWVERSION(1, 4), SVGA3D_HWVERSION_WS65_B1 = SVGA3D_MAKE_HWVERSION(2, 0), - SVGA3D_HWVERSION_CURRENT = SVGA3D_HWVERSION_WS65_B1, + SVGA3D_HWVERSION_WS8_B1 = SVGA3D_MAKE_HWVERSION(2, 1), + SVGA3D_HWVERSION_CURRENT = SVGA3D_HWVERSION_WS8_B1, } SVGA3dHardwareVersion; /* @@ -67,7 +68,8 @@ typedef enum { typedef uint32 SVGA3dBool; /* 32-bit Bool definition */ #define SVGA3D_NUM_CLIPPLANES 6 #define SVGA3D_MAX_SIMULTANEOUS_RENDER_TARGETS 8 - +#define SVGA3D_MAX_CONTEXT_IDS 256 +#define SVGA3D_MAX_SURFACE_IDS (32 * 1024) /* * Surface formats. @@ -79,76 +81,91 @@ typedef uint32 SVGA3dBool; /* 32-bit Bool definition */ */ typedef enum SVGA3dSurfaceFormat { - SVGA3D_FORMAT_INVALID = 0, + SVGA3D_FORMAT_INVALID = 0, - SVGA3D_X8R8G8B8 = 1, - SVGA3D_A8R8G8B8 = 2, + SVGA3D_X8R8G8B8 = 1, + SVGA3D_A8R8G8B8 = 2, - SVGA3D_R5G6B5 = 3, - SVGA3D_X1R5G5B5 = 4, - SVGA3D_A1R5G5B5 = 5, - SVGA3D_A4R4G4B4 = 6, + SVGA3D_R5G6B5 = 3, + SVGA3D_X1R5G5B5 = 4, + SVGA3D_A1R5G5B5 = 5, + SVGA3D_A4R4G4B4 = 6, - SVGA3D_Z_D32 = 7, - SVGA3D_Z_D16 = 8, - SVGA3D_Z_D24S8 = 9, - SVGA3D_Z_D15S1 = 10, + SVGA3D_Z_D32 = 7, + SVGA3D_Z_D16 = 8, + SVGA3D_Z_D24S8 = 9, + SVGA3D_Z_D15S1 = 10, - SVGA3D_LUMINANCE8 = 11, - SVGA3D_LUMINANCE4_ALPHA4 = 12, - SVGA3D_LUMINANCE16 = 13, - SVGA3D_LUMINANCE8_ALPHA8 = 14, + SVGA3D_LUMINANCE8 = 11, + SVGA3D_LUMINANCE4_ALPHA4 = 12, + SVGA3D_LUMINANCE16 = 13, + SVGA3D_LUMINANCE8_ALPHA8 = 14, - SVGA3D_DXT1 = 15, - SVGA3D_DXT2 = 16, - SVGA3D_DXT3 = 17, - SVGA3D_DXT4 = 18, - SVGA3D_DXT5 = 19, + SVGA3D_DXT1 = 15, + SVGA3D_DXT2 = 16, + SVGA3D_DXT3 = 17, + SVGA3D_DXT4 = 18, + SVGA3D_DXT5 = 19, - SVGA3D_BUMPU8V8 = 20, - SVGA3D_BUMPL6V5U5 = 21, - SVGA3D_BUMPX8L8V8U8 = 22, - SVGA3D_BUMPL8V8U8 = 23, + SVGA3D_BUMPU8V8 = 20, + SVGA3D_BUMPL6V5U5 = 21, + SVGA3D_BUMPX8L8V8U8 = 22, + SVGA3D_BUMPL8V8U8 = 23, - SVGA3D_ARGB_S10E5 = 24, /* 16-bit floating-point ARGB */ - SVGA3D_ARGB_S23E8 = 25, /* 32-bit floating-point ARGB */ + SVGA3D_ARGB_S10E5 = 24, /* 16-bit floating-point ARGB */ + SVGA3D_ARGB_S23E8 = 25, /* 32-bit floating-point ARGB */ - SVGA3D_A2R10G10B10 = 26, + SVGA3D_A2R10G10B10 = 26, /* signed formats */ - SVGA3D_V8U8 = 27, - SVGA3D_Q8W8V8U8 = 28, - SVGA3D_CxV8U8 = 29, + SVGA3D_V8U8 = 27, + SVGA3D_Q8W8V8U8 = 28, + SVGA3D_CxV8U8 = 29, /* mixed formats */ - SVGA3D_X8L8V8U8 = 30, - SVGA3D_A2W10V10U10 = 31, + SVGA3D_X8L8V8U8 = 30, + SVGA3D_A2W10V10U10 = 31, - SVGA3D_ALPHA8 = 32, + SVGA3D_ALPHA8 = 32, /* Single- and dual-component floating point formats */ - SVGA3D_R_S10E5 = 33, - SVGA3D_R_S23E8 = 34, - SVGA3D_RG_S10E5 = 35, - SVGA3D_RG_S23E8 = 36, + SVGA3D_R_S10E5 = 33, + SVGA3D_R_S23E8 = 34, + SVGA3D_RG_S10E5 = 35, + SVGA3D_RG_S23E8 = 36, /* * Any surface can be used as a buffer object, but SVGA3D_BUFFER is * the most efficient format to use when creating new surfaces * expressly for index or vertex data. */ - SVGA3D_BUFFER = 37, - SVGA3D_Z_D24X8 = 38, + SVGA3D_BUFFER = 37, + + SVGA3D_Z_D24X8 = 38, - SVGA3D_V16U16 = 39, + SVGA3D_V16U16 = 39, - SVGA3D_G16R16 = 40, - SVGA3D_A16B16G16R16 = 41, + SVGA3D_G16R16 = 40, + SVGA3D_A16B16G16R16 = 41, /* Packed Video formats */ - SVGA3D_UYVY = 42, - SVGA3D_YUY2 = 43, + SVGA3D_UYVY = 42, + SVGA3D_YUY2 = 43, + + /* Planar video formats */ + SVGA3D_NV12 = 44, + + /* Video format with alpha */ + SVGA3D_AYUV = 45, + + SVGA3D_BC4_UNORM = 108, + SVGA3D_BC5_UNORM = 111, + + /* Advanced D3D9 depth formats. */ + SVGA3D_Z_DF16 = 118, + SVGA3D_Z_DF24 = 119, + SVGA3D_Z_D24S8_INT = 120, SVGA3D_FORMAT_MAX } SVGA3dSurfaceFormat; @@ -414,9 +431,19 @@ typedef enum { SVGA3D_RS_SRCBLENDALPHA = 94, /* SVGA3dBlendOp */ SVGA3D_RS_DSTBLENDALPHA = 95, /* SVGA3dBlendOp */ SVGA3D_RS_BLENDEQUATIONALPHA = 96, /* SVGA3dBlendEquation */ + SVGA3D_RS_TRANSPARENCYANTIALIAS = 97, /* SVGA3dTransparencyAntialiasType */ + SVGA3D_RS_LINEAA = 98, /* SVGA3dBool */ + SVGA3D_RS_LINEWIDTH = 99, /* float */ SVGA3D_RS_MAX } SVGA3dRenderStateName; +typedef enum { + SVGA3D_TRANSPARENCYANTIALIAS_NORMAL = 0, + SVGA3D_TRANSPARENCYANTIALIAS_ALPHATOCOVERAGE = 1, + SVGA3D_TRANSPARENCYANTIALIAS_SUPERSAMPLE = 2, + SVGA3D_TRANSPARENCYANTIALIAS_MAX +} SVGA3dTransparencyAntialiasType; + typedef enum { SVGA3D_VERTEXMATERIAL_NONE = 0, /* Use the value in the current material */ SVGA3D_VERTEXMATERIAL_DIFFUSE = 1, /* Use the value in the diffuse component */ @@ -728,10 +755,10 @@ typedef enum { SVGA3D_TEX_FILTER_NEAREST = 1, SVGA3D_TEX_FILTER_LINEAR = 2, SVGA3D_TEX_FILTER_ANISOTROPIC = 3, - SVGA3D_TEX_FILTER_FLATCUBIC = 4, // Deprecated, not implemented - SVGA3D_TEX_FILTER_GAUSSIANCUBIC = 5, // Deprecated, not implemented - SVGA3D_TEX_FILTER_PYRAMIDALQUAD = 6, // Not currently implemented - SVGA3D_TEX_FILTER_GAUSSIANQUAD = 7, // Not currently implemented + SVGA3D_TEX_FILTER_FLATCUBIC = 4, /* Deprecated, not implemented */ + SVGA3D_TEX_FILTER_GAUSSIANCUBIC = 5, /* Deprecated, not implemented */ + SVGA3D_TEX_FILTER_PYRAMIDALQUAD = 6, /* Not currently implemented */ + SVGA3D_TEX_FILTER_GAUSSIANQUAD = 7, /* Not currently implemented */ SVGA3D_TEX_FILTER_MAX } SVGA3dTextureFilter; @@ -799,19 +826,19 @@ typedef enum { typedef enum { SVGA3D_DECLUSAGE_POSITION = 0, - SVGA3D_DECLUSAGE_BLENDWEIGHT, // 1 - SVGA3D_DECLUSAGE_BLENDINDICES, // 2 - SVGA3D_DECLUSAGE_NORMAL, // 3 - SVGA3D_DECLUSAGE_PSIZE, // 4 - SVGA3D_DECLUSAGE_TEXCOORD, // 5 - SVGA3D_DECLUSAGE_TANGENT, // 6 - SVGA3D_DECLUSAGE_BINORMAL, // 7 - SVGA3D_DECLUSAGE_TESSFACTOR, // 8 - SVGA3D_DECLUSAGE_POSITIONT, // 9 - SVGA3D_DECLUSAGE_COLOR, // 10 - SVGA3D_DECLUSAGE_FOG, // 11 - SVGA3D_DECLUSAGE_DEPTH, // 12 - SVGA3D_DECLUSAGE_SAMPLE, // 13 + SVGA3D_DECLUSAGE_BLENDWEIGHT, /* 1 */ + SVGA3D_DECLUSAGE_BLENDINDICES, /* 2 */ + SVGA3D_DECLUSAGE_NORMAL, /* 3 */ + SVGA3D_DECLUSAGE_PSIZE, /* 4 */ + SVGA3D_DECLUSAGE_TEXCOORD, /* 5 */ + SVGA3D_DECLUSAGE_TANGENT, /* 6 */ + SVGA3D_DECLUSAGE_BINORMAL, /* 7 */ + SVGA3D_DECLUSAGE_TESSFACTOR, /* 8 */ + SVGA3D_DECLUSAGE_POSITIONT, /* 9 */ + SVGA3D_DECLUSAGE_COLOR, /* 10 */ + SVGA3D_DECLUSAGE_FOG, /* 11 */ + SVGA3D_DECLUSAGE_DEPTH, /* 12 */ + SVGA3D_DECLUSAGE_SAMPLE, /* 13 */ SVGA3D_DECLUSAGE_MAX } SVGA3dDeclUsage; @@ -819,10 +846,10 @@ typedef enum { SVGA3D_DECLMETHOD_DEFAULT = 0, SVGA3D_DECLMETHOD_PARTIALU, SVGA3D_DECLMETHOD_PARTIALV, - SVGA3D_DECLMETHOD_CROSSUV, // Normal + SVGA3D_DECLMETHOD_CROSSUV, /* Normal */ SVGA3D_DECLMETHOD_UV, - SVGA3D_DECLMETHOD_LOOKUP, // Lookup a displacement map - SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, // Lookup a pre-sampled displacement map + SVGA3D_DECLMETHOD_LOOKUP, /* Lookup a displacement map */ + SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, /* Lookup a pre-sampled displacement map */ } SVGA3dDeclMethod; typedef enum { @@ -930,7 +957,6 @@ typedef enum { } SVGA3dCubeFace; typedef enum { - SVGA3D_SHADERTYPE_COMPILED_DX8 = 0, SVGA3D_SHADERTYPE_VS = 1, SVGA3D_SHADERTYPE_PS = 2, SVGA3D_SHADERTYPE_MAX @@ -968,11 +994,17 @@ typedef enum { } SVGA3dTransferType; /* - * The maximum number vertex arrays we're guaranteed to support in + * The maximum number of vertex arrays we're guaranteed to support in * SVGA_3D_CMD_DRAWPRIMITIVES. */ #define SVGA3D_MAX_VERTEX_ARRAYS 32 +/* + * The maximum number of primitive ranges we're guaranteed to support + * in SVGA_3D_CMD_DRAWPRIMITIVES. + */ +#define SVGA3D_MAX_DRAW_PRIMITIVE_RANGES 32 + /* * Identifiers for commands in the command FIFO. * @@ -990,7 +1022,7 @@ typedef enum { #define SVGA_3D_CMD_LEGACY_BASE 1000 #define SVGA_3D_CMD_BASE 1040 -#define SVGA_3D_CMD_SURFACE_DEFINE SVGA_3D_CMD_BASE + 0 +#define SVGA_3D_CMD_SURFACE_DEFINE SVGA_3D_CMD_BASE + 0 /* Deprecated */ #define SVGA_3D_CMD_SURFACE_DESTROY SVGA_3D_CMD_BASE + 1 #define SVGA_3D_CMD_SURFACE_COPY SVGA_3D_CMD_BASE + 2 #define SVGA_3D_CMD_SURFACE_STRETCHBLT SVGA_3D_CMD_BASE + 3 @@ -1008,7 +1040,7 @@ typedef enum { #define SVGA_3D_CMD_SETVIEWPORT SVGA_3D_CMD_BASE + 15 #define SVGA_3D_CMD_SETCLIPPLANE SVGA_3D_CMD_BASE + 16 #define SVGA_3D_CMD_CLEAR SVGA_3D_CMD_BASE + 17 -#define SVGA_3D_CMD_PRESENT SVGA_3D_CMD_BASE + 18 // Deprecated +#define SVGA_3D_CMD_PRESENT SVGA_3D_CMD_BASE + 18 /* Deprecated */ #define SVGA_3D_CMD_SHADER_DEFINE SVGA_3D_CMD_BASE + 19 #define SVGA_3D_CMD_SHADER_DESTROY SVGA_3D_CMD_BASE + 20 #define SVGA_3D_CMD_SET_SHADER SVGA_3D_CMD_BASE + 21 @@ -1018,9 +1050,13 @@ typedef enum { #define SVGA_3D_CMD_BEGIN_QUERY SVGA_3D_CMD_BASE + 25 #define SVGA_3D_CMD_END_QUERY SVGA_3D_CMD_BASE + 26 #define SVGA_3D_CMD_WAIT_FOR_QUERY SVGA_3D_CMD_BASE + 27 -#define SVGA_3D_CMD_PRESENT_READBACK SVGA_3D_CMD_BASE + 28 // Deprecated +#define SVGA_3D_CMD_PRESENT_READBACK SVGA_3D_CMD_BASE + 28 /* Deprecated */ #define SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN SVGA_3D_CMD_BASE + 29 -#define SVGA_3D_CMD_MAX SVGA_3D_CMD_BASE + 30 +#define SVGA_3D_CMD_SURFACE_DEFINE_V2 SVGA_3D_CMD_BASE + 30 +#define SVGA_3D_CMD_GENERATE_MIPMAPS SVGA_3D_CMD_BASE + 31 +#define SVGA_3D_CMD_ACTIVATE_SURFACE SVGA_3D_CMD_BASE + 40 +#define SVGA_3D_CMD_DEACTIVATE_SURFACE SVGA_3D_CMD_BASE + 41 +#define SVGA_3D_CMD_MAX SVGA_3D_CMD_BASE + 42 #define SVGA_3D_CMD_FUTURE_MAX 2000 @@ -1031,9 +1067,9 @@ typedef enum { typedef struct { union { struct { - uint16 function; // SVGA3dFogFunction - uint8 type; // SVGA3dFogType - uint8 base; // SVGA3dFogBase + uint16 function; /* SVGA3dFogFunction */ + uint8 type; /* SVGA3dFogType */ + uint8 base; /* SVGA3dFogBase */ }; uint32 uintValue; }; @@ -1109,6 +1145,8 @@ typedef enum { SVGA3D_SURFACE_HINT_RENDERTARGET = (1 << 6), SVGA3D_SURFACE_HINT_DEPTHSTENCIL = (1 << 7), SVGA3D_SURFACE_HINT_WRITEONLY = (1 << 8), + SVGA3D_SURFACE_MASKABLE_ANTIALIAS = (1 << 9), + SVGA3D_SURFACE_AUTOGENMIPMAPS = (1 << 10), } SVGA3dSurfaceFlags; typedef @@ -1121,6 +1159,12 @@ struct { uint32 sid; SVGA3dSurfaceFlags surfaceFlags; SVGA3dSurfaceFormat format; + /* + * If surfaceFlags has SVGA3D_SURFACE_CUBEMAP bit set, all SVGA3dSurfaceFace + * structures must have the same value of numMipLevels field. + * Otherwise, all but the first SVGA3dSurfaceFace structures must have the + * numMipLevels set to 0. + */ SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES]; /* * Followed by an SVGA3dSize structure for each mip level in each face. @@ -1133,6 +1177,31 @@ struct { */ } SVGA3dCmdDefineSurface; /* SVGA_3D_CMD_SURFACE_DEFINE */ +typedef +struct { + uint32 sid; + SVGA3dSurfaceFlags surfaceFlags; + SVGA3dSurfaceFormat format; + /* + * If surfaceFlags has SVGA3D_SURFACE_CUBEMAP bit set, all SVGA3dSurfaceFace + * structures must have the same value of numMipLevels field. + * Otherwise, all but the first SVGA3dSurfaceFace structures must have the + * numMipLevels set to 0. + */ + SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES]; + uint32 multisampleCount; + SVGA3dTextureFilter autogenFilter; + /* + * Followed by an SVGA3dSize structure for each mip level in each face. + * + * A note on surface sizes: Sizes are always specified in pixels, + * even if the true surface size is not a multiple of the minimum + * block size of the surface's format. For example, a 3x3x1 DXT1 + * compressed texture would actually be stored as a 4x4x1 image in + * memory. + */ +} SVGA3dCmdDefineSurface_v2; /* SVGA_3D_CMD_SURFACE_DEFINE_V2 */ + typedef struct { uint32 sid; @@ -1474,10 +1543,12 @@ struct { * SVGA3dCmdDrawPrimitives structure. In order, * they are: * - * 1. SVGA3dVertexDecl, quantity 'numVertexDecls' - * 2. SVGA3dPrimitiveRange, quantity 'numRanges' + * 1. SVGA3dVertexDecl, quantity 'numVertexDecls', but no more than + * SVGA3D_MAX_VERTEX_ARRAYS; + * 2. SVGA3dPrimitiveRange, quantity 'numRanges', but no more than + * SVGA3D_MAX_DRAW_PRIMITIVE_RANGES; * 3. Optionally, SVGA3dVertexDivisor, quantity 'numVertexDecls' (contains - * the frequency divisor for this the corresponding vertex decl) + * the frequency divisor for the corresponding vertex decl). */ } SVGA3dCmdDrawPrimitives; /* SVGA_3D_CMD_DRAWPRIMITIVES */ @@ -1671,6 +1742,12 @@ struct { /* Clipping: zero or more SVGASignedRects follow */ } SVGA3dCmdBlitSurfaceToScreen; /* SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN */ +typedef +struct { + uint32 sid; + SVGA3dTextureFilter filter; +} SVGA3dCmdGenerateMipmaps; /* SVGA_3D_CMD_GENERATE_MIPMAPS */ + /* * Capability query index. @@ -1774,6 +1851,32 @@ typedef enum { SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16 = 67, SVGA3D_DEVCAP_SURFACEFMT_UYVY = 68, SVGA3D_DEVCAP_SURFACEFMT_YUY2 = 69, + SVGA3D_DEVCAP_MULTISAMPLE_NONMASKABLESAMPLES = 70, + SVGA3D_DEVCAP_MULTISAMPLE_MASKABLESAMPLES = 71, + SVGA3D_DEVCAP_ALPHATOCOVERAGE = 72, + SVGA3D_DEVCAP_SUPERSAMPLE = 73, + SVGA3D_DEVCAP_AUTOGENMIPMAPS = 74, + SVGA3D_DEVCAP_SURFACEFMT_NV12 = 75, + SVGA3D_DEVCAP_SURFACEFMT_AYUV = 76, + + /* + * This is the maximum number of SVGA context IDs that the guest + * can define using SVGA_3D_CMD_CONTEXT_DEFINE. + */ + SVGA3D_DEVCAP_MAX_CONTEXT_IDS = 77, + + /* + * This is the maximum number of SVGA surface IDs that the guest + * can define using SVGA_3D_CMD_SURFACE_DEFINE*. + */ + SVGA3D_DEVCAP_MAX_SURFACE_IDS = 78, + + SVGA3D_DEVCAP_SURFACEFMT_Z_DF16 = 79, + SVGA3D_DEVCAP_SURFACEFMT_Z_DF24 = 80, + SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8_INT = 81, + + SVGA3D_DEVCAP_SURFACEFMT_BC4_UNORM = 82, + SVGA3D_DEVCAP_SURFACEFMT_BC5_UNORM = 83, /* * Don't add new caps into the previous section; the values in this diff --git a/trunk/drivers/gpu/drm/vmwgfx/svga_escape.h b/trunk/drivers/gpu/drm/vmwgfx/svga_escape.h index 7b85e9b8c854..8e8d9682e018 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/svga_escape.h +++ b/trunk/drivers/gpu/drm/vmwgfx/svga_escape.h @@ -75,7 +75,7 @@ */ #define SVGA_ESCAPE_VMWARE_HINT 0x00030000 -#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN 0x00030001 // Deprecated +#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN 0x00030001 /* Deprecated */ typedef struct { diff --git a/trunk/drivers/gpu/drm/vmwgfx/svga_overlay.h b/trunk/drivers/gpu/drm/vmwgfx/svga_overlay.h index f753d73c14b4..f38416fcb046 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/svga_overlay.h +++ b/trunk/drivers/gpu/drm/vmwgfx/svga_overlay.h @@ -38,9 +38,9 @@ * Video formats we support */ -#define VMWARE_FOURCC_YV12 0x32315659 // 'Y' 'V' '1' '2' -#define VMWARE_FOURCC_YUY2 0x32595559 // 'Y' 'U' 'Y' '2' -#define VMWARE_FOURCC_UYVY 0x59565955 // 'U' 'Y' 'V' 'Y' +#define VMWARE_FOURCC_YV12 0x32315659 /* 'Y' 'V' '1' '2' */ +#define VMWARE_FOURCC_YUY2 0x32595559 /* 'Y' 'U' 'Y' '2' */ +#define VMWARE_FOURCC_UYVY 0x59565955 /* 'U' 'Y' 'V' 'Y' */ typedef enum { SVGA_OVERLAY_FORMAT_INVALID = 0, @@ -68,7 +68,7 @@ struct SVGAEscapeVideoSetRegs { uint32 streamId; } header; - // May include zero or more items. + /* May include zero or more items. */ struct { uint32 registerId; uint32 value; @@ -134,12 +134,12 @@ struct { */ static inline bool -VMwareVideoGetAttributes(const SVGAOverlayFormat format, // IN - uint32 *width, // IN / OUT - uint32 *height, // IN / OUT - uint32 *size, // OUT - uint32 *pitches, // OUT (optional) - uint32 *offsets) // OUT (optional) +VMwareVideoGetAttributes(const SVGAOverlayFormat format, /* IN */ + uint32 *width, /* IN / OUT */ + uint32 *height, /* IN / OUT */ + uint32 *size, /* OUT */ + uint32 *pitches, /* OUT (optional) */ + uint32 *offsets) /* OUT (optional) */ { int tmp; @@ -198,4 +198,4 @@ VMwareVideoGetAttributes(const SVGAOverlayFormat format, // IN return true; } -#endif // _SVGA_OVERLAY_H_ +#endif /* _SVGA_OVERLAY_H_ */ diff --git a/trunk/drivers/gpu/drm/vmwgfx/svga_reg.h b/trunk/drivers/gpu/drm/vmwgfx/svga_reg.h index ec5aad9b6ed3..01f63cb49678 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/svga_reg.h +++ b/trunk/drivers/gpu/drm/vmwgfx/svga_reg.h @@ -276,7 +276,7 @@ enum { * possible. */ #define SVGA_GMR_NULL ((uint32) -1) -#define SVGA_GMR_FRAMEBUFFER ((uint32) -2) // Guest Framebuffer (GFB) +#define SVGA_GMR_FRAMEBUFFER ((uint32) -2) /* Guest Framebuffer (GFB) */ typedef struct SVGAGuestMemDescriptor { @@ -317,13 +317,35 @@ struct SVGAGMRImageFormat { struct { uint32 bitsPerPixel : 8; uint32 colorDepth : 8; - uint32 reserved : 16; // Must be zero + uint32 reserved : 16; /* Must be zero */ }; uint32 value; }; } SVGAGMRImageFormat; +typedef +struct SVGAGuestImage { + SVGAGuestPtr ptr; + + /* + * A note on interpretation of pitch: This value of pitch is the + * number of bytes between vertically adjacent image + * blocks. Normally this is the number of bytes between the first + * pixel of two adjacent scanlines. With compressed textures, + * however, this may represent the number of bytes between + * compression blocks rather than between rows of pixels. + * + * XXX: Compressed textures currently must be tightly packed in guest memory. + * + * If the image is 1-dimensional, pitch is ignored. + * + * If 'pitch' is zero, the SVGA3D device calculates a pitch value + * assuming each row of blocks is tightly packed. + */ + uint32 pitch; +} SVGAGuestImage; + /* * SVGAColorBGRX -- * @@ -339,7 +361,7 @@ struct SVGAColorBGRX { uint32 b : 8; uint32 g : 8; uint32 r : 8; - uint32 x : 8; // Unused + uint32 x : 8; /* Unused */ }; uint32 value; @@ -395,16 +417,16 @@ struct SVGASignedPoint { #define SVGA_CAP_NONE 0x00000000 #define SVGA_CAP_RECT_COPY 0x00000002 #define SVGA_CAP_CURSOR 0x00000020 -#define SVGA_CAP_CURSOR_BYPASS 0x00000040 // Legacy (Use Cursor Bypass 3 instead) -#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080 // Legacy (Use Cursor Bypass 3 instead) +#define SVGA_CAP_CURSOR_BYPASS 0x00000040 /* Legacy (Use Cursor Bypass 3 instead) */ +#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080 /* Legacy (Use Cursor Bypass 3 instead) */ #define SVGA_CAP_8BIT_EMULATION 0x00000100 #define SVGA_CAP_ALPHA_CURSOR 0x00000200 #define SVGA_CAP_3D 0x00004000 #define SVGA_CAP_EXTENDED_FIFO 0x00008000 -#define SVGA_CAP_MULTIMON 0x00010000 // Legacy multi-monitor support +#define SVGA_CAP_MULTIMON 0x00010000 /* Legacy multi-monitor support */ #define SVGA_CAP_PITCHLOCK 0x00020000 #define SVGA_CAP_IRQMASK 0x00040000 -#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000 // Legacy multi-monitor support +#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000 /* Legacy multi-monitor support */ #define SVGA_CAP_GMR 0x00100000 #define SVGA_CAP_TRACES 0x00200000 #define SVGA_CAP_GMR2 0x00400000 @@ -453,7 +475,7 @@ enum { SVGA_FIFO_CAPABILITIES = 4, SVGA_FIFO_FLAGS, - // Valid with SVGA_FIFO_CAP_FENCE: + /* Valid with SVGA_FIFO_CAP_FENCE: */ SVGA_FIFO_FENCE, /* @@ -466,32 +488,46 @@ enum { * extended FIFO. */ - // Valid if exists (i.e. if extended FIFO enabled): + /* Valid if exists (i.e. if extended FIFO enabled): */ SVGA_FIFO_3D_HWVERSION, /* See SVGA3dHardwareVersion in svga3d_reg.h */ - // Valid with SVGA_FIFO_CAP_PITCHLOCK: + /* Valid with SVGA_FIFO_CAP_PITCHLOCK: */ SVGA_FIFO_PITCHLOCK, - // Valid with SVGA_FIFO_CAP_CURSOR_BYPASS_3: + /* Valid with SVGA_FIFO_CAP_CURSOR_BYPASS_3: */ SVGA_FIFO_CURSOR_ON, /* Cursor bypass 3 show/hide register */ SVGA_FIFO_CURSOR_X, /* Cursor bypass 3 x register */ SVGA_FIFO_CURSOR_Y, /* Cursor bypass 3 y register */ SVGA_FIFO_CURSOR_COUNT, /* Incremented when any of the other 3 change */ SVGA_FIFO_CURSOR_LAST_UPDATED,/* Last time the host updated the cursor */ - // Valid with SVGA_FIFO_CAP_RESERVE: + /* Valid with SVGA_FIFO_CAP_RESERVE: */ SVGA_FIFO_RESERVED, /* Bytes past NEXT_CMD with real contents */ /* - * Valid with SVGA_FIFO_CAP_SCREEN_OBJECT: + * Valid with SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2: * * By default this is SVGA_ID_INVALID, to indicate that the cursor * coordinates are specified relative to the virtual root. If this * is set to a specific screen ID, cursor position is reinterpreted - * as a signed offset relative to that screen's origin. This is the - * only way to place the cursor on a non-rooted screen. + * as a signed offset relative to that screen's origin. */ SVGA_FIFO_CURSOR_SCREEN_ID, + /* + * Valid with SVGA_FIFO_CAP_DEAD + * + * An arbitrary value written by the host, drivers should not use it. + */ + SVGA_FIFO_DEAD, + + /* + * Valid with SVGA_FIFO_CAP_3D_HWVERSION_REVISED: + * + * Contains 3D HWVERSION (see SVGA3dHardwareVersion in svga3d_reg.h) + * on platforms that can enforce graphics resource limits. + */ + SVGA_FIFO_3D_HWVERSION_REVISED, + /* * XXX: The gap here, up until SVGA_FIFO_3D_CAPS, can be used for new * registers, but this must be done carefully and with judicious use of @@ -530,7 +566,7 @@ enum { * sets SVGA_FIFO_MIN high enough to leave room for them. */ - // Valid if register exists: + /* Valid if register exists: */ SVGA_FIFO_GUEST_3D_HWVERSION, /* Guest driver's 3D version */ SVGA_FIFO_FENCE_GOAL, /* Matching target for SVGA_IRQFLAG_FENCE_GOAL */ SVGA_FIFO_BUSY, /* See "FIFO Synchronization Registers" */ @@ -731,6 +767,37 @@ enum { * * - When a screen is resized, either using Screen Object commands or * legacy multimon registers, its contents are preserved. + * + * SVGA_FIFO_CAP_GMR2 -- + * + * Provides new commands to define and remap guest memory regions (GMR). + * + * New 2D commands: + * DEFINE_GMR2, REMAP_GMR2. + * + * SVGA_FIFO_CAP_3D_HWVERSION_REVISED -- + * + * Indicates new register SVGA_FIFO_3D_HWVERSION_REVISED exists. + * This register may replace SVGA_FIFO_3D_HWVERSION on platforms + * that enforce graphics resource limits. This allows the platform + * to clear SVGA_FIFO_3D_HWVERSION and disable 3D in legacy guest + * drivers that do not limit their resources. + * + * Note this is an alias to SVGA_FIFO_CAP_GMR2 because these indicators + * are codependent (and thus we use a single capability bit). + * + * SVGA_FIFO_CAP_SCREEN_OBJECT_2 -- + * + * Modifies the DEFINE_SCREEN command to include a guest provided + * backing store in GMR memory and the bytesPerLine for the backing + * store. This capability requires the use of a backing store when + * creating screen objects. However if SVGA_FIFO_CAP_SCREEN_OBJECT + * is present then backing stores are optional. + * + * SVGA_FIFO_CAP_DEAD -- + * + * Drivers should not use this cap bit. This cap bit can not be + * reused since some hosts already expose it. */ #define SVGA_FIFO_CAP_NONE 0 @@ -742,6 +809,10 @@ enum { #define SVGA_FIFO_CAP_ESCAPE (1<<5) #define SVGA_FIFO_CAP_RESERVE (1<<6) #define SVGA_FIFO_CAP_SCREEN_OBJECT (1<<7) +#define SVGA_FIFO_CAP_GMR2 (1<<8) +#define SVGA_FIFO_CAP_3D_HWVERSION_REVISED SVGA_FIFO_CAP_GMR2 +#define SVGA_FIFO_CAP_SCREEN_OBJECT_2 (1<<9) +#define SVGA_FIFO_CAP_DEAD (1<<10) /* @@ -752,7 +823,7 @@ enum { #define SVGA_FIFO_FLAG_NONE 0 #define SVGA_FIFO_FLAG_ACCELFRONT (1<<0) -#define SVGA_FIFO_FLAG_RESERVED (1<<31) // Internal use only +#define SVGA_FIFO_FLAG_RESERVED (1<<31) /* Internal use only */ /* * FIFO reservation sentinel value @@ -785,22 +856,22 @@ enum { SVGA_VIDEO_DATA_OFFSET, SVGA_VIDEO_FORMAT, SVGA_VIDEO_COLORKEY, - SVGA_VIDEO_SIZE, // Deprecated + SVGA_VIDEO_SIZE, /* Deprecated */ SVGA_VIDEO_WIDTH, SVGA_VIDEO_HEIGHT, SVGA_VIDEO_SRC_X, SVGA_VIDEO_SRC_Y, SVGA_VIDEO_SRC_WIDTH, SVGA_VIDEO_SRC_HEIGHT, - SVGA_VIDEO_DST_X, // Signed int32 - SVGA_VIDEO_DST_Y, // Signed int32 + SVGA_VIDEO_DST_X, /* Signed int32 */ + SVGA_VIDEO_DST_Y, /* Signed int32 */ SVGA_VIDEO_DST_WIDTH, SVGA_VIDEO_DST_HEIGHT, SVGA_VIDEO_PITCH_1, SVGA_VIDEO_PITCH_2, SVGA_VIDEO_PITCH_3, - SVGA_VIDEO_DATA_GMRID, // Optional, defaults to SVGA_GMR_FRAMEBUFFER - SVGA_VIDEO_DST_SCREEN_ID, // Optional, defaults to virtual coords (SVGA_ID_INVALID) + SVGA_VIDEO_DATA_GMRID, /* Optional, defaults to SVGA_GMR_FRAMEBUFFER */ + SVGA_VIDEO_DST_SCREEN_ID, /* Optional, defaults to virtual coords (SVGA_ID_INVALID) */ SVGA_VIDEO_NUM_REGS }; @@ -851,15 +922,51 @@ typedef struct SVGAOverlayUnit { * compatibility. New flags can be added, and the struct may grow, * but existing fields must retain their meaning. * + * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2 are required fields of + * a SVGAGuestPtr that is used to back the screen contents. This + * memory must come from the GFB. The guest is not allowed to + * access the memory and doing so will have undefined results. The + * backing store is required to be page aligned and the size is + * padded to the next page boundry. The number of pages is: + * (bytesPerLine * size.width * 4 + PAGE_SIZE - 1) / PAGE_SIZE + * + * The pitch in the backingStore is required to be at least large + * enough to hold a 32bbp scanline. It is recommended that the + * driver pad bytesPerLine for a potential performance win. + * + * The cloneCount field is treated as a hint from the guest that + * the user wants this display to be cloned, countCount times. A + * value of zero means no cloning should happen. */ -#define SVGA_SCREEN_HAS_ROOT (1 << 0) // Screen is present in the virtual coord space -#define SVGA_SCREEN_IS_PRIMARY (1 << 1) // Guest considers this screen to be 'primary' -#define SVGA_SCREEN_FULLSCREEN_HINT (1 << 2) // Guest is running a fullscreen app here +#define SVGA_SCREEN_MUST_BE_SET (1 << 0) /* Must be set or results undefined */ +#define SVGA_SCREEN_HAS_ROOT SVGA_SCREEN_MUST_BE_SET /* Deprecated */ +#define SVGA_SCREEN_IS_PRIMARY (1 << 1) /* Guest considers this screen to be 'primary' */ +#define SVGA_SCREEN_FULLSCREEN_HINT (1 << 2) /* Guest is running a fullscreen app here */ + +/* + * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2. When the screen is + * deactivated the base layer is defined to lose all contents and + * become black. When a screen is deactivated the backing store is + * optional. When set backingPtr and bytesPerLine will be ignored. + */ +#define SVGA_SCREEN_DEACTIVATE (1 << 3) + +/* + * Added with SVGA_FIFO_CAP_SCREEN_OBJECT_2. When this flag is set + * the screen contents will be outputted as all black to the user + * though the base layer contents is preserved. The screen base layer + * can still be read and written to like normal though the no visible + * effect will be seen by the user. When the flag is changed the + * screen will be blanked or redrawn to the current contents as needed + * without any extra commands from the driver. This flag only has an + * effect when the screen is not deactivated. + */ +#define SVGA_SCREEN_BLANKING (1 << 4) typedef struct SVGAScreenObject { - uint32 structSize; // sizeof(SVGAScreenObject) + uint32 structSize; /* sizeof(SVGAScreenObject) */ uint32 id; uint32 flags; struct { @@ -869,7 +976,14 @@ struct SVGAScreenObject { struct { int32 x; int32 y; - } root; // Only used if SVGA_SCREEN_HAS_ROOT is set. + } root; + + /* + * Added and required by SVGA_FIFO_CAP_SCREEN_OBJECT_2, optional + * with SVGA_FIFO_CAP_SCREEN_OBJECT. + */ + SVGAGuestImage backingStore; + uint32 cloneCount; } SVGAScreenObject; @@ -944,7 +1058,7 @@ typedef enum { */ typedef -struct { +struct SVGAFifoCmdUpdate { uint32 x; uint32 y; uint32 width; @@ -963,7 +1077,7 @@ struct { */ typedef -struct { +struct SVGAFifoCmdRectCopy { uint32 srcX; uint32 srcY; uint32 destX; @@ -987,14 +1101,14 @@ struct { */ typedef -struct { - uint32 id; // Reserved, must be zero. +struct SVGAFifoCmdDefineCursor { + uint32 id; /* Reserved, must be zero. */ uint32 hotspotX; uint32 hotspotY; uint32 width; uint32 height; - uint32 andMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL - uint32 xorMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL + uint32 andMaskDepth; /* Value must be 1 or equal to BITS_PER_PIXEL */ + uint32 xorMaskDepth; /* Value must be 1 or equal to BITS_PER_PIXEL */ /* * Followed by scanline data for AND mask, then XOR mask. * Each scanline is padded to a 32-bit boundary. @@ -1016,8 +1130,8 @@ struct { */ typedef -struct { - uint32 id; // Reserved, must be zero. +struct SVGAFifoCmdDefineAlphaCursor { + uint32 id; /* Reserved, must be zero. */ uint32 hotspotX; uint32 hotspotY; uint32 width; @@ -1039,7 +1153,7 @@ struct { */ typedef -struct { +struct SVGAFifoCmdUpdateVerbose { uint32 x; uint32 y; uint32 width; @@ -1064,13 +1178,13 @@ struct { #define SVGA_ROP_COPY 0x03 typedef -struct { - uint32 color; // In the same format as the GFB +struct SVGAFifoCmdFrontRopFill { + uint32 color; /* In the same format as the GFB */ uint32 x; uint32 y; uint32 width; uint32 height; - uint32 rop; // Must be SVGA_ROP_COPY + uint32 rop; /* Must be SVGA_ROP_COPY */ } SVGAFifoCmdFrontRopFill; @@ -1107,7 +1221,7 @@ struct { */ typedef -struct { +struct SVGAFifoCmdEscape { uint32 nsid; uint32 size; /* followed by 'size' bytes of data */ @@ -1137,12 +1251,12 @@ struct { * registers (SVGA_REG_NUM_GUEST_DISPLAYS, SVGA_REG_DISPLAY_*). * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef struct { - SVGAScreenObject screen; // Variable-length according to version + SVGAScreenObject screen; /* Variable-length according to version */ } SVGAFifoCmdDefineScreen; @@ -1153,7 +1267,7 @@ struct { * re-use. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1206,7 +1320,7 @@ struct { * GMRFB. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1243,7 +1357,7 @@ struct { * SVGA_CMD_ANNOTATION_* commands for details. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1291,7 +1405,7 @@ struct { * the time any subsequent FENCE commands are reached. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1326,7 +1440,7 @@ struct { * user's display is being remoted over a network connection. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1358,7 +1472,7 @@ struct { * undefined. * * Availability: - * SVGA_FIFO_CAP_SCREEN_OBJECT + * SVGA_FIFO_CAP_SCREEN_OBJECT or SVGA_FIFO_CAP_SCREEN_OBJECT_2 */ typedef @@ -1381,8 +1495,7 @@ typedef struct { uint32 gmrId; uint32 numPages; -} -SVGAFifoCmdDefineGMR2; +} SVGAFifoCmdDefineGMR2; /* @@ -1424,8 +1537,8 @@ typedef struct { uint32 gmrId; SVGARemapGMR2Flags flags; - uint32 offsetPages; /* offset in pages to begin remap */ - uint32 numPages; /* number of pages to remap */ + uint32 offsetPages; /* offset in pages to begin remap */ + uint32 numPages; /* number of pages to remap */ /* * Followed by additional data depending on SVGARemapGMR2Flags. * @@ -1434,7 +1547,6 @@ struct { * (according to flag SVGA_REMAP_GMR2_PPN64) follows. If flag * SVGA_REMAP_GMR2_SINGLE_PPN is set, array contains a single entry. */ -} -SVGAFifoCmdRemapGMR2; +} SVGAFifoCmdRemapGMR2; #endif diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c index 5d665ce8cbe4..5a72ed908232 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c @@ -42,6 +42,10 @@ static uint32_t sys_placement_flags = TTM_PL_FLAG_SYSTEM | static uint32_t gmr_placement_flags = VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED; +static uint32_t gmr_ne_placement_flags = VMW_PL_FLAG_GMR | + TTM_PL_FLAG_CACHED | + TTM_PL_FLAG_NO_EVICT; + struct ttm_placement vmw_vram_placement = { .fpfn = 0, .lpfn = 0, @@ -56,6 +60,11 @@ static uint32_t vram_gmr_placement_flags[] = { VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED }; +static uint32_t gmr_vram_placement_flags[] = { + VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED, + TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED +}; + struct ttm_placement vmw_vram_gmr_placement = { .fpfn = 0, .lpfn = 0, @@ -65,6 +74,20 @@ struct ttm_placement vmw_vram_gmr_placement = { .busy_placement = &gmr_placement_flags }; +static uint32_t vram_gmr_ne_placement_flags[] = { + TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT, + VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED | TTM_PL_FLAG_NO_EVICT +}; + +struct ttm_placement vmw_vram_gmr_ne_placement = { + .fpfn = 0, + .lpfn = 0, + .num_placement = 2, + .placement = vram_gmr_ne_placement_flags, + .num_busy_placement = 1, + .busy_placement = &gmr_ne_placement_flags +}; + struct ttm_placement vmw_vram_sys_placement = { .fpfn = 0, .lpfn = 0, @@ -92,6 +115,30 @@ struct ttm_placement vmw_sys_placement = { .busy_placement = &sys_placement_flags }; +static uint32_t evictable_placement_flags[] = { + TTM_PL_FLAG_SYSTEM | TTM_PL_FLAG_CACHED, + TTM_PL_FLAG_VRAM | TTM_PL_FLAG_CACHED, + VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED +}; + +struct ttm_placement vmw_evictable_placement = { + .fpfn = 0, + .lpfn = 0, + .num_placement = 3, + .placement = evictable_placement_flags, + .num_busy_placement = 1, + .busy_placement = &sys_placement_flags +}; + +struct ttm_placement vmw_srf_placement = { + .fpfn = 0, + .lpfn = 0, + .num_placement = 1, + .num_busy_placement = 2, + .placement = &gmr_placement_flags, + .busy_placement = gmr_vram_placement_flags +}; + struct vmw_ttm_backend { struct ttm_backend backend; struct page **pages; diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c new file mode 100644 index 000000000000..3fa884db08ab --- /dev/null +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c @@ -0,0 +1,322 @@ +/************************************************************************** + * + * Copyright © 2011 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + **************************************************************************/ + +#include "ttm/ttm_placement.h" + +#include "drmP.h" +#include "vmwgfx_drv.h" + + +/** + * vmw_dmabuf_to_placement - Validate a buffer to placement. + * + * @dev_priv: Driver private. + * @buf: DMA buffer to move. + * @pin: Pin buffer if true. + * @interruptible: Use interruptible wait. + * + * May only be called by the current master since it assumes that the + * master lock is the current master's lock. + * This function takes the master's lock in write mode. + * Flushes and unpins the query bo to avoid failures. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +int vmw_dmabuf_to_placement(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + struct ttm_placement *placement, + bool interruptible) +{ + struct vmw_master *vmaster = dev_priv->active_master; + struct ttm_buffer_object *bo = &buf->base; + int ret; + + ret = ttm_write_lock(&vmaster->lock, interruptible); + if (unlikely(ret != 0)) + return ret; + + vmw_execbuf_release_pinned_bo(dev_priv, false, 0); + + ret = ttm_bo_reserve(bo, interruptible, false, false, 0); + if (unlikely(ret != 0)) + goto err; + + ret = ttm_bo_validate(bo, placement, interruptible, false, false); + + ttm_bo_unreserve(bo); + +err: + ttm_write_unlock(&vmaster->lock); + return ret; +} + +/** + * vmw_dmabuf_to_vram_or_gmr - Move a buffer to vram or gmr. + * + * May only be called by the current master since it assumes that the + * master lock is the current master's lock. + * This function takes the master's lock in write mode. + * Flushes and unpins the query bo if @pin == true to avoid failures. + * + * @dev_priv: Driver private. + * @buf: DMA buffer to move. + * @pin: Pin buffer if true. + * @interruptible: Use interruptible wait. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + bool pin, bool interruptible) +{ + struct vmw_master *vmaster = dev_priv->active_master; + struct ttm_buffer_object *bo = &buf->base; + struct ttm_placement *placement; + int ret; + + ret = ttm_write_lock(&vmaster->lock, interruptible); + if (unlikely(ret != 0)) + return ret; + + if (pin) + vmw_execbuf_release_pinned_bo(dev_priv, false, 0); + + ret = ttm_bo_reserve(bo, interruptible, false, false, 0); + if (unlikely(ret != 0)) + goto err; + + /** + * Put BO in VRAM if there is space, otherwise as a GMR. + * If there is no space in VRAM and GMR ids are all used up, + * start evicting GMRs to make room. If the DMA buffer can't be + * used as a GMR, this will return -ENOMEM. + */ + + if (pin) + placement = &vmw_vram_gmr_ne_placement; + else + placement = &vmw_vram_gmr_placement; + + ret = ttm_bo_validate(bo, placement, interruptible, false, false); + if (likely(ret == 0) || ret == -ERESTARTSYS) + goto err_unreserve; + + + /** + * If that failed, try VRAM again, this time evicting + * previous contents. + */ + + if (pin) + placement = &vmw_vram_ne_placement; + else + placement = &vmw_vram_placement; + + ret = ttm_bo_validate(bo, placement, interruptible, false, false); + +err_unreserve: + ttm_bo_unreserve(bo); +err: + ttm_write_unlock(&vmaster->lock); + return ret; +} + +/** + * vmw_dmabuf_to_vram - Move a buffer to vram. + * + * May only be called by the current master since it assumes that the + * master lock is the current master's lock. + * This function takes the master's lock in write mode. + * + * @dev_priv: Driver private. + * @buf: DMA buffer to move. + * @pin: Pin buffer in vram if true. + * @interruptible: Use interruptible wait. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +int vmw_dmabuf_to_vram(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + bool pin, bool interruptible) +{ + struct ttm_placement *placement; + + if (pin) + placement = &vmw_vram_ne_placement; + else + placement = &vmw_vram_placement; + + return vmw_dmabuf_to_placement(dev_priv, buf, + placement, + interruptible); +} + +/** + * vmw_dmabuf_to_start_of_vram - Move a buffer to start of vram. + * + * May only be called by the current master since it assumes that the + * master lock is the current master's lock. + * This function takes the master's lock in write mode. + * Flushes and unpins the query bo if @pin == true to avoid failures. + * + * @dev_priv: Driver private. + * @buf: DMA buffer to move. + * @pin: Pin buffer in vram if true. + * @interruptible: Use interruptible wait. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + bool pin, bool interruptible) +{ + struct vmw_master *vmaster = dev_priv->active_master; + struct ttm_buffer_object *bo = &buf->base; + struct ttm_placement placement; + int ret = 0; + + if (pin) + placement = vmw_vram_ne_placement; + else + placement = vmw_vram_placement; + placement.lpfn = bo->num_pages; + + ret = ttm_write_lock(&vmaster->lock, interruptible); + if (unlikely(ret != 0)) + return ret; + + if (pin) + vmw_execbuf_release_pinned_bo(dev_priv, false, 0); + + ret = ttm_bo_reserve(bo, interruptible, false, false, 0); + if (unlikely(ret != 0)) + goto err_unlock; + + /* Is this buffer already in vram but not at the start of it? */ + if (bo->mem.mem_type == TTM_PL_VRAM && + bo->mem.start < bo->num_pages && + bo->mem.start > 0) + (void) ttm_bo_validate(bo, &vmw_sys_placement, false, + false, false); + + ret = ttm_bo_validate(bo, &placement, interruptible, false, false); + + /* For some reason we didn't up at the start of vram */ + WARN_ON(ret == 0 && bo->offset != 0); + + ttm_bo_unreserve(bo); +err_unlock: + ttm_write_unlock(&vmaster->lock); + + return ret; +} + + +/** + * vmw_dmabuf_upin - Unpin the buffer given buffer, does not move the buffer. + * + * May only be called by the current master since it assumes that the + * master lock is the current master's lock. + * This function takes the master's lock in write mode. + * + * @dev_priv: Driver private. + * @buf: DMA buffer to unpin. + * @interruptible: Use interruptible wait. + * + * Returns + * -ERESTARTSYS if interrupted by a signal. + */ +int vmw_dmabuf_unpin(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + bool interruptible) +{ + /* + * We could in theory early out if the buffer is + * unpinned but we need to lock and reserve the buffer + * anyways so we don't gain much by that. + */ + return vmw_dmabuf_to_placement(dev_priv, buf, + &vmw_evictable_placement, + interruptible); +} + + +/** + * vmw_bo_get_guest_ptr - Get the guest ptr representing the current placement + * of a buffer. + * + * @bo: Pointer to a struct ttm_buffer_object. Must be pinned or reserved. + * @ptr: SVGAGuestPtr returning the result. + */ +void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *bo, + SVGAGuestPtr *ptr) +{ + if (bo->mem.mem_type == TTM_PL_VRAM) { + ptr->gmrId = SVGA_GMR_FRAMEBUFFER; + ptr->offset = bo->offset; + } else { + ptr->gmrId = bo->mem.start; + ptr->offset = 0; + } +} + + +/** + * vmw_bo_pin - Pin or unpin a buffer object without moving it. + * + * @bo: The buffer object. Must be reserved, and present either in VRAM + * or GMR memory. + * @pin: Whether to pin or unpin. + * + */ +void vmw_bo_pin(struct ttm_buffer_object *bo, bool pin) +{ + uint32_t pl_flags; + struct ttm_placement placement; + uint32_t old_mem_type = bo->mem.mem_type; + int ret; + + BUG_ON(!atomic_read(&bo->reserved)); + BUG_ON(old_mem_type != TTM_PL_VRAM && + old_mem_type != VMW_PL_FLAG_GMR); + + pl_flags = TTM_PL_FLAG_VRAM | VMW_PL_FLAG_GMR | TTM_PL_FLAG_CACHED; + if (pin) + pl_flags |= TTM_PL_FLAG_NO_EVICT; + + memset(&placement, 0, sizeof(placement)); + placement.num_placement = 1; + placement.placement = &pl_flags; + + ret = ttm_bo_validate(bo, &placement, false, true, true); + + BUG_ON(ret != 0 || bo->mem.mem_type != old_mem_type); +} diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index d4829cbf326d..e07dcf40a3ba 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -94,6 +94,15 @@ #define DRM_IOCTL_VMW_FENCE_UNREF \ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_FENCE_UNREF, \ struct drm_vmw_fence_arg) +#define DRM_IOCTL_VMW_FENCE_EVENT \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_FENCE_EVENT, \ + struct drm_vmw_fence_event_arg) +#define DRM_IOCTL_VMW_PRESENT \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_PRESENT, \ + struct drm_vmw_present_arg) +#define DRM_IOCTL_VMW_PRESENT_READBACK \ + DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_PRESENT_READBACK, \ + struct drm_vmw_present_readback_arg) /** * The core DRM version of this macro doesn't account for @@ -144,8 +153,18 @@ static struct drm_ioctl_desc vmw_ioctls[] = { DRM_AUTH | DRM_UNLOCKED), VMW_IOCTL_DEF(VMW_FENCE_UNREF, vmw_fence_obj_unref_ioctl, DRM_AUTH | DRM_UNLOCKED), + VMW_IOCTL_DEF(VMW_FENCE_EVENT, + vmw_fence_event_ioctl, + DRM_AUTH | DRM_UNLOCKED), VMW_IOCTL_DEF(VMW_GET_3D_CAP, vmw_get_cap_3d_ioctl, DRM_AUTH | DRM_UNLOCKED), + + /* these allow direct access to the framebuffers mark as master only */ + VMW_IOCTL_DEF(VMW_PRESENT, vmw_present_ioctl, + DRM_MASTER | DRM_AUTH | DRM_UNLOCKED), + VMW_IOCTL_DEF(VMW_PRESENT_READBACK, + vmw_present_readback_ioctl, + DRM_MASTER | DRM_AUTH | DRM_UNLOCKED), }; static struct pci_device_id vmw_pci_id_list[] = { @@ -200,6 +219,72 @@ static void vmw_print_capabilities(uint32_t capabilities) DRM_INFO(" Screen Object 2.\n"); } + +/** + * vmw_execbuf_prepare_dummy_query - Initialize a query result structure at + * the start of a buffer object. + * + * @dev_priv: The device private structure. + * + * This function will idle the buffer using an uninterruptible wait, then + * map the first page and initialize a pending occlusion query result structure, + * Finally it will unmap the buffer. + * + * TODO: Since we're only mapping a single page, we should optimize the map + * to use kmap_atomic / iomap_atomic. + */ +static void vmw_dummy_query_bo_prepare(struct vmw_private *dev_priv) +{ + struct ttm_bo_kmap_obj map; + volatile SVGA3dQueryResult *result; + bool dummy; + int ret; + struct ttm_bo_device *bdev = &dev_priv->bdev; + struct ttm_buffer_object *bo = dev_priv->dummy_query_bo; + + ttm_bo_reserve(bo, false, false, false, 0); + spin_lock(&bdev->fence_lock); + ret = ttm_bo_wait(bo, false, false, false, TTM_USAGE_READWRITE); + spin_unlock(&bdev->fence_lock); + if (unlikely(ret != 0)) + (void) vmw_fallback_wait(dev_priv, false, true, 0, false, + 10*HZ); + + ret = ttm_bo_kmap(bo, 0, 1, &map); + if (likely(ret == 0)) { + result = ttm_kmap_obj_virtual(&map, &dummy); + result->totalSize = sizeof(*result); + result->state = SVGA3D_QUERYSTATE_PENDING; + result->result32 = 0xff; + ttm_bo_kunmap(&map); + } else + DRM_ERROR("Dummy query buffer map failed.\n"); + ttm_bo_unreserve(bo); +} + + +/** + * vmw_dummy_query_bo_create - create a bo to hold a dummy query result + * + * @dev_priv: A device private structure. + * + * This function creates a small buffer object that holds the query + * result for dummy queries emitted as query barriers. + * No interruptible waits are done within this function. + * + * Returns an error if bo creation fails. + */ +static int vmw_dummy_query_bo_create(struct vmw_private *dev_priv) +{ + return ttm_bo_create(&dev_priv->bdev, + PAGE_SIZE, + ttm_bo_type_device, + &vmw_vram_sys_placement, + 0, 0, false, NULL, + &dev_priv->dummy_query_bo); +} + + static int vmw_request_device(struct vmw_private *dev_priv) { int ret; @@ -210,12 +295,29 @@ static int vmw_request_device(struct vmw_private *dev_priv) return ret; } vmw_fence_fifo_up(dev_priv->fman); + ret = vmw_dummy_query_bo_create(dev_priv); + if (unlikely(ret != 0)) + goto out_no_query_bo; + vmw_dummy_query_bo_prepare(dev_priv); return 0; + +out_no_query_bo: + vmw_fence_fifo_down(dev_priv->fman); + vmw_fifo_release(dev_priv, &dev_priv->fifo); + return ret; } static void vmw_release_device(struct vmw_private *dev_priv) { + /* + * Previous destructions should've released + * the pinned bo. + */ + + BUG_ON(dev_priv->pinned_bo != NULL); + + ttm_bo_unref(&dev_priv->dummy_query_bo); vmw_fence_fifo_down(dev_priv->fman); vmw_fifo_release(dev_priv, &dev_priv->fifo); } @@ -306,6 +408,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) init_waitqueue_head(&dev_priv->fifo_queue); dev_priv->fence_queue_waiters = 0; atomic_set(&dev_priv->fifo_queue_waiters, 0); + INIT_LIST_HEAD(&dev_priv->surface_lru); + dev_priv->used_memory_size = 0; dev_priv->io_start = pci_resource_start(dev->pdev, 0); dev_priv->vram_start = pci_resource_start(dev->pdev, 1); @@ -326,6 +430,10 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES); + dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); + dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE); + dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH); + dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT); if (dev_priv->capabilities & SVGA_CAP_GMR) { dev_priv->max_gmr_descriptors = vmw_read(dev_priv, @@ -338,13 +446,15 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) vmw_read(dev_priv, SVGA_REG_GMRS_MAX_PAGES); dev_priv->memory_size = vmw_read(dev_priv, SVGA_REG_MEMORY_SIZE); + dev_priv->memory_size -= dev_priv->vram_size; + } else { + /* + * An arbitrary limit of 512MiB on surface + * memory. But all HWV8 hardware supports GMR2. + */ + dev_priv->memory_size = 512*1024*1024; } - dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE); - dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE); - dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH); - dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT); - mutex_unlock(&dev_priv->hw_mutex); vmw_print_capabilities(dev_priv->capabilities); @@ -358,8 +468,8 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) if (dev_priv->capabilities & SVGA_CAP_GMR2) { DRM_INFO("Max number of GMR pages is %u\n", (unsigned)dev_priv->max_gmr_pages); - DRM_INFO("Max dedicated hypervisor graphics memory is %u\n", - (unsigned)dev_priv->memory_size); + DRM_INFO("Max dedicated hypervisor surface memory is %u kiB\n", + (unsigned)dev_priv->memory_size / 1024); } DRM_INFO("VRAM at 0x%08x size is %u kiB\n", dev_priv->vram_start, dev_priv->vram_size / 1024); @@ -451,22 +561,30 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) dev_priv->fman = vmw_fence_manager_init(dev_priv); if (unlikely(dev_priv->fman == NULL)) goto out_no_fman; + + /* Need to start the fifo to check if we can do screen objects */ + ret = vmw_3d_resource_inc(dev_priv, true); + if (unlikely(ret != 0)) + goto out_no_fifo; + vmw_kms_save_vga(dev_priv); + + /* Start kms and overlay systems, needs fifo. */ ret = vmw_kms_init(dev_priv); if (unlikely(ret != 0)) goto out_no_kms; vmw_overlay_init(dev_priv); + + /* 3D Depends on Screen Objects being used. */ + DRM_INFO("Detected %sdevice 3D availability.\n", + vmw_fifo_have_3d(dev_priv) ? + "" : "no "); + + /* We might be done with the fifo now */ if (dev_priv->enable_fb) { - ret = vmw_3d_resource_inc(dev_priv, false); - if (unlikely(ret != 0)) - goto out_no_fifo; - vmw_kms_save_vga(dev_priv); vmw_fb_init(dev_priv); - DRM_INFO("%s", vmw_fifo_have_3d(dev_priv) ? - "Detected device 3D availability.\n" : - "Detected no device 3D availability.\n"); } else { - DRM_INFO("Delayed 3D detection since we're not " - "running the device in SVGA mode yet.\n"); + vmw_kms_restore_vga(dev_priv); + vmw_3d_resource_dec(dev_priv, true); } if (dev_priv->capabilities & SVGA_CAP_IRQMASK) { @@ -483,15 +601,17 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) return 0; out_no_irq: - if (dev_priv->enable_fb) { + if (dev_priv->enable_fb) vmw_fb_close(dev_priv); + vmw_overlay_close(dev_priv); + vmw_kms_close(dev_priv); +out_no_kms: + /* We still have a 3D resource reference held */ + if (dev_priv->enable_fb) { vmw_kms_restore_vga(dev_priv); vmw_3d_resource_dec(dev_priv, false); } out_no_fifo: - vmw_overlay_close(dev_priv); - vmw_kms_close(dev_priv); -out_no_kms: vmw_fence_manager_takedown(dev_priv->fman); out_no_fman: if (dev_priv->stealth) @@ -771,7 +891,7 @@ static void vmw_master_drop(struct drm_device *dev, vmw_fp->locked_master = drm_master_get(file_priv->master); ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile); - vmw_kms_idle_workqueues(vmaster); + vmw_execbuf_release_pinned_bo(dev_priv, false, 0); if (unlikely((ret != 0))) { DRM_ERROR("Unable to lock TTM at VT switch.\n"); @@ -823,6 +943,7 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, * This empties VRAM and unbinds all GMR bindings. * Buffer contents is moved to swappable memory. */ + vmw_execbuf_release_pinned_bo(dev_priv, false, 0); ttm_bo_swapout_all(&dev_priv->bdev); break; @@ -964,7 +1085,8 @@ static struct drm_driver driver = { .release = drm_release, .unlocked_ioctl = vmw_unlocked_ioctl, .mmap = vmw_mmap, - .poll = drm_poll, + .poll = vmw_fops_poll, + .read = vmw_fops_read, .fasync = drm_fasync, #if defined(CONFIG_COMPAT) .compat_ioctl = drm_compat_ioctl, diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 564a81582111..0e9b2cefaa9d 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -40,9 +40,9 @@ #include "ttm/ttm_module.h" #include "vmwgfx_fence.h" -#define VMWGFX_DRIVER_DATE "20110901" +#define VMWGFX_DRIVER_DATE "20111008" #define VMWGFX_DRIVER_MAJOR 2 -#define VMWGFX_DRIVER_MINOR 0 +#define VMWGFX_DRIVER_MINOR 2 #define VMWGFX_DRIVER_PATCHLEVEL 0 #define VMWGFX_FILE_PAGE_OFFSET 0x00100000 #define VMWGFX_FIFO_STATIC_SIZE (1024*1024) @@ -79,9 +79,11 @@ struct vmw_resource { int id; enum ttm_object_type res_type; bool avail; + void (*remove_from_lists) (struct vmw_resource *res); void (*hw_destroy) (struct vmw_resource *res); void (*res_free) (struct vmw_resource *res); - bool on_validate_list; + struct list_head validate_head; + struct list_head query_head; /* Protected by the cmdbuf mutex */ /* TODO is a generic snooper needed? */ #if 0 void (*snoop)(struct vmw_resource *res, @@ -97,8 +99,12 @@ struct vmw_cursor_snooper { uint32_t *image; }; +struct vmw_framebuffer; +struct vmw_surface_offset; + struct vmw_surface { struct vmw_resource res; + struct list_head lru_head; /* Protected by the resource lock */ uint32_t flags; uint32_t format; uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES]; @@ -109,6 +115,9 @@ struct vmw_surface { /* TODO so far just a extra pointer */ struct vmw_cursor_snooper snooper; + struct ttm_buffer_object *backup; + struct vmw_surface_offset *offsets; + uint32_t backup_size; }; struct vmw_marker_queue { @@ -139,6 +148,8 @@ struct vmw_sw_context{ struct ida bo_list; uint32_t last_cid; bool cid_valid; + bool kernel; /**< is the called made from the kernel */ + struct vmw_resource *cur_ctx; uint32_t last_sid; uint32_t sid_translation; bool sid_valid; @@ -150,8 +161,12 @@ struct vmw_sw_context{ uint32_t cur_val_buf; uint32_t *cmd_bounce; uint32_t cmd_bounce_size; - struct vmw_resource *resources[VMWGFX_MAX_VALIDATIONS]; - uint32_t num_ref_resources; + struct list_head resource_list; + uint32_t fence_flags; + struct list_head query_list; + struct ttm_buffer_object *cur_query_bo; + uint32_t cur_query_cid; + bool query_cid_valid; }; struct vmw_legacy_display; @@ -216,6 +231,7 @@ struct vmw_private { void *fb_info; struct vmw_legacy_display *ldu_priv; + struct vmw_screen_object_display *sou_priv; struct vmw_overlay *overlay_priv; /* @@ -248,10 +264,12 @@ struct vmw_private { wait_queue_head_t fence_queue; wait_queue_head_t fifo_queue; int fence_queue_waiters; /* Protected by hw_mutex */ + int goal_queue_waiters; /* Protected by hw_mutex */ atomic_t fifo_queue_waiters; uint32_t last_read_seqno; spinlock_t irq_lock; struct vmw_fence_manager *fman; + uint32_t irq_mask; /* * Device state @@ -290,6 +308,26 @@ struct vmw_private { struct mutex release_mutex; uint32_t num_3d_resources; + + /* + * Query processing. These members + * are protected by the cmdbuf mutex. + */ + + struct ttm_buffer_object *dummy_query_bo; + struct ttm_buffer_object *pinned_bo; + uint32_t query_cid; + bool dummy_query_bo_pinned; + + /* + * Surface swapping. The "surface_lru" list is protected by the + * resource lock in order to be able to destroy a surface and take + * it off the lru atomically. "used_memory_size" is currently + * protected by the cmdbuf mutex for simplicity. + */ + + struct list_head surface_lru; + uint32_t used_memory_size; }; static inline struct vmw_private *vmw_priv(struct drm_device *dev) @@ -369,6 +407,8 @@ extern int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, extern int vmw_surface_check(struct vmw_private *dev_priv, struct ttm_object_file *tfile, uint32_t handle, int *id); +extern int vmw_surface_validate(struct vmw_private *dev_priv, + struct vmw_surface *srf); extern void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo); extern int vmw_dmabuf_init(struct vmw_private *dev_priv, struct vmw_dma_buffer *vmw_bo, @@ -384,10 +424,6 @@ extern uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo, extern void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo); extern int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, uint32_t id, struct vmw_dma_buffer **out); -extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, - struct vmw_dma_buffer *bo); -extern int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv, - struct vmw_dma_buffer *bo); extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, @@ -396,7 +432,30 @@ extern int vmw_user_stream_lookup(struct vmw_private *dev_priv, struct ttm_object_file *tfile, uint32_t *inout_id, struct vmw_resource **out); +extern void vmw_resource_unreserve(struct list_head *list); +/** + * DMA buffer helper routines - vmwgfx_dmabuf.c + */ +extern int vmw_dmabuf_to_placement(struct vmw_private *vmw_priv, + struct vmw_dma_buffer *bo, + struct ttm_placement *placement, + bool interruptible); +extern int vmw_dmabuf_to_vram(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + bool pin, bool interruptible); +extern int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + bool pin, bool interruptible); +extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, + struct vmw_dma_buffer *bo, + bool pin, bool interruptible); +extern int vmw_dmabuf_unpin(struct vmw_private *vmw_priv, + struct vmw_dma_buffer *bo, + bool interruptible); +extern void vmw_bo_get_guest_ptr(const struct ttm_buffer_object *buf, + SVGAGuestPtr *ptr); +extern void vmw_bo_pin(struct ttm_buffer_object *bo, bool pin); /** * Misc Ioctl functionality - vmwgfx_ioctl.c @@ -406,6 +465,14 @@ extern int vmw_getparam_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int vmw_present_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern int vmw_present_readback_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); +extern unsigned int vmw_fops_poll(struct file *filp, + struct poll_table_struct *wait); +extern ssize_t vmw_fops_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset); /** * Fifo utilities - vmwgfx_fifo.c @@ -422,6 +489,8 @@ extern int vmw_fifo_send_fence(struct vmw_private *dev_priv, extern void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason); extern bool vmw_fifo_have_3d(struct vmw_private *dev_priv); extern bool vmw_fifo_have_pitchlock(struct vmw_private *dev_priv); +extern int vmw_fifo_emit_dummy_query(struct vmw_private *dev_priv, + uint32_t cid); /** * TTM glue - vmwgfx_ttm_glue.c @@ -439,7 +508,10 @@ extern struct ttm_placement vmw_vram_placement; extern struct ttm_placement vmw_vram_ne_placement; extern struct ttm_placement vmw_vram_sys_placement; extern struct ttm_placement vmw_vram_gmr_placement; +extern struct ttm_placement vmw_vram_gmr_ne_placement; extern struct ttm_placement vmw_sys_placement; +extern struct ttm_placement vmw_evictable_placement; +extern struct ttm_placement vmw_srf_placement; extern struct ttm_bo_driver vmw_bo_driver; extern int vmw_dma_quiescent(struct drm_device *dev); @@ -449,6 +521,30 @@ extern int vmw_dma_quiescent(struct drm_device *dev); extern int vmw_execbuf_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int vmw_execbuf_process(struct drm_file *file_priv, + struct vmw_private *dev_priv, + void __user *user_commands, + void *kernel_commands, + uint32_t command_size, + uint64_t throttle_us, + struct drm_vmw_fence_rep __user + *user_fence_rep); + +extern void +vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, + bool only_on_cid_match, uint32_t cid); + +extern int vmw_execbuf_fence_commands(struct drm_file *file_priv, + struct vmw_private *dev_priv, + struct vmw_fence_obj **p_fence, + uint32_t *p_handle); +extern void vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, + struct vmw_fpriv *vmw_fp, + int ret, + struct drm_vmw_fence_rep __user + *user_fence_rep, + struct vmw_fence_obj *fence, + uint32_t fence_handle); /** * IRQs and wating - vmwgfx_irq.c @@ -473,6 +569,8 @@ extern void vmw_update_seqno(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo_state); extern void vmw_seqno_waiter_add(struct vmw_private *dev_priv); extern void vmw_seqno_waiter_remove(struct vmw_private *dev_priv); +extern void vmw_goal_waiter_add(struct vmw_private *dev_priv); +extern void vmw_goal_waiter_remove(struct vmw_private *dev_priv); /** * Rudimentary fence-like objects currently used only for throttling - @@ -520,6 +618,19 @@ bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv, uint32_t pitch, uint32_t height); u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc); +int vmw_kms_present(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *vfb, + struct vmw_surface *surface, + uint32_t sid, int32_t destX, int32_t destY, + struct drm_vmw_rect *clips, + uint32_t num_clips); +int vmw_kms_readback(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *vfb, + struct drm_vmw_fence_rep __user *user_fence_rep, + struct drm_vmw_rect *clips, + uint32_t num_clips); /** * Overlay control - vmwgfx_overlay.c diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index fa26e647f488..d4a1d8b06336 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -44,28 +44,64 @@ static int vmw_cmd_ok(struct vmw_private *dev_priv, return 0; } - -static int vmw_resource_to_validate_list(struct vmw_sw_context *sw_context, - struct vmw_resource **p_res) +static void vmw_resource_to_validate_list(struct vmw_sw_context *sw_context, + struct vmw_resource **p_res) { - int ret = 0; struct vmw_resource *res = *p_res; - if (!res->on_validate_list) { - if (sw_context->num_ref_resources >= VMWGFX_MAX_VALIDATIONS) { - DRM_ERROR("Too many resources referenced in " - "command stream.\n"); - ret = -ENOMEM; - goto out; - } - sw_context->resources[sw_context->num_ref_resources++] = res; - res->on_validate_list = true; - return 0; + if (list_empty(&res->validate_head)) { + list_add_tail(&res->validate_head, &sw_context->resource_list); + *p_res = NULL; + } else + vmw_resource_unreference(p_res); +} + +/** + * vmw_bo_to_validate_list - add a bo to a validate list + * + * @sw_context: The software context used for this command submission batch. + * @bo: The buffer object to add. + * @fence_flags: Fence flags to be or'ed with any other fence flags for + * this buffer on this submission batch. + * @p_val_node: If non-NULL Will be updated with the validate node number + * on return. + * + * Returns -EINVAL if the limit of number of buffer objects per command + * submission is reached. + */ +static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context, + struct ttm_buffer_object *bo, + uint32_t fence_flags, + uint32_t *p_val_node) +{ + uint32_t val_node; + struct ttm_validate_buffer *val_buf; + + val_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf); + + if (unlikely(val_node >= VMWGFX_MAX_VALIDATIONS)) { + DRM_ERROR("Max number of DMA buffers per submission" + " exceeded.\n"); + return -EINVAL; } -out: - vmw_resource_unreference(p_res); - return ret; + val_buf = &sw_context->val_bufs[val_node]; + if (unlikely(val_node == sw_context->cur_val_buf)) { + val_buf->new_sync_obj_arg = NULL; + val_buf->bo = ttm_bo_reference(bo); + val_buf->usage = TTM_USAGE_READWRITE; + list_add_tail(&val_buf->head, &sw_context->validate_nodes); + ++sw_context->cur_val_buf; + } + + val_buf->new_sync_obj_arg = (void *) + ((unsigned long) val_buf->new_sync_obj_arg | fence_flags); + sw_context->fence_flags |= fence_flags; + + if (p_val_node) + *p_val_node = val_node; + + return 0; } static int vmw_cmd_cid_check(struct vmw_private *dev_priv, @@ -94,7 +130,10 @@ static int vmw_cmd_cid_check(struct vmw_private *dev_priv, sw_context->last_cid = cmd->cid; sw_context->cid_valid = true; - return vmw_resource_to_validate_list(sw_context, &ctx); + sw_context->cur_ctx = ctx; + vmw_resource_to_validate_list(sw_context, &ctx); + + return 0; } static int vmw_cmd_sid_check(struct vmw_private *dev_priv, @@ -114,7 +153,8 @@ static int vmw_cmd_sid_check(struct vmw_private *dev_priv, return 0; } - ret = vmw_user_surface_lookup_handle(dev_priv, sw_context->tfile, + ret = vmw_user_surface_lookup_handle(dev_priv, + sw_context->tfile, *sid, &srf); if (unlikely(ret != 0)) { DRM_ERROR("Could ot find or use surface 0x%08x " @@ -124,13 +164,23 @@ static int vmw_cmd_sid_check(struct vmw_private *dev_priv, return ret; } + ret = vmw_surface_validate(dev_priv, srf); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Could not validate surface.\n"); + vmw_surface_unreference(&srf); + return ret; + } + sw_context->last_sid = *sid; sw_context->sid_valid = true; sw_context->sid_translation = srf->res.id; *sid = sw_context->sid_translation; res = &srf->res; - return vmw_resource_to_validate_list(sw_context, &res); + vmw_resource_to_validate_list(sw_context, &res); + + return 0; } @@ -197,6 +247,12 @@ static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv, } *cmd; cmd = container_of(header, struct vmw_sid_cmd, header); + + if (unlikely(!sw_context->kernel)) { + DRM_ERROR("Kernel only SVGA3d command: %u.\n", cmd->header.id); + return -EPERM; + } + return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.srcImage.sid); } @@ -209,10 +265,179 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv, SVGA3dCmdPresent body; } *cmd; + cmd = container_of(header, struct vmw_sid_cmd, header); + + if (unlikely(!sw_context->kernel)) { + DRM_ERROR("Kernel only SVGA3d command: %u.\n", cmd->header.id); + return -EPERM; + } + return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid); } +/** + * vmw_query_bo_switch_prepare - Prepare to switch pinned buffer for queries. + * + * @dev_priv: The device private structure. + * @cid: The hardware context for the next query. + * @new_query_bo: The new buffer holding query results. + * @sw_context: The software context used for this command submission. + * + * This function checks whether @new_query_bo is suitable for holding + * query results, and if another buffer currently is pinned for query + * results. If so, the function prepares the state of @sw_context for + * switching pinned buffers after successful submission of the current + * command batch. It also checks whether we're using a new query context. + * In that case, it makes sure we emit a query barrier for the old + * context before the current query buffer is fenced. + */ +static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv, + uint32_t cid, + struct ttm_buffer_object *new_query_bo, + struct vmw_sw_context *sw_context) +{ + int ret; + bool add_cid = false; + uint32_t cid_to_add; + + if (unlikely(new_query_bo != sw_context->cur_query_bo)) { + + if (unlikely(new_query_bo->num_pages > 4)) { + DRM_ERROR("Query buffer too large.\n"); + return -EINVAL; + } + + if (unlikely(sw_context->cur_query_bo != NULL)) { + BUG_ON(!sw_context->query_cid_valid); + add_cid = true; + cid_to_add = sw_context->cur_query_cid; + ret = vmw_bo_to_validate_list(sw_context, + sw_context->cur_query_bo, + DRM_VMW_FENCE_FLAG_EXEC, + NULL); + if (unlikely(ret != 0)) + return ret; + } + sw_context->cur_query_bo = new_query_bo; + + ret = vmw_bo_to_validate_list(sw_context, + dev_priv->dummy_query_bo, + DRM_VMW_FENCE_FLAG_EXEC, + NULL); + if (unlikely(ret != 0)) + return ret; + + } + + if (unlikely(cid != sw_context->cur_query_cid && + sw_context->query_cid_valid)) { + add_cid = true; + cid_to_add = sw_context->cur_query_cid; + } + + sw_context->cur_query_cid = cid; + sw_context->query_cid_valid = true; + + if (add_cid) { + struct vmw_resource *ctx = sw_context->cur_ctx; + + if (list_empty(&ctx->query_head)) + list_add_tail(&ctx->query_head, + &sw_context->query_list); + ret = vmw_bo_to_validate_list(sw_context, + dev_priv->dummy_query_bo, + DRM_VMW_FENCE_FLAG_EXEC, + NULL); + if (unlikely(ret != 0)) + return ret; + } + return 0; +} + + +/** + * vmw_query_bo_switch_commit - Finalize switching pinned query buffer + * + * @dev_priv: The device private structure. + * @sw_context: The software context used for this command submission batch. + * + * This function will check if we're switching query buffers, and will then, + * if no other query waits are issued this command submission batch, + * issue a dummy occlusion query wait used as a query barrier. When the fence + * object following that query wait has signaled, we are sure that all + * preseding queries have finished, and the old query buffer can be unpinned. + * However, since both the new query buffer and the old one are fenced with + * that fence, we can do an asynchronus unpin now, and be sure that the + * old query buffer won't be moved until the fence has signaled. + * + * As mentioned above, both the new - and old query buffers need to be fenced + * using a sequence emitted *after* calling this function. + */ +static void vmw_query_bo_switch_commit(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context) +{ + + struct vmw_resource *ctx, *next_ctx; + int ret; + + /* + * The validate list should still hold references to all + * contexts here. + */ + + list_for_each_entry_safe(ctx, next_ctx, &sw_context->query_list, + query_head) { + list_del_init(&ctx->query_head); + + BUG_ON(list_empty(&ctx->validate_head)); + + ret = vmw_fifo_emit_dummy_query(dev_priv, ctx->id); + + if (unlikely(ret != 0)) + DRM_ERROR("Out of fifo space for dummy query.\n"); + } + + if (dev_priv->pinned_bo != sw_context->cur_query_bo) { + if (dev_priv->pinned_bo) { + vmw_bo_pin(dev_priv->pinned_bo, false); + ttm_bo_unref(&dev_priv->pinned_bo); + } + + vmw_bo_pin(sw_context->cur_query_bo, true); + + /* + * We pin also the dummy_query_bo buffer so that we + * don't need to validate it when emitting + * dummy queries in context destroy paths. + */ + + vmw_bo_pin(dev_priv->dummy_query_bo, true); + dev_priv->dummy_query_bo_pinned = true; + + dev_priv->query_cid = sw_context->cur_query_cid; + dev_priv->pinned_bo = + ttm_bo_reference(sw_context->cur_query_bo); + } +} + +/** + * vmw_query_switch_backoff - clear query barrier list + * @sw_context: The sw context used for this submission batch. + * + * This function is used as part of an error path, where a previously + * set up list of query barriers needs to be cleared. + * + */ +static void vmw_query_switch_backoff(struct vmw_sw_context *sw_context) +{ + struct list_head *list, *next; + + list_for_each_safe(list, next, &sw_context->query_list) { + list_del_init(list); + } +} + static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGAGuestPtr *ptr, @@ -222,8 +447,6 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, struct ttm_buffer_object *bo; uint32_t handle = ptr->gmrId; struct vmw_relocation *reloc; - uint32_t cur_validate_node; - struct ttm_validate_buffer *val_buf; int ret; ret = vmw_user_dmabuf_lookup(sw_context->tfile, handle, &vmw_bo); @@ -243,23 +466,11 @@ static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, reloc = &sw_context->relocs[sw_context->cur_reloc++]; reloc->location = ptr; - cur_validate_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf); - if (unlikely(cur_validate_node >= VMWGFX_MAX_VALIDATIONS)) { - DRM_ERROR("Max number of DMA buffers per submission" - " exceeded.\n"); - ret = -EINVAL; + ret = vmw_bo_to_validate_list(sw_context, bo, DRM_VMW_FENCE_FLAG_EXEC, + &reloc->index); + if (unlikely(ret != 0)) goto out_no_reloc; - } - reloc->index = cur_validate_node; - if (unlikely(cur_validate_node == sw_context->cur_val_buf)) { - val_buf = &sw_context->val_bufs[cur_validate_node]; - val_buf->bo = ttm_bo_reference(bo); - val_buf->usage = TTM_USAGE_READWRITE; - val_buf->new_sync_obj_arg = (void *) DRM_VMW_FENCE_FLAG_EXEC; - list_add_tail(&val_buf->head, &sw_context->validate_nodes); - ++sw_context->cur_val_buf; - } *vmw_bo_p = vmw_bo; return 0; @@ -291,8 +502,11 @@ static int vmw_cmd_end_query(struct vmw_private *dev_priv, if (unlikely(ret != 0)) return ret; + ret = vmw_query_bo_switch_prepare(dev_priv, cmd->q.cid, + &vmw_bo->base, sw_context); + vmw_dmabuf_unreference(&vmw_bo); - return 0; + return ret; } static int vmw_cmd_wait_query(struct vmw_private *dev_priv, @@ -305,6 +519,7 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv, SVGA3dCmdWaitForQuery q; } *cmd; int ret; + struct vmw_resource *ctx; cmd = container_of(header, struct vmw_query_cmd, header); ret = vmw_cmd_cid_check(dev_priv, sw_context, header); @@ -318,6 +533,16 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv, return ret; vmw_dmabuf_unreference(&vmw_bo); + + /* + * This wait will act as a barrier for previous waits for this + * context. + */ + + ctx = sw_context->cur_ctx; + if (!list_empty(&ctx->query_head)) + list_del_init(&ctx->query_head); + return 0; } @@ -350,6 +575,13 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, goto out_no_reloc; } + ret = vmw_surface_validate(dev_priv, srf); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Culd not validate surface.\n"); + goto out_no_validate; + } + /* * Patch command stream with device SID. */ @@ -359,8 +591,12 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, vmw_dmabuf_unreference(&vmw_bo); res = &srf->res; - return vmw_resource_to_validate_list(sw_context, &res); + vmw_resource_to_validate_list(sw_context, &res); + + return 0; +out_no_validate: + vmw_surface_unreference(&srf); out_no_reloc: vmw_dmabuf_unreference(&vmw_bo); return ret; @@ -450,6 +686,71 @@ static int vmw_cmd_tex_state(struct vmw_private *dev_priv, return 0; } +static int vmw_cmd_check_define_gmrfb(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + void *buf) +{ + struct vmw_dma_buffer *vmw_bo; + int ret; + + struct { + uint32_t header; + SVGAFifoCmdDefineGMRFB body; + } *cmd = buf; + + ret = vmw_translate_guest_ptr(dev_priv, sw_context, + &cmd->body.ptr, + &vmw_bo); + if (unlikely(ret != 0)) + return ret; + + vmw_dmabuf_unreference(&vmw_bo); + + return ret; +} + +static int vmw_cmd_check_not_3d(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + void *buf, uint32_t *size) +{ + uint32_t size_remaining = *size; + uint32_t cmd_id; + + cmd_id = le32_to_cpu(((uint32_t *)buf)[0]); + switch (cmd_id) { + case SVGA_CMD_UPDATE: + *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdUpdate); + break; + case SVGA_CMD_DEFINE_GMRFB: + *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdDefineGMRFB); + break; + case SVGA_CMD_BLIT_GMRFB_TO_SCREEN: + *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdBlitGMRFBToScreen); + break; + case SVGA_CMD_BLIT_SCREEN_TO_GMRFB: + *size = sizeof(uint32_t) + sizeof(SVGAFifoCmdBlitGMRFBToScreen); + break; + default: + DRM_ERROR("Unsupported SVGA command: %u.\n", cmd_id); + return -EINVAL; + } + + if (*size > size_remaining) { + DRM_ERROR("Invalid SVGA command (size mismatch):" + " %u.\n", cmd_id); + return -EINVAL; + } + + if (unlikely(!sw_context->kernel)) { + DRM_ERROR("Kernel only SVGA command: %u.\n", cmd_id); + return -EPERM; + } + + if (cmd_id == SVGA_CMD_DEFINE_GMRFB) + return vmw_cmd_check_define_gmrfb(dev_priv, sw_context, buf); + + return 0; +} typedef int (*vmw_cmd_func) (struct vmw_private *, struct vmw_sw_context *, @@ -502,11 +803,11 @@ static int vmw_cmd_check(struct vmw_private *dev_priv, SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf; int ret; - cmd_id = ((uint32_t *)buf)[0]; - if (cmd_id == SVGA_CMD_UPDATE) { - *size = 5 << 2; - return 0; - } + cmd_id = le32_to_cpu(((uint32_t *)buf)[0]); + /* Handle any none 3D commands */ + if (unlikely(cmd_id < SVGA_CMD_MAX)) + return vmw_cmd_check_not_3d(dev_priv, sw_context, buf, size); + cmd_id = le32_to_cpu(header->id); *size = le32_to_cpu(header->size) + sizeof(SVGA3dCmdHeader); @@ -531,9 +832,9 @@ static int vmw_cmd_check(struct vmw_private *dev_priv, static int vmw_cmd_check_all(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, + void *buf, uint32_t size) { - void *buf = sw_context->cmd_bounce; int32_t cur_size = size; int ret; @@ -582,7 +883,7 @@ static void vmw_apply_relocations(struct vmw_sw_context *sw_context) static void vmw_clear_validations(struct vmw_sw_context *sw_context) { struct ttm_validate_buffer *entry, *next; - uint32_t i = sw_context->num_ref_resources; + struct vmw_resource *res, *res_next; /* * Drop references to DMA buffers held during command submission. @@ -599,9 +900,11 @@ static void vmw_clear_validations(struct vmw_sw_context *sw_context) /* * Drop references to resources held during command submission. */ - while (i-- > 0) { - sw_context->resources[i]->on_validate_list = false; - vmw_resource_unreference(&sw_context->resources[i]); + vmw_resource_unreserve(&sw_context->resource_list); + list_for_each_entry_safe(res, res_next, &sw_context->resource_list, + validate_head) { + list_del_init(&res->validate_head); + vmw_resource_unreference(&res); } } @@ -610,6 +913,16 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv, { int ret; + + /* + * Don't validate pinned buffers. + */ + + if (bo == dev_priv->pinned_bo || + (bo == dev_priv->dummy_query_bo && + dev_priv->dummy_query_bo_pinned)) + return 0; + /** * Put BO in VRAM if there is space, otherwise as a GMR. * If there is no space in VRAM and GMR ids are all used up, @@ -681,6 +994,9 @@ static int vmw_resize_cmd_bounce(struct vmw_sw_context *sw_context, * Creates a fence object and submits a command stream marker. * If this fails for some reason, We sync the fifo and return NULL. * It is then safe to fence buffers with a NULL pointer. + * + * If @p_handle is not NULL @file_priv must also not be NULL. Creates + * a userspace handle if @p_handle is not NULL, otherwise not. */ int vmw_execbuf_fence_commands(struct drm_file *file_priv, @@ -692,6 +1008,8 @@ int vmw_execbuf_fence_commands(struct drm_file *file_priv, int ret; bool synced = false; + /* p_handle implies file_priv. */ + BUG_ON(p_handle != NULL && file_priv == NULL); ret = vmw_fifo_send_fence(dev_priv, &sequence); if (unlikely(ret != 0)) { @@ -719,69 +1037,125 @@ int vmw_execbuf_fence_commands(struct drm_file *file_priv, return 0; } -int vmw_execbuf_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +/** + * vmw_execbuf_copy_fence_user - copy fence object information to + * user-space. + * + * @dev_priv: Pointer to a vmw_private struct. + * @vmw_fp: Pointer to the struct vmw_fpriv representing the calling file. + * @ret: Return value from fence object creation. + * @user_fence_rep: User space address of a struct drm_vmw_fence_rep to + * which the information should be copied. + * @fence: Pointer to the fenc object. + * @fence_handle: User-space fence handle. + * + * This function copies fence information to user-space. If copying fails, + * The user-space struct drm_vmw_fence_rep::error member is hopefully + * left untouched, and if it's preloaded with an -EFAULT by user-space, + * the error will hopefully be detected. + * Also if copying fails, user-space will be unable to signal the fence + * object so we wait for it immediately, and then unreference the + * user-space reference. + */ +void +vmw_execbuf_copy_fence_user(struct vmw_private *dev_priv, + struct vmw_fpriv *vmw_fp, + int ret, + struct drm_vmw_fence_rep __user *user_fence_rep, + struct vmw_fence_obj *fence, + uint32_t fence_handle) { - struct vmw_private *dev_priv = vmw_priv(dev); - struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data; struct drm_vmw_fence_rep fence_rep; - struct drm_vmw_fence_rep __user *user_fence_rep; - int ret; - void *user_cmd; - void *cmd; - struct vmw_sw_context *sw_context = &dev_priv->ctx; - struct vmw_master *vmaster = vmw_master(file_priv->master); - struct vmw_fence_obj *fence; - uint32_t handle; + + if (user_fence_rep == NULL) + return; + + fence_rep.error = ret; + if (ret == 0) { + BUG_ON(fence == NULL); + + fence_rep.handle = fence_handle; + fence_rep.seqno = fence->seqno; + vmw_update_seqno(dev_priv, &dev_priv->fifo); + fence_rep.passed_seqno = dev_priv->last_read_seqno; + } /* - * This will allow us to extend the ioctl argument while - * maintaining backwards compatibility: - * We take different code paths depending on the value of - * arg->version. + * copy_to_user errors will be detected by user space not + * seeing fence_rep::error filled in. Typically + * user-space would have pre-set that member to -EFAULT. */ + ret = copy_to_user(user_fence_rep, &fence_rep, + sizeof(fence_rep)); - if (unlikely(arg->version != DRM_VMW_EXECBUF_VERSION)) { - DRM_ERROR("Incorrect execbuf version.\n"); - DRM_ERROR("You're running outdated experimental " - "vmwgfx user-space drivers."); - return -EINVAL; + /* + * User-space lost the fence object. We need to sync + * and unreference the handle. + */ + if (unlikely(ret != 0) && (fence_rep.error == 0)) { + ttm_ref_object_base_unref(vmw_fp->tfile, + fence_handle, TTM_REF_USAGE); + DRM_ERROR("Fence copy error. Syncing.\n"); + (void) vmw_fence_obj_wait(fence, fence->signal_mask, + false, false, + VMW_FENCE_WAIT_TIMEOUT); } +} - ret = ttm_read_lock(&vmaster->lock, true); - if (unlikely(ret != 0)) - return ret; +int vmw_execbuf_process(struct drm_file *file_priv, + struct vmw_private *dev_priv, + void __user *user_commands, + void *kernel_commands, + uint32_t command_size, + uint64_t throttle_us, + struct drm_vmw_fence_rep __user *user_fence_rep) +{ + struct vmw_sw_context *sw_context = &dev_priv->ctx; + struct vmw_fence_obj *fence; + uint32_t handle; + void *cmd; + int ret; ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex); - if (unlikely(ret != 0)) { - ret = -ERESTARTSYS; - goto out_no_cmd_mutex; - } - - ret = vmw_resize_cmd_bounce(sw_context, arg->command_size); if (unlikely(ret != 0)) - goto out_unlock; + return -ERESTARTSYS; - user_cmd = (void __user *)(unsigned long)arg->commands; - ret = copy_from_user(sw_context->cmd_bounce, - user_cmd, arg->command_size); + if (kernel_commands == NULL) { + sw_context->kernel = false; - if (unlikely(ret != 0)) { - ret = -EFAULT; - DRM_ERROR("Failed copying commands.\n"); - goto out_unlock; - } + ret = vmw_resize_cmd_bounce(sw_context, command_size); + if (unlikely(ret != 0)) + goto out_unlock; + + + ret = copy_from_user(sw_context->cmd_bounce, + user_commands, command_size); + + if (unlikely(ret != 0)) { + ret = -EFAULT; + DRM_ERROR("Failed copying commands.\n"); + goto out_unlock; + } + kernel_commands = sw_context->cmd_bounce; + } else + sw_context->kernel = true; sw_context->tfile = vmw_fpriv(file_priv)->tfile; sw_context->cid_valid = false; sw_context->sid_valid = false; sw_context->cur_reloc = 0; sw_context->cur_val_buf = 0; - sw_context->num_ref_resources = 0; + sw_context->fence_flags = 0; + INIT_LIST_HEAD(&sw_context->query_list); + INIT_LIST_HEAD(&sw_context->resource_list); + sw_context->cur_query_bo = dev_priv->pinned_bo; + sw_context->cur_query_cid = dev_priv->query_cid; + sw_context->query_cid_valid = (dev_priv->pinned_bo != NULL); INIT_LIST_HEAD(&sw_context->validate_nodes); - ret = vmw_cmd_check_all(dev_priv, sw_context, arg->command_size); + ret = vmw_cmd_check_all(dev_priv, sw_context, kernel_commands, + command_size); if (unlikely(ret != 0)) goto out_err; @@ -795,26 +1169,25 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, vmw_apply_relocations(sw_context); - if (arg->throttle_us) { + if (throttle_us) { ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.marker_queue, - arg->throttle_us); + throttle_us); if (unlikely(ret != 0)) goto out_throttle; } - cmd = vmw_fifo_reserve(dev_priv, arg->command_size); + cmd = vmw_fifo_reserve(dev_priv, command_size); if (unlikely(cmd == NULL)) { DRM_ERROR("Failed reserving fifo space for commands.\n"); ret = -ENOMEM; - goto out_err; + goto out_throttle; } - memcpy(cmd, sw_context->cmd_bounce, arg->command_size); - vmw_fifo_commit(dev_priv, arg->command_size); + memcpy(cmd, kernel_commands, command_size); + vmw_fifo_commit(dev_priv, command_size); - user_fence_rep = (struct drm_vmw_fence_rep __user *) - (unsigned long)arg->fence_rep; + vmw_query_bo_switch_commit(dev_priv, sw_context); ret = vmw_execbuf_fence_commands(file_priv, dev_priv, &fence, (user_fence_rep) ? &handle : NULL); @@ -831,54 +1204,171 @@ int vmw_execbuf_ioctl(struct drm_device *dev, void *data, (void *) fence); vmw_clear_validations(sw_context); - mutex_unlock(&dev_priv->cmdbuf_mutex); - - if (user_fence_rep) { - fence_rep.error = ret; - fence_rep.handle = handle; - fence_rep.seqno = fence->seqno; - vmw_update_seqno(dev_priv, &dev_priv->fifo); - fence_rep.passed_seqno = dev_priv->last_read_seqno; - - /* - * copy_to_user errors will be detected by user space not - * seeing fence_rep::error filled in. Typically - * user-space would have pre-set that member to -EFAULT. - */ - ret = copy_to_user(user_fence_rep, &fence_rep, - sizeof(fence_rep)); - - /* - * User-space lost the fence object. We need to sync - * and unreference the handle. - */ - if (unlikely(ret != 0) && (fence_rep.error == 0)) { - BUG_ON(fence == NULL); - - ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, - handle, TTM_REF_USAGE); - DRM_ERROR("Fence copy error. Syncing.\n"); - (void) vmw_fence_obj_wait(fence, - fence->signal_mask, - false, false, - VMW_FENCE_WAIT_TIMEOUT); - } - } + vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret, + user_fence_rep, fence, handle); if (likely(fence != NULL)) vmw_fence_obj_unreference(&fence); - vmw_kms_cursor_post_execbuf(dev_priv); - ttm_read_unlock(&vmaster->lock); + mutex_unlock(&dev_priv->cmdbuf_mutex); return 0; + out_err: vmw_free_relocations(sw_context); out_throttle: + vmw_query_switch_backoff(sw_context); ttm_eu_backoff_reservation(&sw_context->validate_nodes); vmw_clear_validations(sw_context); out_unlock: mutex_unlock(&dev_priv->cmdbuf_mutex); -out_no_cmd_mutex: + return ret; +} + +/** + * vmw_execbuf_unpin_panic - Idle the fifo and unpin the query buffer. + * + * @dev_priv: The device private structure. + * + * This function is called to idle the fifo and unpin the query buffer + * if the normal way to do this hits an error, which should typically be + * extremely rare. + */ +static void vmw_execbuf_unpin_panic(struct vmw_private *dev_priv) +{ + DRM_ERROR("Can't unpin query buffer. Trying to recover.\n"); + + (void) vmw_fallback_wait(dev_priv, false, true, 0, false, 10*HZ); + vmw_bo_pin(dev_priv->pinned_bo, false); + vmw_bo_pin(dev_priv->dummy_query_bo, false); + dev_priv->dummy_query_bo_pinned = false; +} + + +/** + * vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned + * query bo. + * + * @dev_priv: The device private structure. + * @only_on_cid_match: Only flush and unpin if the current active query cid + * matches @cid. + * @cid: Optional context id to match. + * + * This function should be used to unpin the pinned query bo, or + * as a query barrier when we need to make sure that all queries have + * finished before the next fifo command. (For example on hardware + * context destructions where the hardware may otherwise leak unfinished + * queries). + * + * This function does not return any failure codes, but make attempts + * to do safe unpinning in case of errors. + * + * The function will synchronize on the previous query barrier, and will + * thus not finish until that barrier has executed. + */ +void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, + bool only_on_cid_match, uint32_t cid) +{ + int ret = 0; + struct list_head validate_list; + struct ttm_validate_buffer pinned_val, query_val; + struct vmw_fence_obj *fence; + + mutex_lock(&dev_priv->cmdbuf_mutex); + + if (dev_priv->pinned_bo == NULL) + goto out_unlock; + + if (only_on_cid_match && cid != dev_priv->query_cid) + goto out_unlock; + + INIT_LIST_HEAD(&validate_list); + + pinned_val.new_sync_obj_arg = (void *)(unsigned long) + DRM_VMW_FENCE_FLAG_EXEC; + pinned_val.bo = ttm_bo_reference(dev_priv->pinned_bo); + list_add_tail(&pinned_val.head, &validate_list); + + query_val.new_sync_obj_arg = pinned_val.new_sync_obj_arg; + query_val.bo = ttm_bo_reference(dev_priv->dummy_query_bo); + list_add_tail(&query_val.head, &validate_list); + + do { + ret = ttm_eu_reserve_buffers(&validate_list); + } while (ret == -ERESTARTSYS); + + if (unlikely(ret != 0)) { + vmw_execbuf_unpin_panic(dev_priv); + goto out_no_reserve; + } + + ret = vmw_fifo_emit_dummy_query(dev_priv, dev_priv->query_cid); + if (unlikely(ret != 0)) { + vmw_execbuf_unpin_panic(dev_priv); + goto out_no_emit; + } + + vmw_bo_pin(dev_priv->pinned_bo, false); + vmw_bo_pin(dev_priv->dummy_query_bo, false); + dev_priv->dummy_query_bo_pinned = false; + + (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); + ttm_eu_fence_buffer_objects(&validate_list, (void *) fence); + + ttm_bo_unref(&query_val.bo); + ttm_bo_unref(&pinned_val.bo); + ttm_bo_unref(&dev_priv->pinned_bo); + +out_unlock: + mutex_unlock(&dev_priv->cmdbuf_mutex); + return; + +out_no_emit: + ttm_eu_backoff_reservation(&validate_list); +out_no_reserve: + ttm_bo_unref(&query_val.bo); + ttm_bo_unref(&pinned_val.bo); + ttm_bo_unref(&dev_priv->pinned_bo); + mutex_unlock(&dev_priv->cmdbuf_mutex); +} + + +int vmw_execbuf_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data; + struct vmw_master *vmaster = vmw_master(file_priv->master); + int ret; + + /* + * This will allow us to extend the ioctl argument while + * maintaining backwards compatibility: + * We take different code paths depending on the value of + * arg->version. + */ + + if (unlikely(arg->version != DRM_VMW_EXECBUF_VERSION)) { + DRM_ERROR("Incorrect execbuf version.\n"); + DRM_ERROR("You're running outdated experimental " + "vmwgfx user-space drivers."); + return -EINVAL; + } + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = vmw_execbuf_process(file_priv, dev_priv, + (void __user *)(unsigned long)arg->commands, + NULL, arg->command_size, arg->throttle_us, + (void __user *)(unsigned long)arg->fence_rep); + + if (unlikely(ret != 0)) + goto out_unlock; + + vmw_kms_cursor_post_execbuf(dev_priv); + +out_unlock: ttm_read_unlock(&vmaster->lock); return ret; } diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c index b1888e801e22..070797b7b03a 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c @@ -592,58 +592,6 @@ int vmw_fb_close(struct vmw_private *vmw_priv) return 0; } -int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv, - struct vmw_dma_buffer *vmw_bo) -{ - struct ttm_buffer_object *bo = &vmw_bo->base; - int ret = 0; - - ret = ttm_bo_reserve(bo, false, false, false, 0); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_bo_validate(bo, &vmw_sys_placement, false, false, false); - ttm_bo_unreserve(bo); - - return ret; -} - -int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv, - struct vmw_dma_buffer *vmw_bo) -{ - struct ttm_buffer_object *bo = &vmw_bo->base; - struct ttm_placement ne_placement = vmw_vram_ne_placement; - int ret = 0; - - ne_placement.lpfn = bo->num_pages; - - /* interuptable? */ - ret = ttm_write_lock(&vmw_priv->active_master->lock, false); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_bo_reserve(bo, false, false, false, 0); - if (unlikely(ret != 0)) - goto err_unlock; - - if (bo->mem.mem_type == TTM_PL_VRAM && - bo->mem.start < bo->num_pages && - bo->mem.start > 0) - (void) ttm_bo_validate(bo, &vmw_sys_placement, false, - false, false); - - ret = ttm_bo_validate(bo, &ne_placement, false, false, false); - - /* Could probably bug on */ - WARN_ON(bo->offset != 0); - - ttm_bo_unreserve(bo); -err_unlock: - ttm_write_unlock(&vmw_priv->active_master->lock); - - return ret; -} - int vmw_fb_off(struct vmw_private *vmw_priv) { struct fb_info *info; @@ -665,7 +613,7 @@ int vmw_fb_off(struct vmw_private *vmw_priv) par->bo_ptr = NULL; ttm_bo_kunmap(&par->map); - vmw_dmabuf_from_vram(vmw_priv, par->vmw_bo); + vmw_dmabuf_unpin(vmw_priv, par->vmw_bo, false); return 0; } @@ -691,7 +639,7 @@ int vmw_fb_on(struct vmw_private *vmw_priv) /* Make sure that all overlays are stoped when we take over */ vmw_overlay_stop_all(vmw_priv); - ret = vmw_dmabuf_to_start_of_vram(vmw_priv, par->vmw_bo); + ret = vmw_dmabuf_to_start_of_vram(vmw_priv, par->vmw_bo, true, false); if (unlikely(ret != 0)) { DRM_ERROR("could not move buffer to start of VRAM\n"); goto err_no_buffer; diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index 5065a140fdf8..35d5f61fc7e4 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -34,13 +34,18 @@ struct vmw_fence_manager { int num_fence_objects; struct vmw_private *dev_priv; spinlock_t lock; - u32 next_seqno; struct list_head fence_list; struct work_struct work; u32 user_fence_size; u32 fence_size; + u32 event_fence_action_size; bool fifo_down; struct list_head cleanup_list; + uint32_t pending_actions[VMW_ACTION_MAX]; + struct mutex goal_irq_mutex; + bool goal_irq_on; /* Protected by @goal_irq_mutex */ + bool seqno_valid; /* Protected by @lock, and may not be set to true + without the @goal_irq_mutex held. */ }; struct vmw_user_fence { @@ -49,8 +54,51 @@ struct vmw_user_fence { }; /** - * vmw_fence_destroy_locked + * struct vmw_event_fence_action - fence action that delivers a drm event. * + * @e: A struct drm_pending_event that controls the event delivery. + * @action: A struct vmw_fence_action to hook up to a fence. + * @fence: A referenced pointer to the fence to keep it alive while @action + * hangs on it. + * @dev: Pointer to a struct drm_device so we can access the event stuff. + * @kref: Both @e and @action has destructors, so we need to refcount. + * @size: Size accounted for this object. + * @tv_sec: If non-null, the variable pointed to will be assigned + * current time tv_sec val when the fence signals. + * @tv_usec: Must be set if @tv_sec is set, and the variable pointed to will + * be assigned the current time tv_usec val when the fence signals. + */ +struct vmw_event_fence_action { + struct drm_pending_event e; + struct vmw_fence_action action; + struct vmw_fence_obj *fence; + struct drm_device *dev; + struct kref kref; + uint32_t size; + uint32_t *tv_sec; + uint32_t *tv_usec; +}; + +/** + * Note on fencing subsystem usage of irqs: + * Typically the vmw_fences_update function is called + * + * a) When a new fence seqno has been submitted by the fifo code. + * b) On-demand when we have waiters. Sleeping waiters will switch on the + * ANY_FENCE irq and call vmw_fences_update function each time an ANY_FENCE + * irq is received. When the last fence waiter is gone, that IRQ is masked + * away. + * + * In situations where there are no waiters and we don't submit any new fences, + * fence objects may not be signaled. This is perfectly OK, since there are + * no consumers of the signaled data, but that is NOT ok when there are fence + * actions attached to a fence. The fencing subsystem then makes use of the + * FENCE_GOAL irq and sets the fence goal seqno to that of the next fence + * which has an action attached, and each time vmw_fences_update is called, + * the subsystem makes sure the fence goal seqno is updated. + * + * The fence goal seqno irq is on as long as there are unsignaled fence + * objects with actions attached to them. */ static void vmw_fence_obj_destroy_locked(struct kref *kref) @@ -85,24 +133,36 @@ static void vmw_fence_work_func(struct work_struct *work) container_of(work, struct vmw_fence_manager, work); struct list_head list; struct vmw_fence_action *action, *next_action; + bool seqno_valid; do { INIT_LIST_HEAD(&list); + mutex_lock(&fman->goal_irq_mutex); + spin_lock_irq(&fman->lock); list_splice_init(&fman->cleanup_list, &list); + seqno_valid = fman->seqno_valid; spin_unlock_irq(&fman->lock); + if (!seqno_valid && fman->goal_irq_on) { + fman->goal_irq_on = false; + vmw_goal_waiter_remove(fman->dev_priv); + } + mutex_unlock(&fman->goal_irq_mutex); + if (list_empty(&list)) return; /* * At this point, only we should be able to manipulate the * list heads of the actions we have on the private list. + * hence fman::lock not held. */ list_for_each_entry_safe(action, next_action, &list, head) { list_del_init(&action->head); - action->cleanup(action); + if (action->cleanup) + action->cleanup(action); } } while (1); } @@ -122,6 +182,9 @@ struct vmw_fence_manager *vmw_fence_manager_init(struct vmw_private *dev_priv) fman->fifo_down = true; fman->user_fence_size = ttm_round_pot(sizeof(struct vmw_user_fence)); fman->fence_size = ttm_round_pot(sizeof(struct vmw_fence_obj)); + fman->event_fence_action_size = + ttm_round_pot(sizeof(struct vmw_event_fence_action)); + mutex_init(&fman->goal_irq_mutex); return fman; } @@ -177,6 +240,9 @@ static int vmw_fence_obj_init(struct vmw_fence_manager *fman, struct vmw_fence_obj *vmw_fence_obj_reference(struct vmw_fence_obj *fence) { + if (unlikely(fence == NULL)) + return NULL; + kref_get(&fence->kref); return fence; } @@ -191,8 +257,12 @@ struct vmw_fence_obj *vmw_fence_obj_reference(struct vmw_fence_obj *fence) void vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p) { struct vmw_fence_obj *fence = *fence_p; - struct vmw_fence_manager *fman = fence->fman; + struct vmw_fence_manager *fman; + if (unlikely(fence == NULL)) + return; + + fman = fence->fman; *fence_p = NULL; spin_lock_irq(&fman->lock); BUG_ON(atomic_read(&fence->kref.refcount) == 0); @@ -207,6 +277,7 @@ void vmw_fences_perform_actions(struct vmw_fence_manager *fman, list_for_each_entry_safe(action, next_action, list, head) { list_del_init(&action->head); + fman->pending_actions[action->type]--; if (action->seq_passed != NULL) action->seq_passed(action); @@ -215,17 +286,101 @@ void vmw_fences_perform_actions(struct vmw_fence_manager *fman, * it will be performed by a worker task. */ - if (action->cleanup != NULL) - list_add_tail(&action->head, &fman->cleanup_list); + list_add_tail(&action->head, &fman->cleanup_list); } } -void vmw_fences_update(struct vmw_fence_manager *fman, u32 seqno) +/** + * vmw_fence_goal_new_locked - Figure out a new device fence goal + * seqno if needed. + * + * @fman: Pointer to a fence manager. + * @passed_seqno: The seqno the device currently signals as passed. + * + * This function should be called with the fence manager lock held. + * It is typically called when we have a new passed_seqno, and + * we might need to update the fence goal. It checks to see whether + * the current fence goal has already passed, and, in that case, + * scans through all unsignaled fences to get the next fence object with an + * action attached, and sets the seqno of that fence as a new fence goal. + * + * returns true if the device goal seqno was updated. False otherwise. + */ +static bool vmw_fence_goal_new_locked(struct vmw_fence_manager *fman, + u32 passed_seqno) +{ + u32 goal_seqno; + __le32 __iomem *fifo_mem; + struct vmw_fence_obj *fence; + + if (likely(!fman->seqno_valid)) + return false; + + fifo_mem = fman->dev_priv->mmio_virt; + goal_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE_GOAL); + if (likely(passed_seqno - goal_seqno >= VMW_FENCE_WRAP)) + return false; + + fman->seqno_valid = false; + list_for_each_entry(fence, &fman->fence_list, head) { + if (!list_empty(&fence->seq_passed_actions)) { + fman->seqno_valid = true; + iowrite32(fence->seqno, + fifo_mem + SVGA_FIFO_FENCE_GOAL); + break; + } + } + + return true; +} + + +/** + * vmw_fence_goal_check_locked - Replace the device fence goal seqno if + * needed. + * + * @fence: Pointer to a struct vmw_fence_obj the seqno of which should be + * considered as a device fence goal. + * + * This function should be called with the fence manager lock held. + * It is typically called when an action has been attached to a fence to + * check whether the seqno of that fence should be used for a fence + * goal interrupt. This is typically needed if the current fence goal is + * invalid, or has a higher seqno than that of the current fence object. + * + * returns true if the device goal seqno was updated. False otherwise. + */ +static bool vmw_fence_goal_check_locked(struct vmw_fence_obj *fence) +{ + u32 goal_seqno; + __le32 __iomem *fifo_mem; + + if (fence->signaled & DRM_VMW_FENCE_FLAG_EXEC) + return false; + + fifo_mem = fence->fman->dev_priv->mmio_virt; + goal_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE_GOAL); + if (likely(fence->fman->seqno_valid && + goal_seqno - fence->seqno < VMW_FENCE_WRAP)) + return false; + + iowrite32(fence->seqno, fifo_mem + SVGA_FIFO_FENCE_GOAL); + fence->fman->seqno_valid = true; + + return true; +} + +void vmw_fences_update(struct vmw_fence_manager *fman) { unsigned long flags; struct vmw_fence_obj *fence, *next_fence; struct list_head action_list; + bool needs_rerun; + uint32_t seqno, new_seqno; + __le32 __iomem *fifo_mem = fman->dev_priv->mmio_virt; + seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); +rerun: spin_lock_irqsave(&fman->lock, flags); list_for_each_entry_safe(fence, next_fence, &fman->fence_list, head) { if (seqno - fence->seqno < VMW_FENCE_WRAP) { @@ -236,14 +391,30 @@ void vmw_fences_update(struct vmw_fence_manager *fman, u32 seqno) &action_list); vmw_fences_perform_actions(fman, &action_list); wake_up_all(&fence->queue); - } - + } else + break; } + + needs_rerun = vmw_fence_goal_new_locked(fman, seqno); + if (!list_empty(&fman->cleanup_list)) (void) schedule_work(&fman->work); spin_unlock_irqrestore(&fman->lock, flags); -} + /* + * Rerun if the fence goal seqno was updated, and the + * hardware might have raced with that update, so that + * we missed a fence_goal irq. + */ + + if (unlikely(needs_rerun)) { + new_seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); + if (new_seqno != seqno) { + seqno = new_seqno; + goto rerun; + } + } +} bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence, uint32_t flags) @@ -260,14 +431,8 @@ bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence, if ((signaled & flags) == flags) return 1; - if ((signaled & DRM_VMW_FENCE_FLAG_EXEC) == 0) { - struct vmw_private *dev_priv = fman->dev_priv; - __le32 __iomem *fifo_mem = dev_priv->mmio_virt; - u32 seqno; - - seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); - vmw_fences_update(fman, seqno); - } + if ((signaled & DRM_VMW_FENCE_FLAG_EXEC) == 0) + vmw_fences_update(fman); spin_lock_irqsave(&fman->lock, irq_flags); signaled = fence->signaled; @@ -617,3 +782,344 @@ int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data, arg->handle, TTM_REF_USAGE); } + +/** + * vmw_event_fence_action_destroy + * + * @kref: The struct kref embedded in a struct vmw_event_fence_action. + * + * The vmw_event_fence_action destructor that may be called either after + * the fence action cleanup, or when the event is delivered. + * It frees both the vmw_event_fence_action struct and the actual + * event structure copied to user-space. + */ +static void vmw_event_fence_action_destroy(struct kref *kref) +{ + struct vmw_event_fence_action *eaction = + container_of(kref, struct vmw_event_fence_action, kref); + struct ttm_mem_global *mem_glob = + vmw_mem_glob(vmw_priv(eaction->dev)); + uint32_t size = eaction->size; + + kfree(eaction->e.event); + kfree(eaction); + ttm_mem_global_free(mem_glob, size); +} + + +/** + * vmw_event_fence_action_delivered + * + * @e: The struct drm_pending_event embedded in a struct + * vmw_event_fence_action. + * + * The struct drm_pending_event destructor that is called by drm + * once the event is delivered. Since we don't know whether this function + * will be called before or after the fence action destructor, we + * free a refcount and destroy if it becomes zero. + */ +static void vmw_event_fence_action_delivered(struct drm_pending_event *e) +{ + struct vmw_event_fence_action *eaction = + container_of(e, struct vmw_event_fence_action, e); + + kref_put(&eaction->kref, vmw_event_fence_action_destroy); +} + + +/** + * vmw_event_fence_action_seq_passed + * + * @action: The struct vmw_fence_action embedded in a struct + * vmw_event_fence_action. + * + * This function is called when the seqno of the fence where @action is + * attached has passed. It queues the event on the submitter's event list. + * This function is always called from atomic context, and may be called + * from irq context. It ups a refcount reflecting that we now have two + * destructors. + */ +static void vmw_event_fence_action_seq_passed(struct vmw_fence_action *action) +{ + struct vmw_event_fence_action *eaction = + container_of(action, struct vmw_event_fence_action, action); + struct drm_device *dev = eaction->dev; + struct drm_file *file_priv = eaction->e.file_priv; + unsigned long irq_flags; + + kref_get(&eaction->kref); + spin_lock_irqsave(&dev->event_lock, irq_flags); + + if (likely(eaction->tv_sec != NULL)) { + struct timeval tv; + + do_gettimeofday(&tv); + *eaction->tv_sec = tv.tv_sec; + *eaction->tv_usec = tv.tv_usec; + } + + list_add_tail(&eaction->e.link, &file_priv->event_list); + wake_up_all(&file_priv->event_wait); + spin_unlock_irqrestore(&dev->event_lock, irq_flags); +} + +/** + * vmw_event_fence_action_cleanup + * + * @action: The struct vmw_fence_action embedded in a struct + * vmw_event_fence_action. + * + * This function is the struct vmw_fence_action destructor. It's typically + * called from a workqueue. + */ +static void vmw_event_fence_action_cleanup(struct vmw_fence_action *action) +{ + struct vmw_event_fence_action *eaction = + container_of(action, struct vmw_event_fence_action, action); + + vmw_fence_obj_unreference(&eaction->fence); + kref_put(&eaction->kref, vmw_event_fence_action_destroy); +} + + +/** + * vmw_fence_obj_add_action - Add an action to a fence object. + * + * @fence - The fence object. + * @action - The action to add. + * + * Note that the action callbacks may be executed before this function + * returns. + */ +void vmw_fence_obj_add_action(struct vmw_fence_obj *fence, + struct vmw_fence_action *action) +{ + struct vmw_fence_manager *fman = fence->fman; + unsigned long irq_flags; + bool run_update = false; + + mutex_lock(&fman->goal_irq_mutex); + spin_lock_irqsave(&fman->lock, irq_flags); + + fman->pending_actions[action->type]++; + if (fence->signaled & DRM_VMW_FENCE_FLAG_EXEC) { + struct list_head action_list; + + INIT_LIST_HEAD(&action_list); + list_add_tail(&action->head, &action_list); + vmw_fences_perform_actions(fman, &action_list); + } else { + list_add_tail(&action->head, &fence->seq_passed_actions); + + /* + * This function may set fman::seqno_valid, so it must + * be run with the goal_irq_mutex held. + */ + run_update = vmw_fence_goal_check_locked(fence); + } + + spin_unlock_irqrestore(&fman->lock, irq_flags); + + if (run_update) { + if (!fman->goal_irq_on) { + fman->goal_irq_on = true; + vmw_goal_waiter_add(fman->dev_priv); + } + vmw_fences_update(fman); + } + mutex_unlock(&fman->goal_irq_mutex); + +} + +/** + * vmw_event_fence_action_create - Post an event for sending when a fence + * object seqno has passed. + * + * @file_priv: The file connection on which the event should be posted. + * @fence: The fence object on which to post the event. + * @event: Event to be posted. This event should've been alloced + * using k[mz]alloc, and should've been completely initialized. + * @interruptible: Interruptible waits if possible. + * + * As a side effect, the object pointed to by @event may have been + * freed when this function returns. If this function returns with + * an error code, the caller needs to free that object. + */ + +int vmw_event_fence_action_create(struct drm_file *file_priv, + struct vmw_fence_obj *fence, + struct drm_event *event, + uint32_t *tv_sec, + uint32_t *tv_usec, + bool interruptible) +{ + struct vmw_event_fence_action *eaction = + kzalloc(sizeof(*eaction), GFP_KERNEL); + struct ttm_mem_global *mem_glob = + vmw_mem_glob(fence->fman->dev_priv); + struct vmw_fence_manager *fman = fence->fman; + uint32_t size = fman->event_fence_action_size + + ttm_round_pot(event->length); + int ret; + + /* + * Account for internal structure size as well as the + * event size itself. + */ + + ret = ttm_mem_global_alloc(mem_glob, size, false, interruptible); + if (unlikely(ret != 0)) + return ret; + + eaction = kzalloc(sizeof(*eaction), GFP_KERNEL); + if (unlikely(eaction == NULL)) { + ttm_mem_global_free(mem_glob, size); + return -ENOMEM; + } + + eaction->e.event = event; + eaction->e.file_priv = file_priv; + eaction->e.destroy = vmw_event_fence_action_delivered; + + eaction->action.seq_passed = vmw_event_fence_action_seq_passed; + eaction->action.cleanup = vmw_event_fence_action_cleanup; + eaction->action.type = VMW_ACTION_EVENT; + + eaction->fence = vmw_fence_obj_reference(fence); + eaction->dev = fman->dev_priv->dev; + eaction->size = size; + eaction->tv_sec = tv_sec; + eaction->tv_usec = tv_usec; + + kref_init(&eaction->kref); + vmw_fence_obj_add_action(fence, &eaction->action); + + return 0; +} + +int vmw_fence_event_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_vmw_fence_event_arg *arg = + (struct drm_vmw_fence_event_arg *) data; + struct vmw_fence_obj *fence = NULL; + struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv); + struct drm_vmw_fence_rep __user *user_fence_rep = + (struct drm_vmw_fence_rep __user *)(unsigned long) + arg->fence_rep; + uint32_t handle; + unsigned long irq_flags; + struct drm_vmw_event_fence *event; + int ret; + + /* + * Look up an existing fence object, + * and if user-space wants a new reference, + * add one. + */ + if (arg->handle) { + struct ttm_base_object *base = + ttm_base_object_lookup(vmw_fp->tfile, arg->handle); + + if (unlikely(base == NULL)) { + DRM_ERROR("Fence event invalid fence object handle " + "0x%08lx.\n", + (unsigned long)arg->handle); + return -EINVAL; + } + fence = &(container_of(base, struct vmw_user_fence, + base)->fence); + (void) vmw_fence_obj_reference(fence); + + if (user_fence_rep != NULL) { + bool existed; + + ret = ttm_ref_object_add(vmw_fp->tfile, base, + TTM_REF_USAGE, &existed); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to reference a fence " + "object.\n"); + goto out_no_ref_obj; + } + handle = base->hash.key; + } + ttm_base_object_unref(&base); + } + + /* + * Create a new fence object. + */ + if (!fence) { + ret = vmw_execbuf_fence_commands(file_priv, dev_priv, + &fence, + (user_fence_rep) ? + &handle : NULL); + if (unlikely(ret != 0)) { + DRM_ERROR("Fence event failed to create fence.\n"); + return ret; + } + } + + BUG_ON(fence == NULL); + + spin_lock_irqsave(&dev->event_lock, irq_flags); + + ret = (file_priv->event_space < sizeof(*event)) ? -EBUSY : 0; + if (likely(ret == 0)) + file_priv->event_space -= sizeof(*event); + + spin_unlock_irqrestore(&dev->event_lock, irq_flags); + + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to allocate event space for this file.\n"); + goto out_no_event_space; + } + + event = kzalloc(sizeof(*event), GFP_KERNEL); + if (unlikely(event == NULL)) { + DRM_ERROR("Failed to allocate an event.\n"); + goto out_no_event; + } + + event->base.type = DRM_VMW_EVENT_FENCE_SIGNALED; + event->base.length = sizeof(*event); + event->user_data = arg->user_data; + + if (arg->flags & DRM_VMW_FE_FLAG_REQ_TIME) + ret = vmw_event_fence_action_create(file_priv, fence, + &event->base, + &event->tv_sec, + &event->tv_usec, + true); + else + ret = vmw_event_fence_action_create(file_priv, fence, + &event->base, + NULL, + NULL, + true); + + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Failed to attach event to fence.\n"); + goto out_no_attach; + } + + vmw_execbuf_copy_fence_user(dev_priv, vmw_fp, 0, user_fence_rep, fence, + handle); + vmw_fence_obj_unreference(&fence); + return 0; +out_no_attach: + kfree(event); +out_no_event: + spin_lock_irqsave(&dev->event_lock, irq_flags); + file_priv->event_space += sizeof(*event); + spin_unlock_irqrestore(&dev->event_lock, irq_flags); +out_no_event_space: + if (user_fence_rep != NULL) + ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, + handle, TTM_REF_USAGE); +out_no_ref_obj: + vmw_fence_obj_unreference(&fence); + return ret; +} diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h index 93074064aaf3..0854a2096b55 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fence.h @@ -37,8 +37,14 @@ struct vmw_fence_manager; * * */ +enum vmw_action_type { + VMW_ACTION_EVENT = 0, + VMW_ACTION_MAX +}; + struct vmw_fence_action { struct list_head head; + enum vmw_action_type type; void (*seq_passed) (struct vmw_fence_action *action); void (*cleanup) (struct vmw_fence_action *action); }; @@ -66,8 +72,7 @@ extern void vmw_fence_obj_unreference(struct vmw_fence_obj **fence_p); extern struct vmw_fence_obj * vmw_fence_obj_reference(struct vmw_fence_obj *fence); -extern void vmw_fences_update(struct vmw_fence_manager *fman, - u32 sequence); +extern void vmw_fences_update(struct vmw_fence_manager *fman); extern bool vmw_fence_obj_signaled(struct vmw_fence_obj *fence, uint32_t flags); @@ -102,4 +107,7 @@ extern int vmw_fence_obj_signaled_ioctl(struct drm_device *dev, void *data, extern int vmw_fence_obj_unref_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); +extern int vmw_fence_event_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); + #endif /* _VMWGFX_FENCE_H_ */ diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c index 3ba9cac579e0..03bbc2a6f9a7 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c @@ -45,7 +45,11 @@ bool vmw_fifo_have_3d(struct vmw_private *dev_priv) if (hwversion == 0) return false; - if (hwversion < SVGA3D_HWVERSION_WS65_B1) + if (hwversion < SVGA3D_HWVERSION_WS8_B1) + return false; + + /* Non-Screen Object path does not support surfaces */ + if (!dev_priv->sou_priv) return false; return true; @@ -243,9 +247,8 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv, spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); outl(SVGA_IRQFLAG_FIFO_PROGRESS, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); - vmw_write(dev_priv, SVGA_REG_IRQMASK, - vmw_read(dev_priv, SVGA_REG_IRQMASK) | - SVGA_IRQFLAG_FIFO_PROGRESS); + dev_priv->irq_mask |= SVGA_IRQFLAG_FIFO_PROGRESS; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } mutex_unlock(&dev_priv->hw_mutex); @@ -267,9 +270,8 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv, mutex_lock(&dev_priv->hw_mutex); if (atomic_dec_and_test(&dev_priv->fifo_queue_waiters)) { spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); - vmw_write(dev_priv, SVGA_REG_IRQMASK, - vmw_read(dev_priv, SVGA_REG_IRQMASK) & - ~SVGA_IRQFLAG_FIFO_PROGRESS); + dev_priv->irq_mask &= ~SVGA_IRQFLAG_FIFO_PROGRESS; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } mutex_unlock(&dev_priv->hw_mutex); @@ -277,6 +279,16 @@ static int vmw_fifo_wait(struct vmw_private *dev_priv, return ret; } +/** + * Reserve @bytes number of bytes in the fifo. + * + * This function will return NULL (error) on two conditions: + * If it timeouts waiting for fifo space, or if @bytes is larger than the + * available fifo space. + * + * Returns: + * Pointer to the fifo, or null on error (possible hardware hang). + */ void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes) { struct vmw_fifo_state *fifo_state = &dev_priv->fifo; @@ -491,3 +503,60 @@ int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *seqno) out_err: return ret; } + +/** + * vmw_fifo_emit_dummy_query - emits a dummy query to the fifo. + * + * @dev_priv: The device private structure. + * @cid: The hardware context id used for the query. + * + * This function is used to emit a dummy occlusion query with + * no primitives rendered between query begin and query end. + * It's used to provide a query barrier, in order to know that when + * this query is finished, all preceding queries are also finished. + * + * A Query results structure should have been initialized at the start + * of the dev_priv->dummy_query_bo buffer object. And that buffer object + * must also be either reserved or pinned when this function is called. + * + * Returns -ENOMEM on failure to reserve fifo space. + */ +int vmw_fifo_emit_dummy_query(struct vmw_private *dev_priv, + uint32_t cid) +{ + /* + * A query wait without a preceding query end will + * actually finish all queries for this cid + * without writing to the query result structure. + */ + + struct ttm_buffer_object *bo = dev_priv->dummy_query_bo; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdWaitForQuery body; + } *cmd; + + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + + if (unlikely(cmd == NULL)) { + DRM_ERROR("Out of fifo space for dummy query.\n"); + return -ENOMEM; + } + + cmd->header.id = SVGA_3D_CMD_WAIT_FOR_QUERY; + cmd->header.size = sizeof(cmd->body); + cmd->body.cid = cid; + cmd->body.type = SVGA3D_QUERYTYPE_OCCLUSION; + + if (bo->mem.mem_type == TTM_PL_VRAM) { + cmd->body.guestResult.gmrId = SVGA_GMR_FRAMEBUFFER; + cmd->body.guestResult.offset = bo->offset; + } else { + cmd->body.guestResult.gmrId = bo->mem.start; + cmd->body.guestResult.offset = 0; + } + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); + + return 0; +} diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index 5ecf96660644..97f23abeacda 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -27,6 +27,7 @@ #include "vmwgfx_drv.h" #include "vmwgfx_drm.h" +#include "vmwgfx_kms.h" int vmw_getparam_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) @@ -110,3 +111,217 @@ int vmw_get_cap_3d_ioctl(struct drm_device *dev, void *data, return ret; } + +int vmw_present_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_vmw_present_arg *arg = + (struct drm_vmw_present_arg *)data; + struct vmw_surface *surface; + struct vmw_master *vmaster = vmw_master(file_priv->master); + struct drm_vmw_rect __user *clips_ptr; + struct drm_vmw_rect *clips = NULL; + struct drm_mode_object *obj; + struct vmw_framebuffer *vfb; + uint32_t num_clips; + int ret; + + num_clips = arg->num_clips; + clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr; + + if (unlikely(num_clips == 0)) + return 0; + + if (clips_ptr == NULL) { + DRM_ERROR("Variable clips_ptr must be specified.\n"); + ret = -EINVAL; + goto out_clips; + } + + clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL); + if (clips == NULL) { + DRM_ERROR("Failed to allocate clip rect list.\n"); + ret = -ENOMEM; + goto out_clips; + } + + ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); + if (ret) { + DRM_ERROR("Failed to copy clip rects from userspace.\n"); + goto out_no_copy; + } + + ret = mutex_lock_interruptible(&dev->mode_config.mutex); + if (unlikely(ret != 0)) { + ret = -ERESTARTSYS; + goto out_no_mode_mutex; + } + + obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); + if (!obj) { + DRM_ERROR("Invalid framebuffer id.\n"); + ret = -EINVAL; + goto out_no_fb; + } + + vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); + if (!vfb->dmabuf) { + DRM_ERROR("Framebuffer not dmabuf backed.\n"); + ret = -EINVAL; + goto out_no_fb; + } + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + goto out_no_ttm_lock; + + ret = vmw_user_surface_lookup_handle(dev_priv, tfile, arg->sid, + &surface); + if (ret) + goto out_no_surface; + + ret = vmw_kms_present(dev_priv, file_priv, + vfb, surface, arg->sid, + arg->dest_x, arg->dest_y, + clips, num_clips); + + /* vmw_user_surface_lookup takes one ref so does new_fb */ + vmw_surface_unreference(&surface); + +out_no_surface: + ttm_read_unlock(&vmaster->lock); +out_no_ttm_lock: +out_no_fb: + mutex_unlock(&dev->mode_config.mutex); +out_no_mode_mutex: +out_no_copy: + kfree(clips); +out_clips: + return ret; +} + +int vmw_present_readback_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_vmw_present_readback_arg *arg = + (struct drm_vmw_present_readback_arg *)data; + struct drm_vmw_fence_rep __user *user_fence_rep = + (struct drm_vmw_fence_rep __user *) + (unsigned long)arg->fence_rep; + struct vmw_master *vmaster = vmw_master(file_priv->master); + struct drm_vmw_rect __user *clips_ptr; + struct drm_vmw_rect *clips = NULL; + struct drm_mode_object *obj; + struct vmw_framebuffer *vfb; + uint32_t num_clips; + int ret; + + num_clips = arg->num_clips; + clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr; + + if (unlikely(num_clips == 0)) + return 0; + + if (clips_ptr == NULL) { + DRM_ERROR("Argument clips_ptr must be specified.\n"); + ret = -EINVAL; + goto out_clips; + } + + clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL); + if (clips == NULL) { + DRM_ERROR("Failed to allocate clip rect list.\n"); + ret = -ENOMEM; + goto out_clips; + } + + ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); + if (ret) { + DRM_ERROR("Failed to copy clip rects from userspace.\n"); + goto out_no_copy; + } + + ret = mutex_lock_interruptible(&dev->mode_config.mutex); + if (unlikely(ret != 0)) { + ret = -ERESTARTSYS; + goto out_no_mode_mutex; + } + + obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); + if (!obj) { + DRM_ERROR("Invalid framebuffer id.\n"); + ret = -EINVAL; + goto out_no_fb; + } + + vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); + if (!vfb->dmabuf) { + DRM_ERROR("Framebuffer not dmabuf backed.\n"); + ret = -EINVAL; + goto out_no_fb; + } + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + goto out_no_ttm_lock; + + ret = vmw_kms_readback(dev_priv, file_priv, + vfb, user_fence_rep, + clips, num_clips); + + ttm_read_unlock(&vmaster->lock); +out_no_ttm_lock: +out_no_fb: + mutex_unlock(&dev->mode_config.mutex); +out_no_mode_mutex: +out_no_copy: + kfree(clips); +out_clips: + return ret; +} + + +/** + * vmw_fops_poll - wrapper around the drm_poll function + * + * @filp: See the linux fops poll documentation. + * @wait: See the linux fops poll documentation. + * + * Wrapper around the drm_poll function that makes sure the device is + * processing the fifo if drm_poll decides to wait. + */ +unsigned int vmw_fops_poll(struct file *filp, struct poll_table_struct *wait) +{ + struct drm_file *file_priv = filp->private_data; + struct vmw_private *dev_priv = + vmw_priv(file_priv->minor->dev); + + vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); + return drm_poll(filp, wait); +} + + +/** + * vmw_fops_read - wrapper around the drm_read function + * + * @filp: See the linux fops read documentation. + * @buffer: See the linux fops read documentation. + * @count: See the linux fops read documentation. + * offset: See the linux fops read documentation. + * + * Wrapper around the drm_read function that makes sure the device is + * processing the fifo if drm_read decides to wait. + */ +ssize_t vmw_fops_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset) +{ + struct drm_file *file_priv = filp->private_data; + struct vmw_private *dev_priv = + vmw_priv(file_priv->minor->dev); + + vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); + return drm_read(filp, buffer, count, offset); +} diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c index a005292a8908..cabc95f7517e 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c @@ -34,28 +34,30 @@ irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *)arg; struct vmw_private *dev_priv = vmw_priv(dev); - uint32_t status; + uint32_t status, masked_status; spin_lock(&dev_priv->irq_lock); status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); + masked_status = status & dev_priv->irq_mask; spin_unlock(&dev_priv->irq_lock); - if (status & SVGA_IRQFLAG_ANY_FENCE) { - __le32 __iomem *fifo_mem = dev_priv->mmio_virt; - uint32_t seqno = ioread32(fifo_mem + SVGA_FIFO_FENCE); + if (likely(status)) + outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); + + if (!masked_status) + return IRQ_NONE; - vmw_fences_update(dev_priv->fman, seqno); + if (masked_status & (SVGA_IRQFLAG_ANY_FENCE | + SVGA_IRQFLAG_FENCE_GOAL)) { + vmw_fences_update(dev_priv->fman); wake_up_all(&dev_priv->fence_queue); } - if (status & SVGA_IRQFLAG_FIFO_PROGRESS) + + if (masked_status & SVGA_IRQFLAG_FIFO_PROGRESS) wake_up_all(&dev_priv->fifo_queue); - if (likely(status)) { - outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); - return IRQ_HANDLED; - } - return IRQ_NONE; + return IRQ_HANDLED; } static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t seqno) @@ -78,7 +80,7 @@ void vmw_update_seqno(struct vmw_private *dev_priv, if (dev_priv->last_read_seqno != seqno) { dev_priv->last_read_seqno = seqno; vmw_marker_pull(&fifo_state->marker_queue, seqno); - vmw_fences_update(dev_priv->fman, seqno); + vmw_fences_update(dev_priv->fman); } } @@ -189,9 +191,8 @@ void vmw_seqno_waiter_add(struct vmw_private *dev_priv) spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); outl(SVGA_IRQFLAG_ANY_FENCE, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); - vmw_write(dev_priv, SVGA_REG_IRQMASK, - vmw_read(dev_priv, SVGA_REG_IRQMASK) | - SVGA_IRQFLAG_ANY_FENCE); + dev_priv->irq_mask |= SVGA_IRQFLAG_ANY_FENCE; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } mutex_unlock(&dev_priv->hw_mutex); @@ -204,9 +205,39 @@ void vmw_seqno_waiter_remove(struct vmw_private *dev_priv) unsigned long irq_flags; spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); - vmw_write(dev_priv, SVGA_REG_IRQMASK, - vmw_read(dev_priv, SVGA_REG_IRQMASK) & - ~SVGA_IRQFLAG_ANY_FENCE); + dev_priv->irq_mask &= ~SVGA_IRQFLAG_ANY_FENCE; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); + spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); + } + mutex_unlock(&dev_priv->hw_mutex); +} + + +void vmw_goal_waiter_add(struct vmw_private *dev_priv) +{ + mutex_lock(&dev_priv->hw_mutex); + if (dev_priv->goal_queue_waiters++ == 0) { + unsigned long irq_flags; + + spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); + outl(SVGA_IRQFLAG_FENCE_GOAL, + dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); + dev_priv->irq_mask |= SVGA_IRQFLAG_FENCE_GOAL; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); + spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); + } + mutex_unlock(&dev_priv->hw_mutex); +} + +void vmw_goal_waiter_remove(struct vmw_private *dev_priv) +{ + mutex_lock(&dev_priv->hw_mutex); + if (--dev_priv->goal_queue_waiters == 0) { + unsigned long irq_flags; + + spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); + dev_priv->irq_mask &= ~SVGA_IRQFLAG_FENCE_GOAL; + vmw_write(dev_priv, SVGA_REG_IRQMASK, dev_priv->irq_mask); spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); } mutex_unlock(&dev_priv->hw_mutex); diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 1a4c84cecca7..2421d0cd0df9 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -27,12 +27,10 @@ #include "vmwgfx_kms.h" + /* Might need a hrtimer here? */ #define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1) -static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb); -static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb); - void vmw_display_unit_cleanup(struct vmw_display_unit *du) { if (du->cursor_surface) @@ -329,41 +327,10 @@ struct vmw_framebuffer_surface { struct vmw_framebuffer base; struct vmw_surface *surface; struct vmw_dma_buffer *buffer; - struct delayed_work d_work; - struct mutex work_lock; - bool present_fs; struct list_head head; struct drm_master *master; }; -/** - * vmw_kms_idle_workqueues - Flush workqueues on this master - * - * @vmaster - Pointer identifying the master, for the surfaces of which - * we idle the dirty work queues. - * - * This function should be called with the ttm lock held in exclusive mode - * to idle all dirty work queues before the fifo is taken down. - * - * The work task may actually requeue itself, but after the flush returns we're - * sure that there's nothing to present, since the ttm lock is held in - * exclusive mode, so the fifo will never get used. - */ - -void vmw_kms_idle_workqueues(struct vmw_master *vmaster) -{ - struct vmw_framebuffer_surface *entry; - - mutex_lock(&vmaster->fb_surf_mutex); - list_for_each_entry(entry, &vmaster->fb_surf, head) { - if (cancel_delayed_work_sync(&entry->d_work)) - (void) entry->d_work.work.func(&entry->d_work.work); - - (void) cancel_delayed_work_sync(&entry->d_work); - } - mutex_unlock(&vmaster->fb_surf_mutex); -} - void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) { struct vmw_framebuffer_surface *vfbs = @@ -375,64 +342,129 @@ void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer) list_del(&vfbs->head); mutex_unlock(&vmaster->fb_surf_mutex); - cancel_delayed_work_sync(&vfbs->d_work); drm_master_put(&vfbs->master); drm_framebuffer_cleanup(framebuffer); vmw_surface_unreference(&vfbs->surface); + ttm_base_object_unref(&vfbs->base.user_obj); kfree(vfbs); } -static void vmw_framebuffer_present_fs_callback(struct work_struct *work) +static int do_surface_dirty_sou(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *framebuffer, + struct vmw_surface *surf, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips, int inc) { - struct delayed_work *d_work = - container_of(work, struct delayed_work, work); - struct vmw_framebuffer_surface *vfbs = - container_of(d_work, struct vmw_framebuffer_surface, d_work); - struct vmw_surface *surf = vfbs->surface; - struct drm_framebuffer *framebuffer = &vfbs->base.base; - struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); + struct drm_clip_rect *clips_ptr; + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_crtc *crtc; + size_t fifo_size; + int i, num_units; + int ret = 0; /* silence warning */ + int left, right, top, bottom; struct { SVGA3dCmdHeader header; - SVGA3dCmdPresent body; - SVGA3dCopyRect cr; + SVGA3dCmdBlitSurfaceToScreen body; } *cmd; + SVGASignedRect *blits; - /** - * Strictly we should take the ttm_lock in read mode before accessing - * the fifo, to make sure the fifo is present and up. However, - * instead we flush all workqueues under the ttm lock in exclusive mode - * before taking down the fifo. - */ - mutex_lock(&vfbs->work_lock); - if (!vfbs->present_fs) - goto out_unlock; - - cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); - if (unlikely(cmd == NULL)) - goto out_resched; - - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT); - cmd->header.size = cpu_to_le32(sizeof(cmd->body) + sizeof(cmd->cr)); - cmd->body.sid = cpu_to_le32(surf->res.id); - cmd->cr.x = cpu_to_le32(0); - cmd->cr.y = cpu_to_le32(0); - cmd->cr.srcx = cmd->cr.x; - cmd->cr.srcy = cmd->cr.y; - cmd->cr.w = cpu_to_le32(framebuffer->width); - cmd->cr.h = cpu_to_le32(framebuffer->height); - vfbs->present_fs = false; - vmw_fifo_commit(dev_priv, sizeof(*cmd)); -out_resched: - /** - * Will not re-add if already pending. - */ - schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE); -out_unlock: - mutex_unlock(&vfbs->work_lock); -} + num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, + head) { + if (crtc->fb != &framebuffer->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); + } + + BUG_ON(surf == NULL); + BUG_ON(!clips || !num_clips); + + fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips; + cmd = kzalloc(fifo_size, GFP_KERNEL); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Temporary fifo memory alloc failed.\n"); + return -ENOMEM; + } + + left = clips->x1; + right = clips->x2; + top = clips->y1; + bottom = clips->y2; + + clips_ptr = clips; + for (i = 1; i < num_clips; i++, clips_ptr += inc) { + left = min_t(int, left, (int)clips_ptr->x1); + right = max_t(int, right, (int)clips_ptr->x2); + top = min_t(int, top, (int)clips_ptr->y1); + bottom = max_t(int, bottom, (int)clips_ptr->y2); + } + + /* only need to do this once */ + memset(cmd, 0, fifo_size); + cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN); + cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header)); + + cmd->body.srcRect.left = left; + cmd->body.srcRect.right = right; + cmd->body.srcRect.top = top; + cmd->body.srcRect.bottom = bottom; + + clips_ptr = clips; + blits = (SVGASignedRect *)&cmd[1]; + for (i = 0; i < num_clips; i++, clips_ptr += inc) { + blits[i].left = clips_ptr->x1 - left; + blits[i].right = clips_ptr->x2 - left; + blits[i].top = clips_ptr->y1 - top; + blits[i].bottom = clips_ptr->y2 - top; + } + + /* do per unit writing, reuse fifo for each */ + for (i = 0; i < num_units; i++) { + struct vmw_display_unit *unit = units[i]; + int clip_x1 = left - unit->crtc.x; + int clip_y1 = top - unit->crtc.y; + int clip_x2 = right - unit->crtc.x; + int clip_y2 = bottom - unit->crtc.y; + + /* skip any crtcs that misses the clip region */ + if (clip_x1 >= unit->crtc.mode.hdisplay || + clip_y1 >= unit->crtc.mode.vdisplay || + clip_x2 <= 0 || clip_y2 <= 0) + continue; + + /* need to reset sid as it is changed by execbuf */ + cmd->body.srcImage.sid = cpu_to_le32(framebuffer->user_handle); + + cmd->body.destScreenId = unit->unit; + + /* + * The blit command is a lot more resilient then the + * readback command when it comes to clip rects. So its + * okay to go out of bounds. + */ + + cmd->body.destRect.left = clip_x1; + cmd->body.destRect.right = clip_x2; + cmd->body.destRect.top = clip_y1; + cmd->body.destRect.bottom = clip_y2; + + + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, + fifo_size, 0, NULL); + + if (unlikely(ret != 0)) + break; + } + + kfree(cmd); + + return ret; +} int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, struct drm_file *file_priv, @@ -446,42 +478,19 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, vmw_framebuffer_to_vfbs(framebuffer); struct vmw_surface *surf = vfbs->surface; struct drm_clip_rect norect; - SVGA3dCopyRect *cr; - int i, inc = 1; - int ret; - - struct { - SVGA3dCmdHeader header; - SVGA3dCmdPresent body; - SVGA3dCopyRect cr; - } *cmd; + int ret, inc = 1; if (unlikely(vfbs->master != file_priv->master)) return -EINVAL; + /* Require ScreenObject support for 3D */ + if (!dev_priv->sou_priv) + return -EINVAL; + ret = ttm_read_lock(&vmaster->lock, true); if (unlikely(ret != 0)) return ret; - if (!num_clips || - !(dev_priv->fifo.capabilities & - SVGA_FIFO_CAP_SCREEN_OBJECT)) { - int ret; - - mutex_lock(&vfbs->work_lock); - vfbs->present_fs = true; - ret = schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE); - mutex_unlock(&vfbs->work_lock); - if (ret) { - /** - * No work pending, Force immediate present. - */ - vmw_framebuffer_present_fs_callback(&vfbs->d_work.work); - } - ttm_read_unlock(&vmaster->lock); - return 0; - } - if (!num_clips) { num_clips = 1; clips = &norect; @@ -493,29 +502,10 @@ int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer, inc = 2; /* skip source rects */ } - cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr)); - if (unlikely(cmd == NULL)) { - DRM_ERROR("Fifo reserve failed.\n"); - ttm_read_unlock(&vmaster->lock); - return -ENOMEM; - } - - memset(cmd, 0, sizeof(*cmd)); + ret = do_surface_dirty_sou(dev_priv, file_priv, &vfbs->base, surf, + flags, color, + clips, num_clips, inc); - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT); - cmd->header.size = cpu_to_le32(sizeof(cmd->body) + num_clips * sizeof(cmd->cr)); - cmd->body.sid = cpu_to_le32(surf->res.id); - - for (i = 0, cr = &cmd->cr; i < num_clips; i++, cr++, clips += inc) { - cr->x = cpu_to_le16(clips->x1); - cr->y = cpu_to_le16(clips->y1); - cr->srcx = cr->x; - cr->srcy = cr->y; - cr->w = cpu_to_le16(clips->x2 - clips->x1); - cr->h = cpu_to_le16(clips->y2 - clips->y1); - } - - vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr)); ttm_read_unlock(&vmaster->lock); return 0; } @@ -540,6 +530,10 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, struct vmw_master *vmaster = vmw_master(file_priv->master); int ret; + /* 3D is only supported on HWv8 hosts which supports screen objects */ + if (!dev_priv->sou_priv) + return -ENOSYS; + /* * Sanity checks. */ @@ -602,14 +596,11 @@ static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv, vfbs->base.base.depth = mode_cmd->depth; vfbs->base.base.width = mode_cmd->width; vfbs->base.base.height = mode_cmd->height; - vfbs->base.pin = &vmw_surface_dmabuf_pin; - vfbs->base.unpin = &vmw_surface_dmabuf_unpin; vfbs->surface = surface; + vfbs->base.user_handle = mode_cmd->handle; vfbs->master = drm_master_get(file_priv->master); - mutex_init(&vfbs->work_lock); mutex_lock(&vmaster->fb_surf_mutex); - INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback); list_add_tail(&vfbs->head, &vmaster->fb_surf); mutex_unlock(&vmaster->fb_surf_mutex); @@ -644,48 +635,34 @@ void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer) drm_framebuffer_cleanup(framebuffer); vmw_dmabuf_unreference(&vfbd->buffer); + ttm_base_object_unref(&vfbd->base.user_obj); kfree(vfbd); } -int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, - struct drm_file *file_priv, - unsigned flags, unsigned color, - struct drm_clip_rect *clips, - unsigned num_clips) +static int do_dmabuf_dirty_ldu(struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + struct vmw_dma_buffer *buffer, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips, int increment) { - struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); - struct vmw_master *vmaster = vmw_master(file_priv->master); - struct drm_clip_rect norect; - int ret; + size_t fifo_size; + int i; + struct { uint32_t header; SVGAFifoCmdUpdate body; } *cmd; - int i, increment = 1; - ret = ttm_read_lock(&vmaster->lock, true); - if (unlikely(ret != 0)) - return ret; - - if (!num_clips) { - num_clips = 1; - clips = &norect; - norect.x1 = norect.y1 = 0; - norect.x2 = framebuffer->width; - norect.y2 = framebuffer->height; - } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) { - num_clips /= 2; - increment = 2; - } - - cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips); + fifo_size = sizeof(*cmd) * num_clips; + cmd = vmw_fifo_reserve(dev_priv, fifo_size); if (unlikely(cmd == NULL)) { DRM_ERROR("Fifo reserve failed.\n"); - ttm_read_unlock(&vmaster->lock); return -ENOMEM; } + memset(cmd, 0, fifo_size); for (i = 0; i < num_clips; i++, clips += increment) { cmd[i].header = cpu_to_le32(SVGA_CMD_UPDATE); cmd[i].body.x = cpu_to_le32(clips->x1); @@ -694,57 +671,180 @@ int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, cmd[i].body.height = cpu_to_le32(clips->y2 - clips->y1); } - vmw_fifo_commit(dev_priv, sizeof(*cmd) * num_clips); - ttm_read_unlock(&vmaster->lock); - + vmw_fifo_commit(dev_priv, fifo_size); return 0; } -static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = { - .destroy = vmw_framebuffer_dmabuf_destroy, - .dirty = vmw_framebuffer_dmabuf_dirty, - .create_handle = vmw_framebuffer_create_handle, -}; - -static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb) +static int do_dmabuf_define_gmrfb(struct drm_file *file_priv, + struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer) { - struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); - struct vmw_framebuffer_surface *vfbs = - vmw_framebuffer_to_vfbs(&vfb->base); - unsigned long size = vfbs->base.base.pitch * vfbs->base.base.height; + size_t fifo_size; int ret; - vfbs->buffer = kzalloc(sizeof(*vfbs->buffer), GFP_KERNEL); - if (unlikely(vfbs->buffer == NULL)) + struct { + uint32_t header; + SVGAFifoCmdDefineGMRFB body; + } *cmd; + + fifo_size = sizeof(*cmd); + cmd = kmalloc(fifo_size, GFP_KERNEL); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed to allocate temporary cmd buffer.\n"); return -ENOMEM; + } - vmw_overlay_pause_all(dev_priv); - ret = vmw_dmabuf_init(dev_priv, vfbs->buffer, size, - &vmw_vram_ne_placement, - false, &vmw_dmabuf_bo_free); - vmw_overlay_resume_all(dev_priv); + memset(cmd, 0, fifo_size); + cmd->header = SVGA_CMD_DEFINE_GMRFB; + cmd->body.format.bitsPerPixel = framebuffer->base.bits_per_pixel; + cmd->body.format.colorDepth = framebuffer->base.depth; + cmd->body.format.reserved = 0; + cmd->body.bytesPerLine = framebuffer->base.pitch; + cmd->body.ptr.gmrId = framebuffer->user_handle; + cmd->body.ptr.offset = 0; + + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, + fifo_size, 0, NULL); + + kfree(cmd); + + return ret; +} + +static int do_dmabuf_dirty_sou(struct drm_file *file_priv, + struct vmw_private *dev_priv, + struct vmw_framebuffer *framebuffer, + struct vmw_dma_buffer *buffer, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips, int increment) +{ + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_clip_rect *clips_ptr; + int i, k, num_units, ret; + struct drm_crtc *crtc; + size_t fifo_size; + + struct { + uint32_t header; + SVGAFifoCmdBlitGMRFBToScreen body; + } *blits; + + ret = do_dmabuf_define_gmrfb(file_priv, dev_priv, framebuffer); if (unlikely(ret != 0)) - vfbs->buffer = NULL; + return ret; /* define_gmrfb prints warnings */ + + fifo_size = sizeof(*blits) * num_clips; + blits = kmalloc(fifo_size, GFP_KERNEL); + if (unlikely(blits == NULL)) { + DRM_ERROR("Failed to allocate temporary cmd buffer.\n"); + return -ENOMEM; + } + + num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { + if (crtc->fb != &framebuffer->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); + } + + for (k = 0; k < num_units; k++) { + struct vmw_display_unit *unit = units[k]; + int hit_num = 0; + + clips_ptr = clips; + for (i = 0; i < num_clips; i++, clips_ptr += increment) { + int clip_x1 = clips_ptr->x1 - unit->crtc.x; + int clip_y1 = clips_ptr->y1 - unit->crtc.y; + int clip_x2 = clips_ptr->x2 - unit->crtc.x; + int clip_y2 = clips_ptr->y2 - unit->crtc.y; + + /* skip any crtcs that misses the clip region */ + if (clip_x1 >= unit->crtc.mode.hdisplay || + clip_y1 >= unit->crtc.mode.vdisplay || + clip_x2 <= 0 || clip_y2 <= 0) + continue; + + blits[hit_num].header = SVGA_CMD_BLIT_GMRFB_TO_SCREEN; + blits[hit_num].body.destScreenId = unit->unit; + blits[hit_num].body.srcOrigin.x = clips_ptr->x1; + blits[hit_num].body.srcOrigin.y = clips_ptr->y1; + blits[hit_num].body.destRect.left = clip_x1; + blits[hit_num].body.destRect.top = clip_y1; + blits[hit_num].body.destRect.right = clip_x2; + blits[hit_num].body.destRect.bottom = clip_y2; + hit_num++; + } + + /* no clips hit the crtc */ + if (hit_num == 0) + continue; + + fifo_size = sizeof(*blits) * hit_num; + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, blits, + fifo_size, 0, NULL); + + if (unlikely(ret != 0)) + break; + } + + kfree(blits); return ret; } -static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb) +int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer, + struct drm_file *file_priv, + unsigned flags, unsigned color, + struct drm_clip_rect *clips, + unsigned num_clips) { - struct ttm_buffer_object *bo; - struct vmw_framebuffer_surface *vfbs = - vmw_framebuffer_to_vfbs(&vfb->base); + struct vmw_private *dev_priv = vmw_priv(framebuffer->dev); + struct vmw_master *vmaster = vmw_master(file_priv->master); + struct vmw_framebuffer_dmabuf *vfbd = + vmw_framebuffer_to_vfbd(framebuffer); + struct vmw_dma_buffer *dmabuf = vfbd->buffer; + struct drm_clip_rect norect; + int ret, increment = 1; - if (unlikely(vfbs->buffer == NULL)) - return 0; + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; - bo = &vfbs->buffer->base; - ttm_bo_unref(&bo); - vfbs->buffer = NULL; + if (!num_clips) { + num_clips = 1; + clips = &norect; + norect.x1 = norect.y1 = 0; + norect.x2 = framebuffer->width; + norect.y2 = framebuffer->height; + } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) { + num_clips /= 2; + increment = 2; + } - return 0; + if (dev_priv->ldu_priv) { + ret = do_dmabuf_dirty_ldu(dev_priv, &vfbd->base, dmabuf, + flags, color, + clips, num_clips, increment); + } else { + ret = do_dmabuf_dirty_sou(file_priv, dev_priv, &vfbd->base, + dmabuf, flags, color, + clips, num_clips, increment); + } + + ttm_read_unlock(&vmaster->lock); + return ret; } +static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = { + .destroy = vmw_framebuffer_dmabuf_destroy, + .dirty = vmw_framebuffer_dmabuf_dirty, + .create_handle = vmw_framebuffer_create_handle, +}; + +/** + * Pin the dmabuffer to the start of vram. + */ static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb) { struct vmw_private *dev_priv = vmw_priv(vfb->base.dev); @@ -752,10 +852,12 @@ static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb) vmw_framebuffer_to_vfbd(&vfb->base); int ret; + /* This code should not be used with screen objects */ + BUG_ON(dev_priv->sou_priv); vmw_overlay_pause_all(dev_priv); - ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer); + ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer, true, false); vmw_overlay_resume_all(dev_priv); @@ -775,7 +877,7 @@ static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb) return 0; } - return vmw_dmabuf_from_vram(dev_priv, vfbd->buffer); + return vmw_dmabuf_unpin(dev_priv, vfbd->buffer, false); } static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, @@ -797,6 +899,33 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, return -EINVAL; } + /* Limited framebuffer color depth support for screen objects */ + if (dev_priv->sou_priv) { + switch (mode_cmd->depth) { + case 32: + case 24: + /* Only support 32 bpp for 32 and 24 depth fbs */ + if (mode_cmd->bpp == 32) + break; + + DRM_ERROR("Invalid color depth/bbp: %d %d\n", + mode_cmd->depth, mode_cmd->bpp); + return -EINVAL; + case 16: + case 15: + /* Only support 16 bpp for 16 and 15 depth fbs */ + if (mode_cmd->bpp == 16) + break; + + DRM_ERROR("Invalid color depth/bbp: %d %d\n", + mode_cmd->depth, mode_cmd->bpp); + return -EINVAL; + default: + DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth); + return -EINVAL; + } + } + vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL); if (!vfbd) { ret = -ENOMEM; @@ -818,9 +947,13 @@ static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv, vfbd->base.base.depth = mode_cmd->depth; vfbd->base.base.width = mode_cmd->width; vfbd->base.base.height = mode_cmd->height; - vfbd->base.pin = vmw_framebuffer_dmabuf_pin; - vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin; + if (!dev_priv->sou_priv) { + vfbd->base.pin = vmw_framebuffer_dmabuf_pin; + vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin; + } + vfbd->base.dmabuf = true; vfbd->buffer = dmabuf; + vfbd->base.user_handle = mode_cmd->handle; *out = &vfbd->base; return 0; @@ -846,6 +979,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, struct vmw_framebuffer *vfb = NULL; struct vmw_surface *surface = NULL; struct vmw_dma_buffer *bo = NULL; + struct ttm_base_object *user_obj; u64 required_size; int ret; @@ -861,6 +995,21 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, return NULL; } + /* + * Take a reference on the user object of the resource + * backing the kms fb. This ensures that user-space handle + * lookups on that resource will always work as long as + * it's registered with a kms framebuffer. This is important, + * since vmw_execbuf_process identifies resources in the + * command stream using user-space handles. + */ + + user_obj = ttm_base_object_lookup(tfile, mode_cmd->handle); + if (unlikely(user_obj == NULL)) { + DRM_ERROR("Could not locate requested kms frame buffer.\n"); + return ERR_PTR(-ENOENT); + } + /** * End conditioned code. */ @@ -881,8 +1030,10 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, if (ret) { DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); + ttm_base_object_unref(&user_obj); return ERR_PTR(ret); - } + } else + vfb->user_obj = user_obj; return &vfb->base; try_dmabuf: @@ -902,8 +1053,10 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, if (ret) { DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret); + ttm_base_object_unref(&user_obj); return ERR_PTR(ret); - } + } else + vfb->user_obj = user_obj; return &vfb->base; @@ -911,6 +1064,7 @@ static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev, DRM_ERROR("surface not marked as scanout\n"); /* vmw_user_surface_lookup takes one ref */ vmw_surface_unreference(&surface); + ttm_base_object_unref(&user_obj); return ERR_PTR(-EINVAL); } @@ -919,6 +1073,210 @@ static struct drm_mode_config_funcs vmw_kms_funcs = { .fb_create = vmw_kms_fb_create, }; +int vmw_kms_present(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *vfb, + struct vmw_surface *surface, + uint32_t sid, + int32_t destX, int32_t destY, + struct drm_vmw_rect *clips, + uint32_t num_clips) +{ + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_crtc *crtc; + size_t fifo_size; + int i, k, num_units; + int ret = 0; /* silence warning */ + + struct { + SVGA3dCmdHeader header; + SVGA3dCmdBlitSurfaceToScreen body; + } *cmd; + SVGASignedRect *blits; + + num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { + if (crtc->fb != &vfb->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); + } + + BUG_ON(surface == NULL); + BUG_ON(!clips || !num_clips); + + fifo_size = sizeof(*cmd) + sizeof(SVGASignedRect) * num_clips; + cmd = kmalloc(fifo_size, GFP_KERNEL); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed to allocate temporary fifo memory.\n"); + return -ENOMEM; + } + + /* only need to do this once */ + memset(cmd, 0, fifo_size); + cmd->header.id = cpu_to_le32(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN); + cmd->header.size = cpu_to_le32(fifo_size - sizeof(cmd->header)); + + cmd->body.srcRect.left = 0; + cmd->body.srcRect.right = surface->sizes[0].width; + cmd->body.srcRect.top = 0; + cmd->body.srcRect.bottom = surface->sizes[0].height; + + blits = (SVGASignedRect *)&cmd[1]; + for (i = 0; i < num_clips; i++) { + blits[i].left = clips[i].x; + blits[i].right = clips[i].x + clips[i].w; + blits[i].top = clips[i].y; + blits[i].bottom = clips[i].y + clips[i].h; + } + + for (k = 0; k < num_units; k++) { + struct vmw_display_unit *unit = units[k]; + int clip_x1 = destX - unit->crtc.x; + int clip_y1 = destY - unit->crtc.y; + int clip_x2 = clip_x1 + surface->sizes[0].width; + int clip_y2 = clip_y1 + surface->sizes[0].height; + + /* skip any crtcs that misses the clip region */ + if (clip_x1 >= unit->crtc.mode.hdisplay || + clip_y1 >= unit->crtc.mode.vdisplay || + clip_x2 <= 0 || clip_y2 <= 0) + continue; + + /* need to reset sid as it is changed by execbuf */ + cmd->body.srcImage.sid = sid; + + cmd->body.destScreenId = unit->unit; + + /* + * The blit command is a lot more resilient then the + * readback command when it comes to clip rects. So its + * okay to go out of bounds. + */ + + cmd->body.destRect.left = clip_x1; + cmd->body.destRect.right = clip_x2; + cmd->body.destRect.top = clip_y1; + cmd->body.destRect.bottom = clip_y2; + + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, + fifo_size, 0, NULL); + + if (unlikely(ret != 0)) + break; + } + + kfree(cmd); + + return ret; +} + +int vmw_kms_readback(struct vmw_private *dev_priv, + struct drm_file *file_priv, + struct vmw_framebuffer *vfb, + struct drm_vmw_fence_rep __user *user_fence_rep, + struct drm_vmw_rect *clips, + uint32_t num_clips) +{ + struct vmw_framebuffer_dmabuf *vfbd = + vmw_framebuffer_to_vfbd(&vfb->base); + struct vmw_dma_buffer *dmabuf = vfbd->buffer; + struct vmw_display_unit *units[VMWGFX_NUM_DISPLAY_UNITS]; + struct drm_crtc *crtc; + size_t fifo_size; + int i, k, ret, num_units, blits_pos; + + struct { + uint32_t header; + SVGAFifoCmdDefineGMRFB body; + } *cmd; + struct { + uint32_t header; + SVGAFifoCmdBlitScreenToGMRFB body; + } *blits; + + num_units = 0; + list_for_each_entry(crtc, &dev_priv->dev->mode_config.crtc_list, head) { + if (crtc->fb != &vfb->base) + continue; + units[num_units++] = vmw_crtc_to_du(crtc); + } + + BUG_ON(dmabuf == NULL); + BUG_ON(!clips || !num_clips); + + /* take a safe guess at fifo size */ + fifo_size = sizeof(*cmd) + sizeof(*blits) * num_clips * num_units; + cmd = kmalloc(fifo_size, GFP_KERNEL); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed to allocate temporary fifo memory.\n"); + return -ENOMEM; + } + + memset(cmd, 0, fifo_size); + cmd->header = SVGA_CMD_DEFINE_GMRFB; + cmd->body.format.bitsPerPixel = vfb->base.bits_per_pixel; + cmd->body.format.colorDepth = vfb->base.depth; + cmd->body.format.reserved = 0; + cmd->body.bytesPerLine = vfb->base.pitch; + cmd->body.ptr.gmrId = vfb->user_handle; + cmd->body.ptr.offset = 0; + + blits = (void *)&cmd[1]; + blits_pos = 0; + for (i = 0; i < num_units; i++) { + struct drm_vmw_rect *c = clips; + for (k = 0; k < num_clips; k++, c++) { + /* transform clip coords to crtc origin based coords */ + int clip_x1 = c->x - units[i]->crtc.x; + int clip_x2 = c->x - units[i]->crtc.x + c->w; + int clip_y1 = c->y - units[i]->crtc.y; + int clip_y2 = c->y - units[i]->crtc.y + c->h; + int dest_x = c->x; + int dest_y = c->y; + + /* compensate for clipping, we negate + * a negative number and add that. + */ + if (clip_x1 < 0) + dest_x += -clip_x1; + if (clip_y1 < 0) + dest_y += -clip_y1; + + /* clip */ + clip_x1 = max(clip_x1, 0); + clip_y1 = max(clip_y1, 0); + clip_x2 = min(clip_x2, units[i]->crtc.mode.hdisplay); + clip_y2 = min(clip_y2, units[i]->crtc.mode.vdisplay); + + /* and cull any rects that misses the crtc */ + if (clip_x1 >= units[i]->crtc.mode.hdisplay || + clip_y1 >= units[i]->crtc.mode.vdisplay || + clip_x2 <= 0 || clip_y2 <= 0) + continue; + + blits[blits_pos].header = SVGA_CMD_BLIT_SCREEN_TO_GMRFB; + blits[blits_pos].body.srcScreenId = units[i]->unit; + blits[blits_pos].body.destOrigin.x = dest_x; + blits[blits_pos].body.destOrigin.y = dest_y; + + blits[blits_pos].body.srcRect.left = clip_x1; + blits[blits_pos].body.srcRect.top = clip_y1; + blits[blits_pos].body.srcRect.right = clip_x2; + blits[blits_pos].body.srcRect.bottom = clip_y2; + blits_pos++; + } + } + /* reset size here and use calculated exact size from loops */ + fifo_size = sizeof(*cmd) + sizeof(*blits) * blits_pos; + + ret = vmw_execbuf_process(file_priv, dev_priv, NULL, cmd, fifo_size, + 0, user_fence_rep); + + kfree(cmd); + + return ret; +} + int vmw_kms_init(struct vmw_private *dev_priv) { struct drm_device *dev = dev_priv->dev; @@ -932,7 +1290,9 @@ int vmw_kms_init(struct vmw_private *dev_priv) dev->mode_config.max_width = 8192; dev->mode_config.max_height = 8192; - ret = vmw_kms_init_legacy_display_system(dev_priv); + ret = vmw_kms_init_screen_object_display(dev_priv); + if (ret) /* Fallback */ + (void)vmw_kms_init_legacy_display_system(dev_priv); return 0; } @@ -1103,3 +1463,242 @@ u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc) { return 0; } + + +/* + * Small shared kms functions. + */ + +int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, + struct drm_vmw_rect *rects) +{ + struct drm_device *dev = dev_priv->dev; + struct vmw_display_unit *du; + struct drm_connector *con; + + mutex_lock(&dev->mode_config.mutex); + +#if 0 + { + unsigned int i; + + DRM_INFO("%s: new layout ", __func__); + for (i = 0; i < num; i++) + DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y, + rects[i].w, rects[i].h); + DRM_INFO("\n"); + } +#endif + + list_for_each_entry(con, &dev->mode_config.connector_list, head) { + du = vmw_connector_to_du(con); + if (num > du->unit) { + du->pref_width = rects[du->unit].w; + du->pref_height = rects[du->unit].h; + du->pref_active = true; + } else { + du->pref_width = 800; + du->pref_height = 600; + du->pref_active = false; + } + con->status = vmw_du_connector_detect(con, true); + } + + mutex_unlock(&dev->mode_config.mutex); + + return 0; +} + +void vmw_du_crtc_save(struct drm_crtc *crtc) +{ +} + +void vmw_du_crtc_restore(struct drm_crtc *crtc) +{ +} + +void vmw_du_crtc_gamma_set(struct drm_crtc *crtc, + u16 *r, u16 *g, u16 *b, + uint32_t start, uint32_t size) +{ + struct vmw_private *dev_priv = vmw_priv(crtc->dev); + int i; + + for (i = 0; i < size; i++) { + DRM_DEBUG("%d r/g/b = 0x%04x / 0x%04x / 0x%04x\n", i, + r[i], g[i], b[i]); + vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 0, r[i] >> 8); + vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 1, g[i] >> 8); + vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 2, b[i] >> 8); + } +} + +void vmw_du_connector_dpms(struct drm_connector *connector, int mode) +{ +} + +void vmw_du_connector_save(struct drm_connector *connector) +{ +} + +void vmw_du_connector_restore(struct drm_connector *connector) +{ +} + +enum drm_connector_status +vmw_du_connector_detect(struct drm_connector *connector, bool force) +{ + uint32_t num_displays; + struct drm_device *dev = connector->dev; + struct vmw_private *dev_priv = vmw_priv(dev); + + mutex_lock(&dev_priv->hw_mutex); + num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS); + mutex_unlock(&dev_priv->hw_mutex); + + return ((vmw_connector_to_du(connector)->unit < num_displays) ? + connector_status_connected : connector_status_disconnected); +} + +static struct drm_display_mode vmw_kms_connector_builtin[] = { + /* 640x480@60Hz */ + { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, + 752, 800, 0, 480, 489, 492, 525, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 800x600@60Hz */ + { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, + 968, 1056, 0, 600, 601, 605, 628, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1024x768@60Hz */ + { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, + 1184, 1344, 0, 768, 771, 777, 806, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 1152x864@75Hz */ + { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, + 1344, 1600, 0, 864, 865, 868, 900, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x768@60Hz */ + { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, + 1472, 1664, 0, 768, 771, 778, 798, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x800@60Hz */ + { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, + 1480, 1680, 0, 800, 803, 809, 831, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, + /* 1280x960@60Hz */ + { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, + 1488, 1800, 0, 960, 961, 964, 1000, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1280x1024@60Hz */ + { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, + 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1360x768@60Hz */ + { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, + 1536, 1792, 0, 768, 771, 777, 795, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1440x1050@60Hz */ + { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, + 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1440x900@60Hz */ + { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, + 1672, 1904, 0, 900, 903, 909, 934, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1600x1200@60Hz */ + { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, + 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1680x1050@60Hz */ + { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, + 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1792x1344@60Hz */ + { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, + 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1853x1392@60Hz */ + { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, + 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1920x1200@60Hz */ + { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, + 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 1920x1440@60Hz */ + { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, + 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* 2560x1600@60Hz */ + { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, + 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, + /* Terminate */ + { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }, +}; + +int vmw_du_connector_fill_modes(struct drm_connector *connector, + uint32_t max_width, uint32_t max_height) +{ + struct vmw_display_unit *du = vmw_connector_to_du(connector); + struct drm_device *dev = connector->dev; + struct vmw_private *dev_priv = vmw_priv(dev); + struct drm_display_mode *mode = NULL; + struct drm_display_mode *bmode; + struct drm_display_mode prefmode = { DRM_MODE("preferred", + DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) + }; + int i; + + /* Add preferred mode */ + { + mode = drm_mode_duplicate(dev, &prefmode); + if (!mode) + return 0; + mode->hdisplay = du->pref_width; + mode->vdisplay = du->pref_height; + mode->vrefresh = drm_mode_vrefresh(mode); + if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2, + mode->vdisplay)) { + drm_mode_probed_add(connector, mode); + + if (du->pref_mode) { + list_del_init(&du->pref_mode->head); + drm_mode_destroy(dev, du->pref_mode); + } + + du->pref_mode = mode; + } + } + + for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) { + bmode = &vmw_kms_connector_builtin[i]; + if (bmode->hdisplay > max_width || + bmode->vdisplay > max_height) + continue; + + if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2, + bmode->vdisplay)) + continue; + + mode = drm_mode_duplicate(dev, bmode); + if (!mode) + return 0; + mode->vrefresh = drm_mode_vrefresh(mode); + + drm_mode_probed_add(connector, mode); + } + + drm_mode_connector_list_update(connector); + + return 1; +} + +int vmw_du_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val) +{ + return 0; +} diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index 8a398a0339b6..db0b901f8c3f 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -31,6 +31,8 @@ #include "drmP.h" #include "vmwgfx_drv.h" +#define VMWGFX_NUM_DISPLAY_UNITS 8 + #define vmw_framebuffer_to_vfb(x) \ container_of(x, struct vmw_framebuffer, base) @@ -45,6 +47,9 @@ struct vmw_framebuffer { struct drm_framebuffer base; int (*pin)(struct vmw_framebuffer *fb); int (*unpin)(struct vmw_framebuffer *fb); + bool dmabuf; + struct ttm_base_object *user_obj; + uint32_t user_handle; }; @@ -83,22 +88,59 @@ struct vmw_display_unit { int hotspot_y; unsigned unit; + + /* + * Prefered mode tracking. + */ + unsigned pref_width; + unsigned pref_height; + bool pref_active; + struct drm_display_mode *pref_mode; }; +#define vmw_crtc_to_du(x) \ + container_of(x, struct vmw_display_unit, crtc) +#define vmw_connector_to_du(x) \ + container_of(x, struct vmw_display_unit, connector) + + /* * Shared display unit functions - vmwgfx_kms.c */ void vmw_display_unit_cleanup(struct vmw_display_unit *du); +void vmw_du_crtc_save(struct drm_crtc *crtc); +void vmw_du_crtc_restore(struct drm_crtc *crtc); +void vmw_du_crtc_gamma_set(struct drm_crtc *crtc, + u16 *r, u16 *g, u16 *b, + uint32_t start, uint32_t size); int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, uint32_t handle, uint32_t width, uint32_t height); int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); +void vmw_du_connector_dpms(struct drm_connector *connector, int mode); +void vmw_du_connector_save(struct drm_connector *connector); +void vmw_du_connector_restore(struct drm_connector *connector); +enum drm_connector_status +vmw_du_connector_detect(struct drm_connector *connector, bool force); +int vmw_du_connector_fill_modes(struct drm_connector *connector, + uint32_t max_width, uint32_t max_height); +int vmw_du_connector_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t val); +int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num, + struct drm_vmw_rect *rects); /* * Legacy display unit functions - vmwgfx_ldu.c */ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv); int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv); -int vmw_kms_ldu_update_layout(struct vmw_private *dev_priv, unsigned num, + +/* + * Screen Objects display functions - vmwgfx_scrn.c + */ +int vmw_kms_init_screen_object_display(struct vmw_private *dev_priv); +int vmw_kms_close_screen_object_display(struct vmw_private *dev_priv); +int vmw_kms_sou_update_layout(struct vmw_private *dev_priv, unsigned num, struct drm_vmw_rect *rects); #endif diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 7e1901c4f065..7fc8e7de180b 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -27,7 +27,6 @@ #include "vmwgfx_kms.h" -#define VMWGFX_LDU_NUM_DU 8 #define vmw_crtc_to_ldu(x) \ container_of(x, struct vmw_legacy_display_unit, base.crtc) @@ -51,11 +50,6 @@ struct vmw_legacy_display { struct vmw_legacy_display_unit { struct vmw_display_unit base; - unsigned pref_width; - unsigned pref_height; - bool pref_active; - struct drm_display_mode *pref_mode; - struct list_head active; }; @@ -71,29 +65,6 @@ static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu) * Legacy Display Unit CRTC functions */ -static void vmw_ldu_crtc_save(struct drm_crtc *crtc) -{ -} - -static void vmw_ldu_crtc_restore(struct drm_crtc *crtc) -{ -} - -static void vmw_ldu_crtc_gamma_set(struct drm_crtc *crtc, - u16 *r, u16 *g, u16 *b, - uint32_t start, uint32_t size) -{ - struct vmw_private *dev_priv = vmw_priv(crtc->dev); - int i; - - for (i = 0; i < size; i++) { - DRM_DEBUG("%d r/g/b = 0x%04x / 0x%04x / 0x%04x\n", i, r[i], g[i], b[i]); - vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 0, r[i] >> 8); - vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 1, g[i] >> 8); - vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 2, b[i] >> 8); - } -} - static void vmw_ldu_crtc_destroy(struct drm_crtc *crtc) { vmw_ldu_destroy(vmw_crtc_to_ldu(crtc)); @@ -301,15 +272,16 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) } static struct drm_crtc_funcs vmw_legacy_crtc_funcs = { - .save = vmw_ldu_crtc_save, - .restore = vmw_ldu_crtc_restore, + .save = vmw_du_crtc_save, + .restore = vmw_du_crtc_restore, .cursor_set = vmw_du_crtc_cursor_set, .cursor_move = vmw_du_crtc_cursor_move, - .gamma_set = vmw_ldu_crtc_gamma_set, + .gamma_set = vmw_du_crtc_gamma_set, .destroy = vmw_ldu_crtc_destroy, .set_config = vmw_ldu_crtc_set_config, }; + /* * Legacy Display Unit encoder functions */ @@ -327,190 +299,18 @@ static struct drm_encoder_funcs vmw_legacy_encoder_funcs = { * Legacy Display Unit connector functions */ -static void vmw_ldu_connector_dpms(struct drm_connector *connector, int mode) -{ -} - -static void vmw_ldu_connector_save(struct drm_connector *connector) -{ -} - -static void vmw_ldu_connector_restore(struct drm_connector *connector) -{ -} - -static enum drm_connector_status - vmw_ldu_connector_detect(struct drm_connector *connector, - bool force) -{ - uint32_t num_displays; - struct drm_device *dev = connector->dev; - struct vmw_private *dev_priv = vmw_priv(dev); - - mutex_lock(&dev_priv->hw_mutex); - num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS); - mutex_unlock(&dev_priv->hw_mutex); - - return ((vmw_connector_to_ldu(connector)->base.unit < num_displays) ? - connector_status_connected : connector_status_disconnected); -} - -static const struct drm_display_mode vmw_ldu_connector_builtin[] = { - /* 640x480@60Hz */ - { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656, - 752, 800, 0, 480, 489, 492, 525, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 800x600@60Hz */ - { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, - 968, 1056, 0, 600, 601, 605, 628, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1024x768@60Hz */ - { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048, - 1184, 1344, 0, 768, 771, 777, 806, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 1152x864@75Hz */ - { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216, - 1344, 1600, 0, 864, 865, 868, 900, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x768@60Hz */ - { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344, - 1472, 1664, 0, 768, 771, 778, 798, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x800@60Hz */ - { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352, - 1480, 1680, 0, 800, 803, 809, 831, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, - /* 1280x960@60Hz */ - { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376, - 1488, 1800, 0, 960, 961, 964, 1000, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1280x1024@60Hz */ - { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328, - 1440, 1688, 0, 1024, 1025, 1028, 1066, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1360x768@60Hz */ - { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424, - 1536, 1792, 0, 768, 771, 777, 795, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1440x1050@60Hz */ - { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488, - 1632, 1864, 0, 1050, 1053, 1057, 1089, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1440x900@60Hz */ - { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520, - 1672, 1904, 0, 900, 903, 909, 934, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1600x1200@60Hz */ - { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664, - 1856, 2160, 0, 1200, 1201, 1204, 1250, 0, - DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1680x1050@60Hz */ - { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784, - 1960, 2240, 0, 1050, 1053, 1059, 1089, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1792x1344@60Hz */ - { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920, - 2120, 2448, 0, 1344, 1345, 1348, 1394, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1853x1392@60Hz */ - { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952, - 2176, 2528, 0, 1392, 1393, 1396, 1439, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1920x1200@60Hz */ - { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056, - 2256, 2592, 0, 1200, 1203, 1209, 1245, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 1920x1440@60Hz */ - { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048, - 2256, 2600, 0, 1440, 1441, 1444, 1500, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* 2560x1600@60Hz */ - { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752, - 3032, 3504, 0, 1600, 1603, 1609, 1658, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) }, - /* Terminate */ - { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) }, -}; - -static int vmw_ldu_connector_fill_modes(struct drm_connector *connector, - uint32_t max_width, uint32_t max_height) -{ - struct vmw_legacy_display_unit *ldu = vmw_connector_to_ldu(connector); - struct drm_device *dev = connector->dev; - struct vmw_private *dev_priv = vmw_priv(dev); - struct drm_display_mode *mode = NULL; - struct drm_display_mode prefmode = { DRM_MODE("preferred", - DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) - }; - int i; - - /* Add preferred mode */ - { - mode = drm_mode_duplicate(dev, &prefmode); - if (!mode) - return 0; - mode->hdisplay = ldu->pref_width; - mode->vdisplay = ldu->pref_height; - mode->vrefresh = drm_mode_vrefresh(mode); - if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2, - mode->vdisplay)) { - drm_mode_probed_add(connector, mode); - - if (ldu->pref_mode) { - list_del_init(&ldu->pref_mode->head); - drm_mode_destroy(dev, ldu->pref_mode); - } - - ldu->pref_mode = mode; - } - } - - for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) { - const struct drm_display_mode *bmode; - - bmode = &vmw_ldu_connector_builtin[i]; - if (bmode->hdisplay > max_width || - bmode->vdisplay > max_height) - continue; - - if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2, - bmode->vdisplay)) - continue; - - mode = drm_mode_duplicate(dev, bmode); - if (!mode) - return 0; - mode->vrefresh = drm_mode_vrefresh(mode); - - drm_mode_probed_add(connector, mode); - } - - drm_mode_connector_list_update(connector); - - return 1; -} - -static int vmw_ldu_connector_set_property(struct drm_connector *connector, - struct drm_property *property, - uint64_t val) -{ - return 0; -} - static void vmw_ldu_connector_destroy(struct drm_connector *connector) { vmw_ldu_destroy(vmw_connector_to_ldu(connector)); } static struct drm_connector_funcs vmw_legacy_connector_funcs = { - .dpms = vmw_ldu_connector_dpms, - .save = vmw_ldu_connector_save, - .restore = vmw_ldu_connector_restore, - .detect = vmw_ldu_connector_detect, - .fill_modes = vmw_ldu_connector_fill_modes, - .set_property = vmw_ldu_connector_set_property, + .dpms = vmw_du_connector_dpms, + .save = vmw_du_connector_save, + .restore = vmw_du_connector_restore, + .detect = vmw_du_connector_detect, + .fill_modes = vmw_du_connector_fill_modes, + .set_property = vmw_du_connector_set_property, .destroy = vmw_ldu_connector_destroy, }; @@ -533,14 +333,14 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) INIT_LIST_HEAD(&ldu->active); - ldu->pref_active = (unit == 0); - ldu->pref_width = 800; - ldu->pref_height = 600; - ldu->pref_mode = NULL; + ldu->base.pref_active = (unit == 0); + ldu->base.pref_width = 800; + ldu->base.pref_height = 600; + ldu->base.pref_mode = NULL; drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, DRM_MODE_CONNECTOR_LVDS); - connector->status = vmw_ldu_connector_detect(connector, true); + connector->status = vmw_du_connector_detect(connector, true); drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, DRM_MODE_ENCODER_LVDS); @@ -583,9 +383,9 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv) drm_mode_create_dirty_info_property(dev_priv->dev); if (dev_priv->capabilities & SVGA_CAP_MULTIMON) { - for (i = 0; i < VMWGFX_LDU_NUM_DU; ++i) + for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) vmw_ldu_init(dev_priv, i); - ret = drm_vblank_init(dev, VMWGFX_LDU_NUM_DU); + ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS); } else { /* for old hardware without multimon only enable one display */ vmw_ldu_init(dev_priv, 0); @@ -609,42 +409,3 @@ int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv) return 0; } - -int vmw_kms_ldu_update_layout(struct vmw_private *dev_priv, unsigned num, - struct drm_vmw_rect *rects) -{ - struct drm_device *dev = dev_priv->dev; - struct vmw_legacy_display_unit *ldu; - struct drm_connector *con; - int i; - - mutex_lock(&dev->mode_config.mutex); - -#if 0 - DRM_INFO("%s: new layout ", __func__); - for (i = 0; i < (int)num; i++) - DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y, - rects[i].w, rects[i].h); - DRM_INFO("\n"); -#else - (void)i; -#endif - - list_for_each_entry(con, &dev->mode_config.connector_list, head) { - ldu = vmw_connector_to_ldu(con); - if (num > ldu->base.unit) { - ldu->pref_width = rects[ldu->base.unit].w; - ldu->pref_height = rects[ldu->base.unit].h; - ldu->pref_active = true; - } else { - ldu->pref_width = 800; - ldu->pref_height = 600; - ldu->pref_active = false; - } - con->status = vmw_ldu_connector_detect(con, true); - } - - mutex_unlock(&dev->mode_config.mutex); - - return 0; -} diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c index 07ce02da78a4..14399eec9c3c 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c @@ -86,48 +86,6 @@ static inline void fill_flush(struct vmw_escape_video_flush *cmd, cmd->flush.streamId = stream_id; } -/** - * Pin or unpin a buffer in vram. - * - * @dev_priv: Driver private. - * @buf: DMA buffer to pin or unpin. - * @pin: Pin buffer in vram if true. - * @interruptible: Use interruptible wait. - * - * Takes the current masters ttm lock in read. - * - * Returns - * -ERESTARTSYS if interrupted by a signal. - */ -static int vmw_dmabuf_pin_in_vram(struct vmw_private *dev_priv, - struct vmw_dma_buffer *buf, - bool pin, bool interruptible) -{ - struct ttm_buffer_object *bo = &buf->base; - struct ttm_placement *overlay_placement = &vmw_vram_placement; - int ret; - - ret = ttm_read_lock(&dev_priv->active_master->lock, interruptible); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_bo_reserve(bo, interruptible, false, false, 0); - if (unlikely(ret != 0)) - goto err; - - if (pin) - overlay_placement = &vmw_vram_ne_placement; - - ret = ttm_bo_validate(bo, overlay_placement, interruptible, false, false); - - ttm_bo_unreserve(bo); - -err: - ttm_read_unlock(&dev_priv->active_master->lock); - - return ret; -} - /** * Send put command to hw. * @@ -139,68 +97,80 @@ static int vmw_overlay_send_put(struct vmw_private *dev_priv, struct drm_vmw_control_stream_arg *arg, bool interruptible) { + struct vmw_escape_video_flush *flush; + size_t fifo_size; + bool have_so = dev_priv->sou_priv ? true : false; + int i, num_items; + SVGAGuestPtr ptr; + struct { struct vmw_escape_header escape; struct { - struct { - uint32_t cmdType; - uint32_t streamId; - } header; - struct { - uint32_t registerId; - uint32_t value; - } items[SVGA_VIDEO_PITCH_3 + 1]; - } body; - struct vmw_escape_video_flush flush; + uint32_t cmdType; + uint32_t streamId; + } header; } *cmds; - uint32_t offset; - int i, ret; + struct { + uint32_t registerId; + uint32_t value; + } *items; - for (;;) { - cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds)); - if (cmds) - break; + /* defines are a index needs + 1 */ + if (have_so) + num_items = SVGA_VIDEO_DST_SCREEN_ID + 1; + else + num_items = SVGA_VIDEO_PITCH_3 + 1; - ret = vmw_fallback_wait(dev_priv, false, true, 0, - interruptible, 3*HZ); - if (interruptible && ret == -ERESTARTSYS) - return ret; - else - BUG_ON(ret != 0); + fifo_size = sizeof(*cmds) + sizeof(*flush) + sizeof(*items) * num_items; + + cmds = vmw_fifo_reserve(dev_priv, fifo_size); + /* hardware has hung, can't do anything here */ + if (!cmds) + return -ENOMEM; + + items = (typeof(items))&cmds[1]; + flush = (struct vmw_escape_video_flush *)&items[num_items]; + + /* the size is header + number of items */ + fill_escape(&cmds->escape, sizeof(*items) * (num_items + 1)); + + cmds->header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; + cmds->header.streamId = arg->stream_id; + + /* the IDs are neatly numbered */ + for (i = 0; i < num_items; i++) + items[i].registerId = i; + + vmw_bo_get_guest_ptr(&buf->base, &ptr); + ptr.offset += arg->offset; + + items[SVGA_VIDEO_ENABLED].value = true; + items[SVGA_VIDEO_FLAGS].value = arg->flags; + items[SVGA_VIDEO_DATA_OFFSET].value = ptr.offset; + items[SVGA_VIDEO_FORMAT].value = arg->format; + items[SVGA_VIDEO_COLORKEY].value = arg->color_key; + items[SVGA_VIDEO_SIZE].value = arg->size; + items[SVGA_VIDEO_WIDTH].value = arg->width; + items[SVGA_VIDEO_HEIGHT].value = arg->height; + items[SVGA_VIDEO_SRC_X].value = arg->src.x; + items[SVGA_VIDEO_SRC_Y].value = arg->src.y; + items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w; + items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h; + items[SVGA_VIDEO_DST_X].value = arg->dst.x; + items[SVGA_VIDEO_DST_Y].value = arg->dst.y; + items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w; + items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h; + items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0]; + items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1]; + items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2]; + if (have_so) { + items[SVGA_VIDEO_DATA_GMRID].value = ptr.gmrId; + items[SVGA_VIDEO_DST_SCREEN_ID].value = SVGA_ID_INVALID; } - fill_escape(&cmds->escape, sizeof(cmds->body)); - cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS; - cmds->body.header.streamId = arg->stream_id; - - for (i = 0; i <= SVGA_VIDEO_PITCH_3; i++) - cmds->body.items[i].registerId = i; - - offset = buf->base.offset + arg->offset; - - cmds->body.items[SVGA_VIDEO_ENABLED].value = true; - cmds->body.items[SVGA_VIDEO_FLAGS].value = arg->flags; - cmds->body.items[SVGA_VIDEO_DATA_OFFSET].value = offset; - cmds->body.items[SVGA_VIDEO_FORMAT].value = arg->format; - cmds->body.items[SVGA_VIDEO_COLORKEY].value = arg->color_key; - cmds->body.items[SVGA_VIDEO_SIZE].value = arg->size; - cmds->body.items[SVGA_VIDEO_WIDTH].value = arg->width; - cmds->body.items[SVGA_VIDEO_HEIGHT].value = arg->height; - cmds->body.items[SVGA_VIDEO_SRC_X].value = arg->src.x; - cmds->body.items[SVGA_VIDEO_SRC_Y].value = arg->src.y; - cmds->body.items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w; - cmds->body.items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h; - cmds->body.items[SVGA_VIDEO_DST_X].value = arg->dst.x; - cmds->body.items[SVGA_VIDEO_DST_Y].value = arg->dst.y; - cmds->body.items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w; - cmds->body.items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h; - cmds->body.items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0]; - cmds->body.items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1]; - cmds->body.items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2]; - - fill_flush(&cmds->flush, arg->stream_id); + fill_flush(flush, arg->stream_id); - vmw_fifo_commit(dev_priv, sizeof(*cmds)); + vmw_fifo_commit(dev_priv, fifo_size); return 0; } @@ -247,6 +217,25 @@ static int vmw_overlay_send_stop(struct vmw_private *dev_priv, return 0; } +/** + * Move a buffer to vram or gmr if @pin is set, else unpin the buffer. + * + * With the introduction of screen objects buffers could now be + * used with GMRs instead of being locked to vram. + */ +static int vmw_overlay_move_buffer(struct vmw_private *dev_priv, + struct vmw_dma_buffer *buf, + bool pin, bool inter) +{ + if (!pin) + return vmw_dmabuf_unpin(dev_priv, buf, inter); + + if (!dev_priv->sou_priv) + return vmw_dmabuf_to_vram(dev_priv, buf, true, inter); + + return vmw_dmabuf_to_vram_or_gmr(dev_priv, buf, true, inter); +} + /** * Stop or pause a stream. * @@ -279,8 +268,8 @@ static int vmw_overlay_stop(struct vmw_private *dev_priv, return ret; /* We just remove the NO_EVICT flag so no -ENOMEM */ - ret = vmw_dmabuf_pin_in_vram(dev_priv, stream->buf, false, - interruptible); + ret = vmw_overlay_move_buffer(dev_priv, stream->buf, false, + interruptible); if (interruptible && ret == -ERESTARTSYS) return ret; else @@ -342,7 +331,7 @@ static int vmw_overlay_update_stream(struct vmw_private *dev_priv, /* We don't start the old stream if we are interrupted. * Might return -ENOMEM if it can't fit the buffer in vram. */ - ret = vmw_dmabuf_pin_in_vram(dev_priv, buf, true, interruptible); + ret = vmw_overlay_move_buffer(dev_priv, buf, true, interruptible); if (ret) return ret; @@ -351,7 +340,8 @@ static int vmw_overlay_update_stream(struct vmw_private *dev_priv, /* This one needs to happen no matter what. We only remove * the NO_EVICT flag so this is safe from -ENOMEM. */ - BUG_ON(vmw_dmabuf_pin_in_vram(dev_priv, buf, false, false) != 0); + BUG_ON(vmw_overlay_move_buffer(dev_priv, buf, false, false) + != 0); return ret; } diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index c1b6ffd4ce7b..c7cff3debe11 100644 --- a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -39,6 +39,7 @@ struct vmw_user_context { struct vmw_user_surface { struct ttm_base_object base; struct vmw_surface srf; + uint32_t size; }; struct vmw_user_dma_buffer { @@ -61,6 +62,17 @@ struct vmw_user_stream { struct vmw_stream stream; }; +struct vmw_surface_offset { + uint32_t face; + uint32_t mip; + uint32_t bo_offset; +}; + + +static uint64_t vmw_user_context_size; +static uint64_t vmw_user_surface_size; +static uint64_t vmw_user_stream_size; + static inline struct vmw_dma_buffer * vmw_dma_buffer(struct ttm_buffer_object *bo) { @@ -80,13 +92,36 @@ struct vmw_resource *vmw_resource_reference(struct vmw_resource *res) return res; } + +/** + * vmw_resource_release_id - release a resource id to the id manager. + * + * @res: Pointer to the resource. + * + * Release the resource id to the resource id manager and set it to -1 + */ +static void vmw_resource_release_id(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + + write_lock(&dev_priv->resource_lock); + if (res->id != -1) + idr_remove(res->idr, res->id); + res->id = -1; + write_unlock(&dev_priv->resource_lock); +} + static void vmw_resource_release(struct kref *kref) { struct vmw_resource *res = container_of(kref, struct vmw_resource, kref); struct vmw_private *dev_priv = res->dev_priv; + int id = res->id; + struct idr *idr = res->idr; - idr_remove(res->idr, res->id); + res->avail = false; + if (res->remove_from_lists != NULL) + res->remove_from_lists(res); write_unlock(&dev_priv->resource_lock); if (likely(res->hw_destroy != NULL)) @@ -98,6 +133,9 @@ static void vmw_resource_release(struct kref *kref) kfree(res); write_lock(&dev_priv->resource_lock); + + if (id != -1) + idr_remove(idr, id); } void vmw_resource_unreference(struct vmw_resource **p_res) @@ -111,28 +149,29 @@ void vmw_resource_unreference(struct vmw_resource **p_res) write_unlock(&dev_priv->resource_lock); } -static int vmw_resource_init(struct vmw_private *dev_priv, - struct vmw_resource *res, - struct idr *idr, - enum ttm_object_type obj_type, - void (*res_free) (struct vmw_resource *res)) + +/** + * vmw_resource_alloc_id - release a resource id to the id manager. + * + * @dev_priv: Pointer to the device private structure. + * @res: Pointer to the resource. + * + * Allocate the lowest free resource from the resource manager, and set + * @res->id to that id. Returns 0 on success and -ENOMEM on failure. + */ +static int vmw_resource_alloc_id(struct vmw_private *dev_priv, + struct vmw_resource *res) { int ret; - kref_init(&res->kref); - res->hw_destroy = NULL; - res->res_free = res_free; - res->res_type = obj_type; - res->idr = idr; - res->avail = false; - res->dev_priv = dev_priv; + BUG_ON(res->id != -1); do { - if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0)) + if (unlikely(idr_pre_get(res->idr, GFP_KERNEL) == 0)) return -ENOMEM; write_lock(&dev_priv->resource_lock); - ret = idr_get_new_above(idr, res, 1, &res->id); + ret = idr_get_new_above(res->idr, res, 1, &res->id); write_unlock(&dev_priv->resource_lock); } while (ret == -EAGAIN); @@ -140,6 +179,33 @@ static int vmw_resource_init(struct vmw_private *dev_priv, return ret; } + +static int vmw_resource_init(struct vmw_private *dev_priv, + struct vmw_resource *res, + struct idr *idr, + enum ttm_object_type obj_type, + bool delay_id, + void (*res_free) (struct vmw_resource *res), + void (*remove_from_lists) + (struct vmw_resource *res)) +{ + kref_init(&res->kref); + res->hw_destroy = NULL; + res->res_free = res_free; + res->remove_from_lists = remove_from_lists; + res->res_type = obj_type; + res->idr = idr; + res->avail = false; + res->dev_priv = dev_priv; + INIT_LIST_HEAD(&res->query_head); + INIT_LIST_HEAD(&res->validate_head); + res->id = -1; + if (delay_id) + return 0; + else + return vmw_resource_alloc_id(dev_priv, res); +} + /** * vmw_resource_activate * @@ -194,8 +260,12 @@ static void vmw_hw_context_destroy(struct vmw_resource *res) struct { SVGA3dCmdHeader header; SVGA3dCmdDestroyContext body; - } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + } *cmd; + + vmw_execbuf_release_pinned_bo(dev_priv, true, res->id); + + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); if (unlikely(cmd == NULL)) { DRM_ERROR("Failed reserving FIFO space for surface " "destruction.\n"); @@ -222,14 +292,17 @@ static int vmw_context_init(struct vmw_private *dev_priv, } *cmd; ret = vmw_resource_init(dev_priv, res, &dev_priv->context_idr, - VMW_RES_CONTEXT, res_free); + VMW_RES_CONTEXT, false, res_free, NULL); if (unlikely(ret != 0)) { - if (res_free == NULL) - kfree(res); - else - res_free(res); - return ret; + DRM_ERROR("Failed to allocate a resource id.\n"); + goto out_early; + } + + if (unlikely(res->id >= SVGA3D_MAX_CONTEXT_IDS)) { + DRM_ERROR("Out of hw context ids.\n"); + vmw_resource_unreference(&res); + return -ENOMEM; } cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); @@ -247,6 +320,13 @@ static int vmw_context_init(struct vmw_private *dev_priv, (void) vmw_3d_resource_inc(dev_priv, false); vmw_resource_activate(res, vmw_hw_context_destroy); return 0; + +out_early: + if (res_free == NULL) + kfree(res); + else + res_free(res); + return ret; } struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv) @@ -269,8 +349,11 @@ static void vmw_user_context_free(struct vmw_resource *res) { struct vmw_user_context *ctx = container_of(res, struct vmw_user_context, res); + struct vmw_private *dev_priv = res->dev_priv; kfree(ctx); + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_context_size); } /** @@ -324,23 +407,56 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_context *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + struct vmw_user_context *ctx; struct vmw_resource *res; struct vmw_resource *tmp; struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_master *vmaster = vmw_master(file_priv->master); int ret; - if (unlikely(ctx == NULL)) - return -ENOMEM; + + /* + * Approximate idr memory usage with 128 bytes. It will be limited + * by maximum number_of contexts anyway. + */ + + if (unlikely(vmw_user_context_size == 0)) + vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128; + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + vmw_user_context_size, + false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for context" + " creation.\n"); + goto out_unlock; + } + + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + if (unlikely(ctx == NULL)) { + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_context_size); + ret = -ENOMEM; + goto out_unlock; + } res = &ctx->res; ctx->base.shareable = false; ctx->base.tfile = NULL; + /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_context_init(dev_priv, res, vmw_user_context_free); if (unlikely(ret != 0)) - return ret; + goto out_unlock; tmp = vmw_resource_reference(&ctx->res); ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT, @@ -354,6 +470,8 @@ int vmw_context_define_ioctl(struct drm_device *dev, void *data, arg->cid = res->id; out_err: vmw_resource_unreference(&res); +out_unlock: + ttm_read_unlock(&vmaster->lock); return ret; } @@ -382,31 +500,285 @@ int vmw_context_check(struct vmw_private *dev_priv, return ret; } +struct vmw_bpp { + uint8_t bpp; + uint8_t s_bpp; +}; + +/* + * Size table for the supported SVGA3D surface formats. It consists of + * two values. The bpp value and the s_bpp value which is short for + * "stride bits per pixel" The values are given in such a way that the + * minimum stride for the image is calculated using + * + * min_stride = w*s_bpp + * + * and the total memory requirement for the image is + * + * h*min_stride*bpp/s_bpp + * + */ +static const struct vmw_bpp vmw_sf_bpp[] = { + [SVGA3D_FORMAT_INVALID] = {0, 0}, + [SVGA3D_X8R8G8B8] = {32, 32}, + [SVGA3D_A8R8G8B8] = {32, 32}, + [SVGA3D_R5G6B5] = {16, 16}, + [SVGA3D_X1R5G5B5] = {16, 16}, + [SVGA3D_A1R5G5B5] = {16, 16}, + [SVGA3D_A4R4G4B4] = {16, 16}, + [SVGA3D_Z_D32] = {32, 32}, + [SVGA3D_Z_D16] = {16, 16}, + [SVGA3D_Z_D24S8] = {32, 32}, + [SVGA3D_Z_D15S1] = {16, 16}, + [SVGA3D_LUMINANCE8] = {8, 8}, + [SVGA3D_LUMINANCE4_ALPHA4] = {8, 8}, + [SVGA3D_LUMINANCE16] = {16, 16}, + [SVGA3D_LUMINANCE8_ALPHA8] = {16, 16}, + [SVGA3D_DXT1] = {4, 16}, + [SVGA3D_DXT2] = {8, 32}, + [SVGA3D_DXT3] = {8, 32}, + [SVGA3D_DXT4] = {8, 32}, + [SVGA3D_DXT5] = {8, 32}, + [SVGA3D_BUMPU8V8] = {16, 16}, + [SVGA3D_BUMPL6V5U5] = {16, 16}, + [SVGA3D_BUMPX8L8V8U8] = {32, 32}, + [SVGA3D_ARGB_S10E5] = {16, 16}, + [SVGA3D_ARGB_S23E8] = {32, 32}, + [SVGA3D_A2R10G10B10] = {32, 32}, + [SVGA3D_V8U8] = {16, 16}, + [SVGA3D_Q8W8V8U8] = {32, 32}, + [SVGA3D_CxV8U8] = {16, 16}, + [SVGA3D_X8L8V8U8] = {32, 32}, + [SVGA3D_A2W10V10U10] = {32, 32}, + [SVGA3D_ALPHA8] = {8, 8}, + [SVGA3D_R_S10E5] = {16, 16}, + [SVGA3D_R_S23E8] = {32, 32}, + [SVGA3D_RG_S10E5] = {16, 16}, + [SVGA3D_RG_S23E8] = {32, 32}, + [SVGA3D_BUFFER] = {8, 8}, + [SVGA3D_Z_D24X8] = {32, 32}, + [SVGA3D_V16U16] = {32, 32}, + [SVGA3D_G16R16] = {32, 32}, + [SVGA3D_A16B16G16R16] = {64, 64}, + [SVGA3D_UYVY] = {12, 12}, + [SVGA3D_YUY2] = {12, 12}, + [SVGA3D_NV12] = {12, 8}, + [SVGA3D_AYUV] = {32, 32}, + [SVGA3D_BC4_UNORM] = {4, 16}, + [SVGA3D_BC5_UNORM] = {8, 32}, + [SVGA3D_Z_DF16] = {16, 16}, + [SVGA3D_Z_DF24] = {24, 24}, + [SVGA3D_Z_D24S8_INT] = {32, 32} +}; + /** * Surface management. */ +struct vmw_surface_dma { + SVGA3dCmdHeader header; + SVGA3dCmdSurfaceDMA body; + SVGA3dCopyBox cb; + SVGA3dCmdSurfaceDMASuffix suffix; +}; + +struct vmw_surface_define { + SVGA3dCmdHeader header; + SVGA3dCmdDefineSurface body; +}; + +struct vmw_surface_destroy { + SVGA3dCmdHeader header; + SVGA3dCmdDestroySurface body; +}; + + +/** + * vmw_surface_dma_size - Compute fifo size for a dma command. + * + * @srf: Pointer to a struct vmw_surface + * + * Computes the required size for a surface dma command for backup or + * restoration of the surface represented by @srf. + */ +static inline uint32_t vmw_surface_dma_size(const struct vmw_surface *srf) +{ + return srf->num_sizes * sizeof(struct vmw_surface_dma); +} + + +/** + * vmw_surface_define_size - Compute fifo size for a surface define command. + * + * @srf: Pointer to a struct vmw_surface + * + * Computes the required size for a surface define command for the definition + * of the surface represented by @srf. + */ +static inline uint32_t vmw_surface_define_size(const struct vmw_surface *srf) +{ + return sizeof(struct vmw_surface_define) + srf->num_sizes * + sizeof(SVGA3dSize); +} + + +/** + * vmw_surface_destroy_size - Compute fifo size for a surface destroy command. + * + * Computes the required size for a surface destroy command for the destruction + * of a hw surface. + */ +static inline uint32_t vmw_surface_destroy_size(void) +{ + return sizeof(struct vmw_surface_destroy); +} + +/** + * vmw_surface_destroy_encode - Encode a surface_destroy command. + * + * @id: The surface id + * @cmd_space: Pointer to memory area in which the commands should be encoded. + */ +static void vmw_surface_destroy_encode(uint32_t id, + void *cmd_space) +{ + struct vmw_surface_destroy *cmd = (struct vmw_surface_destroy *) + cmd_space; + + cmd->header.id = SVGA_3D_CMD_SURFACE_DESTROY; + cmd->header.size = sizeof(cmd->body); + cmd->body.sid = id; +} + +/** + * vmw_surface_define_encode - Encode a surface_define command. + * + * @srf: Pointer to a struct vmw_surface object. + * @cmd_space: Pointer to memory area in which the commands should be encoded. + */ +static void vmw_surface_define_encode(const struct vmw_surface *srf, + void *cmd_space) +{ + struct vmw_surface_define *cmd = (struct vmw_surface_define *) + cmd_space; + struct drm_vmw_size *src_size; + SVGA3dSize *cmd_size; + uint32_t cmd_len; + int i; + + cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize); + + cmd->header.id = SVGA_3D_CMD_SURFACE_DEFINE; + cmd->header.size = cmd_len; + cmd->body.sid = srf->res.id; + cmd->body.surfaceFlags = srf->flags; + cmd->body.format = cpu_to_le32(srf->format); + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) + cmd->body.face[i].numMipLevels = srf->mip_levels[i]; + + cmd += 1; + cmd_size = (SVGA3dSize *) cmd; + src_size = srf->sizes; + + for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) { + cmd_size->width = src_size->width; + cmd_size->height = src_size->height; + cmd_size->depth = src_size->depth; + } +} + + +/** + * vmw_surface_dma_encode - Encode a surface_dma command. + * + * @srf: Pointer to a struct vmw_surface object. + * @cmd_space: Pointer to memory area in which the commands should be encoded. + * @ptr: Pointer to an SVGAGuestPtr indicating where the surface contents + * should be placed or read from. + * @to_surface: Boolean whether to DMA to the surface or from the surface. + */ +static void vmw_surface_dma_encode(struct vmw_surface *srf, + void *cmd_space, + const SVGAGuestPtr *ptr, + bool to_surface) +{ + uint32_t i; + uint32_t bpp = vmw_sf_bpp[srf->format].bpp; + uint32_t stride_bpp = vmw_sf_bpp[srf->format].s_bpp; + struct vmw_surface_dma *cmd = (struct vmw_surface_dma *)cmd_space; + + for (i = 0; i < srf->num_sizes; ++i) { + SVGA3dCmdHeader *header = &cmd->header; + SVGA3dCmdSurfaceDMA *body = &cmd->body; + SVGA3dCopyBox *cb = &cmd->cb; + SVGA3dCmdSurfaceDMASuffix *suffix = &cmd->suffix; + const struct vmw_surface_offset *cur_offset = &srf->offsets[i]; + const struct drm_vmw_size *cur_size = &srf->sizes[i]; + + header->id = SVGA_3D_CMD_SURFACE_DMA; + header->size = sizeof(*body) + sizeof(*cb) + sizeof(*suffix); + + body->guest.ptr = *ptr; + body->guest.ptr.offset += cur_offset->bo_offset; + body->guest.pitch = (cur_size->width * stride_bpp + 7) >> 3; + body->host.sid = srf->res.id; + body->host.face = cur_offset->face; + body->host.mipmap = cur_offset->mip; + body->transfer = ((to_surface) ? SVGA3D_WRITE_HOST_VRAM : + SVGA3D_READ_HOST_VRAM); + cb->x = 0; + cb->y = 0; + cb->z = 0; + cb->srcx = 0; + cb->srcy = 0; + cb->srcz = 0; + cb->w = cur_size->width; + cb->h = cur_size->height; + cb->d = cur_size->depth; + + suffix->suffixSize = sizeof(*suffix); + suffix->maximumOffset = body->guest.pitch*cur_size->height* + cur_size->depth*bpp / stride_bpp; + suffix->flags.discard = 0; + suffix->flags.unsynchronized = 0; + suffix->flags.reserved = 0; + ++cmd; + } +}; + + static void vmw_hw_surface_destroy(struct vmw_resource *res) { struct vmw_private *dev_priv = res->dev_priv; - struct { - SVGA3dCmdHeader header; - SVGA3dCmdDestroySurface body; - } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + struct vmw_surface *srf; + void *cmd; - if (unlikely(cmd == NULL)) { - DRM_ERROR("Failed reserving FIFO space for surface " - "destruction.\n"); - return; - } + if (res->id != -1) { - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DESTROY); - cmd->header.size = cpu_to_le32(sizeof(cmd->body)); - cmd->body.sid = cpu_to_le32(res->id); + cmd = vmw_fifo_reserve(dev_priv, vmw_surface_destroy_size()); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed reserving FIFO space for surface " + "destruction.\n"); + return; + } - vmw_fifo_commit(dev_priv, sizeof(*cmd)); + vmw_surface_destroy_encode(res->id, cmd); + vmw_fifo_commit(dev_priv, vmw_surface_destroy_size()); + + /* + * used_memory_size_atomic, or separate lock + * to avoid taking dev_priv::cmdbuf_mutex in + * the destroy path. + */ + + mutex_lock(&dev_priv->cmdbuf_mutex); + srf = container_of(res, struct vmw_surface, res); + dev_priv->used_memory_size -= srf->backup_size; + mutex_unlock(&dev_priv->cmdbuf_mutex); + + } vmw_3d_resource_dec(dev_priv, false); } @@ -414,70 +786,352 @@ void vmw_surface_res_free(struct vmw_resource *res) { struct vmw_surface *srf = container_of(res, struct vmw_surface, res); + if (srf->backup) + ttm_bo_unref(&srf->backup); + kfree(srf->offsets); kfree(srf->sizes); kfree(srf->snooper.image); kfree(srf); } -int vmw_surface_init(struct vmw_private *dev_priv, - struct vmw_surface *srf, - void (*res_free) (struct vmw_resource *res)) + +/** + * vmw_surface_do_validate - make a surface available to the device. + * + * @dev_priv: Pointer to a device private struct. + * @srf: Pointer to a struct vmw_surface. + * + * If the surface doesn't have a hw id, allocate one, and optionally + * DMA the backed up surface contents to the device. + * + * Returns -EBUSY if there wasn't sufficient device resources to + * complete the validation. Retry after freeing up resources. + * + * May return other errors if the kernel is out of guest resources. + */ +int vmw_surface_do_validate(struct vmw_private *dev_priv, + struct vmw_surface *srf) { - int ret; - struct { - SVGA3dCmdHeader header; - SVGA3dCmdDefineSurface body; - } *cmd; - SVGA3dSize *cmd_size; struct vmw_resource *res = &srf->res; - struct drm_vmw_size *src_size; - size_t submit_size; - uint32_t cmd_len; - int i; + struct list_head val_list; + struct ttm_validate_buffer val_buf; + uint32_t submit_size; + uint8_t *cmd; + int ret; - BUG_ON(res_free == NULL); - ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr, - VMW_RES_SURFACE, res_free); + if (likely(res->id != -1)) + return 0; + + if (unlikely(dev_priv->used_memory_size + srf->backup_size >= + dev_priv->memory_size)) + return -EBUSY; + + /* + * Reserve- and validate the backup DMA bo. + */ + + if (srf->backup) { + INIT_LIST_HEAD(&val_list); + val_buf.bo = ttm_bo_reference(srf->backup); + val_buf.new_sync_obj_arg = (void *)((unsigned long) + DRM_VMW_FENCE_FLAG_EXEC); + list_add_tail(&val_buf.head, &val_list); + ret = ttm_eu_reserve_buffers(&val_list); + if (unlikely(ret != 0)) + goto out_no_reserve; + + ret = ttm_bo_validate(srf->backup, &vmw_srf_placement, + true, false, false); + if (unlikely(ret != 0)) + goto out_no_validate; + } + + /* + * Alloc id for the resource. + */ + ret = vmw_resource_alloc_id(dev_priv, res); if (unlikely(ret != 0)) { - res_free(res); - return ret; + DRM_ERROR("Failed to allocate a surface id.\n"); + goto out_no_id; + } + if (unlikely(res->id >= SVGA3D_MAX_SURFACE_IDS)) { + ret = -EBUSY; + goto out_no_fifo; } - submit_size = sizeof(*cmd) + srf->num_sizes * sizeof(SVGA3dSize); - cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize); + + /* + * Encode surface define- and dma commands. + */ + + submit_size = vmw_surface_define_size(srf); + if (srf->backup) + submit_size += vmw_surface_dma_size(srf); cmd = vmw_fifo_reserve(dev_priv, submit_size); if (unlikely(cmd == NULL)) { - DRM_ERROR("Fifo reserve failed for create surface.\n"); - vmw_resource_unreference(&res); - return -ENOMEM; + DRM_ERROR("Failed reserving FIFO space for surface " + "validation.\n"); + ret = -ENOMEM; + goto out_no_fifo; } - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DEFINE); - cmd->header.size = cpu_to_le32(cmd_len); - cmd->body.sid = cpu_to_le32(res->id); - cmd->body.surfaceFlags = cpu_to_le32(srf->flags); - cmd->body.format = cpu_to_le32(srf->format); - for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { - cmd->body.face[i].numMipLevels = - cpu_to_le32(srf->mip_levels[i]); + vmw_surface_define_encode(srf, cmd); + if (srf->backup) { + SVGAGuestPtr ptr; + + cmd += vmw_surface_define_size(srf); + vmw_bo_get_guest_ptr(srf->backup, &ptr); + vmw_surface_dma_encode(srf, cmd, &ptr, true); } - cmd += 1; - cmd_size = (SVGA3dSize *) cmd; - src_size = srf->sizes; + vmw_fifo_commit(dev_priv, submit_size); - for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) { - cmd_size->width = cpu_to_le32(src_size->width); - cmd_size->height = cpu_to_le32(src_size->height); - cmd_size->depth = cpu_to_le32(src_size->depth); + /* + * Create a fence object and fence the backup buffer. + */ + + if (srf->backup) { + struct vmw_fence_obj *fence; + + (void) vmw_execbuf_fence_commands(NULL, dev_priv, + &fence, NULL); + ttm_eu_fence_buffer_objects(&val_list, fence); + if (likely(fence != NULL)) + vmw_fence_obj_unreference(&fence); + ttm_bo_unref(&val_buf.bo); + ttm_bo_unref(&srf->backup); + } + + /* + * Surface memory usage accounting. + */ + + dev_priv->used_memory_size += srf->backup_size; + + return 0; + +out_no_fifo: + vmw_resource_release_id(res); +out_no_id: +out_no_validate: + if (srf->backup) + ttm_eu_backoff_reservation(&val_list); +out_no_reserve: + if (srf->backup) + ttm_bo_unref(&val_buf.bo); + return ret; +} + +/** + * vmw_surface_evict - Evict a hw surface. + * + * @dev_priv: Pointer to a device private struct. + * @srf: Pointer to a struct vmw_surface + * + * DMA the contents of a hw surface to a backup guest buffer object, + * and destroy the hw surface, releasing its id. + */ +int vmw_surface_evict(struct vmw_private *dev_priv, + struct vmw_surface *srf) +{ + struct vmw_resource *res = &srf->res; + struct list_head val_list; + struct ttm_validate_buffer val_buf; + uint32_t submit_size; + uint8_t *cmd; + int ret; + struct vmw_fence_obj *fence; + SVGAGuestPtr ptr; + + BUG_ON(res->id == -1); + + /* + * Create a surface backup buffer object. + */ + + if (!srf->backup) { + ret = ttm_bo_create(&dev_priv->bdev, srf->backup_size, + ttm_bo_type_device, + &vmw_srf_placement, 0, 0, true, + NULL, &srf->backup); + if (unlikely(ret != 0)) + return ret; } + /* + * Reserve- and validate the backup DMA bo. + */ + + INIT_LIST_HEAD(&val_list); + val_buf.bo = ttm_bo_reference(srf->backup); + val_buf.new_sync_obj_arg = (void *)(unsigned long) + DRM_VMW_FENCE_FLAG_EXEC; + list_add_tail(&val_buf.head, &val_list); + ret = ttm_eu_reserve_buffers(&val_list); + if (unlikely(ret != 0)) + goto out_no_reserve; + + ret = ttm_bo_validate(srf->backup, &vmw_srf_placement, + true, false, false); + if (unlikely(ret != 0)) + goto out_no_validate; + + + /* + * Encode the dma- and surface destroy commands. + */ + + submit_size = vmw_surface_dma_size(srf) + vmw_surface_destroy_size(); + cmd = vmw_fifo_reserve(dev_priv, submit_size); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed reserving FIFO space for surface " + "eviction.\n"); + ret = -ENOMEM; + goto out_no_fifo; + } + + vmw_bo_get_guest_ptr(srf->backup, &ptr); + vmw_surface_dma_encode(srf, cmd, &ptr, false); + cmd += vmw_surface_dma_size(srf); + vmw_surface_destroy_encode(res->id, cmd); vmw_fifo_commit(dev_priv, submit_size); + + /* + * Surface memory usage accounting. + */ + + dev_priv->used_memory_size -= srf->backup_size; + + /* + * Create a fence object and fence the DMA buffer. + */ + + (void) vmw_execbuf_fence_commands(NULL, dev_priv, + &fence, NULL); + ttm_eu_fence_buffer_objects(&val_list, fence); + if (likely(fence != NULL)) + vmw_fence_obj_unreference(&fence); + ttm_bo_unref(&val_buf.bo); + + /* + * Release the surface ID. + */ + + vmw_resource_release_id(res); + + return 0; + +out_no_fifo: +out_no_validate: + if (srf->backup) + ttm_eu_backoff_reservation(&val_list); +out_no_reserve: + ttm_bo_unref(&val_buf.bo); + ttm_bo_unref(&srf->backup); + return ret; +} + + +/** + * vmw_surface_validate - make a surface available to the device, evicting + * other surfaces if needed. + * + * @dev_priv: Pointer to a device private struct. + * @srf: Pointer to a struct vmw_surface. + * + * Try to validate a surface and if it fails due to limited device resources, + * repeatedly try to evict other surfaces until the request can be + * acommodated. + * + * May return errors if out of resources. + */ +int vmw_surface_validate(struct vmw_private *dev_priv, + struct vmw_surface *srf) +{ + int ret; + struct vmw_surface *evict_srf; + + do { + write_lock(&dev_priv->resource_lock); + list_del_init(&srf->lru_head); + write_unlock(&dev_priv->resource_lock); + + ret = vmw_surface_do_validate(dev_priv, srf); + if (likely(ret != -EBUSY)) + break; + + write_lock(&dev_priv->resource_lock); + if (list_empty(&dev_priv->surface_lru)) { + DRM_ERROR("Out of device memory for surfaces.\n"); + ret = -EBUSY; + write_unlock(&dev_priv->resource_lock); + break; + } + + evict_srf = vmw_surface_reference + (list_first_entry(&dev_priv->surface_lru, + struct vmw_surface, + lru_head)); + list_del_init(&evict_srf->lru_head); + + write_unlock(&dev_priv->resource_lock); + (void) vmw_surface_evict(dev_priv, evict_srf); + + vmw_surface_unreference(&evict_srf); + + } while (1); + + if (unlikely(ret != 0 && srf->res.id != -1)) { + write_lock(&dev_priv->resource_lock); + list_add_tail(&srf->lru_head, &dev_priv->surface_lru); + write_unlock(&dev_priv->resource_lock); + } + + return ret; +} + + +/** + * vmw_surface_remove_from_lists - Remove surface resources from lookup lists + * + * @res: Pointer to a struct vmw_resource embedded in a struct vmw_surface + * + * As part of the resource destruction, remove the surface from any + * lookup lists. + */ +static void vmw_surface_remove_from_lists(struct vmw_resource *res) +{ + struct vmw_surface *srf = container_of(res, struct vmw_surface, res); + + list_del_init(&srf->lru_head); +} + +int vmw_surface_init(struct vmw_private *dev_priv, + struct vmw_surface *srf, + void (*res_free) (struct vmw_resource *res)) +{ + int ret; + struct vmw_resource *res = &srf->res; + + BUG_ON(res_free == NULL); + INIT_LIST_HEAD(&srf->lru_head); + ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr, + VMW_RES_SURFACE, true, res_free, + vmw_surface_remove_from_lists); + + if (unlikely(ret != 0)) + res_free(res); + + /* + * The surface won't be visible to hardware until a + * surface validate. + */ + (void) vmw_3d_resource_inc(dev_priv, false); vmw_resource_activate(res, vmw_hw_surface_destroy); - return 0; + return ret; } static void vmw_user_surface_free(struct vmw_resource *res) @@ -485,12 +1139,58 @@ static void vmw_user_surface_free(struct vmw_resource *res) struct vmw_surface *srf = container_of(res, struct vmw_surface, res); struct vmw_user_surface *user_srf = container_of(srf, struct vmw_user_surface, srf); + struct vmw_private *dev_priv = srf->res.dev_priv; + uint32_t size = user_srf->size; + if (srf->backup) + ttm_bo_unref(&srf->backup); + kfree(srf->offsets); kfree(srf->sizes); kfree(srf->snooper.image); kfree(user_srf); + ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +} + +/** + * vmw_resource_unreserve - unreserve resources previously reserved for + * command submission. + * + * @list_head: list of resources to unreserve. + * + * Currently only surfaces are considered, and unreserving a surface + * means putting it back on the device's surface lru list, + * so that it can be evicted if necessary. + * This function traverses the resource list and + * checks whether resources are surfaces, and in that case puts them back + * on the device's surface LRU list. + */ +void vmw_resource_unreserve(struct list_head *list) +{ + struct vmw_resource *res; + struct vmw_surface *srf; + rwlock_t *lock = NULL; + + list_for_each_entry(res, list, validate_head) { + + if (res->res_free != &vmw_surface_res_free && + res->res_free != &vmw_user_surface_free) + continue; + + if (unlikely(lock == NULL)) { + lock = &res->dev_priv->resource_lock; + write_lock(lock); + } + + srf = container_of(res, struct vmw_surface, res); + list_del_init(&srf->lru_head); + list_add_tail(&srf->lru_head, &res->dev_priv->surface_lru); + } + + if (lock != NULL) + write_unlock(lock); } + int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv, struct ttm_object_file *tfile, uint32_t handle, struct vmw_surface **out) @@ -567,10 +1267,51 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; struct drm_vmw_size __user *user_sizes; int ret; - int i; + int i, j; + uint32_t cur_bo_offset; + struct drm_vmw_size *cur_size; + struct vmw_surface_offset *cur_offset; + uint32_t stride_bpp; + uint32_t bpp; + uint32_t num_sizes; + uint32_t size; + struct vmw_master *vmaster = vmw_master(file_priv->master); - if (unlikely(user_srf == NULL)) - return -ENOMEM; + if (unlikely(vmw_user_surface_size == 0)) + vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + + 128; + + num_sizes = 0; + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) + num_sizes += req->mip_levels[i]; + + if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * + DRM_VMW_MAX_MIP_LEVELS) + return -EINVAL; + + size = vmw_user_surface_size + 128 + + ttm_round_pot(num_sizes * sizeof(struct drm_vmw_size)) + + ttm_round_pot(num_sizes * sizeof(struct vmw_surface_offset)); + + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + size, false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for surface" + " creation.\n"); + goto out_unlock; + } + + user_srf = kmalloc(sizeof(*user_srf), GFP_KERNEL); + if (unlikely(user_srf == NULL)) { + ret = -ENOMEM; + goto out_no_user_srf; + } srf = &user_srf->srf; res = &srf->res; @@ -578,21 +1319,22 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, srf->flags = req->flags; srf->format = req->format; srf->scanout = req->scanout; - memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); - srf->num_sizes = 0; - for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) - srf->num_sizes += srf->mip_levels[i]; + srf->backup = NULL; - if (srf->num_sizes > DRM_VMW_MAX_SURFACE_FACES * - DRM_VMW_MAX_MIP_LEVELS) { - ret = -EINVAL; - goto out_err0; - } + memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); + srf->num_sizes = num_sizes; + user_srf->size = size; srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL); if (unlikely(srf->sizes == NULL)) { ret = -ENOMEM; - goto out_err0; + goto out_no_sizes; + } + srf->offsets = kmalloc(srf->num_sizes * sizeof(*srf->offsets), + GFP_KERNEL); + if (unlikely(srf->sizes == NULL)) { + ret = -ENOMEM; + goto out_no_offsets; } user_sizes = (struct drm_vmw_size __user *)(unsigned long) @@ -602,9 +1344,32 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, srf->num_sizes * sizeof(*srf->sizes)); if (unlikely(ret != 0)) { ret = -EFAULT; - goto out_err1; + goto out_no_copy; } + cur_bo_offset = 0; + cur_offset = srf->offsets; + cur_size = srf->sizes; + + bpp = vmw_sf_bpp[srf->format].bpp; + stride_bpp = vmw_sf_bpp[srf->format].s_bpp; + + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { + for (j = 0; j < srf->mip_levels[i]; ++j) { + uint32_t stride = + (cur_size->width * stride_bpp + 7) >> 3; + + cur_offset->face = i; + cur_offset->mip = j; + cur_offset->bo_offset = cur_bo_offset; + cur_bo_offset += stride * cur_size->height * + cur_size->depth * bpp / stride_bpp; + ++cur_offset; + ++cur_size; + } + } + srf->backup_size = cur_bo_offset; + if (srf->scanout && srf->num_sizes == 1 && srf->sizes[0].width == 64 && @@ -616,7 +1381,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, if (!srf->snooper.image) { DRM_ERROR("Failed to allocate cursor_image\n"); ret = -ENOMEM; - goto out_err1; + goto out_no_copy; } } else { srf->snooper.image = NULL; @@ -633,7 +1398,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free); if (unlikely(ret != 0)) - return ret; + goto out_unlock; tmp = vmw_resource_reference(&srf->res); ret = ttm_base_object_init(tfile, &user_srf->base, @@ -643,7 +1408,7 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, if (unlikely(ret != 0)) { vmw_resource_unreference(&tmp); vmw_resource_unreference(&res); - return ret; + goto out_unlock; } rep->sid = user_srf->base.hash.key; @@ -651,11 +1416,19 @@ int vmw_surface_define_ioctl(struct drm_device *dev, void *data, DRM_ERROR("Created bad Surface ID.\n"); vmw_resource_unreference(&res); + + ttm_read_unlock(&vmaster->lock); return 0; -out_err1: +out_no_copy: + kfree(srf->offsets); +out_no_offsets: kfree(srf->sizes); -out_err0: +out_no_sizes: kfree(user_srf); +out_no_user_srf: + ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +out_unlock: + ttm_read_unlock(&vmaster->lock); return ret; } @@ -969,7 +1742,7 @@ static int vmw_stream_init(struct vmw_private *dev_priv, int ret; ret = vmw_resource_init(dev_priv, res, &dev_priv->stream_idr, - VMW_RES_STREAM, res_free); + VMW_RES_STREAM, false, res_free, NULL); if (unlikely(ret != 0)) { if (res_free == NULL) @@ -999,8 +1772,11 @@ static void vmw_user_stream_free(struct vmw_resource *res) { struct vmw_user_stream *stream = container_of(res, struct vmw_user_stream, stream.res); + struct vmw_private *dev_priv = res->dev_priv; kfree(stream); + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_stream_size); } /** @@ -1054,23 +1830,56 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL); + struct vmw_user_stream *stream; struct vmw_resource *res; struct vmw_resource *tmp; struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_master *vmaster = vmw_master(file_priv->master); int ret; - if (unlikely(stream == NULL)) - return -ENOMEM; + /* + * Approximate idr memory usage with 128 bytes. It will be limited + * by maximum number_of streams anyway? + */ + + if (unlikely(vmw_user_stream_size == 0)) + vmw_user_stream_size = ttm_round_pot(sizeof(*stream)) + 128; + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + vmw_user_stream_size, + false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for stream" + " creation.\n"); + goto out_unlock; + } + + + stream = kmalloc(sizeof(*stream), GFP_KERNEL); + if (unlikely(stream == NULL)) { + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_stream_size); + ret = -ENOMEM; + goto out_unlock; + } res = &stream->stream.res; stream->base.shareable = false; stream->base.tfile = NULL; + /* + * From here on, the destructor takes over resource freeing. + */ + ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free); if (unlikely(ret != 0)) - return ret; + goto out_unlock; tmp = vmw_resource_reference(res); ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM, @@ -1084,6 +1893,8 @@ int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, arg->stream_id = res->id; out_err: vmw_resource_unreference(&res); +out_unlock: + ttm_read_unlock(&vmaster->lock); return ret; } diff --git a/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c new file mode 100644 index 000000000000..347e40699443 --- /dev/null +++ b/trunk/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -0,0 +1,566 @@ +/************************************************************************** + * + * Copyright © 2011 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * 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, sub license, 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 (including the + * next paragraph) 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + **************************************************************************/ + +#include "vmwgfx_kms.h" + + +#define vmw_crtc_to_sou(x) \ + container_of(x, struct vmw_screen_object_unit, base.crtc) +#define vmw_encoder_to_sou(x) \ + container_of(x, struct vmw_screen_object_unit, base.encoder) +#define vmw_connector_to_sou(x) \ + container_of(x, struct vmw_screen_object_unit, base.connector) + +struct vmw_screen_object_display { + struct list_head active; + + unsigned num_active; + unsigned last_num_active; + + struct vmw_framebuffer *fb; +}; + +/** + * Display unit using screen objects. + */ +struct vmw_screen_object_unit { + struct vmw_display_unit base; + + unsigned long buffer_size; /**< Size of allocated buffer */ + struct vmw_dma_buffer *buffer; /**< Backing store buffer */ + + bool defined; + + struct list_head active; +}; + +static void vmw_sou_destroy(struct vmw_screen_object_unit *sou) +{ + list_del_init(&sou->active); + vmw_display_unit_cleanup(&sou->base); + kfree(sou); +} + + +/* + * Screen Object Display Unit CRTC functions + */ + +static void vmw_sou_crtc_destroy(struct drm_crtc *crtc) +{ + vmw_sou_destroy(vmw_crtc_to_sou(crtc)); +} + +static int vmw_sou_del_active(struct vmw_private *vmw_priv, + struct vmw_screen_object_unit *sou) +{ + struct vmw_screen_object_display *ld = vmw_priv->sou_priv; + if (list_empty(&sou->active)) + return 0; + + /* Must init otherwise list_empty(&sou->active) will not work. */ + list_del_init(&sou->active); + if (--(ld->num_active) == 0) { + BUG_ON(!ld->fb); + if (ld->fb->unpin) + ld->fb->unpin(ld->fb); + ld->fb = NULL; + } + + return 0; +} + +static int vmw_sou_add_active(struct vmw_private *vmw_priv, + struct vmw_screen_object_unit *sou, + struct vmw_framebuffer *vfb) +{ + struct vmw_screen_object_display *ld = vmw_priv->sou_priv; + struct vmw_screen_object_unit *entry; + struct list_head *at; + + BUG_ON(!ld->num_active && ld->fb); + if (vfb != ld->fb) { + if (ld->fb && ld->fb->unpin) + ld->fb->unpin(ld->fb); + if (vfb->pin) + vfb->pin(vfb); + ld->fb = vfb; + } + + if (!list_empty(&sou->active)) + return 0; + + at = &ld->active; + list_for_each_entry(entry, &ld->active, active) { + if (entry->base.unit > sou->base.unit) + break; + + at = &entry->active; + } + + list_add(&sou->active, at); + + ld->num_active++; + + return 0; +} + +/** + * Send the fifo command to create a screen. + */ +static int vmw_sou_fifo_create(struct vmw_private *dev_priv, + struct vmw_screen_object_unit *sou, + uint32_t x, uint32_t y, + struct drm_display_mode *mode) +{ + size_t fifo_size; + + struct { + struct { + uint32_t cmdType; + } header; + SVGAScreenObject obj; + } *cmd; + + BUG_ON(!sou->buffer); + + fifo_size = sizeof(*cmd); + cmd = vmw_fifo_reserve(dev_priv, fifo_size); + /* The hardware has hung, nothing we can do about it here. */ + if (unlikely(cmd == NULL)) { + DRM_ERROR("Fifo reserve failed.\n"); + return -ENOMEM; + } + + memset(cmd, 0, fifo_size); + cmd->header.cmdType = SVGA_CMD_DEFINE_SCREEN; + cmd->obj.structSize = sizeof(SVGAScreenObject); + cmd->obj.id = sou->base.unit; + cmd->obj.flags = SVGA_SCREEN_HAS_ROOT | + (sou->base.unit == 0 ? SVGA_SCREEN_IS_PRIMARY : 0); + cmd->obj.size.width = mode->hdisplay; + cmd->obj.size.height = mode->vdisplay; + cmd->obj.root.x = x; + cmd->obj.root.y = y; + + /* Ok to assume that buffer is pinned in vram */ + vmw_bo_get_guest_ptr(&sou->buffer->base, &cmd->obj.backingStore.ptr); + cmd->obj.backingStore.pitch = mode->hdisplay * 4; + + vmw_fifo_commit(dev_priv, fifo_size); + + sou->defined = true; + + return 0; +} + +/** + * Send the fifo command to destroy a screen. + */ +static int vmw_sou_fifo_destroy(struct vmw_private *dev_priv, + struct vmw_screen_object_unit *sou) +{ + size_t fifo_size; + int ret; + + struct { + struct { + uint32_t cmdType; + } header; + SVGAFifoCmdDestroyScreen body; + } *cmd; + + /* no need to do anything */ + if (unlikely(!sou->defined)) + return 0; + + fifo_size = sizeof(*cmd); + cmd = vmw_fifo_reserve(dev_priv, fifo_size); + /* the hardware has hung, nothing we can do about it here */ + if (unlikely(cmd == NULL)) { + DRM_ERROR("Fifo reserve failed.\n"); + return -ENOMEM; + } + + memset(cmd, 0, fifo_size); + cmd->header.cmdType = SVGA_CMD_DESTROY_SCREEN; + cmd->body.screenId = sou->base.unit; + + vmw_fifo_commit(dev_priv, fifo_size); + + /* Force sync */ + ret = vmw_fallback_wait(dev_priv, false, true, 0, false, 3*HZ); + if (unlikely(ret != 0)) + DRM_ERROR("Failed to sync with HW"); + else + sou->defined = false; + + return ret; +} + +/** + * Free the backing store. + */ +static void vmw_sou_backing_free(struct vmw_private *dev_priv, + struct vmw_screen_object_unit *sou) +{ + struct ttm_buffer_object *bo; + + if (unlikely(sou->buffer == NULL)) + return; + + bo = &sou->buffer->base; + ttm_bo_unref(&bo); + sou->buffer = NULL; + sou->buffer_size = 0; +} + +/** + * Allocate the backing store for the buffer. + */ +static int vmw_sou_backing_alloc(struct vmw_private *dev_priv, + struct vmw_screen_object_unit *sou, + unsigned long size) +{ + int ret; + + if (sou->buffer_size == size) + return 0; + + if (sou->buffer) + vmw_sou_backing_free(dev_priv, sou); + + sou->buffer = kzalloc(sizeof(*sou->buffer), GFP_KERNEL); + if (unlikely(sou->buffer == NULL)) + return -ENOMEM; + + /* After we have alloced the backing store might not be able to + * resume the overlays, this is preferred to failing to alloc. + */ + vmw_overlay_pause_all(dev_priv); + ret = vmw_dmabuf_init(dev_priv, sou->buffer, size, + &vmw_vram_ne_placement, + false, &vmw_dmabuf_bo_free); + vmw_overlay_resume_all(dev_priv); + + if (unlikely(ret != 0)) + sou->buffer = NULL; /* vmw_dmabuf_init frees on error */ + else + sou->buffer_size = size; + + return ret; +} + +static int vmw_sou_crtc_set_config(struct drm_mode_set *set) +{ + struct vmw_private *dev_priv; + struct vmw_screen_object_unit *sou; + struct drm_connector *connector; + struct drm_display_mode *mode; + struct drm_encoder *encoder; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + struct drm_crtc *crtc; + int ret = 0; + + if (!set) + return -EINVAL; + + if (!set->crtc) + return -EINVAL; + + /* get the sou */ + crtc = set->crtc; + sou = vmw_crtc_to_sou(crtc); + vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL; + dev_priv = vmw_priv(crtc->dev); + + if (set->num_connectors > 1) { + DRM_ERROR("to many connectors\n"); + return -EINVAL; + } + + if (set->num_connectors == 1 && + set->connectors[0] != &sou->base.connector) { + DRM_ERROR("connector doesn't match %p %p\n", + set->connectors[0], &sou->base.connector); + return -EINVAL; + } + + /* sou only supports one fb active at the time */ + if (dev_priv->sou_priv->fb && vfb && + !(dev_priv->sou_priv->num_active == 1 && + !list_empty(&sou->active)) && + dev_priv->sou_priv->fb != vfb) { + DRM_ERROR("Multiple framebuffers not supported\n"); + return -EINVAL; + } + + /* since they always map one to one these are safe */ + connector = &sou->base.connector; + encoder = &sou->base.encoder; + + /* should we turn the crtc off */ + if (set->num_connectors == 0 || !set->mode || !set->fb) { + ret = vmw_sou_fifo_destroy(dev_priv, sou); + /* the hardware has hung don't do anything more */ + if (unlikely(ret != 0)) + return ret; + + connector->encoder = NULL; + encoder->crtc = NULL; + crtc->fb = NULL; + crtc->x = 0; + crtc->y = 0; + + vmw_sou_del_active(dev_priv, sou); + + vmw_sou_backing_free(dev_priv, sou); + + return 0; + } + + + /* we now know we want to set a mode */ + mode = set->mode; + fb = set->fb; + + if (set->x + mode->hdisplay > fb->width || + set->y + mode->vdisplay > fb->height) { + DRM_ERROR("set outside of framebuffer\n"); + return -EINVAL; + } + + vmw_fb_off(dev_priv); + + if (mode->hdisplay != crtc->mode.hdisplay || + mode->vdisplay != crtc->mode.vdisplay) { + /* no need to check if depth is different, because backing + * store depth is forced to 4 by the device. + */ + + ret = vmw_sou_fifo_destroy(dev_priv, sou); + /* the hardware has hung don't do anything more */ + if (unlikely(ret != 0)) + return ret; + + vmw_sou_backing_free(dev_priv, sou); + } + + if (!sou->buffer) { + /* forced to depth 4 by the device */ + size_t size = mode->hdisplay * mode->vdisplay * 4; + ret = vmw_sou_backing_alloc(dev_priv, sou, size); + if (unlikely(ret != 0)) + return ret; + } + + ret = vmw_sou_fifo_create(dev_priv, sou, set->x, set->y, mode); + if (unlikely(ret != 0)) { + /* + * We are in a bit of a situation here, the hardware has + * hung and we may or may not have a buffer hanging of + * the screen object, best thing to do is not do anything + * if we where defined, if not just turn the crtc of. + * Not what userspace wants but it needs to htfu. + */ + if (sou->defined) + return ret; + + connector->encoder = NULL; + encoder->crtc = NULL; + crtc->fb = NULL; + crtc->x = 0; + crtc->y = 0; + + return ret; + } + + vmw_sou_add_active(dev_priv, sou, vfb); + + connector->encoder = encoder; + encoder->crtc = crtc; + crtc->mode = *mode; + crtc->fb = fb; + crtc->x = set->x; + crtc->y = set->y; + + return 0; +} + +static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = { + .save = vmw_du_crtc_save, + .restore = vmw_du_crtc_restore, + .cursor_set = vmw_du_crtc_cursor_set, + .cursor_move = vmw_du_crtc_cursor_move, + .gamma_set = vmw_du_crtc_gamma_set, + .destroy = vmw_sou_crtc_destroy, + .set_config = vmw_sou_crtc_set_config, +}; + +/* + * Screen Object Display Unit encoder functions + */ + +static void vmw_sou_encoder_destroy(struct drm_encoder *encoder) +{ + vmw_sou_destroy(vmw_encoder_to_sou(encoder)); +} + +static struct drm_encoder_funcs vmw_screen_object_encoder_funcs = { + .destroy = vmw_sou_encoder_destroy, +}; + +/* + * Screen Object Display Unit connector functions + */ + +static void vmw_sou_connector_destroy(struct drm_connector *connector) +{ + vmw_sou_destroy(vmw_connector_to_sou(connector)); +} + +static struct drm_connector_funcs vmw_legacy_connector_funcs = { + .dpms = vmw_du_connector_dpms, + .save = vmw_du_connector_save, + .restore = vmw_du_connector_restore, + .detect = vmw_du_connector_detect, + .fill_modes = vmw_du_connector_fill_modes, + .set_property = vmw_du_connector_set_property, + .destroy = vmw_sou_connector_destroy, +}; + +static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) +{ + struct vmw_screen_object_unit *sou; + struct drm_device *dev = dev_priv->dev; + struct drm_connector *connector; + struct drm_encoder *encoder; + struct drm_crtc *crtc; + + sou = kzalloc(sizeof(*sou), GFP_KERNEL); + if (!sou) + return -ENOMEM; + + sou->base.unit = unit; + crtc = &sou->base.crtc; + encoder = &sou->base.encoder; + connector = &sou->base.connector; + + INIT_LIST_HEAD(&sou->active); + + sou->base.pref_active = (unit == 0); + sou->base.pref_width = 800; + sou->base.pref_height = 600; + sou->base.pref_mode = NULL; + + drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, + DRM_MODE_CONNECTOR_LVDS); + connector->status = vmw_du_connector_detect(connector, true); + + drm_encoder_init(dev, encoder, &vmw_screen_object_encoder_funcs, + DRM_MODE_ENCODER_LVDS); + drm_mode_connector_attach_encoder(connector, encoder); + encoder->possible_crtcs = (1 << unit); + encoder->possible_clones = 0; + + drm_crtc_init(dev, crtc, &vmw_screen_object_crtc_funcs); + + drm_mode_crtc_set_gamma_size(crtc, 256); + + drm_connector_attach_property(connector, + dev->mode_config.dirty_info_property, + 1); + + return 0; +} + +int vmw_kms_init_screen_object_display(struct vmw_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + int i; + int ret; + + if (dev_priv->sou_priv) { + DRM_INFO("sou system already on\n"); + return -EINVAL; + } + + if (!(dev_priv->fifo.capabilities & SVGA_FIFO_CAP_SCREEN_OBJECT_2)) { + DRM_INFO("Not using screen objects," + " missing cap SCREEN_OBJECT_2\n"); + return -ENOSYS; + } + + ret = -ENOMEM; + dev_priv->sou_priv = kmalloc(sizeof(*dev_priv->sou_priv), GFP_KERNEL); + if (unlikely(!dev_priv->sou_priv)) + goto err_no_mem; + + INIT_LIST_HEAD(&dev_priv->sou_priv->active); + dev_priv->sou_priv->num_active = 0; + dev_priv->sou_priv->last_num_active = 0; + dev_priv->sou_priv->fb = NULL; + + ret = drm_vblank_init(dev, VMWGFX_NUM_DISPLAY_UNITS); + if (unlikely(ret != 0)) + goto err_free; + + ret = drm_mode_create_dirty_info_property(dev_priv->dev); + if (unlikely(ret != 0)) + goto err_vblank_cleanup; + + for (i = 0; i < VMWGFX_NUM_DISPLAY_UNITS; ++i) + vmw_sou_init(dev_priv, i); + + DRM_INFO("Screen objects system initialized\n"); + + return 0; + +err_vblank_cleanup: + drm_vblank_cleanup(dev); +err_free: + kfree(dev_priv->sou_priv); +err_no_mem: + return ret; +} + +int vmw_kms_close_screen_object_display(struct vmw_private *dev_priv) +{ + struct drm_device *dev = dev_priv->dev; + + drm_vblank_cleanup(dev); + if (!dev_priv->sou_priv) + return -ENOSYS; + + if (!list_empty(&dev_priv->sou_priv->active)) + DRM_ERROR("Still have active outputs when unloading driver"); + + kfree(dev_priv->sou_priv); + + return 0; +} diff --git a/trunk/include/drm/exynos_drm.h b/trunk/include/drm/exynos_drm.h new file mode 100644 index 000000000000..874c4d271328 --- /dev/null +++ b/trunk/include/drm/exynos_drm.h @@ -0,0 +1,104 @@ +/* exynos_drm.h + * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * Authors: + * Inki Dae + * Joonyoung Shim + * Seung-Woo Kim + * + * 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 (including the next + * paragraph) 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 + * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS 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. + */ + +#ifndef _EXYNOS_DRM_H_ +#define _EXYNOS_DRM_H_ + +/** + * User-desired buffer creation information structure. + * + * @size: requested size for the object. + * - this size value would be page-aligned internally. + * @flags: user request for setting memory type or cache attributes. + * @handle: returned handle for the object. + */ +struct drm_exynos_gem_create { + unsigned int size; + unsigned int flags; + unsigned int handle; +}; + +/** + * A structure for getting buffer offset. + * + * @handle: a pointer to gem object created. + * @pad: just padding to be 64-bit aligned. + * @offset: relatived offset value of the memory region allocated. + * - this value should be set by user. + */ +struct drm_exynos_gem_map_off { + unsigned int handle; + unsigned int pad; + uint64_t offset; +}; + +/** + * A structure for mapping buffer. + * + * @handle: a handle to gem object created. + * @size: memory size to be mapped. + * @mapped: having user virtual address mmaped. + * - this variable would be filled by exynos gem module + * of kernel side with user virtual address which is allocated + * by do_mmap(). + */ +struct drm_exynos_gem_mmap { + unsigned int handle; + unsigned int size; + uint64_t mapped; +}; + +#define DRM_EXYNOS_GEM_CREATE 0x00 +#define DRM_EXYNOS_GEM_MAP_OFFSET 0x01 +#define DRM_EXYNOS_GEM_MMAP 0x02 + +#define DRM_IOCTL_EXYNOS_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_GEM_CREATE, struct drm_exynos_gem_create) + +#define DRM_IOCTL_EXYNOS_GEM_MAP_OFFSET DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_GEM_MAP_OFFSET, struct drm_exynos_gem_map_off) + +#define DRM_IOCTL_EXYNOS_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + \ + DRM_EXYNOS_GEM_MMAP, struct drm_exynos_gem_mmap) + +/** + * Platform Specific Structure for DRM based FIMD. + * + * @timing: default video mode for initializing + * @default_win: default window layer number to be used for UI. + * @bpp: default bit per pixel. + */ +struct exynos_drm_fimd_pdata { + struct fb_videomode timing; + u32 vidcon0; + u32 vidcon1; + unsigned int default_win; + unsigned int bpp; +}; + +#endif diff --git a/trunk/include/drm/vmwgfx_drm.h b/trunk/include/drm/vmwgfx_drm.h index 29cd9cfdd611..cd7cd8162ed6 100644 --- a/trunk/include/drm/vmwgfx_drm.h +++ b/trunk/include/drm/vmwgfx_drm.h @@ -52,6 +52,8 @@ #define DRM_VMW_FENCE_SIGNALED 15 #define DRM_VMW_FENCE_UNREF 16 #define DRM_VMW_FENCE_EVENT 17 +#define DRM_VMW_PRESENT 18 +#define DRM_VMW_PRESENT_READBACK 19 /*************************************************************************/ @@ -681,5 +683,109 @@ struct drm_vmw_fence_arg { }; +/*************************************************************************/ +/** + * DRM_VMW_FENCE_EVENT + * + * Queues an event on a fence to be delivered on the drm character device + * when the fence has signaled the DRM_VMW_FENCE_FLAG_EXEC flag. + * Optionally the approximate time when the fence signaled is + * given by the event. + */ + +/* + * The event type + */ +#define DRM_VMW_EVENT_FENCE_SIGNALED 0x80000000 + +struct drm_vmw_event_fence { + struct drm_event base; + uint64_t user_data; + uint32_t tv_sec; + uint32_t tv_usec; +}; + +/* + * Flags that may be given to the command. + */ +/* Request fence signaled time on the event. */ +#define DRM_VMW_FE_FLAG_REQ_TIME (1 << 0) + +/** + * struct drm_vmw_fence_event_arg + * + * @fence_rep: Pointer to fence_rep structure cast to uint64_t or 0 if + * the fence is not supposed to be referenced by user-space. + * @user_info: Info to be delivered with the event. + * @handle: Attach the event to this fence only. + * @flags: A set of flags as defined above. + */ +struct drm_vmw_fence_event_arg { + uint64_t fence_rep; + uint64_t user_data; + uint32_t handle; + uint32_t flags; +}; + +/*************************************************************************/ +/** + * DRM_VMW_PRESENT + * + * Executes an SVGA present on a given fb for a given surface. The surface + * is placed on the framebuffer. Cliprects are given relative to the given + * point (the point disignated by dest_{x|y}). + * + */ + +/** + * struct drm_vmw_present_arg + * @fb_id: framebuffer id to present / read back from. + * @sid: Surface id to present from. + * @dest_x: X placement coordinate for surface. + * @dest_y: Y placement coordinate for surface. + * @clips_ptr: Pointer to an array of clip rects cast to an uint64_t. + * @num_clips: Number of cliprects given relative to the framebuffer origin, + * in the same coordinate space as the frame buffer. + * @pad64: Unused 64-bit padding. + * + * Input argument to the DRM_VMW_PRESENT ioctl. + */ + +struct drm_vmw_present_arg { + uint32_t fb_id; + uint32_t sid; + int32_t dest_x; + int32_t dest_y; + uint64_t clips_ptr; + uint32_t num_clips; + uint32_t pad64; +}; + + +/*************************************************************************/ +/** + * DRM_VMW_PRESENT_READBACK + * + * Executes an SVGA present readback from a given fb to the dma buffer + * currently bound as the fb. If there is no dma buffer bound to the fb, + * an error will be returned. + * + */ + +/** + * struct drm_vmw_present_arg + * @fb_id: fb_id to present / read back from. + * @num_clips: Number of cliprects. + * @clips_ptr: Pointer to an array of clip rects cast to an uint64_t. + * @fence_rep: Pointer to a struct drm_vmw_fence_rep, cast to an uint64_t. + * If this member is NULL, then the ioctl should not return a fence. + */ + +struct drm_vmw_present_readback_arg { + uint32_t fb_id; + uint32_t num_clips; + uint64_t clips_ptr; + uint64_t fence_rep; +}; #endif