diff --git a/drivers/gpu/drm/xen/Kconfig b/drivers/gpu/drm/xen/Kconfig index fc097516c62d59..64c818c54edb0e 100644 --- a/drivers/gpu/drm/xen/Kconfig +++ b/drivers/gpu/drm/xen/Kconfig @@ -5,6 +5,10 @@ config DRM_XEN Choose this option if you want to enable DRM support for Xen. +choice + prompt "DRM driver sub-type selection" + depends on DRM_XEN + config DRM_XEN_FRONTEND tristate "Front-end paravirtualized driver for Xen guest OS" depends on DRM_XEN @@ -60,3 +64,6 @@ config DRM_XEN_ZCOPY_CMA This is only available for built-in driver, because there is a dependency on DMA ops which are only available for built-in code. + +endchoice + diff --git a/drivers/gpu/drm/xen/Makefile b/drivers/gpu/drm/xen/Makefile index 3b0629fdc2d319..7fd39fa68bca5c 100644 --- a/drivers/gpu/drm/xen/Makefile +++ b/drivers/gpu/drm/xen/Makefile @@ -2,15 +2,18 @@ # Makefile for the drm device driver. This driver provides support for the # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. -drm_xen_front-objs := xen_drm_front.o xen_drm_drv.o xen_drm_kms.o xen_drm_crtc.o \ - xen_drm_timer.o xen_drm_shbuf.o +drm_xen_front-objs := xen_drm_front.o xen_drm_front_drv.o xen_drm_front_kms.o \ + xen_drm_front_crtc.o xen_drm_front_timer.o xen_drm_front_shbuf.o \ + xen_drm_balloon.o xen_drm_front_evtchnl.o xen_drm_front_cfg.o -ifneq ($(CONFIG_DRM_XEN_FRONTEND_CMA),y) - drm_xen_front-objs += xen_drm_gem.o +ifeq ($(CONFIG_DRM_XEN_FRONTEND_CMA),y) + drm_xen_front-objs += xen_drm_front_gem_cma.o +else + drm_xen_front-objs += xen_drm_front_gem.o endif obj-$(CONFIG_DRM_XEN_FRONTEND) += drm_xen_front.o -drm_xen_zcopy-objs := xen_drm_zcopy_drv.o +drm_xen_zcopy-objs := xen_drm_zcopy_drv.o xen_drm_balloon.o obj-$(CONFIG_DRM_XEN_ZCOPY) += drm_xen_zcopy.o diff --git a/drivers/gpu/drm/xen/xen_drm_balloon.c b/drivers/gpu/drm/xen/xen_drm_balloon.c new file mode 100644 index 00000000000000..ccefc40dfc30cb --- /dev/null +++ b/drivers/gpu/drm/xen/xen_drm_balloon.c @@ -0,0 +1,154 @@ +/* + * Xen para-virtual DRM device + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2016-2017 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko + */ + +#include + +#if defined(CONFIG_DRM_XEN_ZCOPY_CMA) +#include +#include +#include +#else +#include +#endif + +#include "xen_drm_balloon.h" + +#if defined(CONFIG_DRM_XEN_ZCOPY_CMA) +int xen_drm_ballooned_pages_alloc(struct device *dev, + struct xen_drm_balloon *obj, int num_pages, struct page **pages) +{ + xen_pfn_t *frame_list; + size_t size; + int i, ret; + dma_addr_t dev_addr, cpu_addr; + void *vaddr = NULL; + struct xen_memory_reservation reservation = { + .address_bits = 0, + .extent_order = 0, + .domid = DOMID_SELF + }; + + size = num_pages * PAGE_SIZE; + DRM_DEBUG("Ballooning out %d pages, size %zu\n", num_pages, size); + frame_list = kcalloc(num_pages, sizeof(*frame_list), GFP_KERNEL); + if (!frame_list) + return -ENOMEM; + + vaddr = dma_alloc_wc(dev, size, &dev_addr, GFP_KERNEL | __GFP_NOWARN); + if (!vaddr) { + DRM_ERROR("Failed to allocate DMA buffer with size %zu\n", + size); + ret = -ENOMEM; + goto fail; + } + + cpu_addr = dev_addr; + for (i = 0; i < num_pages; i++) { + pages[i] = pfn_to_page(__phys_to_pfn(cpu_addr)); + /* XENMEM_populate_physmap requires a PFN based on Xen + * granularity. + */ + frame_list[i] = page_to_xen_pfn(pages[i]); + cpu_addr += PAGE_SIZE; + } + set_xen_guest_handle(reservation.extent_start, frame_list); + reservation.nr_extents = num_pages; + /* rc will hold number of pages processed */ + ret = HYPERVISOR_memory_op(XENMEM_decrease_reservation, &reservation); + if (ret <= 0) { + DRM_ERROR("Failed to balloon out %d pages (%d), retrying\n", + num_pages, ret); + WARN_ON(ret != num_pages); + ret = -EFAULT; + goto fail; + } + + obj->vaddr = vaddr; + obj->dev_bus_addr = dev_addr; + kfree(frame_list); + return 0; + +fail: + if (vaddr) + dma_free_wc(dev, size, vaddr, dev_addr); + kfree(frame_list); + return ret; +} + +void xen_drm_ballooned_pages_free(struct device *dev, + struct xen_drm_balloon *obj, int num_pages, struct page **pages) +{ + xen_pfn_t *frame_list; + int i, ret; + size_t size; + struct xen_memory_reservation reservation = { + .address_bits = 0, + .extent_order = 0, + .domid = DOMID_SELF + }; + + if (!pages) + return; + + if (!obj->vaddr) + return; + + frame_list = kcalloc(num_pages, sizeof(*frame_list), GFP_KERNEL); + if (!frame_list) { + DRM_ERROR("Failed to balloon in %d pages\n", num_pages); + return; + } + + DRM_DEBUG("Ballooning in %d pages\n", num_pages); + size = num_pages * PAGE_SIZE; + for (i = 0; i < num_pages; i++) { + /* + * XENMEM_populate_physmap requires a PFN based on Xen + * granularity. + */ + frame_list[i] = page_to_xen_pfn(pages[i]); + } + set_xen_guest_handle(reservation.extent_start, frame_list); + reservation.nr_extents = num_pages; + /* rc will hold number of pages processed */ + ret = HYPERVISOR_memory_op(XENMEM_populate_physmap, &reservation); + if (ret <= 0) { + DRM_ERROR("Failed to balloon in %d pages\n", num_pages); + WARN_ON(ret != num_pages); + } + + if (obj->vaddr) + dma_free_wc(dev, size, obj->vaddr, obj->dev_bus_addr); + + obj->vaddr = NULL; + obj->dev_bus_addr = 0; + kfree(frame_list); +} +#else +int xen_drm_ballooned_pages_alloc(struct device *dev, + struct xen_drm_balloon *obj, int num_pages, struct page **pages) +{ + return alloc_xenballooned_pages(num_pages, pages); +} + +void xen_drm_ballooned_pages_free(struct device *dev, + struct xen_drm_balloon *obj, int num_pages, struct page **pages) +{ + free_xenballooned_pages(num_pages, pages); +} +#endif /* defined(CONFIG_DRM_XEN_ZCOPY_CMA) */ diff --git a/drivers/gpu/drm/xen/xen_drm_balloon.h b/drivers/gpu/drm/xen/xen_drm_balloon.h new file mode 100644 index 00000000000000..a3b6a60a197963 --- /dev/null +++ b/drivers/gpu/drm/xen/xen_drm_balloon.h @@ -0,0 +1,36 @@ +/* + * Xen para-virtual DRM device + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2016-2017 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko + */ + +#ifndef __XEN_DRM_BALLOON_H_ +#define __XEN_DRM_BALLOON_H_ + +#define GRANT_INVALID_REF 0 + +#include + +struct xen_drm_balloon { + void *vaddr; + dma_addr_t dev_bus_addr; +}; + +int xen_drm_ballooned_pages_alloc(struct device *dev, + struct xen_drm_balloon *obj, int num_pages, struct page **pages); +void xen_drm_ballooned_pages_free(struct device *dev, + struct xen_drm_balloon *obj,int num_pages, struct page **pages); + +#endif /* __XEN_DRM_BALLOON_H_ */ diff --git a/drivers/gpu/drm/xen/xen_drm_drv.c b/drivers/gpu/drm/xen/xen_drm_drv.c deleted file mode 100644 index 79798f8bef9807..00000000000000 --- a/drivers/gpu/drm/xen/xen_drm_drv.c +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Xen para-virtual DRM device - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Copyright (C) 2016 EPAM Systems Inc. - * - * Author: Oleksandr Andrushchenko - */ - -#include -#include - -#include - -#include "xen_drm_drv.h" -#include "xen_drm_front.h" -#include "xen_drm_gem.h" -#include "xen_drm_kms.h" - -int xendrm_enable_vblank(struct drm_device *dev, unsigned int pipe) -{ - struct xendrm_device *xendrm_dev = dev->dev_private; - - if (unlikely(pipe >= xendrm_dev->num_crtcs)) - return -EINVAL; - if (atomic_read(&xendrm_dev->vblank_enabled[pipe]) == 0) - xendrm_timer_start(&xendrm_dev->vblank_timer); - atomic_set(&xendrm_dev->vblank_enabled[pipe], 1); - return 0; -} - -void xendrm_disable_vblank(struct drm_device *dev, unsigned int pipe) -{ - struct xendrm_device *xendrm_dev = dev->dev_private; - - if (unlikely(pipe >= xendrm_dev->num_crtcs)) - return; - if (atomic_read(&xendrm_dev->vblank_enabled[pipe])) - xendrm_timer_stop(&xendrm_dev->vblank_timer, false); - atomic_set(&xendrm_dev->vblank_enabled[pipe], 0); -} - -static int xendrm_dumb_create(struct drm_file *file_priv, - struct drm_device *dev, struct drm_mode_create_dumb *args) -{ - struct xendrm_device *xendrm_dev = dev->dev_private; - struct drm_gem_object *gem_obj; - struct page **pages = NULL; - struct sg_table *sgt = NULL; - bool be_alloc; - int ret; - - ret = xendrm_gem_dumb_create(file_priv, dev, args); - if (ret < 0) - goto fail; - gem_obj = drm_gem_object_lookup(file_priv, args->handle); - if (!gem_obj) { - ret = -EINVAL; - goto fail_destroy; - } - drm_gem_object_unreference_unlocked(gem_obj); - - be_alloc = xendrm_dev->platdata->be_alloc; - /* - * if buffers are allocated on backend's side, then - * pass NULL for pages and have backend to provide them - */ - if (!be_alloc) { - pages = xendrm_gem_get_pages(gem_obj); - if (!pages) - sgt = xendrm_gem_get_sg_table(gem_obj); - } - pages = xendrm_dev->front_ops->dbuf_create( - xendrm_dev->xdrv_info, xendrm_dbuf_to_cookie(gem_obj), - args->width, args->height, args->bpp, args->size, - pages, sgt); - if (IS_ERR_OR_NULL(pages)) { - ret = PTR_ERR(pages); - goto fail_destroy; - } - if (be_alloc) - xendrm_gem_set_pages(gem_obj, pages); - return 0; - -fail_destroy: - drm_gem_dumb_destroy(file_priv, dev, args->handle); -fail: - DRM_ERROR("Failed to create dumb buffer: %d\n", ret); - return ret; -} - -static void xendrm_free_object(struct drm_gem_object *gem_obj) -{ - struct xendrm_device *xendrm_dev = gem_obj->dev->dev_private; - - xendrm_dev->front_ops->dbuf_destroy(xendrm_dev->xdrv_info, - xendrm_dbuf_to_cookie(gem_obj)); - xendrm_gem_free_object(gem_obj); -} - -static void xendrm_on_page_flip(struct platform_device *pdev, - int conn_idx, uint64_t fb_cookie) -{ - struct xendrm_device *xendrm_dev = platform_get_drvdata(pdev); - - if (unlikely(conn_idx >= xendrm_dev->num_crtcs)) - return; - xendrm_crtc_on_page_flip_done(&xendrm_dev->crtcs[conn_idx], fb_cookie); -} - -static void xendrm_handle_vblank(unsigned long data) -{ - struct xendrm_device *xendrm_dev = (struct xendrm_device *)data; - int i; - - for (i = 0; i < ARRAY_SIZE(xendrm_dev->crtcs); i++) { - if (atomic_read(&xendrm_dev->vblank_enabled[i])) { - struct xendrm_crtc *xen_crtc = &xendrm_dev->crtcs[i]; - - drm_crtc_handle_vblank(&xen_crtc->crtc); - /* handle page flip time outs */ - if (likely(atomic_read(&xendrm_dev->pflip_to_cnt_armed[i]))) - if (unlikely(atomic_dec_and_test( - &xendrm_dev->pflip_to_cnt[i]))) { - atomic_set(&xendrm_dev->pflip_to_cnt_armed[i], 0); - xendrm_crtc_on_page_flip_to(xen_crtc); - } - } - } -} - -static void xendrm_lastclose(struct drm_device *dev) -{ - struct xendrm_device *xendrm_dev = dev->dev_private; - - xendrm_dev->front_ops->drm_last_close(xendrm_dev->xdrv_info); -} - -void xendrm_vtimer_restart_to(struct xendrm_device *xendrm_dev, int index) -{ - atomic_set(&xendrm_dev->pflip_to_cnt[index], - xendrm_dev->vblank_timer.to_period); - atomic_set(&xendrm_dev->pflip_to_cnt_armed[index], 1); -} - -void xendrm_vtimer_cancel_to(struct xendrm_device *xendrm_dev, int index) -{ - atomic_set(&xendrm_dev->pflip_to_cnt_armed[index], 0); -} - -static const struct file_operations xendrm_fops = { - .owner = THIS_MODULE, - .open = drm_open, - .release = drm_release, - .unlocked_ioctl = drm_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = drm_compat_ioctl, -#endif - .poll = drm_poll, - .read = drm_read, - .llseek = no_llseek, - .mmap = xendrm_gem_mmap, -}; - -static const struct vm_operations_struct xendrm_vm_ops = { - .open = drm_gem_vm_open, - .close = drm_gem_vm_close, -}; - -struct drm_driver xendrm_driver = { - .driver_features = DRIVER_GEM | DRIVER_MODESET | - DRIVER_PRIME | DRIVER_ATOMIC, - .lastclose = xendrm_lastclose, - .get_vblank_counter = drm_vblank_no_hw_counter, - .enable_vblank = xendrm_enable_vblank, - .disable_vblank = xendrm_disable_vblank, - .get_vblank_counter = drm_vblank_no_hw_counter, - .gem_free_object_unlocked = xendrm_free_object, - .gem_vm_ops = &xendrm_vm_ops, - .prime_handle_to_fd = drm_gem_prime_handle_to_fd, - .prime_fd_to_handle = drm_gem_prime_fd_to_handle, - .gem_prime_import = drm_gem_prime_import, - .gem_prime_export = drm_gem_prime_export, - .gem_prime_get_sg_table = xendrm_gem_get_sg_table, - .gem_prime_import_sg_table = xendrm_gem_import_sg_table, - .gem_prime_vmap = xendrm_gem_prime_vmap, - .gem_prime_vunmap = xendrm_gem_prime_vunmap, - .gem_prime_mmap = xendrm_gem_prime_mmap, - .dumb_create = xendrm_dumb_create, - .dumb_map_offset = xendrm_gem_dumb_map_offset, - .dumb_destroy = drm_gem_dumb_destroy, - .fops = &xendrm_fops, - .name = "xendrm-du", - .desc = "Xen PV DRM Display Unit", - .date = "20161109", - .major = 1, - .minor = 0, -}; - -static struct xendrm_timer_callbacks vblank_timer_ops = { - .on_period = xendrm_handle_vblank, -}; - -int xendrm_probe(struct platform_device *pdev, - struct xendispl_front_ops *xendrm_front_funcs) -{ - struct xendrm_plat_data *platdata; - struct xendrm_device *xendrm_dev; - struct drm_device *ddev; - int ret; - - platdata = dev_get_platdata(&pdev->dev); - DRM_INFO("Creating %s\n", xendrm_driver.desc); - /* Allocate and initialize the DRM and xendrm device structures. */ - xendrm_dev = devm_kzalloc(&pdev->dev, sizeof(*xendrm_dev), GFP_KERNEL); - if (!xendrm_dev) - return -ENOMEM; - - xendrm_dev->front_ops = xendrm_front_funcs; - xendrm_dev->front_ops->on_page_flip = xendrm_on_page_flip; - xendrm_dev->xdrv_info = platdata->xdrv_info; - - ddev = drm_dev_alloc(&xendrm_driver, &pdev->dev); - if (!ddev) - return -ENOMEM; - - xendrm_dev->drm = ddev; - /* - * FIXME: assume 1 CRTC and 1 Encoder per each connector - */ - xendrm_dev->num_crtcs = platdata->num_connectors; - xendrm_dev->platdata = platdata; - ddev->dev_private = xendrm_dev; - platform_set_drvdata(pdev, xendrm_dev); - - ret = drm_vblank_init(ddev, xendrm_dev->num_crtcs); - if (ret < 0) - goto fail_vblank; - /* DRM/KMS objects */ - ret = xendrm_kms_init(xendrm_dev); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, - "failed to initialize DRM/KMS (%d)\n", ret); - goto fail_modeset; - } - /* setup vblank emulation: all CRTCs are set for - * XENDRM_CRTC_VREFRESH_HZ and lots of operations during vblank - * interrupt are handled under drm_dev->event_lock. This allows - * having a single vblank "interrupt" - */ - xendrm_timer_init(&xendrm_dev->vblank_timer, - (unsigned long)xendrm_dev, &vblank_timer_ops); - xendrm_timer_setup(&xendrm_dev->vblank_timer, - XENDRM_CRTC_VREFRESH_HZ, XENDRM_CRTC_PFLIP_TO_MS); - ddev->irq_enabled = 1; - - /* Register the DRM device with the core and the connectors, - * encoders, planes with sysfs. - */ - ret = drm_dev_register(ddev, 0); - if (ret) - goto fail_register; - - DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", - xendrm_driver.name, xendrm_driver.major, - xendrm_driver.minor, xendrm_driver.patchlevel, - xendrm_driver.date, ddev->primary->index); - return 0; - -fail_register: - xendrm_timer_cleanup(&xendrm_dev->vblank_timer); - drm_dev_unregister(ddev); -fail_modeset: - drm_mode_config_cleanup(ddev); -fail_vblank: - drm_vblank_cleanup(ddev); - return ret; -} - -int xendrm_remove(struct platform_device *pdev) -{ - struct xendrm_device *xendrm_dev = platform_get_drvdata(pdev); - struct drm_device *drm_dev = xendrm_dev->drm; - - xendrm_timer_cleanup(&xendrm_dev->vblank_timer); - drm_dev_unregister(drm_dev); - drm_vblank_cleanup(drm_dev); - drm_mode_config_cleanup(drm_dev); - drm_dev_unref(drm_dev); - return 0; -} - -bool xendrm_is_used(struct platform_device *pdev) -{ - struct xendrm_device *xendrm_dev = platform_get_drvdata(pdev); - struct drm_device *drm_dev; - - if (!xendrm_dev) - return false; - drm_dev = xendrm_dev->drm; - if (!drm_dev) - return false; - - /* FIXME: the code below must be protected by drm_global_mutex, - * but it is not accessible to us and anyways there is a - * race condition. - */ - return drm_dev->open_count != 0; -} diff --git a/drivers/gpu/drm/xen/xen_drm_drv.h b/drivers/gpu/drm/xen/xen_drm_drv.h deleted file mode 100644 index eb886cd469d7d2..00000000000000 --- a/drivers/gpu/drm/xen/xen_drm_drv.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Xen para-virtual DRM device - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * Copyright (C) 2016 EPAM Systems Inc. - * - * Author: Oleksandr Andrushchenko - */ - -#ifndef __XEN_DRM_H -#define __XEN_DRM_H - -#include "xen_drm_crtc.h" -#include "xen_drm_timer.h" - -#define XENDRM_MAX_CRTCS 4 - -struct xendispl_front_ops; -struct platform_device; -struct drm_framebuffer; -struct drm_gem_object; - -struct xendrm_device { - struct xdrv_info *xdrv_info; - struct xendispl_front_ops *front_ops; - struct drm_device *drm; - int num_crtcs; - struct xendrm_plat_data *platdata; - struct xendrm_crtc crtcs[XENDRM_MAX_CRTCS]; - - /* vblank and page flip handling */ - struct xendrm_timer vblank_timer; - atomic_t pflip_to_cnt[XENDRM_MAX_CRTCS]; - atomic_t pflip_to_cnt_armed[XENDRM_MAX_CRTCS]; - atomic_t vblank_enabled[XENDRM_MAX_CRTCS]; -}; - -struct xendrm_cfg_connector { - int width; - int height; - char *xenstore_path; -}; - -struct xendrm_plat_data { - struct xdrv_info *xdrv_info; - /* number of connectors in this configuration */ - int num_connectors; - /* connector configurations */ - struct xendrm_cfg_connector connectors[XENDRM_MAX_CRTCS]; - /* set if dumb buffers are allocated externally on backend side */ - bool be_alloc; -}; - -static inline uint64_t xendrm_fb_to_cookie(struct drm_framebuffer *fb) -{ - return (uint64_t)fb; -} - -static inline uint64_t xendrm_dbuf_to_cookie(struct drm_gem_object *gem_obj) -{ - return (uint64_t)gem_obj; -} - -int xendrm_probe(struct platform_device *pdev, - struct xendispl_front_ops *xendrm_front_funcs); -int xendrm_remove(struct platform_device *pdev); -bool xendrm_is_used(struct platform_device *pdev); - -void xendrm_vtimer_restart_to(struct xendrm_device *xendrm_dev, int index); -void xendrm_vtimer_cancel_to(struct xendrm_device *xendrm_dev, int index); - -#endif /* __XEN_DRM_H*/ - diff --git a/drivers/gpu/drm/xen/xen_drm_front.c b/drivers/gpu/drm/xen/xen_drm_front.c index 60c03dc0dcf68c..b2e31570932696 100644 --- a/drivers/gpu/drm/xen/xen_drm_front.c +++ b/drivers/gpu/drm/xen/xen_drm_front.c @@ -11,100 +11,30 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * Copyright (C) 2016 EPAM Systems Inc. + * Copyright (C) 2016-2017 EPAM Systems Inc. * - * Author: Oleksandr Andrushchenko + * Author: Oleksandr Andrushchenko */ #include #include #include #include -#include -#include -#include #include -#include #include +#include #include -#include -#include -#include -#include -#include "xen_drm_drv.h" +#include "xen_drm_balloon.h" #include "xen_drm_front.h" -#include "xen_drm_shbuf.h" - -/* all operations which are not connector oriented use this ctrl event channel, - * e.g. fb_attach/destroy which belong to a DRM device, not to a CRTC - */ -#define GENERIC_OP_EVT_CHNL 0 - -enum xdrv_evtchnl_state { - EVTCHNL_STATE_DISCONNECTED, - EVTCHNL_STATE_CONNECTED, -}; -enum xdrv_evtchnl_type { - EVTCHNL_TYPE_REQ, - EVTCHNL_TYPE_EVT, -}; - -struct xdrv_evtchnl_info { - struct xdrv_info *drv_info; - int gref; - int port; - int irq; - int index; - /* state of the event channel */ - enum xdrv_evtchnl_state state; - enum xdrv_evtchnl_type type; - /* either response id or incoming event id */ - uint16_t evt_id; - /* next request id or next expected event id */ - uint16_t evt_next_id; - union { - struct { - struct xen_displif_front_ring ring; - struct completion completion; - /* latest response status */ - int resp_status; - } req; - struct { - struct xendispl_event_page *page; - } evt; - } u; -}; - -struct xdrv_evtchnl_pair_info { - struct xdrv_evtchnl_info req; - struct xdrv_evtchnl_info evt; -}; +#include "xen_drm_front_drv.h" +#include "xen_drm_front_evtchnl.h" +#include "xen_drm_front_shbuf.h" -struct xdrv_info { - struct xenbus_device *xb_dev; - spinlock_t io_lock; - struct mutex mutex; - bool drm_pdrv_registered; - /* virtual DRM platform device */ - struct platform_device *drm_pdev; - - int num_evt_pairs; - struct xdrv_evtchnl_pair_info *evt_pairs; - struct xendrm_plat_data cfg_plat_data; - - /* display buffers */ - struct list_head dbuf_list; -}; - -static inline void xdrv_evtchnl_flush( - struct xdrv_evtchnl_info *channel); -static void xdrv_drm_unload(struct xdrv_info *drv_info); - -static inline struct xendispl_req *ddrv_be_prepare_req( - struct xdrv_evtchnl_info *evtchnl, uint8_t operation) +static struct xendispl_req *be_prepare_req( + struct xen_drm_front_evtchnl *evtchnl, uint8_t operation) { struct xendispl_req *req; @@ -116,88 +46,95 @@ static inline struct xendispl_req *ddrv_be_prepare_req( return req; } -/* CAUTION!!! Call this with the spin lock held. - * This function will release it - */ -static int ddrv_be_stream_do_io(struct xdrv_evtchnl_info *evtchnl, - struct xendispl_req *req, unsigned long flags) +static int be_stream_do_io(struct xen_drm_front_evtchnl *evtchnl, + struct xendispl_req *req) { - int ret; - reinit_completion(&evtchnl->u.req.completion); - if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED)) { - spin_unlock_irqrestore(&evtchnl->drv_info->io_lock, flags); + if (unlikely(evtchnl->state != EVTCHNL_STATE_CONNECTED)) return -EIO; - } - xdrv_evtchnl_flush(evtchnl); - spin_unlock_irqrestore(&evtchnl->drv_info->io_lock, flags); - ret = 0; + + xen_drm_front_evtchnl_flush(evtchnl); + return 0; +} + +static int be_stream_wait_io(struct xen_drm_front_evtchnl *evtchnl) +{ if (wait_for_completion_timeout( &evtchnl->u.req.completion, msecs_to_jiffies(VDRM_WAIT_BACK_MS)) <= 0) - ret = -ETIMEDOUT; - if (ret < 0) - return ret; + return -ETIMEDOUT; + return evtchnl->u.req.resp_status; } -int xendispl_front_mode_set(struct xendrm_crtc *xen_crtc, uint32_t x, +static int be_mode_set(struct xen_drm_front_crtc *xen_crtc, uint32_t x, uint32_t y, uint32_t width, uint32_t height, uint32_t bpp, uint64_t fb_cookie) { - struct xdrv_evtchnl_info *evtchnl; - struct xdrv_info *drv_info; + struct xen_drm_front_evtchnl *evtchnl; + struct xen_drm_front_info *front_info; struct xendispl_req *req; unsigned long flags; + int ret; - drv_info = xen_crtc->xendrm_dev->xdrv_info; - evtchnl = &drv_info->evt_pairs[xen_crtc->index].req; + front_info = xen_crtc->drm_info->front_info; + evtchnl = &front_info->evt_pairs[xen_crtc->index].req; if (unlikely(!evtchnl)) return -EIO; - spin_lock_irqsave(&drv_info->io_lock, flags); - req = ddrv_be_prepare_req(evtchnl, XENDISPL_OP_SET_CONFIG); + + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_SET_CONFIG); req->op.set_config.x = x; req->op.set_config.y = y; req->op.set_config.width = width; req->op.set_config.height = height; req->op.set_config.bpp = bpp; req->op.set_config.fb_cookie = fb_cookie; - return ddrv_be_stream_do_io(evtchnl, req, flags); + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret < 0) + return ret; + + return be_stream_wait_io(evtchnl); } -struct page **xendispl_front_dbuf_create(struct xdrv_info *drv_info, +static int be_dbuf_create_int(struct xen_drm_front_info *front_info, uint64_t dbuf_cookie, uint32_t width, uint32_t height, - uint32_t bpp, uint64_t size, struct page **pages, struct sg_table *sgt) + uint32_t bpp, uint64_t size, struct page **pages, + struct sg_table *sgt) { - struct xdrv_evtchnl_info *evtchnl; - struct xdrv_shared_buffer_info *buf; + struct xen_drm_front_evtchnl *evtchnl; + struct xen_drm_front_shbuf *buf; struct xendispl_req *req; - struct xdrv_shared_buffer_alloc_info alloc_info; + struct xen_drm_front_shbuf_alloc alloc_info; unsigned long flags; bool be_alloc; int ret; - evtchnl = &drv_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; + evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; if (unlikely(!evtchnl)) - return ERR_PTR(-EIO); - be_alloc = drv_info->cfg_plat_data.be_alloc; + return -EIO; + + be_alloc = front_info->cfg_plat_data.be_alloc; memset(&alloc_info, 0, sizeof(alloc_info)); - alloc_info.xb_dev = drv_info->xb_dev; - alloc_info.dbuf_list = &drv_info->dbuf_list; + alloc_info.xb_dev = front_info->xb_dev; + alloc_info.dbuf_list = &front_info->dbuf_list; alloc_info.dbuf_cookie = dbuf_cookie; alloc_info.pages = pages; - alloc_info.num_pages = DIV_ROUND_UP(size, XEN_PAGE_SIZE); + alloc_info.size = size; alloc_info.sgt = sgt; alloc_info.be_alloc = be_alloc; - buf = xdrv_shbuf_alloc(&alloc_info); + buf = xen_drm_front_shbuf_alloc(&alloc_info); if (!buf) - return ERR_PTR(-ENOMEM); + return -ENOMEM; - spin_lock_irqsave(&drv_info->io_lock, flags); - req = ddrv_be_prepare_req(evtchnl, XENDISPL_OP_DBUF_CREATE); - req->op.dbuf_create.gref_directory = xdrv_shbuf_get_dir_start(buf); + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_DBUF_CREATE); + req->op.dbuf_create.gref_directory = xen_drm_front_shbuf_get_dir_start(buf); req->op.dbuf_create.buffer_sz = size; req->op.dbuf_create.dbuf_cookie = dbuf_cookie; req->op.dbuf_create.width = width; @@ -205,116 +142,195 @@ struct page **xendispl_front_dbuf_create(struct xdrv_info *drv_info, req->op.dbuf_create.bpp = bpp; if (be_alloc) req->op.dbuf_create.flags |= XENDISPL_DBUF_FLG_REQ_ALLOC; - ret = ddrv_be_stream_do_io(evtchnl, req, flags); + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret < 0) + goto fail; + + ret = be_stream_wait_io(evtchnl); if (ret < 0) goto fail; + if (be_alloc) { - ret = xdrv_shbuf_be_alloc_map(buf); + ret = xen_drm_front_shbuf_be_alloc_map(buf); if (ret < 0) goto fail; } - return xdrv_shbuf_get_pages(buf); + + return 0; + fail: - xdrv_shbuf_free_by_dbuf_cookie(&drv_info->dbuf_list, dbuf_cookie); - return ERR_PTR(ret); + xen_drm_front_shbuf_free_by_dbuf_cookie(&front_info->dbuf_list, dbuf_cookie); + return ret; +} + +static int be_dbuf_create_from_sgt(struct xen_drm_front_info *front_info, + uint64_t dbuf_cookie, uint32_t width, uint32_t height, + uint32_t bpp, uint64_t size, struct sg_table *sgt) +{ + return be_dbuf_create_int(front_info, + dbuf_cookie, width, height, bpp, size, NULL, sgt); } -int xendispl_front_dbuf_destroy(struct xdrv_info *drv_info, +static int be_dbuf_create(struct xen_drm_front_info *front_info, + uint64_t dbuf_cookie, uint32_t width, uint32_t height, + uint32_t bpp, uint64_t size, struct page **pages) +{ + return be_dbuf_create_int(front_info, + dbuf_cookie, width, height, bpp, size, pages, NULL); +} + +static int be_dbuf_destroy(struct xen_drm_front_info *front_info, uint64_t dbuf_cookie) { - struct xdrv_evtchnl_info *evtchnl; + struct xen_drm_front_evtchnl *evtchnl; struct xendispl_req *req; unsigned long flags; bool be_alloc; int ret; - evtchnl = &drv_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; + evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; if (unlikely(!evtchnl)) return -EIO; - spin_lock_irqsave(&drv_info->io_lock, flags); - req = ddrv_be_prepare_req(evtchnl, XENDISPL_OP_DBUF_DESTROY); - req->op.dbuf_destroy.dbuf_cookie = dbuf_cookie; - be_alloc = drv_info->cfg_plat_data.be_alloc; + + be_alloc = front_info->cfg_plat_data.be_alloc; + if (be_alloc) - xdrv_shbuf_free_by_dbuf_cookie(&drv_info->dbuf_list, + xen_drm_front_shbuf_free_by_dbuf_cookie(&front_info->dbuf_list, dbuf_cookie); - ret = ddrv_be_stream_do_io(evtchnl, req, flags); + + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_DBUF_DESTROY); + req->op.dbuf_destroy.dbuf_cookie = dbuf_cookie; + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret == 0) + ret = be_stream_wait_io(evtchnl); + + /* + * do this regardless of communication status with the backend: + * if we cannot remove remote resources remove what we can locally + */ if (!be_alloc) - xdrv_shbuf_free_by_dbuf_cookie(&drv_info->dbuf_list, + xen_drm_front_shbuf_free_by_dbuf_cookie(&front_info->dbuf_list, dbuf_cookie); return ret; } -int xendispl_front_fb_attach(struct xdrv_info *drv_info, +static int be_fb_attach(struct xen_drm_front_info *front_info, uint64_t dbuf_cookie, uint64_t fb_cookie, uint32_t width, uint32_t height, uint32_t pixel_format) { - struct xdrv_evtchnl_info *evtchnl; - struct xdrv_shared_buffer_info *buf; + struct xen_drm_front_evtchnl *evtchnl; + struct xen_drm_front_shbuf *buf; struct xendispl_req *req; unsigned long flags; + int ret; - evtchnl = &drv_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; + evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; if (unlikely(!evtchnl)) return -EIO; - buf = xdrv_shbuf_get_by_dbuf_cookie(&drv_info->dbuf_list, + + buf = xen_drm_front_shbuf_get_by_dbuf_cookie(&front_info->dbuf_list, dbuf_cookie); if (!buf) return -EINVAL; + buf->fb_cookie = fb_cookie; - spin_lock_irqsave(&drv_info->io_lock, flags); - req = ddrv_be_prepare_req(evtchnl, XENDISPL_OP_FB_ATTACH); + + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_FB_ATTACH); req->op.fb_attach.dbuf_cookie = dbuf_cookie; req->op.fb_attach.fb_cookie = fb_cookie; req->op.fb_attach.width = width; req->op.fb_attach.height = height; req->op.fb_attach.pixel_format = pixel_format; - return ddrv_be_stream_do_io(evtchnl, req, flags); + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret < 0) + return ret; + + return be_stream_wait_io(evtchnl); } -int xendispl_front_fb_detach(struct xdrv_info *drv_info, uint64_t fb_cookie) +static int be_fb_detach(struct xen_drm_front_info *front_info, + uint64_t fb_cookie) { - struct xdrv_evtchnl_info *evtchnl; + struct xen_drm_front_evtchnl *evtchnl; struct xendispl_req *req; unsigned long flags; + int ret; - evtchnl = &drv_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; + evtchnl = &front_info->evt_pairs[GENERIC_OP_EVT_CHNL].req; if (unlikely(!evtchnl)) return -EIO; - spin_lock_irqsave(&drv_info->io_lock, flags); - req = ddrv_be_prepare_req(evtchnl, XENDISPL_OP_FB_DETACH); + + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_FB_DETACH); req->op.fb_detach.fb_cookie = fb_cookie; - return ddrv_be_stream_do_io(evtchnl, req, flags); + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret < 0) + return ret; + + return be_stream_wait_io(evtchnl); } -int xendispl_front_page_flip(struct xdrv_info *drv_info, int conn_idx, +static int be_page_flip(struct xen_drm_front_info *front_info, int conn_idx, uint64_t fb_cookie) { - struct xdrv_evtchnl_info *evtchnl; + struct xen_drm_front_evtchnl *evtchnl; struct xendispl_req *req; unsigned long flags; + int ret; - if (unlikely(conn_idx >= drv_info->num_evt_pairs)) + if (unlikely(conn_idx >= front_info->num_evt_pairs)) return -EINVAL; - xdrv_shbuf_flush_fb(&drv_info->dbuf_list, fb_cookie); - evtchnl = &drv_info->evt_pairs[conn_idx].req; - spin_lock_irqsave(&drv_info->io_lock, flags); - req = ddrv_be_prepare_req(evtchnl, XENDISPL_OP_PG_FLIP); + + xen_drm_front_shbuf_flush_fb(&front_info->dbuf_list, fb_cookie); + evtchnl = &front_info->evt_pairs[conn_idx].req; + spin_lock_irqsave(&front_info->io_lock, flags); + req = be_prepare_req(evtchnl, XENDISPL_OP_PG_FLIP); req->op.pg_flip.fb_cookie = fb_cookie; - return ddrv_be_stream_do_io(evtchnl, req, flags); + + ret = be_stream_do_io(evtchnl, req); + spin_unlock_irqrestore(&front_info->io_lock, flags); + + if (ret < 0) + return ret; + + return be_stream_wait_io(evtchnl); } -static struct xendispl_front_ops xendispl_front_funcs = { - .mode_set = xendispl_front_mode_set, - .dbuf_create = xendispl_front_dbuf_create, - .dbuf_destroy = xendispl_front_dbuf_destroy, - .fb_attach = xendispl_front_fb_attach, - .fb_detach = xendispl_front_fb_detach, - .page_flip = xendispl_front_page_flip, - .drm_last_close = xdrv_drm_unload, +static void drm_drv_unload(struct xen_drm_front_info *front_info) +{ + if (front_info->xb_dev->state != XenbusStateReconfiguring) + return; + + DRM_INFO("Can try removing driver now\n"); + xenbus_switch_state(front_info->xb_dev, XenbusStateInitialising); +} + +static struct xen_drm_front_ops xen_drm_backend_ops = { + .mode_set = be_mode_set, + .dbuf_create = be_dbuf_create, + .dbuf_create_from_sgt = be_dbuf_create_from_sgt, + .dbuf_destroy = be_dbuf_destroy, + .fb_attach = be_fb_attach, + .fb_detach = be_fb_detach, + .page_flip = be_page_flip, + .drm_last_close = drm_drv_unload, }; -static int ddrv_probe(struct platform_device *pdev) +static int drm_drv_probe(struct platform_device *pdev) { #ifdef CONFIG_DRM_XEN_FRONTEND_CMA struct device *dev = &pdev->dev; @@ -322,578 +338,129 @@ static int ddrv_probe(struct platform_device *pdev) /* make sure we have DMA ops set up, so no dummy ops are in use */ arch_setup_dma_ops(dev, 0, *dev->dma_mask, NULL, false); #endif - return xendrm_probe(pdev, &xendispl_front_funcs); + return xen_drm_front_drv_probe(pdev, &xen_drm_backend_ops); } -struct platform_device_info ddrv_platform_info = { +static int drm_drv_remove(struct platform_device *pdev) +{ + return xen_drm_front_drv_remove(pdev); +} + +struct platform_device_info xen_drm_front_platform_info = { .name = XENDISPL_DRIVER_NAME, .id = 0, .num_res = 0, .dma_mask = DMA_BIT_MASK(32), }; -static struct platform_driver ddrv_info = { - .probe = ddrv_probe, - .remove = xendrm_remove, +static struct platform_driver xen_drm_front_front_info = { + .probe = drm_drv_probe, + .remove = drm_drv_remove, .driver = { .name = XENDISPL_DRIVER_NAME, }, }; -static void ddrv_cleanup(struct xdrv_info *drv_info) -{ - if (!drv_info->drm_pdrv_registered) - return; - if (drv_info->drm_pdev) - platform_device_unregister(drv_info->drm_pdev); - platform_driver_unregister(&ddrv_info); - drv_info->drm_pdrv_registered = false; - drv_info->drm_pdev = NULL; -} - -static int ddrv_init(struct xdrv_info *drv_info) -{ - struct xendrm_plat_data *platdata; - int ret; - - ret = platform_driver_register(&ddrv_info); - if (ret < 0) - return ret; - drv_info->drm_pdrv_registered = true; - platdata = &drv_info->cfg_plat_data; - /* pass card configuration via platform data */ - ddrv_platform_info.data = platdata; - ddrv_platform_info.size_data = sizeof(struct xendrm_plat_data); - drv_info->drm_pdev = platform_device_register_full(&ddrv_platform_info); - if (IS_ERR(drv_info->drm_pdev)) { - drv_info->drm_pdev = NULL; - goto fail; - } - return 0; - -fail: - DRM_ERROR("Failed to register DRM driver\n"); - ddrv_cleanup(drv_info); - return -ENODEV; -} - -static irqreturn_t xdrv_evtchnl_interrupt_ctrl(int irq, void *dev_id) -{ - struct xdrv_evtchnl_info *channel = dev_id; - struct xdrv_info *drv_info = channel->drv_info; - struct xendispl_resp *resp; - RING_IDX i, rp; - unsigned long flags; - - spin_lock_irqsave(&drv_info->io_lock, flags); - if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED)) - goto out; -again: - rp = channel->u.req.ring.sring->rsp_prod; - /* Ensure we see queued responses up to 'rp'. */ - virt_rmb(); - for (i = channel->u.req.ring.rsp_cons; i != rp; i++) { - resp = RING_GET_RESPONSE(&channel->u.req.ring, i); - if (unlikely(resp->id != channel->evt_id)) - continue; - switch (resp->operation) { - case XENDISPL_OP_PG_FLIP: - case XENDISPL_OP_FB_ATTACH: - case XENDISPL_OP_FB_DETACH: - case XENDISPL_OP_DBUF_CREATE: - case XENDISPL_OP_DBUF_DESTROY: - case XENDISPL_OP_SET_CONFIG: - channel->u.req.resp_status = resp->status; - complete(&channel->u.req.completion); - break; - default: - DRM_ERROR("Operation %d is not supported\n", - resp->operation); - break; - } - } - channel->u.req.ring.rsp_cons = i; - if (i != channel->u.req.ring.req_prod_pvt) { - int more_to_do; - - RING_FINAL_CHECK_FOR_RESPONSES(&channel->u.req.ring, - more_to_do); - if (more_to_do) - goto again; - } else - channel->u.req.ring.sring->rsp_event = i + 1; -out: - spin_unlock_irqrestore(&drv_info->io_lock, flags); - return IRQ_HANDLED; -} - -static irqreturn_t xdrv_evtchnl_interrupt_evt(int irq, void *dev_id) +static void drm_drv_deinit(struct xen_drm_front_info *front_info) { - struct xdrv_evtchnl_info *channel = dev_id; - struct xdrv_info *drv_info = channel->drv_info; - struct xendispl_event_page *page = channel->u.evt.page; - uint32_t cons, prod; - unsigned long flags; - - spin_lock_irqsave(&drv_info->io_lock, flags); - if (unlikely(channel->state != EVTCHNL_STATE_CONNECTED)) - goto out; - prod = page->in_prod; - /* ensure we see ring contents up to prod */ - virt_rmb(); - if (prod == page->in_cons) - goto out; - for (cons = page->in_cons; cons != prod; cons++) { - struct xendispl_evt *event; - - event = &XENDISPL_IN_RING_REF(page, cons); - if (unlikely(event->id != channel->evt_id++)) - continue; - switch (event->type) { - case XENDISPL_EVT_PG_FLIP: - if (likely(xendispl_front_funcs.on_page_flip)) { - xendispl_front_funcs.on_page_flip( - drv_info->drm_pdev, channel->index, - event->op.pg_flip.fb_cookie); - } - break; - } - } - page->in_cons = cons; - /* ensure ring contents */ - virt_wmb(); -out: - spin_unlock_irqrestore(&drv_info->io_lock, flags); - return IRQ_HANDLED; -} - -static void xdrv_evtchnl_free(struct xdrv_info *drv_info, - struct xdrv_evtchnl_info *channel) -{ - unsigned long page = 0; - - if (channel->type == EVTCHNL_TYPE_REQ) - page = (unsigned long)channel->u.req.ring.sring; - else if (channel->type == EVTCHNL_TYPE_EVT) - page = (unsigned long)channel->u.evt.page; - if (!page) + if (!front_info->drm_pdrv_registered) return; - channel->state = EVTCHNL_STATE_DISCONNECTED; - if (channel->type == EVTCHNL_TYPE_REQ) { - /* release all who still waits for response if any */ - channel->u.req.resp_status = -EIO; - complete_all(&channel->u.req.completion); - } - if (channel->irq) - unbind_from_irqhandler(channel->irq, channel); - if (channel->port) - xenbus_free_evtchn(drv_info->xb_dev, channel->port); - /* End access and free the pages */ - if (channel->gref != GRANT_INVALID_REF) - gnttab_end_foreign_access(channel->gref, 0, page); - if (channel->type == EVTCHNL_TYPE_REQ) - channel->u.req.ring.sring = NULL; - else - channel->u.evt.page = NULL; - memset(channel, 0, sizeof(*channel)); -} -static void xdrv_evtchnl_free_all(struct xdrv_info *drv_info) -{ - int i; + if (front_info->drm_pdev) + platform_device_unregister(front_info->drm_pdev); - if (!drv_info->evt_pairs) - return; - for (i = 0; i < drv_info->num_evt_pairs; i++) { - xdrv_evtchnl_free(drv_info, - &drv_info->evt_pairs[i].req); - xdrv_evtchnl_free(drv_info, - &drv_info->evt_pairs[i].evt); - } - devm_kfree(&drv_info->xb_dev->dev, drv_info->evt_pairs); - drv_info->evt_pairs = NULL; + platform_driver_unregister(&xen_drm_front_front_info); + front_info->drm_pdrv_registered = false; + front_info->drm_pdev = NULL; } -static int xdrv_evtchnl_alloc(struct xdrv_info *drv_info, int index, - struct xdrv_evtchnl_info *evt_channel, - enum xdrv_evtchnl_type type) +static int drm_drv_init(struct xen_drm_front_info *front_info) { - struct xenbus_device *xb_dev = drv_info->xb_dev; - unsigned long page; - grant_ref_t gref; - irq_handler_t handler; + struct xen_drm_front_cfg_plat_data *platdata; int ret; - memset(evt_channel, 0, sizeof(*evt_channel)); - evt_channel->type = type; - evt_channel->index = index; - evt_channel->drv_info = drv_info; - evt_channel->state = EVTCHNL_STATE_DISCONNECTED; - evt_channel->gref = GRANT_INVALID_REF; - page = get_zeroed_page(GFP_NOIO | __GFP_HIGH); - if (!page) { - ret = -ENOMEM; - goto fail; - } - if (type == EVTCHNL_TYPE_REQ) { - struct xen_displif_sring *sring; - - init_completion(&evt_channel->u.req.completion); - sring = (struct xen_displif_sring *)page; - SHARED_RING_INIT(sring); - FRONT_RING_INIT(&evt_channel->u.req.ring, - sring, XEN_PAGE_SIZE); - - ret = xenbus_grant_ring(xb_dev, sring, 1, &gref); - if (ret < 0) - goto fail; - handler = xdrv_evtchnl_interrupt_ctrl; - } else { - evt_channel->u.evt.page = (struct xendispl_event_page *)page; - ret = gnttab_grant_foreign_access(xb_dev->otherend_id, - virt_to_gfn((void *)page), 0); - if (ret < 0) - goto fail; - gref = ret; - handler = xdrv_evtchnl_interrupt_evt; - } - evt_channel->gref = gref; - - ret = xenbus_alloc_evtchn(xb_dev, &evt_channel->port); - if (ret < 0) - goto fail; - - ret = bind_evtchn_to_irqhandler(evt_channel->port, - handler, 0, xb_dev->devicetype, evt_channel); + ret = platform_driver_register(&xen_drm_front_front_info); if (ret < 0) - goto fail; - evt_channel->irq = ret; - return 0; - -fail: - DRM_ERROR("Failed to allocate ring: %d\n", ret); - return ret; -} - -static int xdrv_evtchnl_publish(struct xenbus_transaction xbt, - struct xdrv_evtchnl_info *evt_channel, - const char *path, const char *node_ring, - const char *node_chnl) -{ - const char *message; - int ret; - - /* write control channel ring reference */ - ret = xenbus_printf(xbt, path, node_ring, "%u", - evt_channel->gref); - if (ret < 0) { - message = "writing ring-ref"; - goto fail; - } - /* write event channel ring reference */ - ret = xenbus_printf(xbt, path, node_chnl, "%u", - evt_channel->port); - if (ret < 0) { - message = "writing event channel"; - goto fail; - } - return 0; - -fail: - DRM_ERROR("Error %s: %d\n", message, ret); - return ret; -} - -static inline void xdrv_evtchnl_flush( - struct xdrv_evtchnl_info *channel) -{ - int notify; - - channel->u.req.ring.req_prod_pvt++; - RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&channel->u.req.ring, notify); - if (notify) - notify_remote_via_irq(channel->irq); -} - -static int xdrv_evtchnl_create_all(struct xdrv_info *drv_info) -{ - struct xendrm_plat_data *plat_data; - int ret, conn; - - plat_data = &drv_info->cfg_plat_data; - drv_info->evt_pairs = devm_kcalloc(&drv_info->xb_dev->dev, - plat_data->num_connectors, - sizeof(struct xdrv_evtchnl_pair_info), GFP_KERNEL); - if (!drv_info->evt_pairs) { - ret = -ENOMEM; - goto fail; - } - for (conn = 0; conn < plat_data->num_connectors; conn++) { - ret = xdrv_evtchnl_alloc(drv_info, conn, - &drv_info->evt_pairs[conn].req, EVTCHNL_TYPE_REQ); - if (ret < 0) { - DRM_ERROR("Error allocating control channel\n"); - goto fail; - } - ret = xdrv_evtchnl_alloc(drv_info, conn, - &drv_info->evt_pairs[conn].evt, EVTCHNL_TYPE_EVT); - if (ret < 0) { - DRM_ERROR("Error allocating in-event channel\n"); - goto fail; - } - } - drv_info->num_evt_pairs = plat_data->num_connectors; - return 0; -fail: - xdrv_evtchnl_free_all(drv_info); - return ret; -} - -static int xdrv_evtchnl_publish_all(struct xdrv_info *drv_info) -{ - struct xenbus_transaction xbt; - struct xendrm_plat_data *plat_data; - int ret, conn; - - plat_data = &drv_info->cfg_plat_data; -again: - ret = xenbus_transaction_start(&xbt); - if (ret < 0) { - xenbus_dev_fatal(drv_info->xb_dev, ret, "starting transaction"); return ret; - } - for (conn = 0; conn < plat_data->num_connectors; conn++) { - ret = xdrv_evtchnl_publish(xbt, - &drv_info->evt_pairs[conn].req, - plat_data->connectors[conn].xenstore_path, - XENDISPL_FIELD_REQ_RING_REF, - XENDISPL_FIELD_REQ_CHANNEL); - if (ret < 0) - goto fail; - ret = xdrv_evtchnl_publish(xbt, - &drv_info->evt_pairs[conn].evt, - plat_data->connectors[conn].xenstore_path, - XENDISPL_FIELD_EVT_RING_REF, - XENDISPL_FIELD_EVT_CHANNEL); - if (ret < 0) - goto fail; - } - ret = xenbus_transaction_end(xbt, 0); - if (ret < 0) { - if (ret == -EAGAIN) - goto again; - xenbus_dev_fatal(drv_info->xb_dev, ret, - "completing transaction"); - goto fail_to_end; - } - return 0; -fail: - xenbus_transaction_end(xbt, 1); -fail_to_end: - xenbus_dev_fatal(drv_info->xb_dev, ret, "writing XenStore"); - return ret; -} - -static void xdrv_evtchnl_set_state(struct xdrv_info *drv_info, - enum xdrv_evtchnl_state state) -{ - unsigned long flags; - int i; - if (!drv_info->evt_pairs) - return; - spin_lock_irqsave(&drv_info->io_lock, flags); - for (i = 0; i < drv_info->num_evt_pairs; i++) { - drv_info->evt_pairs[i].req.state = state; - drv_info->evt_pairs[i].evt.state = state; - } - spin_unlock_irqrestore(&drv_info->io_lock, flags); - -} - -static int xdrv_cfg_connector(struct xdrv_info *drv_info, - struct xendrm_cfg_connector *connector, - const char *path, int index) -{ - char *connector_path; - int ret; - - connector_path = devm_kasprintf(&drv_info->xb_dev->dev, - GFP_KERNEL, "%s/%d", path, index); - if (!connector_path) - return -ENOMEM; - connector->xenstore_path = connector_path; - if (xenbus_scanf(XBT_NIL, connector_path, XENDISPL_FIELD_RESOLUTION, - "%d" XENDISPL_RESOLUTION_SEPARATOR "%d", - &connector->width, &connector->height) < 0) { - /* either no entry configured or wrong resolution set */ - connector->width = 0; - connector->height = 0; - ret = -EINVAL; + front_info->drm_pdrv_registered = true; + platdata = &front_info->cfg_plat_data; + /* pass card configuration via platform data */ + xen_drm_front_platform_info.data = platdata; + xen_drm_front_platform_info.size_data = sizeof(*platdata); + front_info->drm_pdev = platform_device_register_full( + &xen_drm_front_platform_info); + if (IS_ERR(front_info->drm_pdev)) { + front_info->drm_pdev = NULL; goto fail; } - DRM_INFO("Connector %s: resolution %dx%d\n", - connector_path, connector->width, connector->height); - ret = 0; -fail: - return ret; -} - -static int xdrv_cfg_card(struct xdrv_info *drv_info, - struct xendrm_plat_data *plat_data) -{ - struct xenbus_device *xb_dev = drv_info->xb_dev; - int ret, i; - if (xenbus_read_unsigned(drv_info->xb_dev->nodename, - XENDISPL_FIELD_BE_ALLOC, 0)) { - DRM_INFO("Backend can provide dumb buffers\n"); -#ifdef CONFIG_DRM_XEN_FRONTEND_CMA - DRM_WARN("Cannot use backend's buffers with Xen CMA enabled\n"); -#else - /* - * FIXME: this mapping will need extra care in case of - * XENFEAT_auto_translated_physmap == 0 (see gntdev driver). - * For now, only support BE allocated buffers on platforms, - * that do auto translation - */ - if (!xen_feature(XENFEAT_auto_translated_physmap)) - DRM_WARN("Cannot use backend's buffers on this platform\n"); - else - plat_data->be_alloc = true; -#endif - } - plat_data->num_connectors = 0; - for (i = 0; i < XENDRM_MAX_CRTCS; i++) { - ret = xdrv_cfg_connector(drv_info, - &plat_data->connectors[i], xb_dev->nodename, i); - if (ret < 0) - break; - plat_data->num_connectors++; - } - if (!plat_data->num_connectors) { - DRM_ERROR("No connector(s) configured at %s\n", - xb_dev->nodename); - return -ENODEV; - } return 0; -} - -static void xdrv_remove_internal(struct xdrv_info *drv_info) -{ - ddrv_cleanup(drv_info); - xdrv_evtchnl_free_all(drv_info); - xdrv_shbuf_free_all(&drv_info->dbuf_list); -} - -static int xdrv_probe(struct xenbus_device *xb_dev, - const struct xenbus_device_id *id) -{ - struct xdrv_info *drv_info; - int ret; - drv_info = devm_kzalloc(&xb_dev->dev, sizeof(*drv_info), GFP_KERNEL); - if (!drv_info) { - ret = -ENOMEM; - goto fail; - } - xenbus_switch_state(xb_dev, XenbusStateInitialising); - drv_info->xb_dev = xb_dev; - spin_lock_init(&drv_info->io_lock); - INIT_LIST_HEAD(&drv_info->dbuf_list); - mutex_init(&drv_info->mutex); - drv_info->drm_pdrv_registered = false; - dev_set_drvdata(&xb_dev->dev, drv_info); - return 0; fail: - xenbus_dev_fatal(xb_dev, ret, "allocating device memory"); - return ret; + DRM_ERROR("Failed to register DRM driver\n"); + drm_drv_deinit(front_info); + return -ENODEV; } -static int xdrv_remove(struct xenbus_device *dev) +static void remove_internal(struct xen_drm_front_info *front_info) { - struct xdrv_info *drv_info = dev_get_drvdata(&dev->dev); - int to = 10; - - /* - * FIXME: on driver removal it is disconnected from XenBus, - * so no backend state change events come in via .otherend_changed - * callback. This prevents us from exiting gracefully, e.g. - * signaling the backend to free event channels, waiting for its - * state change to closed and cleaning at our end. - * Workaround: read backend's state manually - */ - xenbus_switch_state(dev, XenbusStateClosing); - while ((xenbus_read_unsigned(drv_info->xb_dev->otherend, - "state", XenbusStateUnknown) != XenbusStateInitWait) && to--) - msleep(10); - if (!to) - DRM_ERROR("Backend state is %s while removing driver\n", - xenbus_strstate(xenbus_read_unsigned( - drv_info->xb_dev->otherend, - "state", XenbusStateUnknown))); - mutex_lock(&drv_info->mutex); - xdrv_remove_internal(drv_info); - mutex_unlock(&drv_info->mutex); - xenbus_switch_state(dev, XenbusStateClosed); - return 0; + drm_drv_deinit(front_info); + xen_drm_front_evtchnl_free_all(front_info); + xen_drm_front_shbuf_free_all(&front_info->dbuf_list); } -static int xdrv_be_on_initwait(struct xdrv_info *drv_info) +static int be_on_initwait(struct xen_drm_front_info *front_info) { - struct xendrm_plat_data *cfg_plat_data; + struct xen_drm_front_cfg_plat_data *cfg_plat_data; int ret; - cfg_plat_data = &drv_info->cfg_plat_data; - cfg_plat_data->xdrv_info = drv_info; - ret = xdrv_cfg_card(drv_info, cfg_plat_data); + cfg_plat_data = &front_info->cfg_plat_data; + cfg_plat_data->front_info = front_info; + ret = xen_drm_front_cfg_card(front_info, cfg_plat_data); if (ret < 0) return ret; + DRM_INFO("Have %d conector(s)\n", cfg_plat_data->num_connectors); /* create event channels for all streams and publish */ - ret = xdrv_evtchnl_create_all(drv_info); + ret = xen_drm_front_evtchnl_create_all(front_info, &xen_drm_backend_ops); if (ret < 0) return ret; - return xdrv_evtchnl_publish_all(drv_info); + + return xen_drm_front_evtchnl_publish_all(front_info); } -static int xdrv_be_on_connected(struct xdrv_info *drv_info) +static int be_on_connected(struct xen_drm_front_info *front_info) { - xdrv_evtchnl_set_state(drv_info, EVTCHNL_STATE_CONNECTED); - return ddrv_init(drv_info); + xen_drm_front_evtchnl_set_state(front_info, EVTCHNL_STATE_CONNECTED); + return drm_drv_init(front_info); } -static void xdrv_be_on_disconnected(struct xdrv_info *drv_info) +static void be_on_disconnected(struct xen_drm_front_info *front_info) { bool removed = true; - if (drv_info->drm_pdev) { - if (xendrm_is_used(drv_info->drm_pdev)) { + if (front_info->drm_pdev) { + if (xen_drm_front_drv_is_used(front_info->drm_pdev)) { DRM_WARN("DRM driver still in use, deferring removal\n"); removed = false; } else { - xdrv_remove_internal(drv_info); + remove_internal(front_info); } } - xdrv_evtchnl_set_state(drv_info, EVTCHNL_STATE_DISCONNECTED); + + xen_drm_front_evtchnl_set_state(front_info, EVTCHNL_STATE_DISCONNECTED); + if (removed) - xenbus_switch_state(drv_info->xb_dev, XenbusStateInitialising); + xenbus_switch_state(front_info->xb_dev, XenbusStateInitialising); else - xenbus_switch_state(drv_info->xb_dev, XenbusStateReconfiguring); -} - -static void xdrv_drm_unload(struct xdrv_info *drv_info) -{ - if (drv_info->xb_dev->state != XenbusStateReconfiguring) - return; - DRM_INFO("Can try removing driver now\n"); - xenbus_switch_state(drv_info->xb_dev, XenbusStateInitialising); + xenbus_switch_state(front_info->xb_dev, XenbusStateReconfiguring); } -static void xdrv_be_on_changed(struct xenbus_device *xb_dev, +static void be_on_changed(struct xenbus_device *xb_dev, enum xenbus_state backend_state) { - struct xdrv_info *drv_info = dev_get_drvdata(&xb_dev->dev); + struct xen_drm_front_info *front_info = dev_get_drvdata(&xb_dev->dev); int ret; DRM_DEBUG("Backend state is %s, front is %s\n", @@ -909,39 +476,43 @@ static void xdrv_be_on_changed(struct xenbus_device *xb_dev, case XenbusStateInitialising: /* recovering after backend unexpected closure */ - mutex_lock(&drv_info->mutex); - xdrv_be_on_disconnected(drv_info); - mutex_unlock(&drv_info->mutex); + mutex_lock(&front_info->mutex); + be_on_disconnected(front_info); + mutex_unlock(&front_info->mutex); break; case XenbusStateInitWait: /* recovering after backend unexpected closure */ - mutex_lock(&drv_info->mutex); - xdrv_be_on_disconnected(drv_info); + mutex_lock(&front_info->mutex); + be_on_disconnected(front_info); if (xb_dev->state != XenbusStateInitialising) { - mutex_unlock(&drv_info->mutex); + mutex_unlock(&front_info->mutex); break; } - ret = xdrv_be_on_initwait(drv_info); - mutex_unlock(&drv_info->mutex); + + ret = be_on_initwait(front_info); + mutex_unlock(&front_info->mutex); if (ret < 0) { xenbus_dev_fatal(xb_dev, ret, "initializing frontend"); break; } + xenbus_switch_state(xb_dev, XenbusStateInitialised); break; case XenbusStateConnected: if (xb_dev->state != XenbusStateInitialised) break; - mutex_lock(&drv_info->mutex); - ret = xdrv_be_on_connected(drv_info); - mutex_unlock(&drv_info->mutex); + + mutex_lock(&front_info->mutex); + ret = be_on_connected(front_info); + mutex_unlock(&front_info->mutex); if (ret < 0) { xenbus_dev_fatal(xb_dev, ret, "initializing DRM driver"); break; } + xenbus_switch_state(xb_dev, XenbusStateConnected); break; @@ -958,49 +529,111 @@ static void xdrv_be_on_changed(struct xenbus_device *xb_dev, case XenbusStateClosed: if (xb_dev->state == XenbusStateClosed) break; - mutex_lock(&drv_info->mutex); - xdrv_be_on_disconnected(drv_info); - mutex_unlock(&drv_info->mutex); + + mutex_lock(&front_info->mutex); + be_on_disconnected(front_info); + mutex_unlock(&front_info->mutex); break; } } -static const struct xenbus_device_id xdrv_ids[] = { +static int xen_drv_probe(struct xenbus_device *xb_dev, + const struct xenbus_device_id *id) +{ + struct xen_drm_front_info *front_info; + int ret; + + front_info = devm_kzalloc(&xb_dev->dev, sizeof(*front_info), GFP_KERNEL); + if (!front_info) { + ret = -ENOMEM; + goto fail; + } + + xenbus_switch_state(xb_dev, XenbusStateInitialising); + front_info->xb_dev = xb_dev; + spin_lock_init(&front_info->io_lock); + INIT_LIST_HEAD(&front_info->dbuf_list); + mutex_init(&front_info->mutex); + front_info->drm_pdrv_registered = false; + dev_set_drvdata(&xb_dev->dev, front_info); + return 0; + +fail: + xenbus_dev_fatal(xb_dev, ret, "allocating device memory"); + return ret; +} + +static int xen_drv_remove(struct xenbus_device *dev) +{ + struct xen_drm_front_info *front_info = dev_get_drvdata(&dev->dev); + int to = 10; + + /* + * FIXME: on driver removal it is disconnected from XenBus, + * so no backend state change events come in via .otherend_changed + * callback. This prevents us from exiting gracefully, e.g. + * signaling the backend to free event channels, waiting for its + * state change to closed and cleaning at our end. + * Workaround: read backend's state manually + */ + xenbus_switch_state(dev, XenbusStateClosing); + + while ((xenbus_read_unsigned(front_info->xb_dev->otherend, + "state", XenbusStateUnknown) != XenbusStateInitWait) && to--) + msleep(10); + + if (!to) + DRM_ERROR("Backend state is %s while removing driver\n", + xenbus_strstate(xenbus_read_unsigned( + front_info->xb_dev->otherend, + "state", XenbusStateUnknown))); + + mutex_lock(&front_info->mutex); + remove_internal(front_info); + mutex_unlock(&front_info->mutex); + xenbus_switch_state(dev, XenbusStateClosed); + return 0; +} + +static const struct xenbus_device_id xen_drv_ids[] = { { XENDISPL_DRIVER_NAME }, { "" } }; static struct xenbus_driver xen_driver = { - .ids = xdrv_ids, - .probe = xdrv_probe, - .remove = xdrv_remove, - .otherend_changed = xdrv_be_on_changed, + .ids = xen_drv_ids, + .probe = xen_drv_probe, + .remove = xen_drv_remove, + .otherend_changed = be_on_changed, }; -static int __init xdrv_init(void) +static int __init xen_drv_init(void) { BUILD_BUG_ON(XEN_PAGE_SIZE > PAGE_SIZE); if (!xen_domain()) return -ENODEV; + if (xen_initial_domain()) { DRM_ERROR(XENDISPL_DRIVER_NAME " cannot run in Dom0\n"); return -ENODEV; } + if (!xen_has_pv_devices()) return -ENODEV; + DRM_INFO("Registering XEN PV " XENDISPL_DRIVER_NAME "\n"); return xenbus_register_frontend(&xen_driver); } -static void __exit xdrv_cleanup(void) +static void __exit xen_drv_cleanup(void) { DRM_INFO("Unregistering XEN PV " XENDISPL_DRIVER_NAME "\n"); xenbus_unregister_driver(&xen_driver); } -module_init(xdrv_init); -module_exit(xdrv_cleanup); +module_init(xen_drv_init); +module_exit(xen_drv_cleanup); MODULE_DESCRIPTION("Xen virtual display device frontend"); MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/xen/xen_drm_front.h b/drivers/gpu/drm/xen/xen_drm_front.h index c2d30f7e99258a..891c00fef10dea 100644 --- a/drivers/gpu/drm/xen/xen_drm_front.h +++ b/drivers/gpu/drm/xen/xen_drm_front.h @@ -11,41 +11,66 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * Copyright (C) 2016 EPAM Systems Inc. + * Copyright (C) 2016-2017 EPAM Systems Inc. * - * Author: Oleksandr Andrushchenko + * Author: Oleksandr Andrushchenko */ #ifndef __XEN_DRM_FRONT_H_ #define __XEN_DRM_FRONT_H_ -struct xdrv_info; -struct platform_device; -struct xendrm_crtc; -struct sg_table; +#include +#include +#include +#include +#include + +#include "xen_drm_front_cfg.h" + +struct xen_drm_front_crtc; /* timeout in ms to wait for backend to respond */ #define VDRM_WAIT_BACK_MS 3000 -struct xendispl_front_ops { - int (*mode_set)(struct xendrm_crtc *xen_crtc, uint32_t x, uint32_t y, - uint32_t width, uint32_t height, uint32_t bpp, - uint64_t fb_cookie); - struct page **(*dbuf_create)(struct xdrv_info *drv_info, +struct xen_drm_front_ops { + int (*mode_set)(struct xen_drm_front_crtc *xen_crtc, + uint32_t x, uint32_t y, uint32_t width, uint32_t height, + uint32_t bpp, uint64_t fb_cookie); + int (*dbuf_create)(struct xen_drm_front_info *front_info, uint64_t dbuf_cookie, uint32_t width, uint32_t height, - uint32_t bpp, uint64_t size, struct page **pages, - struct sg_table *sgt); - int (*dbuf_destroy)(struct xdrv_info *drv_info, uint64_t dbuf_cookie); - int (*fb_attach)(struct xdrv_info *drv_info, uint64_t dbuf_cookie, - uint64_t fb_cookie, uint32_t width, uint32_t height, - uint32_t pixel_format); - int (*fb_detach)(struct xdrv_info *drv_info, uint64_t fb_cookie); - int (*page_flip)(struct xdrv_info *drv_info, int conn_idx, + uint32_t bpp, uint64_t size, struct page **pages); + int (*dbuf_create_from_sgt)(struct xen_drm_front_info *front_info, + uint64_t dbuf_cookie, uint32_t width, uint32_t height, + uint32_t bpp, uint64_t size, struct sg_table *sgt); + int (*dbuf_destroy)(struct xen_drm_front_info *front_info, + uint64_t dbuf_cookie); + int (*fb_attach)(struct xen_drm_front_info *front_info, + uint64_t dbuf_cookie, uint64_t fb_cookie, uint32_t width, + uint32_t height, uint32_t pixel_format); + int (*fb_detach)(struct xen_drm_front_info *front_info, + uint64_t fb_cookie); + int (*page_flip)(struct xen_drm_front_info *front_info, int conn_idx, uint64_t fb_cookie); /* CAUTION! this is called with a spin_lock held! */ void (*on_page_flip)(struct platform_device *pdev, int conn_idx, uint64_t fb_cookie); - void (*drm_last_close)(struct xdrv_info *drv_info); + void (*drm_last_close)(struct xen_drm_front_info *front_info); +}; + +struct xen_drm_front_info { + struct xenbus_device *xb_dev; + spinlock_t io_lock; + struct mutex mutex; + bool drm_pdrv_registered; + /* virtual DRM platform device */ + struct platform_device *drm_pdev; + + int num_evt_pairs; + struct xen_drm_front_evtchnl_pair *evt_pairs; + struct xen_drm_front_cfg_plat_data cfg_plat_data; + + /* display buffers */ + struct list_head dbuf_list; }; #endif /* __XEN_DRM_FRONT_H_ */ diff --git a/drivers/gpu/drm/xen/xen_drm_front_cfg.c b/drivers/gpu/drm/xen/xen_drm_front_cfg.c new file mode 100644 index 00000000000000..b887f6d3fe3194 --- /dev/null +++ b/drivers/gpu/drm/xen/xen_drm_front_cfg.c @@ -0,0 +1,71 @@ +#include + +#include + +#include +#include + +#include "xen_drm_front.h" +#include "xen_drm_front_cfg.h" + +static int cfg_connector(struct xen_drm_front_info *front_info, + struct xen_drm_front_cfg_connector *connector, + const char *path, int index) +{ + char *connector_path; + int ret; + + connector_path = devm_kasprintf(&front_info->xb_dev->dev, + GFP_KERNEL, "%s/%d", path, index); + if (!connector_path) + return -ENOMEM; + + connector->xenstore_path = connector_path; + if (xenbus_scanf(XBT_NIL, connector_path, XENDISPL_FIELD_RESOLUTION, + "%d" XENDISPL_RESOLUTION_SEPARATOR "%d", + &connector->width, &connector->height) < 0) { + /* either no entry configured or wrong resolution set */ + connector->width = 0; + connector->height = 0; + ret = -EINVAL; + goto fail; + } + + DRM_INFO("Connector %s: resolution %dx%d\n", + connector_path, connector->width, connector->height); + ret = 0; + +fail: + return ret; +} + +int xen_drm_front_cfg_card(struct xen_drm_front_info *front_info, + struct xen_drm_front_cfg_plat_data *plat_data) +{ + struct xenbus_device *xb_dev = front_info->xb_dev; + int ret, i; + + if (xenbus_read_unsigned(front_info->xb_dev->nodename, + XENDISPL_FIELD_BE_ALLOC, 0)) { + DRM_INFO("Backend can provide dumb buffers\n"); + plat_data->be_alloc = true; + } + + plat_data->num_connectors = 0; + for (i = 0; i < ARRAY_SIZE(plat_data->connectors); i++) { + ret = cfg_connector(front_info, + &plat_data->connectors[i], xb_dev->nodename, i); + if (ret < 0) + break; + plat_data->num_connectors++; + } + + if (!plat_data->num_connectors) { + DRM_ERROR("No connector(s) configured at %s\n", + xb_dev->nodename); + return -ENODEV; + } + + return 0; +} + diff --git a/drivers/gpu/drm/xen/xen_drm_front_cfg.h b/drivers/gpu/drm/xen/xen_drm_front_cfg.h new file mode 100644 index 00000000000000..5621700ac4a2ac --- /dev/null +++ b/drivers/gpu/drm/xen/xen_drm_front_cfg.h @@ -0,0 +1,45 @@ +/* + * Xen para-virtual DRM device + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * Copyright (C) 2016-2017 EPAM Systems Inc. + * + * Author: Oleksandr Andrushchenko + */ + +#ifndef __XEN_DRM_FRONT_CFG_H_ +#define __XEN_DRM_FRONT_CFG_H_ + +#include + +#define XEN_DRM_FRONT_MAX_CRTCS 4 + +struct xen_drm_front_cfg_connector { + int width; + int height; + char *xenstore_path; +}; + +struct xen_drm_front_cfg_plat_data { + struct xen_drm_front_info *front_info; + /* number of connectors in this configuration */ + int num_connectors; + /* connector configurations */ + struct xen_drm_front_cfg_connector connectors[XEN_DRM_FRONT_MAX_CRTCS]; + /* set if dumb buffers are allocated externally on backend side */ + bool be_alloc; +}; + +int xen_drm_front_cfg_card(struct xen_drm_front_info *front_info, + struct xen_drm_front_cfg_plat_data *plat_data); + +#endif /* __XEN_DRM_FRONT_CFG_H_ */ diff --git a/drivers/gpu/drm/xen/xen_drm_crtc.c b/drivers/gpu/drm/xen/xen_drm_front_crtc.c similarity index 57% rename from drivers/gpu/drm/xen/xen_drm_crtc.c rename to drivers/gpu/drm/xen/xen_drm_front_crtc.c index 36cf1eb5e0d574..ed5e272b2e8fa6 100644 --- a/drivers/gpu/drm/xen/xen_drm_crtc.c +++ b/drivers/gpu/drm/xen/xen_drm_front_crtc.c @@ -11,48 +11,51 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * - * Copyright (C) 2016 EPAM Systems Inc. + * Copyright (C) 2016-2017 EPAM Systems Inc. * - * Author: Oleksandr Andrushchenko + * Author: Oleksandr Andrushchenko */ +#include "xen_drm_front_crtc.h" + #include #include #include #include