This series adds an initial framework that allows to register a graphics complex as found on many SoCs with the DRM/KMS subsystem. Patch 1 adds the actual framework and patch 2 implements a very rudimentary skeleton driver on top of it. It's really just to show off how to use the DRM/SoC API and of no use otherwise.
The reason for implementing this is that the approach currently used by many SoC DRM drivers is to instantiate a dummy platform device for the DRM driver to bind against, which is somewhat of a kludge. The proposal in this series takes care of this implicitly and instantiates only what is really needed to make the DRM core happy.
I fully realize that this may not be that much of an improvement over using the drm_platform_init()/drm_platform_exit() functions, but I'm still interested in what people think about it.
Thierry
Thierry Reding (2): drm: Add SoC framework drm: Add SoC skeleton driver
drivers/gpu/drm/Kconfig | 6 ++ drivers/gpu/drm/Makefile | 3 + drivers/gpu/drm/drm_soc.c | 151 ++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/soc/Kconfig | 13 ++++ drivers/gpu/drm/soc/Makefile | 6 ++ drivers/gpu/drm/soc/soc-drv.c | 166 ++++++++++++++++++++++++++++++++++++++++++ include/drm/drmP.h | 5 ++ 7 files changed, 350 insertions(+) create mode 100644 drivers/gpu/drm/drm_soc.c create mode 100644 drivers/gpu/drm/soc/Kconfig create mode 100644 drivers/gpu/drm/soc/Makefile create mode 100644 drivers/gpu/drm/soc/soc-drv.c
This commit adds an initial framework that can be used to plug SoC graphics devices into the DRM/KMS subsystem.
SoCs often don't have a dedicated device to which a DRM driver can bind, but instead the graphics complex typically consists of several platform- type devices that work together. The DRM SoC support code therefore adds a dummy device that is associated with the DRM driver, which typically provides some kind of API for other drivers to register with.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- drivers/gpu/drm/Kconfig | 4 ++ drivers/gpu/drm/Makefile | 2 + drivers/gpu/drm/drm_soc.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++ include/drm/drmP.h | 5 ++ 4 files changed, 162 insertions(+) create mode 100644 drivers/gpu/drm/drm_soc.c
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 23120c0..529dede 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -24,6 +24,10 @@ config DRM_USB depends on DRM select USB
+config DRM_SOC + tristate + depends on DRM + config DRM_KMS_HELPER tristate depends on DRM diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index f65f65e..4731cbc 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -17,6 +17,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-usb-y := drm_usb.o +drm-soc-y := drm_soc.o
drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o @@ -27,6 +28,7 @@ CFLAGS_drm_trace_points.o := -I$(src)
obj-$(CONFIG_DRM) += drm.o obj-$(CONFIG_DRM_USB) += drm_usb.o +obj-$(CONFIG_DRM_SOC) += drm-soc.o obj-$(CONFIG_DRM_TTM) += ttm/ obj-$(CONFIG_DRM_TDFX) += tdfx/ obj-$(CONFIG_DRM_R128) += r128/ diff --git a/drivers/gpu/drm/drm_soc.c b/drivers/gpu/drm/drm_soc.c new file mode 100644 index 0000000..bc120ae --- /dev/null +++ b/drivers/gpu/drm/drm_soc.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2012 Avionic Design GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <drm/drmP.h> +#include <drm/drm_sysfs.h> + +static const char *drm_soc_get_name(struct drm_device *dev) +{ + DRM_DEBUG("\n"); + return dev_name(dev->dev); +} + +static int drm_soc_set_busid(struct drm_device *dev, struct drm_master *master) +{ + DRM_DEBUG("\n"); + return -ENOSYS; +} + +static int drm_soc_set_unique(struct drm_device *dev, struct drm_master *master, + struct drm_unique *unique) +{ + DRM_DEBUG("\n"); + return -ENOSYS; +} + +static struct drm_bus drm_soc_bus = { + .bus_type = DRIVER_BUS_SOC, + .get_name = drm_soc_get_name, + .set_busid = drm_soc_set_busid, + .set_unique = drm_soc_set_unique, +}; + +static struct device_type drm_soc_device_type = { + .name = "soc", +}; + +static void drm_soc_device_release(struct device *dev) +{ + kfree(dev); +} + +static struct device *drm_create_soc_device(const char *name) +{ + struct device *dev; + int err; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; + + dev->release = drm_soc_device_release; + dev->type = &drm_soc_device_type; + dev_set_name(dev, name); + + err = drm_class_device_register(dev); + if (err < 0) + goto put; + + return dev; + +put: + put_device(dev); + return NULL; +} + +int drm_soc_init(struct drm_driver *driver, const char *name) +{ + struct drm_device *dev; + int ret; + + INIT_LIST_HEAD(&driver->device_list); + driver->bus = &drm_soc_bus; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->dev = drm_create_soc_device(name); + if (!dev->dev) { + ret = -ENOMEM; + goto free; + } + + ret = drm_fill_in_dev(dev, NULL, driver); + if (ret < 0) + goto unregister; + + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL); + if (ret < 0) + goto unregister; + } + + ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY); + if (ret < 0) + goto put_control; + + if (dev->driver->load) { + ret = dev->driver->load(dev, 0); + if (ret < 0) + goto put_legacy; + } + + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + ret = drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group); + if (ret < 0) + goto put_legacy; + } + + list_add_tail(&dev->driver_item, &driver->device_list); + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name, + driver->major, driver->minor, driver->patchlevel, + driver->date, dev->primary->index); + + return 0; + +put_legacy: + drm_put_minor(&dev->primary); +put_control: + if (drm_core_check_feature(dev, DRIVER_MODESET)) + drm_put_minor(&dev->control); +unregister: + drm_class_device_unregister(dev->dev); +free: + kfree(dev); + return ret; +} +EXPORT_SYMBOL_GPL(drm_soc_init); + +void drm_soc_exit(struct drm_driver *driver) +{ + struct drm_device *drm, *tmp; + + list_for_each_entry_safe(drm, tmp, &driver->device_list, driver_item) { + struct device *dev = drm->dev; + drm_put_dev(drm); + drm_class_device_unregister(dev); + } +} +EXPORT_SYMBOL_GPL(drm_soc_exit); + +MODULE_AUTHOR("Thierry Reding thierry.reding@avionic-design.de"); +MODULE_DESCRIPTION("SoC DRM support"); +MODULE_LICENSE("GPL"); diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 31ad880..bc1edd1 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -156,6 +156,7 @@ int drm_err(const char *func, const char *format, ...); #define DRIVER_BUS_PCI 0x1 #define DRIVER_BUS_PLATFORM 0x2 #define DRIVER_BUS_USB 0x3 +#define DRIVER_BUS_SOC 0x4
/***********************************************************************/ /** \name Begin the DRM... */ @@ -1769,6 +1770,10 @@ extern void drm_platform_exit(struct drm_driver *driver, struct platform_device extern int drm_get_platform_dev(struct platform_device *pdev, struct drm_driver *driver);
+/* SoC section */ +extern int drm_soc_init(struct drm_driver *driver, const char *name); +extern void drm_soc_exit(struct drm_driver *driver); + /* returns true if currently okay to sleep */ static __inline__ bool drm_can_sleep(void) {
This is a skeleton driver for the DRM/SoC framework.
Signed-off-by: Thierry Reding thierry.reding@avionic-design.de --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/soc/Kconfig | 13 ++++ drivers/gpu/drm/soc/Makefile | 6 ++ drivers/gpu/drm/soc/soc-drv.c | 166 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 188 insertions(+) create mode 100644 drivers/gpu/drm/soc/Kconfig create mode 100644 drivers/gpu/drm/soc/Makefile create mode 100644 drivers/gpu/drm/soc/soc-drv.c
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 529dede..a23b436 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -196,3 +196,5 @@ source "drivers/gpu/drm/ast/Kconfig" source "drivers/gpu/drm/mgag200/Kconfig"
source "drivers/gpu/drm/cirrus/Kconfig" + +source "drivers/gpu/drm/soc/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 4731cbc..b7dde4e 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -47,4 +47,5 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ +obj-$(CONFIG_DRM_SOC_TEST) += soc/ obj-y += i2c/ diff --git a/drivers/gpu/drm/soc/Kconfig b/drivers/gpu/drm/soc/Kconfig new file mode 100644 index 0000000..c983bfe --- /dev/null +++ b/drivers/gpu/drm/soc/Kconfig @@ -0,0 +1,13 @@ +config DRM_SOC_TEST + tristate "SoC DRM" + depends on DRM + select DRM_SOC + +if DRM_SOC_TEST + +config DRM_SOC_TEST_DEBUG + bool "SoC DRM debug support" + help + Say Y here to enable debugging support. + +endif diff --git a/drivers/gpu/drm/soc/Makefile b/drivers/gpu/drm/soc/Makefile new file mode 100644 index 0000000..c42f540 --- /dev/null +++ b/drivers/gpu/drm/soc/Makefile @@ -0,0 +1,6 @@ +ccflags-y := -Iinclude/drm +ccflags-$(CONFIG_DRM_SOC_TEST_DEBUG) += -DDEBUG + +soc-drm-y := soc-drv.o + +obj-$(CONFIG_DRM_SOC_TEST) += soc-drm.o diff --git a/drivers/gpu/drm/soc/soc-drv.c b/drivers/gpu/drm/soc/soc-drv.c new file mode 100644 index 0000000..90b1705 --- /dev/null +++ b/drivers/gpu/drm/soc/soc-drv.c @@ -0,0 +1,166 @@ +#include <linux/module.h> + +#include <drm/drmP.h> +#include <drm/drm_sysfs.h> + +static int soc_drm_load(struct drm_device *dev, unsigned long flags) +{ + int ret = 0; + + pr_debug("> %s(dev=%p, flags=%#lx)\n", __func__, dev, flags); + + drm_mode_config_init(dev); + + pr_debug("< %s() = %d\n", __func__, ret); + return ret; +} + +static int soc_drm_unload(struct drm_device *dev) +{ + int ret = 0; + + pr_debug("> %s(dev=%p)\n", __func__, dev); + + drm_mode_config_cleanup(dev); + + pr_debug("< %s() = %d\n", __func__, ret); + return ret; +} + +static int soc_drm_open(struct drm_device *dev, struct drm_file *file) +{ + int ret = 0; + pr_debug("> %s(dev=%p, file=%p)\n", __func__, dev, file); + pr_debug("< %s() = %d\n", __func__, ret); + return ret; +} + +static void soc_drm_preclose(struct drm_device *dev, struct drm_file *file) +{ + pr_debug("> %s(dev=%p, file=%p)\n", __func__, dev, file); + pr_debug("< %s()\n", __func__); +} + +static void soc_drm_postclose(struct drm_device *dev, struct drm_file *file) +{ + pr_debug("> %s(dev=%p, file=%p)\n", __func__, dev, file); + pr_debug("< %s()\n", __func__); +} + +static void soc_drm_lastclose(struct drm_device *dev) +{ + pr_debug("> %s(dev=%p)\n", __func__, dev); + pr_debug("< %s()\n", __func__); +} + +static int drm_soc_open(struct inode *inode, struct file *filp) +{ + int ret = 0; + pr_debug("> %s(inode=%p, filp=%p)\n", __func__, inode, filp); + ret = drm_open(inode, filp); + pr_debug("< %s() = %d\n", __func__, ret); + return ret; +} + +static int drm_soc_mmap(struct file *filp, struct vm_area_struct *vma) +{ + int ret = 0; + pr_debug("> %s(filp=%p, vma=%p)\n", __func__, filp, vma); + pr_debug("< %s() = %d\n", __func__, ret); + return ret; +} + +static unsigned int drm_soc_poll(struct file *filp, + struct poll_table_struct *wait) +{ + unsigned int ret = 0; + pr_debug("> %s(filp=%p, wait=%p)\n", __func__, filp, wait); + ret = drm_poll(filp, wait); + pr_debug("< %s() = %u\n", __func__, ret); + return ret; +} + +static ssize_t drm_soc_read(struct file *filp, char __user *buffer, + size_t count, loff_t *offset) +{ + ssize_t ret = 0; + pr_debug("> %s(filp=%p, buffer=%p, count=%zu, offset=%p)\n", __func__, + filp, buffer, count, offset); + ret = drm_read(filp, buffer, count, offset); + pr_debug("< %s() = %zd\n", __func__, ret); + return ret; +} + +static long drm_soc_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) +{ + long ret = 0; + pr_debug("> %s(filp=%p, cmd=%#x, arg=%#lx)\n", __func__, filp, cmd, + arg); + ret = drm_ioctl(filp, cmd, arg); + pr_debug("< %s() = %ld\n", __func__, ret); + return ret; +} + +static int drm_soc_release(struct inode *inode, struct file *filp) +{ + int ret = 0; + pr_debug("> %s(inode=%p, file=%p)\n", __func__, inode, filp); + ret = drm_release(inode, filp); + pr_debug("< %s() = %d\n", __func__, ret); + return ret; +} + +static const struct file_operations soc_drm_fops = { + .owner = THIS_MODULE, + .open = drm_soc_open, + .mmap = drm_soc_mmap, + .poll = drm_soc_poll, + .read = drm_soc_read, + .unlocked_ioctl = drm_soc_ioctl, + .release = drm_soc_release, +}; + +static struct drm_driver soc_drm = { + .driver_features = DRIVER_MODESET | DRIVER_GEM, + .load = soc_drm_load, + .unload = soc_drm_unload, + .open = soc_drm_open, + .preclose = soc_drm_preclose, + .lastclose = soc_drm_lastclose, + .postclose = soc_drm_postclose, + .fops = &soc_drm_fops, + .name = "soc-drm", + .desc = "SoC DRM driver", + .date = "20120706", + .major = 0, + .minor = 0, + .patchlevel = 0, +}; + +static int __init soc_drm_init(void) +{ + int ret = 0; + + pr_debug("> %s()\n", __func__); + + ret = drm_soc_init(&soc_drm, "soc"); + + pr_debug("< %s() = %d\n", __func__, ret); + return ret; +} +module_init(soc_drm_init); + +static void __exit soc_drm_exit(void) +{ + pr_debug("> %s()\n", __func__); + + drm_soc_exit(&soc_drm); + + pr_debug("< %s()\n", __func__); +} +module_exit(soc_drm_exit); + +MODULE_AUTHOR("Thierry Reding thierry.reding@avionic-design.de"); +MODULE_DESCRIPTION("SoC DRM driver"); +MODULE_LICENSE("GPL");
dri-devel@lists.freedesktop.org