Hi
With the upcoming 3.12 merge-window, I thought people might find themselves with nothing to do, so here's a new SimpleDRM series. Comments welcome!
This depends on the tip/x86/fb series in the x86-tip tree: http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/log/?h=x86/fb Which, as far as I understood, will be pushed into 3.12 by the x86 people.
Patches #1 and #2 implement the SimpleDRM driver which can replace vesafb, efifb, simplefb, vgafb and more. Just enable the CONFIG_X86_SYSFB option and you're good to go.
Patches #3 to #6 implement drm_kick_out_firmware() to make x86 drivers kick out firmware-DRM drivers instead of only fbdev via remove_conflicting_framebuffers().
I tested this based on drm-next plus the x86/fb series merged with i915 and nouveau and it worked just fine.
Regards David
David Herrmann (6): drm: add SimpleDRM driver drm: simpledrm: add fbdev fallback support drm: add helpers to kick out firmware drivers drm: nouveau: kick out firmware drivers during probe drm/i915: use new drm_kick_out_firmware() drm/radeon: use new drm_kick_out_firmware()
MAINTAINERS | 8 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_pci.c | 1 + drivers/gpu/drm/drm_platform.c | 1 + drivers/gpu/drm/drm_stub.c | 118 +++++++++ drivers/gpu/drm/drm_usb.c | 1 + drivers/gpu/drm/i915/i915_dma.c | 6 +- drivers/gpu/drm/nouveau/nouveau_drm.c | 29 ++- drivers/gpu/drm/radeon/radeon_drv.c | 28 --- drivers/gpu/drm/radeon/radeon_kms.c | 30 +++ drivers/gpu/drm/simpledrm/Kconfig | 29 +++ drivers/gpu/drm/simpledrm/Makefile | 4 + drivers/gpu/drm/simpledrm/simpledrm.h | 112 +++++++++ drivers/gpu/drm/simpledrm/simpledrm_drv.c | 226 +++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 153 ++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_main.c | 363 ++++++++++++++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_mem.c | 242 +++++++++++++++++++ include/drm/drmP.h | 26 ++ 19 files changed, 1342 insertions(+), 38 deletions(-) create mode 100644 drivers/gpu/drm/simpledrm/Kconfig create mode 100644 drivers/gpu/drm/simpledrm/Makefile create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.h create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_drv.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_main.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_mem.c
The SimpleDRM driver binds to simple-framebuffer devices and provides a DRM/KMS API. It provides only a single CRTC+encoder+connector combination plus one initial mode.
Userspace can create one dumb-buffer and attach it to the CRTC. Only if the buffer is destroyed, a new buffer can be created. The buffer is directly mapped into user-space, so we have only resources for a single buffer. Otherwise, shadow buffers plus damage-request would be needed.
Signed-off-by: David Herrmann dh.herrmann@gmail.com Tested-by: Stephen Warren swarren@nvidia.com --- MAINTAINERS | 8 + drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/simpledrm/Kconfig | 18 ++ drivers/gpu/drm/simpledrm/Makefile | 3 + drivers/gpu/drm/simpledrm/simpledrm.h | 89 ++++++++ drivers/gpu/drm/simpledrm/simpledrm_drv.c | 225 ++++++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_main.c | 328 +++++++++++++++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_mem.c | 242 +++++++++++++++++++++ 9 files changed, 916 insertions(+) create mode 100644 drivers/gpu/drm/simpledrm/Kconfig create mode 100644 drivers/gpu/drm/simpledrm/Makefile create mode 100644 drivers/gpu/drm/simpledrm/simpledrm.h create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_drv.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_main.c create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_mem.c
diff --git a/MAINTAINERS b/MAINTAINERS index a26b10e..35c2fab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7435,6 +7435,14 @@ S: Odd Fixes F: drivers/media/platform/sh_vou.c F: include/media/sh_vou.h
+SIMPLE DRM DRIVER +M: David Herrmann dh.herrmann@gmail.com +L: dri-devel@lists.freedesktop.org +T: git git://people.freedesktop.org/~dvdhrm/linux +S: Maintained +F: drivers/gpu/drm/simpledrm +F: include/linux/platform_data/simpledrm.h + SIMPLE FIRMWARE INTERFACE (SFI) M: Len Brown lenb@kernel.org L: sfi-devel@simplefirmware.org diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 955555d..33c1765 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -236,3 +236,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig" source "drivers/gpu/drm/qxl/Kconfig"
source "drivers/gpu/drm/msm/Kconfig" + +source "drivers/gpu/drm/simpledrm/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index f089adf..fe23d6f 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -55,4 +55,5 @@ obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_DRM_TILCDC) += tilcdc/ obj-$(CONFIG_DRM_QXL) += qxl/ obj-$(CONFIG_DRM_MSM) += msm/ +obj-$(CONFIG_DRM_SIMPLEDRM) += simpledrm/ obj-y += i2c/ diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig new file mode 100644 index 0000000..35bcce8 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/Kconfig @@ -0,0 +1,18 @@ +config DRM_SIMPLEDRM + tristate "Simple firmware framebuffer DRM driver" + depends on DRM && (FB_SIMPLE = n) + help + SimpleDRM can run on all systems with pre-initialized graphics + hardware. It uses a framebuffer that was initialized during + firmware boot. No page-flipping, modesetting or other advanced + features are available. However, other DRM drivers can be loaded + later and take over from SimpleDRM if they provide real hardware + support. + + SimpleDRM supports "simple-framebuffer" DeviceTree objects and + compatible platform framebuffers. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the + module will be called simpledrm. diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile new file mode 100644 index 0000000..ceb97eb --- /dev/null +++ b/drivers/gpu/drm/simpledrm/Makefile @@ -0,0 +1,3 @@ +simpledrm-y := simpledrm_drv.o simpledrm_main.o simpledrm_mem.o + +obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h new file mode 100644 index 0000000..977b344 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm.h @@ -0,0 +1,89 @@ +/* + * SimpleDRM firmware framebuffer driver + * Copyright (c) 2012-2013 David Herrmann dh.herrmann@gmail.com + * + * 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. + */ + +#ifndef SDRM_DRV_H +#define SDRM_DRV_H + +#include <linux/errno.h> +#include <linux/fb.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/platform_data/simplefb.h> +#include <linux/string.h> +#include <drm/drmP.h> + +struct sdrm_device; +struct sdrm_gem_object; +struct sdrm_framebuffer; + +/* simpledrm devices */ + +struct sdrm_device { + struct drm_device *ddev; + + /* framebuffer information */ + const struct simplefb_format *fb_sformat; + u32 fb_format; + u32 fb_width; + u32 fb_height; + u32 fb_stride; + u32 fb_bpp; + unsigned long fb_base; + unsigned long fb_size; + void *fb_map; + + /* mode-setting objects */ + struct sdrm_gem_object *fb_obj; + struct drm_crtc crtc; + struct drm_encoder enc; + struct drm_connector conn; + struct drm_display_mode *mode; +}; + +int sdrm_drm_load(struct drm_device *ddev, unsigned long flags); +int sdrm_drm_unload(struct drm_device *ddev); +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma); +int sdrm_pdev_init(struct sdrm_device *sdrm); +void sdrm_pdev_destroy(struct sdrm_device *sdrm); + +/* simpledrm gem objects */ + +struct sdrm_gem_object { + struct drm_gem_object base; + unsigned long fb_base; + unsigned long fb_size; +}; + +#define to_sdrm_bo(x) container_of(x, struct sdrm_gem_object, base) + +int sdrm_gem_init_object(struct drm_gem_object *obj); +void sdrm_gem_free_object(struct drm_gem_object *obj); +void sdrm_gem_unmap_object(struct sdrm_gem_object *obj); + +/* dumb buffers */ + +int sdrm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev, + struct drm_mode_create_dumb *arg); +int sdrm_dumb_destroy(struct drm_file *file_priv, struct drm_device *ddev, + uint32_t handle); +int sdrm_dumb_map_offset(struct drm_file *file_priv, struct drm_device *ddev, + uint32_t handle, uint64_t *offset); + +/* simpledrm framebuffers */ + +struct sdrm_framebuffer { + struct drm_framebuffer base; + struct sdrm_gem_object *obj; +}; + +#define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base) + +#endif /* SDRM_DRV_H */ diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c new file mode 100644 index 0000000..8a34051 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c @@ -0,0 +1,225 @@ +/* + * SimpleDRM firmware framebuffer driver + * Copyright (c) 2012-2013 David Herrmann dh.herrmann@gmail.com + * + * 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 <linux/errno.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/platform_data/simplefb.h> +#include <linux/string.h> +#include <drm/drmP.h> +#include "simpledrm.h" + +static const struct file_operations sdrm_drm_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .mmap = sdrm_drm_mmap, + .poll = drm_poll, + .read = drm_read, + .unlocked_ioctl = drm_ioctl, + .release = drm_release, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .llseek = noop_llseek, +}; + +static struct drm_driver sdrm_drm_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM, + .load = sdrm_drm_load, + .unload = sdrm_drm_unload, + .fops = &sdrm_drm_fops, + + .gem_init_object = sdrm_gem_init_object, + .gem_free_object = sdrm_gem_free_object, + + .dumb_create = sdrm_dumb_create, + .dumb_map_offset = sdrm_dumb_map_offset, + .dumb_destroy = sdrm_dumb_destroy, + + .name = "simpledrm", + .desc = "Simple firmware framebuffer DRM driver", + .date = "20130601", + .major = 0, + .minor = 0, + .patchlevel = 1, +}; + +static int parse_dt(struct platform_device *pdev, + struct simplefb_platform_data *mode) +{ + struct device_node *np = pdev->dev.of_node; + const char *format; + int ret; + + if (!np) + return -ENODEV; + + ret = of_property_read_u32(np, "width", &mode->width); + if (ret) { + dev_err(&pdev->dev, "Can't parse width property\n"); + return ret; + } + + ret = of_property_read_u32(np, "height", &mode->height); + if (ret) { + dev_err(&pdev->dev, "Can't parse height property\n"); + return ret; + } + + ret = of_property_read_u32(np, "stride", &mode->stride); + if (ret) { + dev_err(&pdev->dev, "Can't parse stride property\n"); + return ret; + } + + ret = of_property_read_string(np, "format", &format); + if (ret) { + dev_err(&pdev->dev, "Can't parse format property\n"); + return ret; + } + mode->format = format; + + return 0; +} + +static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; + +int sdrm_pdev_init(struct sdrm_device *sdrm) +{ + struct platform_device *pdev = sdrm->ddev->platformdev; + struct simplefb_platform_data *mode = pdev->dev.platform_data; + struct simplefb_platform_data pmode; + struct resource *mem; + unsigned int depth; + int ret, i, bpp; + + if (!mode) { + mode = &pmode; + ret = parse_dt(pdev, mode); + if (ret) + return ret; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(sdrm->ddev->dev, "No memory resource\n"); + return -ENODEV; + } + + for (i = 0; i < ARRAY_SIZE(simplefb_formats); ++i) { + if (strcmp(mode->format, simplefb_formats[i].name)) + continue; + + sdrm->fb_sformat = &simplefb_formats[i]; + sdrm->fb_format = simplefb_formats[i].fourcc; + sdrm->fb_width = mode->width; + sdrm->fb_height = mode->height; + sdrm->fb_stride = mode->stride; + sdrm->fb_base = mem->start; + sdrm->fb_size = resource_size(mem); + break; + } + + if (i >= ARRAY_SIZE(simplefb_formats)) { + dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format); + return -ENODEV; + } + + drm_fb_get_bpp_depth(sdrm->fb_format, &depth, &bpp); + if (!bpp) { + dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format); + return -ENODEV; + } + + if (sdrm->fb_size < sdrm->fb_stride * sdrm->fb_height) { + dev_err(sdrm->ddev->dev, "FB too small\n"); + return -ENODEV; + } else if ((bpp + 7) / 8 * sdrm->fb_width > sdrm->fb_stride) { + dev_err(sdrm->ddev->dev, "Invalid stride\n"); + return -ENODEV; + } + + sdrm->fb_bpp = bpp; + + if (!request_mem_region(sdrm->fb_base, sdrm->fb_size, + "simple-framebuffer")) { + dev_err(sdrm->ddev->dev, "cannot reserve VMEM\n"); + return -EIO; + } + + sdrm->fb_map = ioremap_wc(sdrm->fb_base, sdrm->fb_size); + if (!sdrm->fb_map) { + dev_err(sdrm->ddev->dev, "cannot remap VMEM\n"); + ret = -EIO; + goto err_region; + } + + return 0; + +err_region: + release_mem_region(sdrm->fb_base, sdrm->fb_size); + return ret; +} + +void sdrm_pdev_destroy(struct sdrm_device *sdrm) +{ + if (sdrm->fb_map) { + iounmap(sdrm->fb_map); + release_mem_region(sdrm->fb_base, sdrm->fb_size); + sdrm->fb_map = NULL; + } +} + +static int sdrm_simplefb_probe(struct platform_device *pdev) +{ + return drm_platform_init(&sdrm_drm_driver, pdev); +} + +static int sdrm_simplefb_remove(struct platform_device *pdev) +{ + drm_platform_exit(&sdrm_drm_driver, pdev); + + return 0; +} + +static const struct of_device_id simplefb_of_match[] = { + { .compatible = "simple-framebuffer", }, + { }, +}; +MODULE_DEVICE_TABLE(of, simplefb_of_match); + +static struct platform_driver sdrm_simplefb_driver = { + .probe = sdrm_simplefb_probe, + .remove = sdrm_simplefb_remove, + .driver = { + .name = "simple-framebuffer", + .mod_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .of_match_table = simplefb_of_match, + }, +}; + +static int __init sdrm_init(void) +{ + return platform_driver_register(&sdrm_simplefb_driver); +} + +static void __exit sdrm_exit(void) +{ + platform_driver_unregister(&sdrm_simplefb_driver); +} + +module_init(sdrm_init); +module_exit(sdrm_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Herrmann dh.herrmann@gmail.com"); +MODULE_DESCRIPTION("Simple firmware framebuffer DRM driver"); diff --git a/drivers/gpu/drm/simpledrm/simpledrm_main.c b/drivers/gpu/drm/simpledrm/simpledrm_main.c new file mode 100644 index 0000000..ae507e3 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_main.c @@ -0,0 +1,328 @@ +/* + * SimpleDRM firmware framebuffer driver + * Copyright (c) 2012-2013 David Herrmann dh.herrmann@gmail.com + * + * 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 <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/string.h> +#include <drm/drmP.h> +#include <drm/drm_crtc.h> +#include "simpledrm.h" + +/* crtcs */ + +static int sdrm_crtc_set_config(struct drm_mode_set *set) +{ + struct drm_device *ddev; + struct sdrm_device *sdrm; + struct sdrm_framebuffer *fb; + + if (!set || !set->crtc) + return -EINVAL; + + ddev = set->crtc->dev; + sdrm = ddev->dev_private; + + if (set->crtc != &sdrm->crtc) + return -EINVAL; + + if (!set->mode || !set->fb || !set->num_connectors) { + sdrm->conn.encoder = NULL; + sdrm->conn.dpms = DRM_MODE_DPMS_OFF; + sdrm->enc.crtc = NULL; + sdrm->crtc.fb = NULL; + sdrm->crtc.enabled = false; + return 0; + } + + fb = to_sdrm_fb(set->fb); + + if (set->num_connectors != 1 || set->connectors[0] != &sdrm->conn) + return -EINVAL; + if (set->x || set->y) + return -EINVAL; + if (set->mode->hdisplay != sdrm->fb_width || + set->mode->vdisplay != sdrm->fb_height) + return -EINVAL; + + sdrm->conn.encoder = &sdrm->enc; + sdrm->conn.dpms = DRM_MODE_DPMS_ON; + sdrm->enc.crtc = &sdrm->crtc; + sdrm->crtc.fb = set->fb; + sdrm->crtc.enabled = true; + sdrm->crtc.mode = *set->mode; + sdrm->crtc.hwmode = *set->mode; + sdrm->crtc.x = 0; + sdrm->crtc.y = 0; + + drm_calc_timestamping_constants(&sdrm->crtc); + return 0; +} + +static const struct drm_crtc_funcs sdrm_crtc_ops = { + .set_config = sdrm_crtc_set_config, + .destroy = drm_crtc_cleanup, +}; + +/* encoders */ + +static const struct drm_encoder_funcs sdrm_enc_ops = { + .destroy = drm_encoder_cleanup, +}; + +/* connectors */ + +static void sdrm_conn_dpms(struct drm_connector *conn, int mode) +{ + conn->dpms = mode; +} + +static enum drm_connector_status sdrm_conn_detect(struct drm_connector *conn, + bool force) +{ + /* We simulate an always connected monitor. simple-fb doesn't + * provide any way to detect whether the connector is active. Hence, + * signal DRM core that it is always connected. */ + + return connector_status_connected; +} + +static int sdrm_conn_fill_modes(struct drm_connector *conn, uint32_t max_x, + uint32_t max_y) +{ + struct sdrm_device *sdrm = conn->dev->dev_private; + struct drm_display_mode *mode; + int ret; + + if (conn->force == DRM_FORCE_ON) + conn->status = connector_status_connected; + else if (conn->force) + conn->status = connector_status_disconnected; + else + conn->status = connector_status_connected; + + list_for_each_entry(mode, &conn->modes, head) + mode->status = MODE_UNVERIFIED; + + mode = drm_gtf_mode(sdrm->ddev, sdrm->fb_width, sdrm->fb_height, + 60, 0, 0); + if (mode) { + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(conn, mode); + sdrm->mode = mode; + drm_mode_connector_list_update(conn); + ret = 1; + } else { + ret = 0; + } + + if (max_x && max_y) + drm_mode_validate_size(conn->dev, &conn->modes, + max_x, max_y, 0); + + drm_mode_prune_invalid(conn->dev, &conn->modes, false); + if (list_empty(&conn->modes)) + return 0; + + drm_mode_sort(&conn->modes); + + list_for_each_entry(mode, &conn->modes, head) { + mode->vrefresh = drm_mode_vrefresh(mode); + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + } + + return ret; +} + +static void sdrm_conn_destroy(struct drm_connector *conn) +{ + /* Remove the fake-connector from sysfs and then let the DRM core + * clean up all associated resources. */ + if (device_is_registered(&conn->kdev)) + drm_sysfs_connector_remove(conn); + drm_connector_cleanup(conn); +} + +static const struct drm_connector_funcs sdrm_conn_ops = { + .dpms = sdrm_conn_dpms, + .detect = sdrm_conn_detect, + .fill_modes = sdrm_conn_fill_modes, + .destroy = sdrm_conn_destroy, +}; + +/* framebuffers */ + +static int sdrm_fb_create_handle(struct drm_framebuffer *fb, + struct drm_file *dfile, + unsigned int *handle) +{ + struct sdrm_framebuffer *sfb = to_sdrm_fb(fb); + + return drm_gem_handle_create(dfile, &sfb->obj->base, handle); +} + +static void sdrm_fb_destroy(struct drm_framebuffer *fb) +{ + struct sdrm_framebuffer *sfb = to_sdrm_fb(fb); + + drm_framebuffer_cleanup(fb); + drm_gem_object_unreference_unlocked(&sfb->obj->base); + kfree(sfb); +} + +static const struct drm_framebuffer_funcs sdrm_fb_ops = { + .create_handle = sdrm_fb_create_handle, + .destroy = sdrm_fb_destroy, +}; + +static struct drm_framebuffer *sdrm_fb_create(struct drm_device *ddev, + struct drm_file *dfile, + struct drm_mode_fb_cmd2 *cmd) +{ + struct sdrm_device *sdrm = ddev->dev_private; + struct sdrm_framebuffer *fb; + struct drm_gem_object *gobj; + int ret, i; + void *err; + + if (cmd->flags || cmd->pixel_format != sdrm->fb_format) + return ERR_PTR(-EINVAL); + if (cmd->height != sdrm->fb_height || cmd->width != sdrm->fb_width) + return ERR_PTR(-EINVAL); + if (cmd->offsets[0] || cmd->pitches[0] != sdrm->fb_stride) + return ERR_PTR(-EINVAL); + + gobj = drm_gem_object_lookup(ddev, dfile, cmd->handles[0]); + if (!gobj) + return ERR_PTR(-EINVAL); + + fb = kzalloc(sizeof(*fb), GFP_KERNEL); + if (!fb) { + err = ERR_PTR(-ENOMEM); + goto err_unref; + } + fb->obj = to_sdrm_bo(gobj); + + fb->base.pitches[0] = cmd->pitches[0]; + fb->base.offsets[0] = cmd->offsets[0]; + for (i = 1; i < 4; i++) { + fb->base.pitches[i] = 0; + fb->base.offsets[i] = 0; + } + + fb->base.width = cmd->width; + fb->base.height = cmd->height; + fb->base.pixel_format = cmd->pixel_format; + drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth, + &fb->base.bits_per_pixel); + + ret = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops); + if (ret < 0) { + err = ERR_PTR(ret); + goto err_free; + } + + return &fb->base; + +err_free: + kfree(fb); +err_unref: + drm_gem_object_unreference_unlocked(gobj); + return err; +} + +static const struct drm_mode_config_funcs sdrm_mode_config_ops = { + .fb_create = sdrm_fb_create, +}; + +/* initialization */ + +int sdrm_drm_load(struct drm_device *ddev, unsigned long flags) +{ + struct sdrm_device *sdrm; + int ret; + + sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL); + if (!sdrm) + return -ENOMEM; + + sdrm->ddev = ddev; + ddev->dev_private = sdrm; + + ddev->devname = kstrdup("simpledrm", GFP_KERNEL); + if (!ddev->devname) { + ret = -ENOMEM; + goto err_free; + } + + ret = sdrm_pdev_init(sdrm); + if (ret) + goto err_name; + + drm_mode_config_init(ddev); + ddev->mode_config.min_width = 0; + ddev->mode_config.min_height = 0; + ddev->mode_config.max_width = 8192; + ddev->mode_config.max_height = 8192; + ddev->mode_config.funcs = &sdrm_mode_config_ops; + + ret = drm_crtc_init(ddev, &sdrm->crtc, &sdrm_crtc_ops); + if (ret) + goto err_cleanup; + + sdrm->enc.possible_crtcs = 1; + sdrm->enc.possible_clones = 0; + ret = drm_encoder_init(ddev, &sdrm->enc, &sdrm_enc_ops, + DRM_MODE_ENCODER_VIRTUAL); + if (ret) + goto err_cleanup; + + sdrm->conn.display_info.width_mm = 0; + sdrm->conn.display_info.height_mm = 0; + sdrm->conn.interlace_allowed = false; + sdrm->conn.doublescan_allowed = false; + sdrm->conn.polled = 0; + ret = drm_connector_init(ddev, &sdrm->conn, &sdrm_conn_ops, + DRM_MODE_CONNECTOR_VIRTUAL); + if (ret) + goto err_cleanup; + + ret = drm_mode_connector_attach_encoder(&sdrm->conn, &sdrm->enc); + if (ret) + goto err_cleanup; + + ret = drm_sysfs_connector_add(&sdrm->conn); + if (ret) + goto err_cleanup; + + return 0; + +err_cleanup: + drm_mode_config_cleanup(ddev); + sdrm_pdev_destroy(sdrm); +err_name: + kfree(ddev->devname); + ddev->devname = NULL; +err_free: + kfree(sdrm); + return ret; +} + +int sdrm_drm_unload(struct drm_device *ddev) +{ + struct sdrm_device *sdrm = ddev->dev_private; + + drm_mode_config_cleanup(ddev); + sdrm_pdev_destroy(sdrm); + kfree(sdrm); + + return 0; +} diff --git a/drivers/gpu/drm/simpledrm/simpledrm_mem.c b/drivers/gpu/drm/simpledrm/simpledrm_mem.c new file mode 100644 index 0000000..1bcd3f1 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_mem.c @@ -0,0 +1,242 @@ +/* + * SimpleDRM firmware framebuffer driver + * Copyright (c) 2012-2013 David Herrmann dh.herrmann@gmail.com + * + * 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 <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/string.h> +#include <drm/drmP.h> +#include "simpledrm.h" + +/* + * Create GEM Object + * Allocates a new GEM object to manage the physical memory at @fb_base with + * size @fb_size. Both parameters must be page-aligned and point to the + * physical memory of the framebuffer to manage. They must not have a + * "struct page" and have to be reserved before. + * It is the callers responsibility to create only one object per framebuffer. + */ +static struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev, + unsigned long fb_base, + unsigned long fb_size) +{ + struct sdrm_gem_object *obj; + + WARN_ON((fb_base & ~PAGE_MASK) != 0); + WARN_ON((fb_size & ~PAGE_MASK) != 0); + + /* align to page-size */ + fb_size = fb_size + (fb_base & ~PAGE_MASK); + fb_base = fb_base & PAGE_MASK; + fb_size = PAGE_ALIGN(fb_size); + + if (fb_base + fb_size < fb_base) + return NULL; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return NULL; + obj->fb_base = fb_base; + obj->fb_size = fb_size; + + drm_gem_private_object_init(ddev, &obj->base, fb_size); + + return obj; +} + +/* drm_gem_object_alloc() is not supported */ +int sdrm_gem_init_object(struct drm_gem_object *gobj) +{ + return -EINVAL; +} + +/* + * Unmap GEM Object + * Destroy any memory-mappings that user-space created on this object. Note + * that this will cause SIGBUS errors if user-space continues writing to it. + * There is no way to remap the pages in fault-handlers as this is not what + * we want. You should destroy the mappings only when destroying the object + * so no remapping will be needed. + * It's the callers responsibility to prevent any further mappings. This only + * destroys all current mappings. + */ +void sdrm_gem_unmap_object(struct sdrm_gem_object *obj) +{ + struct drm_device *ddev = obj->base.dev; + + drm_vma_node_unmap(&obj->base.vma_node, ddev->dev_mapping); +} + +/* + * Free GEM Object + * Frees the given GEM object. It does not release the framebuffer memory that + * was passed during allocation, but destroys all user-space mappings. + */ +void sdrm_gem_free_object(struct drm_gem_object *gobj) +{ + struct sdrm_gem_object *obj = to_sdrm_bo(gobj); + struct drm_device *ddev = gobj->dev; + struct sdrm_device *sdrm = ddev->dev_private; + + if (sdrm->fb_obj == obj) + sdrm->fb_obj = NULL; + + sdrm_gem_unmap_object(obj); + + drm_gem_free_mmap_offset(gobj); + drm_gem_object_release(gobj); + kfree(obj); +} + +/* + * Create Dumb Buffer + * IOCTL backend for dumb-buffers. We only support one framebuffer per + * simple-DRM device so this function fails if there is already a framebuffer + * allocated. If not, an initial GEM-object plus framebuffer is created and + * forwarded to the caller. + * + * We could try to kill off the previous framebuffer and create a new one for + * the caller. However, user-space often allocates two buffers in a row to + * allow double-buffering. If we kill the previous buffer, user-space would + * have no chance to notice that only one buffer is available. + * + * So user-space must make sure they either destroy their buffer when dropping + * DRM-Master or leave the CRTC intact and let others share the buffer via + * drmModeGetFB(). + * + * The buffer parameters must be the same as from the default-mode of the CRTC. + * No other sizes can be supported! + */ +int sdrm_dumb_create(struct drm_file *dfile, struct drm_device *ddev, + struct drm_mode_create_dumb *args) +{ + struct drm_device *dev = dfile->minor->dev; + struct sdrm_device *sdrm = dev->dev_private; + struct sdrm_gem_object *obj; + int ret; + + /* only allow one framebuffer at a time */ + if (sdrm->fb_obj) + return -ENOMEM; + + if (args->width != sdrm->fb_width || + args->height != sdrm->fb_height || + args->bpp != sdrm->fb_bpp || + args->flags) + return -EINVAL; + + args->pitch = sdrm->fb_stride; + args->size = sdrm->fb_size; + obj = sdrm_gem_alloc_object(ddev, sdrm->fb_base, sdrm->fb_size); + if (!obj) + return -ENOMEM; + + ret = drm_gem_handle_create(dfile, &obj->base, &args->handle); + if (ret) { + drm_gem_object_unreference(&obj->base); + return ret; + } + + /* fb_obj is cleared by sdrm_gem_free_object() */ + sdrm->fb_obj = obj; + drm_gem_object_unreference(&obj->base); + + return 0; +} + +int sdrm_dumb_destroy(struct drm_file *dfile, struct drm_device *ddev, + uint32_t handle) +{ + return drm_gem_handle_delete(dfile, handle); +} + +int sdrm_dumb_map_offset(struct drm_file *dfile, struct drm_device *ddev, + uint32_t handle, uint64_t *offset) +{ + struct drm_gem_object *gobj; + int ret; + + mutex_lock(&ddev->struct_mutex); + + gobj = drm_gem_object_lookup(ddev, dfile, handle); + if (!gobj) { + ret = -ENOENT; + goto out_unlock; + } + + ret = drm_gem_create_mmap_offset(gobj); + if (ret) + goto out_unref; + + *offset = drm_vma_node_offset_addr(&gobj->vma_node); + +out_unref: + drm_gem_object_unreference(gobj); +out_unlock: + mutex_unlock(&ddev->struct_mutex); + return ret; +} + +/* + * mmap ioctl + * We simply map the physical range of the FB into user-space as requested. We + * perform few sanity-checks and then let io_remap_pfn_range() do all the work. + * No vma_ops are needed this way as pages are either cleared or present. + */ +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct drm_file *priv = filp->private_data; + struct drm_device *dev = priv->minor->dev; + struct drm_gem_mm *mm = dev->mm_private; + struct drm_vma_offset_node *node; + struct drm_gem_object *gobj; + struct sdrm_gem_object *obj; + int ret; + + if (drm_device_is_unplugged(dev)) + return -ENODEV; + + mutex_lock(&dev->struct_mutex); + + node = drm_vma_offset_exact_lookup(&mm->vma_manager, vma->vm_pgoff, + vma_pages(vma)); + if (!node) { + mutex_unlock(&dev->struct_mutex); + return drm_mmap(filp, vma); + } else if (!drm_vma_node_is_allowed(node, filp)) { + mutex_unlock(&dev->struct_mutex); + return -EACCES; + } + + /* verify mapping size */ + if (vma_pages(vma) > drm_vma_node_size(node)) { + ret = -EINVAL; + goto out_unlock; + } + + gobj = container_of(node, struct drm_gem_object, vma_node); + obj = to_sdrm_bo(gobj); + + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + /* FIXME: do we need fb_pgprotect() here? */ + + /* This object is _not_ referenced here. Therefore, we _must_ destroy + * the mapping before destroying the bo! We do this in + * sdrm_gem_free_object(). */ + + ret = io_remap_pfn_range(vma, vma->vm_start, obj->fb_base >> PAGE_SHIFT, + obj->fb_size, vma->vm_page_prot); + +out_unlock: + mutex_unlock(&dev->struct_mutex); + return ret; +}
Hi David,
On Sun, Sep 1, 2013 at 3:36 PM, David Herrmann dh.herrmann@gmail.com wrote:
The SimpleDRM driver binds to simple-framebuffer devices and provides a DRM/KMS API. It provides only a single CRTC+encoder+connector combination plus one initial mode.
Userspace can create one dumb-buffer and attach it to the CRTC. Only if the buffer is destroyed, a new buffer can be created. The buffer is directly mapped into user-space, so we have only resources for a single buffer. Otherwise, shadow buffers plus damage-request would be needed.
Signed-off-by: David Herrmann dh.herrmann@gmail.com Tested-by: Stephen Warren swarren@nvidia.com
[...]
+static int sdrm_conn_fill_modes(struct drm_connector *conn, uint32_t max_x,
uint32_t max_y)
+{
struct sdrm_device *sdrm = conn->dev->dev_private;
struct drm_display_mode *mode;
int ret;
if (conn->force == DRM_FORCE_ON)
conn->status = connector_status_connected;
else if (conn->force)
conn->status = connector_status_disconnected;
else
conn->status = connector_status_connected;
list_for_each_entry(mode, &conn->modes, head)
mode->status = MODE_UNVERIFIED;
mode = drm_gtf_mode(sdrm->ddev, sdrm->fb_width, sdrm->fb_height,
60, 0, 0);
if (mode) {
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(conn, mode);
sdrm->mode = mode;
Should you also be setting sdrm->fb_{width,height} to mode->{v,h}display here? Otherwise, due to the rounding in drm_gtf_mode(), these values won't necessarily match (which I suppose they must?).
Cheers,
Tom
Hi Tom
On Sat, Sep 21, 2013 at 4:18 PM, Tom Gundersen teg@jklm.no wrote:
Hi David,
On Sun, Sep 1, 2013 at 3:36 PM, David Herrmann dh.herrmann@gmail.com wrote:
The SimpleDRM driver binds to simple-framebuffer devices and provides a DRM/KMS API. It provides only a single CRTC+encoder+connector combination plus one initial mode.
Userspace can create one dumb-buffer and attach it to the CRTC. Only if the buffer is destroyed, a new buffer can be created. The buffer is directly mapped into user-space, so we have only resources for a single buffer. Otherwise, shadow buffers plus damage-request would be needed.
Signed-off-by: David Herrmann dh.herrmann@gmail.com Tested-by: Stephen Warren swarren@nvidia.com
[...]
+static int sdrm_conn_fill_modes(struct drm_connector *conn, uint32_t max_x,
uint32_t max_y)
+{
struct sdrm_device *sdrm = conn->dev->dev_private;
struct drm_display_mode *mode;
int ret;
if (conn->force == DRM_FORCE_ON)
conn->status = connector_status_connected;
else if (conn->force)
conn->status = connector_status_disconnected;
else
conn->status = connector_status_connected;
list_for_each_entry(mode, &conn->modes, head)
mode->status = MODE_UNVERIFIED;
mode = drm_gtf_mode(sdrm->ddev, sdrm->fb_width, sdrm->fb_height,
60, 0, 0);
if (mode) {
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(conn, mode);
sdrm->mode = mode;
Should you also be setting sdrm->fb_{width,height} to mode->{v,h}display here? Otherwise, due to the rounding in drm_gtf_mode(), these values won't necessarily match (which I suppose they must?).
What the ****. I wasn't aware drm_gtf_mode() modifies the v/hdisplay values. Hm.. I will probably have to use something else then.
Thanks David
Create a simple fbdev device during SimpleDRM setup so legacy user-space and fbcon can use it.
Signed-off-by: David Herrmann dh.herrmann@gmail.com Tested-by: Stephen Warren swarren@nvidia.com --- drivers/gpu/drm/simpledrm/Kconfig | 11 ++ drivers/gpu/drm/simpledrm/Makefile | 1 + drivers/gpu/drm/simpledrm/simpledrm.h | 22 ++++ drivers/gpu/drm/simpledrm/simpledrm_fbdev.c | 153 ++++++++++++++++++++++++++++ drivers/gpu/drm/simpledrm/simpledrm_main.c | 2 + 5 files changed, 189 insertions(+) create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_fbdev.c
diff --git a/drivers/gpu/drm/simpledrm/Kconfig b/drivers/gpu/drm/simpledrm/Kconfig index 35bcce8..eef2a36 100644 --- a/drivers/gpu/drm/simpledrm/Kconfig +++ b/drivers/gpu/drm/simpledrm/Kconfig @@ -12,7 +12,18 @@ config DRM_SIMPLEDRM SimpleDRM supports "simple-framebuffer" DeviceTree objects and compatible platform framebuffers.
+ If fbdev support is enabled, this driver will also provide an fbdev + compatibility layer. + If unsure, say Y.
To compile this driver as a module, choose M here: the module will be called simpledrm. + +config DRM_SIMPLEDRM_FBDEV + bool + depends on DRM_SIMPLEDRM && FB + default y + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT diff --git a/drivers/gpu/drm/simpledrm/Makefile b/drivers/gpu/drm/simpledrm/Makefile index ceb97eb..2144787 100644 --- a/drivers/gpu/drm/simpledrm/Makefile +++ b/drivers/gpu/drm/simpledrm/Makefile @@ -1,3 +1,4 @@ simpledrm-y := simpledrm_drv.o simpledrm_main.o simpledrm_mem.o +simpledrm-$(CONFIG_DRM_SIMPLEDRM_FBDEV) += simpledrm_fbdev.o
obj-$(CONFIG_DRM_SIMPLEDRM) := simpledrm.o diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h index 977b344..b854981 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm.h +++ b/drivers/gpu/drm/simpledrm/simpledrm.h @@ -46,6 +46,9 @@ struct sdrm_device { struct drm_encoder enc; struct drm_connector conn; struct drm_display_mode *mode; + + /* fbdev */ + struct fb_info *fbdev; };
int sdrm_drm_load(struct drm_device *ddev, unsigned long flags); @@ -86,4 +89,23 @@ struct sdrm_framebuffer {
#define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base)
+/* simpledrm fbdev helpers */ + +#ifdef CONFIG_DRM_SIMPLEDRM_FBDEV + +void sdrm_fbdev_init(struct sdrm_device *sdrm); +void sdrm_fbdev_cleanup(struct sdrm_device *sdrm); + +#else /* CONFIG_DRM_SIMPLEDRM_FBDEV */ + +static inline void sdrm_fbdev_init(struct sdrm_device *sdrm) +{ +} + +static inline void sdrm_fbdev_cleanup(struct sdrm_device *sdrm) +{ +} + +#endif /* CONFIG_DRM_SIMPLEDRM_FBDEV */ + #endif /* SDRM_DRV_H */ diff --git a/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c new file mode 100644 index 0000000..7e2e4a2 --- /dev/null +++ b/drivers/gpu/drm/simpledrm/simpledrm_fbdev.c @@ -0,0 +1,153 @@ +/* + * SimpleDRM firmware framebuffer driver + * Copyright (c) 2012-2013 David Herrmann dh.herrmann@gmail.com + * + * 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. + */ + +/* + * fbdev compatibility layer + * We provide a basic fbdev device for the same framebuffer that is used for + * the pseudo CRTC. + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/string.h> +#include <linux/fb.h> +#include "simpledrm.h" + +struct sdrm_fbdev { + u32 palette[16]; +}; + +static int sdrm_fbdev_setcolreg(u_int regno, u_int red, u_int green, + u_int blue, u_int transp, struct fb_info *info) +{ + u32 *pal = info->pseudo_palette; + u32 cr = red >> (16 - info->var.red.length); + u32 cg = green >> (16 - info->var.green.length); + u32 cb = blue >> (16 - info->var.blue.length); + u32 value; + + if (regno >= 16) + return -EINVAL; + + value = (cr << info->var.red.offset) | + (cg << info->var.green.offset) | + (cb << info->var.blue.offset); + + if (info->var.transp.length > 0) { + u32 mask = (1 << info->var.transp.length) - 1; + mask <<= info->var.transp.offset; + value |= mask; + } + + pal[regno] = value; + + return 0; +} + +static struct fb_ops sdrm_fbdev_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = sdrm_fbdev_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +void sdrm_fbdev_init(struct sdrm_device *sdrm) +{ + struct sdrm_fbdev *fb; + struct fb_info *info; + int ret; + + if (fb_get_options("simpledrmfb", NULL)) + return; + + info = framebuffer_alloc(sizeof(struct sdrm_fbdev), sdrm->ddev->dev); + if (!info) + goto err_out; + + fb = info->par; + info->flags = FBINFO_DEFAULT | FBINFO_MISC_FIRMWARE; + info->pseudo_palette = fb->palette; + info->fbops = &sdrm_fbdev_ops; + info->screen_base = sdrm->fb_map; + + strncpy(info->fix.id, "simpledrmfb", 15); + info->fix.type = FB_TYPE_PACKED_PIXELS; + info->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.accel = FB_ACCEL_NONE; + info->fix.smem_start = (unsigned long)sdrm->fb_base; + info->fix.smem_len = sdrm->fb_size; + info->fix.line_length = sdrm->fb_stride; + + info->var.activate = FB_ACTIVATE_NOW; + info->var.vmode = FB_VMODE_NONINTERLACED; + info->var.bits_per_pixel = sdrm->fb_bpp; + info->var.height = -1; + info->var.width = -1; + info->var.xres = sdrm->fb_width; + info->var.yres = sdrm->fb_height; + info->var.xres_virtual = info->var.xres; + info->var.yres_virtual = info->var.yres; + info->var.red = sdrm->fb_sformat->red; + info->var.green = sdrm->fb_sformat->green; + info->var.blue = sdrm->fb_sformat->blue; + info->var.transp = sdrm->fb_sformat->transp; + + /* some dummy values for timing to make fbset happy */ + info->var.pixclock = 10000000 / info->var.xres * 1000 / info->var.yres; + info->var.left_margin = (info->var.xres / 8) & 0xf8; + info->var.right_margin = 32; + info->var.upper_margin = 16; + info->var.lower_margin = 4; + info->var.hsync_len = (info->var.xres / 8) & 0xf8; + info->var.vsync_len = 4; + + info->apertures = alloc_apertures(1); + if (!info->apertures) + goto err_free; + + info->apertures->ranges[0].base = (unsigned long)sdrm->fb_base; + info->apertures->ranges[0].size = sdrm->fb_size; + + sdrm->fbdev = info; + ret = register_framebuffer(info); + if (ret < 0) + goto err_free; + + dev_info(sdrm->ddev->dev, "fbdev frontend %s as fb%d\n", + info->fix.id, info->node); + + return; + +err_free: + framebuffer_release(info); + sdrm->fbdev = NULL; +err_out: + dev_warn(sdrm->ddev->dev, "cannot create fbdev frontend\n"); +} + +void sdrm_fbdev_cleanup(struct sdrm_device *sdrm) +{ + struct fb_info *info; + + if (!sdrm->fbdev) + return; + + dev_info(sdrm->ddev->dev, "fbdev cleanup\n"); + info = sdrm->fbdev; + sdrm->fbdev = NULL; + + if (unregister_framebuffer(info)) + dev_warn(sdrm->ddev->dev, "unregister_framebuffer() failed, leaking fbdev device\n"); + else + framebuffer_release(info); +} diff --git a/drivers/gpu/drm/simpledrm/simpledrm_main.c b/drivers/gpu/drm/simpledrm/simpledrm_main.c index ae507e3..497542d 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm_main.c +++ b/drivers/gpu/drm/simpledrm/simpledrm_main.c @@ -303,6 +303,7 @@ int sdrm_drm_load(struct drm_device *ddev, unsigned long flags) if (ret) goto err_cleanup;
+ sdrm_fbdev_init(sdrm); return 0;
err_cleanup: @@ -320,6 +321,7 @@ int sdrm_drm_unload(struct drm_device *ddev) { struct sdrm_device *sdrm = ddev->dev_private;
+ sdrm_fbdev_cleanup(sdrm); drm_mode_config_cleanup(ddev); sdrm_pdev_destroy(sdrm); kfree(sdrm);
If we load a real hardware DRM driver, we want all firmware drivers to be unloaded. Historically, this was done via remove_conflicting_framebuffers(), but for DRM drivers (like SimpleDRM) we need a new way.
This patch introduces DRIVER_FIRMWARE and DRM apertures to provide a quite similar way to kick out firmware DRM drivers. Additionally, unlike the fbdev equivalent, DRM firmware drivers can now query the system whether a real hardware driver is already loaded and prevent loading themselves.
Whenever a real hardware DRM driver is loaded which claims to provide the boot fw FB, we invalidate the firmware framebuffer. Otherwise, simpledrm could be loaded again, after a real hw-driver was unloaded (which is very unlikely to work, except if hw-drivers reset fw FBs during unload). Note that fbdev doesn't provide such protection against late driver probing.
Signed-off-by: David Herrmann dh.herrmann@gmail.com Tested-by: Stephen Warren swarren@nvidia.com --- drivers/gpu/drm/drm_pci.c | 1 + drivers/gpu/drm/drm_platform.c | 1 + drivers/gpu/drm/drm_stub.c | 118 +++++++++++++++++++++++++++++ drivers/gpu/drm/drm_usb.c | 1 + drivers/gpu/drm/simpledrm/simpledrm.h | 1 + drivers/gpu/drm/simpledrm/simpledrm_drv.c | 3 +- drivers/gpu/drm/simpledrm/simpledrm_main.c | 33 ++++++++ include/drm/drmP.h | 26 +++++++ 8 files changed, 183 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index 1f96cee..f3de4d6 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -378,6 +378,7 @@ int drm_get_pci_dev(struct pci_dev *pdev, const struct pci_device_id *ent, }
list_add_tail(&dev->driver_item, &driver->device_list); + list_add_tail(&dev->global_item, &drm_devlist);
DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", driver->name, driver->major, driver->minor, driver->patchlevel, diff --git a/drivers/gpu/drm/drm_platform.c b/drivers/gpu/drm/drm_platform.c index f7a18c6..a287429 100644 --- a/drivers/gpu/drm/drm_platform.c +++ b/drivers/gpu/drm/drm_platform.c @@ -94,6 +94,7 @@ static int drm_get_platform_dev(struct platform_device *platdev, }
list_add_tail(&dev->driver_item, &driver->device_list); + list_add_tail(&dev->global_item, &drm_devlist);
mutex_unlock(&drm_global_mutex);
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index e7eb027..363e47b 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -49,6 +49,10 @@ EXPORT_SYMBOL(drm_vblank_offdelay); unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ EXPORT_SYMBOL(drm_timestamp_precision);
+static bool drm_fw_invalid; /* true if fw FBs have been destroyed */ +LIST_HEAD(drm_devlist); /* device list; protected by global lock */ +EXPORT_SYMBOL(drm_devlist); + /* * Default to use monotonic timestamps for wait-for-vblank and page-flip * complete events. @@ -459,7 +463,9 @@ void drm_put_dev(struct drm_device *dev)
drm_put_minor(&dev->primary);
+ list_del(&dev->global_item); list_del(&dev->driver_item); + kfree(dev->apertures); kfree(dev->devname); kfree(dev); } @@ -484,3 +490,115 @@ void drm_unplug_dev(struct drm_device *dev) mutex_unlock(&drm_global_mutex); } EXPORT_SYMBOL(drm_unplug_dev); + +void drm_unplug_dev_locked(struct drm_device *dev) +{ + /* for a USB device */ + if (drm_core_check_feature(dev, DRIVER_MODESET)) + drm_unplug_minor(dev->control); + drm_unplug_minor(dev->primary); + + drm_device_set_unplugged(dev); + + /* TODO: schedule drm_put_dev if open_count == 0 */ +} +EXPORT_SYMBOL(drm_unplug_dev_locked); + +#define VGA_FB_PHYS 0xa0000 + +static bool apertures_overlap(struct drm_device *dev, + struct apertures_struct *ap, + bool boot) +{ + unsigned int i, j; + struct aperture *a, *b; + + if (!dev->apertures) + return false; + + for (i = 0; i < dev->apertures->count; ++i) { + a = &dev->apertures->ranges[i]; + + if (boot && a->base == VGA_FB_PHYS) + return true; + + for (j = 0; ap && j < ap->count; ++j) { + b = &ap->ranges[j]; + if (a->base <= b->base && a->base + a->size > b->base) + return true; + if (b->base <= a->base && b->base + b->size > a->base) + return true; + } + } + + return false; +} + +/** + * Kick out firmware + * + * Virtually unplug any firmware graphics devices which overlap the given + * region. This must be called with the global-drm-mutex locked. + * This calls the kick_out_firmware() callback on any firmware DRM driver and + * after that remove_conflicting_framebuffers() to remove legacy fbdev + * framebuffers. + */ +void drm_kick_out_firmware(struct apertures_struct *ap, bool boot) +{ + struct drm_device *dev; + + if (boot) + drm_fw_invalid = true; + + list_for_each_entry(dev, &drm_devlist, global_item) { + if (!drm_core_check_feature(dev, DRIVER_FIRMWARE)) + continue; + if (apertures_overlap(dev, ap, boot)) { + DRM_INFO("kicking out firmware %s\n", + dev->devname); + dev->driver->kick_out_firmware(dev); + } + } + +#ifdef CONFIG_FB + remove_conflicting_framebuffers(ap, "DRM", boot); +#endif +} +EXPORT_SYMBOL(drm_kick_out_firmware); + +/** + * Verify that no driver uses firmware FBs + * + * Whenever you register a firmware framebuffer driver, you should store the + * apertures in @ap and test whether any other registered driver already + * claimed this area. Hence, if this function returns true, you should _not_ + * register your driver! + */ +bool drm_is_firmware_used(struct apertures_struct *ap) +{ + struct drm_device *dev; + unsigned int i; + bool boot = false; + + /* If a real DRM driver was loaded once, we cannot assume that the + * firmware framebuffers are still valid. */ + if (drm_fw_invalid) + return true; + + for (i = 0; ap && i < ap->count; ++i) { + if (ap->ranges[i].base == VGA_FB_PHYS) { + boot = true; + break; + } + } + + list_for_each_entry(dev, &drm_devlist, global_item) { + if (dev->apert_boot && boot) + return true; + if (apertures_overlap(dev, ap, false)) + return true; + } + + return false; +} +EXPORT_SYMBOL(drm_is_firmware_used); diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c index 8766472..b672cf2 100644 --- a/drivers/gpu/drm/drm_usb.c +++ b/drivers/gpu/drm/drm_usb.c @@ -56,6 +56,7 @@ int drm_get_usb_dev(struct usb_interface *interface, goto err_g3;
list_add_tail(&dev->driver_item, &driver->device_list); + list_add_tail(&dev->global_item, &drm_devlist);
mutex_unlock(&drm_global_mutex);
diff --git a/drivers/gpu/drm/simpledrm/simpledrm.h b/drivers/gpu/drm/simpledrm/simpledrm.h index b854981..c093ad5 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm.h +++ b/drivers/gpu/drm/simpledrm/simpledrm.h @@ -53,6 +53,7 @@ struct sdrm_device {
int sdrm_drm_load(struct drm_device *ddev, unsigned long flags); int sdrm_drm_unload(struct drm_device *ddev); +void sdrm_drm_kick_out_firmware(struct drm_device *ddev); int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma); int sdrm_pdev_init(struct sdrm_device *sdrm); void sdrm_pdev_destroy(struct sdrm_device *sdrm); diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c index 8a34051..aea702c 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm_drv.c +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c @@ -33,9 +33,10 @@ static const struct file_operations sdrm_drm_fops = { };
static struct drm_driver sdrm_drm_driver = { - .driver_features = DRIVER_MODESET | DRIVER_GEM, + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_FIRMWARE, .load = sdrm_drm_load, .unload = sdrm_drm_unload, + .kick_out_firmware = sdrm_drm_kick_out_firmware, .fops = &sdrm_drm_fops,
.gem_init_object = sdrm_gem_init_object, diff --git a/drivers/gpu/drm/simpledrm/simpledrm_main.c b/drivers/gpu/drm/simpledrm/simpledrm_main.c index 497542d..38571c2 100644 --- a/drivers/gpu/drm/simpledrm/simpledrm_main.c +++ b/drivers/gpu/drm/simpledrm/simpledrm_main.c @@ -267,6 +267,21 @@ int sdrm_drm_load(struct drm_device *ddev, unsigned long flags) if (ret) goto err_name;
+ ddev->apertures = alloc_apertures(1); + if (!ddev->apertures) { + ret = -ENOMEM; + goto err_pdev; + } + + ddev->apertures->ranges[0].base = sdrm->fb_base; + ddev->apertures->ranges[0].size = sdrm->fb_size; + + if (drm_is_firmware_used(ddev->apertures)) { + dev_info(ddev->dev, "firmware framebuffer is already in use\n"); + ret = -EBUSY; + goto err_apert; + } + drm_mode_config_init(ddev); ddev->mode_config.min_width = 0; ddev->mode_config.min_height = 0; @@ -308,6 +323,9 @@ int sdrm_drm_load(struct drm_device *ddev, unsigned long flags)
err_cleanup: drm_mode_config_cleanup(ddev); +err_apert: + kfree(ddev->apertures); +err_pdev: sdrm_pdev_destroy(sdrm); err_name: kfree(ddev->devname); @@ -328,3 +346,18 @@ int sdrm_drm_unload(struct drm_device *ddev)
return 0; } + +void sdrm_drm_kick_out_firmware(struct drm_device *ddev) +{ + struct sdrm_device *sdrm = ddev->dev_private; + + mutex_lock(&ddev->struct_mutex); + + sdrm_fbdev_cleanup(sdrm); + drm_unplug_dev_locked(ddev); + if (sdrm->fb_obj) + sdrm_gem_unmap_object(sdrm->fb_obj); + sdrm_pdev_destroy(sdrm); + + mutex_unlock(&ddev->struct_mutex); +} diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 2907341..d197033 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -46,6 +46,7 @@ #include <linux/miscdevice.h> #include <linux/fs.h> #include <linux/init.h> +#include <linux/fb.h> #include <linux/file.h> #include <linux/platform_device.h> #include <linux/pci.h> @@ -146,6 +147,7 @@ int drm_err(const char *func, const char *format, ...); #define DRIVER_MODESET 0x2000 #define DRIVER_PRIME 0x4000 #define DRIVER_RENDER 0x8000 +#define DRIVER_FIRMWARE 0x10000
#define DRIVER_BUS_PCI 0x1 #define DRIVER_BUS_PLATFORM 0x2 @@ -966,6 +968,23 @@ struct drm_driver { struct drm_device *dev, uint32_t handle);
+ /** + * kick_out_firmware - kick out firmware driver + * @dev: DRM device + * + * Iff this driver has DRIVER_FIRMWARE set, this function is called + * when a real hw-driver is loaded and claims the framebuffer memory + * that is mapped by the firmware driver. This is called with the + * global drm mutex held. + * Drivers should unmap any memory-mappings, disable the device and + * schedule a driver removal. + * + * Note that a driver setting DRIVER_FIRMWARE is supposed to also set + * the "apertures" information on @dev. It is used to match the memory + * regions that are used by firmware drivers. + */ + void (*kick_out_firmware) (struct drm_device *dev); + /* Driver private ops for this object */ const struct vm_operations_struct *gem_vm_ops;
@@ -1087,6 +1106,7 @@ struct drm_pending_vblank_event { */ struct drm_device { struct list_head driver_item; /**< list of devices per driver */ + struct list_head global_item; /**< global list of devices */ char *devname; /**< For /proc/interrupts */ int if_version; /**< Highest interface version set */
@@ -1218,6 +1238,8 @@ struct drm_device { int switch_power_state;
atomic_t unplugged; /* device has been unplugged or gone away */ + struct apertures_struct *apertures; /**< fbmem apertures */ + bool apert_boot; /**< true if mapped as boot fb */ };
#define DRM_SWITCH_POWER_ON 0 @@ -1457,6 +1479,10 @@ extern void drm_master_put(struct drm_master **master); extern void drm_put_dev(struct drm_device *dev); extern int drm_put_minor(struct drm_minor **minor); extern void drm_unplug_dev(struct drm_device *dev); +extern void drm_unplug_dev_locked(struct drm_device *dev); +extern void drm_kick_out_firmware(struct apertures_struct *ap, bool boot); +extern bool drm_is_firmware_used(struct apertures_struct *ap); +extern struct list_head drm_devlist; extern unsigned int drm_debug; extern unsigned int drm_rnodes;
Use the new DRM infrastructure to kick out firmware drivers before probing the real driver.
Cc: Maarten Lankhorst maarten.lankhorst@canonical.com Cc: Ben Skeggs bskeggs@redhat.com Signed-off-by: David Herrmann dh.herrmann@gmail.com --- drivers/gpu/drm/nouveau/nouveau_drm.c | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 62c6118..452cc21 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -252,13 +252,11 @@ nouveau_accel_init(struct nouveau_drm *drm) nouveau_bo_move_init(drm); }
-static int nouveau_drm_probe(struct pci_dev *pdev, - const struct pci_device_id *pent) +static int nouveau_kick_out_firmware(struct drm_device *dev) { - struct nouveau_device *device; struct apertures_struct *aper; bool boot = false; - int ret; + struct pci_dev *pdev = dev->pdev;
/* remove conflicting drivers (vesafb, efifb etc) */ aper = alloc_apertures(3); @@ -284,8 +282,18 @@ static int nouveau_drm_probe(struct pci_dev *pdev, #ifdef CONFIG_X86 boot = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; #endif - remove_conflicting_framebuffers(aper, "nouveaufb", boot); - kfree(aper); + + drm_kick_out_firmware(aper, boot); + dev->apertures = aper; + dev->apert_boot = boot; + return 0; +} + +static int nouveau_drm_probe(struct pci_dev *pdev, + const struct pci_device_id *pent) +{ + struct nouveau_device *device; + int ret;
ret = nouveau_device_create(pdev, nouveau_name(pdev), pci_name(pdev), nouveau_config, nouveau_debug, &device); @@ -336,10 +344,14 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags) struct nouveau_drm *drm; int ret;
- ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm); + ret = nouveau_kick_out_firmware(dev); if (ret) return ret;
+ ret = nouveau_cli_create(pdev, "DRM", sizeof(*drm), (void**)&drm); + if (ret) + goto fail_apert; + dev->dev_private = drm; drm->dev = dev;
@@ -444,6 +456,9 @@ fail_ttm: nouveau_vga_fini(drm); fail_device: nouveau_cli_destroy(&drm->client); +fail_apert: + kfree(dev->apertures); + dev->apertures = NULL; return ret; }
Use the new DRM infrastructure to kick out firmware DRM drivers before loading i915.
Cc: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: David Herrmann dh.herrmann@gmail.com --- drivers/gpu/drm/i915/i915_dma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3e4e607..9d375a6 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1414,9 +1414,9 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
- remove_conflicting_framebuffers(ap, "inteldrmfb", primary); - - kfree(ap); + drm_kick_out_firmware(ap, primary); + dev_priv->dev->apertures = ap; + dev_priv->dev->apert_boot = primary; }
static void i915_dump_device_info(struct drm_i915_private *dev_priv)
On Sun, Sep 01, 2013 at 03:36:51PM +0200, David Herrmann wrote:
Use the new DRM infrastructure to kick out firmware DRM drivers before loading i915.
Cc: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: David Herrmann dh.herrmann@gmail.com
drivers/gpu/drm/i915/i915_dma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3e4e607..9d375a6 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1414,9 +1414,9 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
- remove_conflicting_framebuffers(ap, "inteldrmfb", primary);
- kfree(ap);
- drm_kick_out_firmware(ap, primary);
- dev_priv->dev->apertures = ap;
- dev_priv->dev->apert_boot = primary;
What about passing dev to drm_kick_out_firmware and shovelling these two assignments in there? I've check your nouveau/radeon patches and I think this would work ... -Daniel
Hi
On Mon, Sep 2, 2013 at 9:43 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sun, Sep 01, 2013 at 03:36:51PM +0200, David Herrmann wrote:
Use the new DRM infrastructure to kick out firmware DRM drivers before loading i915.
Cc: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: David Herrmann dh.herrmann@gmail.com
drivers/gpu/drm/i915/i915_dma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3e4e607..9d375a6 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1414,9 +1414,9 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
remove_conflicting_framebuffers(ap, "inteldrmfb", primary);
kfree(ap);
drm_kick_out_firmware(ap, primary);
dev_priv->dev->apertures = ap;
dev_priv->dev->apert_boot = primary;
What about passing dev to drm_kick_out_firmware and shovelling these two assignments in there? I've check your nouveau/radeon patches and I think this would work ...
Yepp, that sounds reasonable. And I guess the kfree() of apertures can also be done in drm_put_dev(). Maybe we can even to the whole stuff in drm_pci.c, I will have a look.
Thanks David
On Mon, Sep 02, 2013 at 02:02:06PM +0200, David Herrmann wrote:
Hi
On Mon, Sep 2, 2013 at 9:43 AM, Daniel Vetter daniel@ffwll.ch wrote:
On Sun, Sep 01, 2013 at 03:36:51PM +0200, David Herrmann wrote:
Use the new DRM infrastructure to kick out firmware DRM drivers before loading i915.
Cc: Daniel Vetter daniel.vetter@ffwll.ch Signed-off-by: David Herrmann dh.herrmann@gmail.com
drivers/gpu/drm/i915/i915_dma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3e4e607..9d375a6 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c @@ -1414,9 +1414,9 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv) primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW;
remove_conflicting_framebuffers(ap, "inteldrmfb", primary);
kfree(ap);
drm_kick_out_firmware(ap, primary);
dev_priv->dev->apertures = ap;
dev_priv->dev->apert_boot = primary;
What about passing dev to drm_kick_out_firmware and shovelling these two assignments in there? I've check your nouveau/radeon patches and I think this would work ...
Yepp, that sounds reasonable. And I guess the kfree() of apertures can also be done in drm_put_dev(). Maybe we can even to the whole stuff in drm_pci.c, I will have a look.
I like the helper approach used here since it allows drivers to pick an appropriate spot. Moving it into drm_pci.c could be a bit ugly. Aside: I'm pondering ideas to weed out the midlayer smell from the drm device init sequence. I've played around with a few things and it looks like we're really close to doing the Right Thing ;-)
Cheers, Daniel
Kick out firmware DRM and fbdev drivers via the new drm_kick_out_firmware() before loading radeon.
Cc: Alex Deucher alexander.deucher@amd.com Signed-off-by: David Herrmann dh.herrmann@gmail.com --- drivers/gpu/drm/radeon/radeon_drv.c | 28 ---------------------------- drivers/gpu/drm/radeon/radeon_kms.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 28 deletions(-)
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 1f93dd5..13f1e15 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -311,37 +311,9 @@ static struct drm_driver driver_old = {
static struct drm_driver kms_driver;
-static int radeon_kick_out_firmware_fb(struct pci_dev *pdev) -{ - struct apertures_struct *ap; - bool primary = false; - - ap = alloc_apertures(1); - if (!ap) - return -ENOMEM; - - ap->ranges[0].base = pci_resource_start(pdev, 0); - ap->ranges[0].size = pci_resource_len(pdev, 0); - -#ifdef CONFIG_X86 - primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; -#endif - remove_conflicting_framebuffers(ap, "radeondrmfb", primary); - kfree(ap); - - return 0; -} - static int radeon_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - int ret; - - /* Get rid of things like offb */ - ret = radeon_kick_out_firmware_fb(pdev); - if (ret) - return ret; - return drm_get_pci_dev(pdev, ent, &kms_driver); }
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index b46a561..d8f2a38 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -62,6 +62,30 @@ done_free: return 0; }
+static int radeon_kick_out_firmware(struct drm_device *dev) +{ + struct pci_dev *pdev = dev->pdev; + struct apertures_struct *ap; + bool primary = false; + + ap = alloc_apertures(1); + if (!ap) + return -ENOMEM; + + ap->ranges[0].base = pci_resource_start(pdev, 0); + ap->ranges[0].size = pci_resource_len(pdev, 0); + +#ifdef CONFIG_X86 + primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif + + drm_kick_out_firmware(ap, primary); + dev->apertures = ap; + dev->apert_boot = primary; + + return 0; +} + /** * radeon_driver_load_kms - Main load function for KMS. * @@ -86,6 +110,12 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags) } dev->dev_private = (void *)rdev;
+ r = radeon_kick_out_firmware(dev); + if (r) { + kfree(rdev); + return r; + } + /* update BUS flag */ if (drm_pci_device_is_agp(dev)) { flags |= RADEON_IS_AGP;
Hi
On Sun, Sep 1, 2013 at 3:36 PM, David Herrmann dh.herrmann@gmail.com wrote:
Hi
With the upcoming 3.12 merge-window, I thought people might find themselves with nothing to do, so here's a new SimpleDRM series. Comments welcome!
This depends on the tip/x86/fb series in the x86-tip tree: http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/log/?h=x86/fb Which, as far as I understood, will be pushed into 3.12 by the x86 people.
FYI, this is now merged in Linus' tree. See: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2...
So hopefully we can get SimpleDRM ready for 3.13.
Cheers David
Hi David,
On Wed, Sep 4, 2013 at 7:34 PM, David Herrmann dh.herrmann@gmail.com wrote:
Hi
On Sun, Sep 1, 2013 at 3:36 PM, David Herrmann dh.herrmann@gmail.com wrote:
Hi
With the upcoming 3.12 merge-window, I thought people might find themselves with nothing to do, so here's a new SimpleDRM series. Comments welcome!
This depends on the tip/x86/fb series in the x86-tip tree: http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/log/?h=x86/fb Which, as far as I understood, will be pushed into 3.12 by the x86 people.
FYI, this is now merged in Linus' tree. See: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2...
So hopefully we can get SimpleDRM ready for 3.13.
Now that simplefb works for me, I finally got around to testing this. Just a couple of comments:
* I guess you need to add the modalias "platform:simple-framebuffer" in addition to the "of:..." one to get module auto loading working.
* the driver currently doesn't work with your IORESOURCE_BUSY fix to sysfb (as might be expected(?)): simple-framebuffer simple-framebuffer.0: cannot reserve VMEM simple-framebuffer: probe of simple-framebuffer.0 failed with error -5
* except for that, fbcon on top of the fbdev fallback support works fine for me. I didn't yet try the drm driver itself, what clients (if any) are supposed to work with this, kmscon, weston?
Cheers,
Tom
Hi
On Sun, Sep 8, 2013 at 1:33 PM, Tom Gundersen teg@jklm.no wrote:
Hi David,
On Wed, Sep 4, 2013 at 7:34 PM, David Herrmann dh.herrmann@gmail.com wrote:
Hi
On Sun, Sep 1, 2013 at 3:36 PM, David Herrmann dh.herrmann@gmail.com wrote:
Hi
With the upcoming 3.12 merge-window, I thought people might find themselves with nothing to do, so here's a new SimpleDRM series. Comments welcome!
This depends on the tip/x86/fb series in the x86-tip tree: http://git.kernel.org/cgit/linux/kernel/git/tip/tip.git/log/?h=x86/fb Which, as far as I understood, will be pushed into 3.12 by the x86 people.
FYI, this is now merged in Linus' tree. See: https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=2...
So hopefully we can get SimpleDRM ready for 3.13.
Now that simplefb works for me, I finally got around to testing this. Just a couple of comments:
- I guess you need to add the modalias "platform:simple-framebuffer"
in addition to the "of:..." one to get module auto loading working.
Yes, sounds good.
- the driver currently doesn't work with your IORESOURCE_BUSY fix to
sysfb (as might be expected(?)): simple-framebuffer simple-framebuffer.0: cannot reserve VMEM simple-framebuffer: probe of simple-framebuffer.0 failed with error -5
Yes, if the simple-framebuffer region is already marked BUSY, simpleDRM must not (and doesn't have to) call __request_region() (or request_mem_region()). I have to remove that call if the BUSY fix gets applied.
- except for that, fbcon on top of the fbdev fallback support works
fine for me. I didn't yet try the drm driver itself, what clients (if any) are supposed to work with this, kmscon, weston?
Obviously, simpledrm doesn't support double-buffering, page-flipping or other advanced techniques. So I currently doubt you can use any real application on it as they all at least require 2 buffers. I haven't decided whether to emulate these in the kernel driver or to rely on user-space to deal with this reduced driver. It's quite likely I will go with both. That means, a compatibility option that makes simpledrm emulate any required techniques (multiple FBs, page-flipping) but also user-space patches to maybe some day be able to disable the kernel emulation.
Thanks a lot for testing all this. I will try to get the fixes into rc2. The speed-improvements might have to wait for 3.13, though.
Cheers David
dri-devel@lists.freedesktop.org