On Fri, Sep 2, 2016 at 10:22 AM, David Herrmann <dh.herrmann at 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 dumb-buffers which can be blit into the real > framebuffer similar to UDL. No access to the real framebuffer is allowed > (compared to earlier version of this driver) to avoid security issues. > Furthermore, this way we can support arbitrary modes as long as we have a > conversion-helper. > > Signed-off-by: David Herrmann <dh.herrmann at gmail.com> > --- > MAINTAINERS | 6 + > drivers/gpu/drm/Kconfig | 2 + > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/simpledrm/Kconfig | 19 ++ > drivers/gpu/drm/simpledrm/Makefile | 8 + > drivers/gpu/drm/simpledrm/simpledrm.h | 83 +++++ > drivers/gpu/drm/simpledrm/simpledrm_damage.c | 194 +++++++++++ > drivers/gpu/drm/simpledrm/simpledrm_drv.c | 464 > +++++++++++++++++++++++++++ > drivers/gpu/drm/simpledrm/simpledrm_gem.c | 109 +++++++ > drivers/gpu/drm/simpledrm/simpledrm_kms.c | 263 +++++++++++++++ > drivers/gpu/drm/simpledrm/simpledrm_of.c | 265 +++++++++++++++ > 11 files changed, 1414 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_damage.c > create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_drv.c > create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_gem.c > create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_kms.c > create mode 100644 drivers/gpu/drm/simpledrm/simpledrm_of.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 0bbe4b1..408863d 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -4140,6 +4140,12 @@ S: Orphan / Obsolete > F: drivers/gpu/drm/savage/ > F: include/uapi/drm/savage_drm.h > > +DRM DRIVER FOR SIMPLE FRAMEBUFFER DEVICES > +M: David Herrmann <dh.herrmann at gmail.com> > +L: dri-devel at lists.freedesktop.org > +S: Maintained > +F: drivers/gpu/drm/simpledrm/ > + > DRM DRIVER FOR SIS VIDEO CARDS > S: Orphan / Obsolete > F: drivers/gpu/drm/sis/ > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index f27f9b5..61cbcd1 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -291,3 +291,5 @@ source "drivers/gpu/drm/arc/Kconfig" > source "drivers/gpu/drm/hisilicon/Kconfig" > > source "drivers/gpu/drm/mediatek/Kconfig" > + > +source "drivers/gpu/drm/simpledrm/Kconfig" > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile > index 0238bf8..3e6fe99 100644 > --- a/drivers/gpu/drm/Makefile > +++ b/drivers/gpu/drm/Makefile > @@ -83,3 +83,4 @@ obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu/ > obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ > obj-$(CONFIG_DRM_ARCPGU)+= arc/ > obj-y += hisilicon/ > +obj-y += simpledrm/ > diff --git a/drivers/gpu/drm/simpledrm/Kconfig > b/drivers/gpu/drm/simpledrm/Kconfig > new file mode 100644 > index 0000000..f45b25d > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/Kconfig > @@ -0,0 +1,19 @@ > +config DRM_SIMPLEDRM > + tristate "Simple firmware framebuffer DRM driver" > + depends on DRM > + select DRM_KMS_HELPER > + 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..d7b179d > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/Makefile > @@ -0,0 +1,8 @@ > +simpledrm-y := \ > + simpledrm_damage.o \ > + simpledrm_drv.o \ > + simpledrm_gem.o \ > + simpledrm_kms.o \ > + simpledrm_of.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..ed6d725 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm.h > @@ -0,0 +1,83 @@ > +#ifndef __SDRM_SIMPLEDRM_H > +#define __SDRM_SIMPLEDRM_H > + > +/* > + * Copyright (C) 2012-2016 Red Hat, Inc. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU Lesser General Public License as published by > the > + * Free Software Foundation; either version 2.1 of the License, or (at your > + * option) any later version. > + */ > + > +#include <drm/drmP.h> > +#include <drm/drm_crtc.h> > +#include <drm/drm_gem.h> > +#include <drm/drm_simple_kms_helper.h> > +#include <linux/atomic.h> > +#include <linux/kernel.h> > +#include <linux/mutex.h> > + > +struct clk; > +struct regulator; > +struct simplefb_format; > + > +struct sdrm_hw { > + struct mutex lock; > + u32 width; > + u32 height; > + u32 stride; > + u32 bpp; > + u32 format; > + unsigned long base; > + unsigned long size; > + void *map; > +}; > + > +struct sdrm_bo { > + struct drm_gem_object base; > + struct page **pages; > + void *vmapping; > +}; > + > +struct sdrm_fb { > + struct drm_framebuffer base; > + struct sdrm_bo *bo; > +}; > + > +struct sdrm_device { > + atomic_t n_used; > + struct drm_device *ddev; > + struct sdrm_hw *hw; > + > + size_t n_clks; > + size_t n_regulators; > + struct clk **clks; > + struct regulator **regulators; > + > + struct drm_simple_display_pipe pipe; > + struct drm_connector conn; > +}; > + > +void sdrm_of_bootstrap(void); > +int sdrm_of_bind(struct sdrm_device *sdrm); > +void sdrm_of_unbind(struct sdrm_device *sdrm); > + > +int sdrm_kms_bind(struct sdrm_device *sdrm); > +void sdrm_kms_unbind(struct sdrm_device *sdrm); > + > +void sdrm_dirty(struct sdrm_fb *fb, u32 x, u32 y, u32 width, u32 height); > + > +struct sdrm_bo *sdrm_bo_new(struct drm_device *ddev, size_t size); > +void sdrm_bo_free(struct drm_gem_object *obj); > +int sdrm_bo_vmap(struct sdrm_bo *bo); > + > +int sdrm_dumb_create(struct drm_file *dfile, > + struct drm_device *ddev, > + struct drm_mode_create_dumb *arg); > +int sdrm_dumb_map_offset(struct drm_file *dfile, > + struct drm_device *ddev, > + uint32_t handle, > + uint64_t *offset); > + > +#endif /* __SDRM_SIMPLEDRM_H */ > diff --git a/drivers/gpu/drm/simpledrm/simpledrm_damage.c > b/drivers/gpu/drm/simpledrm/simpledrm_damage.c > new file mode 100644 > index 0000000..4f7af5d > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c > @@ -0,0 +1,194 @@ > +/* > + * Copyright (C) 2012-2016 Red Hat, Inc. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU Lesser General Public License as published by > the > + * Free Software Foundation; either version 2.1 of the License, or (at your > + * option) any later version. > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > +#include <asm/unaligned.h> > +#include <drm/drmP.h> > +#include <drm/drm_crtc.h> > +#include <linux/kernel.h> > +#include "simpledrm.h" > + > +static inline void sdrm_put(u8 *dst, u32 four_cc, u16 r, u16 g, u16 b) > +{ > + switch (four_cc) { > + case DRM_FORMAT_RGB565: > + r >>= 11; > + g >>= 10; > + b >>= 11; > + put_unaligned((u16)((r << 11) | (g << 5) | b), (u16 *)dst); > + break; > + case DRM_FORMAT_XRGB1555: > + case DRM_FORMAT_ARGB1555: > + r >>= 11; > + g >>= 11; > + b >>= 11; > + put_unaligned((u16)((r << 10) | (g << 5) | b), (u16 *)dst); > + break; > + case DRM_FORMAT_RGB888: > + r >>= 8; > + g >>= 8; > + b >>= 8; > +#ifdef __LITTLE_ENDIAN > + dst[2] = r; > + dst[1] = g; > + dst[0] = b; > +#elif defined(__BIG_ENDIAN) > + dst[0] = r; > + dst[1] = g; > + dst[2] = b; > +#endif > + break; > + case DRM_FORMAT_XRGB8888: > + case DRM_FORMAT_ARGB8888: > + r >>= 8; > + g >>= 8; > + b >>= 8; > + put_unaligned((u32)((r << 16) | (g << 8) | b), (u32 *)dst); > + break; > + case DRM_FORMAT_ABGR8888: > + r >>= 8; > + g >>= 8; > + b >>= 8; > + put_unaligned((u32)((b << 16) | (g << 8) | r), (u32 *)dst); > + break; > + case DRM_FORMAT_XRGB2101010: > + case DRM_FORMAT_ARGB2101010: > + r >>= 4; > + g >>= 4; > + b >>= 4; > + put_unaligned((u32)((r << 20) | (g << 10) | b), (u32 *)dst); > + break; > + } > +} > + > +static void sdrm_blit_from_xrgb8888(const u8 *src, > + u32 src_stride, > + u32 src_bpp, > + u8 *dst, > + u32 dst_stride, > + u32 dst_bpp, > + u32 dst_four_cc, > + u32 width, > + u32 height) > +{ > + u32 val, i; > + > + while (height--) { > + for (i = 0; i < width; ++i) { > + val = get_unaligned((const u32 *)&src[i * src_bpp]); > + sdrm_put(&dst[i * dst_bpp], dst_four_cc, > + (val & 0x00ff0000U) >> 8, > + (val & 0x0000ff00U), > + (val & 0x000000ffU) << 8); > + } > + > + src += src_stride; > + dst += dst_stride; > + } > +} > + > +static void sdrm_blit_from_rgb565(const u8 *src, > + u32 src_stride, > + u32 src_bpp, > + u8 *dst, > + u32 dst_stride, > + u32 dst_bpp, > + u32 dst_four_cc, > + u32 width, > + u32 height) > +{ > + u32 val, i; > + > + while (height--) { > + for (i = 0; i < width; ++i) { > + val = get_unaligned((const u16 *)&src[i * src_bpp]); > + sdrm_put(&dst[i * dst_bpp], dst_four_cc, > + (val & 0xf800), > + (val & 0x07e0) << 5, > + (val & 0x001f) << 11); > + } > + > + src += src_stride; > + dst += dst_stride; > + } > +} > + > +static void sdrm_blit_lines(const u8 *src, > + u32 src_stride, > + u8 *dst, > + u32 dst_stride, > + u32 bpp, > + u32 width, > + u32 height) > +{ > + u32 len; > + > + len = width * bpp; > + > + while (height--) { > + memcpy(dst, src, len); > + src += src_stride; > + dst += dst_stride; > + } > +} > + > +static void sdrm_blit(struct sdrm_hw *hw, > + struct sdrm_fb *fb, > + u32 x, > + u32 y, > + u32 width, > + u32 height) > +{ > + u32 src_bpp, dst_bpp; > + u8 *src, *dst; > + > + src = fb->bo->vmapping; > + src_bpp = DIV_ROUND_UP(fb->base.bits_per_pixel, 8); > + src += fb->base.offsets[0] + y * fb->base.pitches[0] + x * src_bpp; > + > + dst = hw->map; > + dst_bpp = DIV_ROUND_UP(hw->bpp, 8); > + dst += y * hw->stride + x * dst_bpp; > + > + if (fb->base.pixel_format == hw->format) { > + /* if formats are identical, do a line-by-line copy.. */ > + sdrm_blit_lines(src, fb->base.pitches[0], > + dst, hw->stride, src_bpp, width, height); > + } else { > + /* ..otherwise call slow blit-function */ > + switch (fb->base.pixel_format) { > + case DRM_FORMAT_ARGB8888: > + case DRM_FORMAT_XRGB8888: > + sdrm_blit_from_xrgb8888(src, fb->base.pitches[0], > + src_bpp, dst, hw->stride, > + dst_bpp, hw->format, > + width, height); > + break; > + case DRM_FORMAT_RGB565: > + sdrm_blit_from_rgb565(src, fb->base.pitches[0], > + src_bpp, dst, hw->stride, > + dst_bpp, hw->format, > + width, height); > + break; > + } > + } > +} > + > +void sdrm_dirty(struct sdrm_fb *fb, u32 x, u32 y, u32 width, u32 height) > +{ > + struct sdrm_device *sdrm = fb->base.dev->dev_private; > + > + if (WARN_ON(!fb->bo->vmapping)) > + return; > + > + mutex_lock(&sdrm->hw->lock); > + if (sdrm->hw->map) > + sdrm_blit(sdrm->hw, fb, x, y, width, height); > + mutex_unlock(&sdrm->hw->lock); > +} > diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c > b/drivers/gpu/drm/simpledrm/simpledrm_drv.c > new file mode 100644 > index 0000000..d569120 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c > @@ -0,0 +1,464 @@ > +/* > + * Copyright (C) 2012-2016 Red Hat, Inc. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU Lesser General Public License as published by > the > + * Free Software Foundation; either version 2.1 of the License, or (at your > + * option) any later version. > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > +#include <drm/drmP.h> > +#include <linux/atomic.h> > +#include <linux/err.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/mutex.h> > +#include <linux/of.h> > +#include <linux/of_platform.h> > +#include <linux/platform_data/simplefb.h> > +#include <linux/string.h> > +#include "simpledrm.h" > + > +static struct drm_driver sdrm_drm_driver; > +static DEFINE_MUTEX(sdrm_lock); > + > +static int sdrm_hw_identify(struct platform_device *pdev, > + struct simplefb_platform_data *modep, > + struct simplefb_format *formatp, > + struct resource **memp, > + u32 *bppp) > +{ > + static const struct simplefb_format valid_formats[] = > SIMPLEFB_FORMATS; > + struct simplefb_platform_data pm = {}, *mode = > pdev->dev.platform_data; > + struct device_node *np = pdev->dev.of_node; > + const struct simplefb_format *format = NULL; > + struct resource *mem; > + unsigned int depth; > + int r, bpp; > + size_t i; > + > + if (!mode) { > + if (!np) > + return -ENODEV; > + > + mode = ± > + > + r = of_property_read_u32(np, "width", &mode->width); > + if (r >= 0) > + r = of_property_read_u32(np, "height", &mode->height); > + if (r >= 0) > + r = of_property_read_u32(np, "stride", &mode->stride); > + if (r >= 0) > + r = of_property_read_string(np, "format", > + &mode->format); > + if (r < 0) > + return r; > + } > + > + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!mem) > + return -ENODEV; > + > + for (i = 0; i < ARRAY_SIZE(valid_formats); ++i) { > + if (!strcmp(mode->format, valid_formats[i].name)) { > + format = &valid_formats[i]; > + break; > + } > + } > + > + if (!format) > + return -ENODEV; > + > + switch (format->fourcc) { > + case DRM_FORMAT_RGB565: > + case DRM_FORMAT_XRGB1555: > + case DRM_FORMAT_ARGB1555: > + case DRM_FORMAT_RGB888: > + case DRM_FORMAT_XRGB8888: > + case DRM_FORMAT_ARGB8888: > + case DRM_FORMAT_ABGR8888: > + case DRM_FORMAT_XRGB2101010: > + case DRM_FORMAT_ARGB2101010: > + /* > + * You must adjust sdrm_put() whenever you add a new format > + * here, otherwise, blitting operations will not work. > + * Furthermore, include/linux/platform_data/simplefb.h needs > + * to be adjusted so the platform-device actually allows this > + * format. > + */ > + break; > + default: > + return -ENODEV; > + } > + > + drm_fb_get_bpp_depth(format->fourcc, &depth, &bpp); > + if (!bpp) > + return -ENODEV; > + if (resource_size(mem) < mode->stride * mode->height) > + return -ENODEV; > + if ((bpp + 7) / 8 * mode->width > mode->stride)
DIV_ROUND_UP? > + return -ENODEV; > + > + *modep = *mode; > + *formatp = *format; > + *memp = mem; > + *bppp = bpp; > + return 0; > +} > + > +static struct sdrm_hw *sdrm_hw_new(const struct simplefb_platform_data *mode, > + const struct simplefb_format *format, > + const struct resource *mem, > + u32 bpp) > +{ > + struct sdrm_hw *hw; > + > + hw = kzalloc(sizeof(*hw), GFP_KERNEL); > + if (!hw) > + return ERR_PTR(-ENOMEM); > + > + mutex_init(&hw->lock); > + hw->width = mode->width; > + hw->height = mode->height; > + hw->stride = mode->stride; > + hw->bpp = bpp; > + hw->format = format->fourcc; > + hw->base = mem->start; > + hw->size = resource_size(mem); > + > + return hw; > +} > + > +static struct sdrm_hw *sdrm_hw_free(struct sdrm_hw *hw) > +{ > + if (!hw) > + return NULL; > + > + WARN_ON(hw->map); > + mutex_destroy(&hw->lock); > + kfree(hw); > + > + return NULL; > +} > + > +static int sdrm_hw_bind(struct sdrm_hw *hw) > +{ > + mutex_lock(&hw->lock); > + if (!hw->map) > + hw->map = ioremap_wc(hw->base, hw->size); > + mutex_unlock(&hw->lock); > + > + return hw->map ? 0 : -EIO; > +} > + > +static void sdrm_hw_unbind(struct sdrm_hw *hw) > +{ > + if (!hw) > + return; > + > + mutex_lock(&hw->lock); > + if (hw->map) { > + iounmap(hw->map); > + hw->map = NULL; > + } > + mutex_unlock(&hw->lock); > +} > + > +static struct sdrm_device *sdrm_device_free(struct sdrm_device *sdrm) > +{ > + if (!sdrm) > + return NULL; > + > + WARN_ON(atomic_read(&sdrm->n_used) != INT_MIN); > + sdrm->hw = sdrm_hw_free(sdrm->hw); > + drm_dev_unref(sdrm->ddev); > + kfree(sdrm); > + > + return NULL; > +} > + > +static struct sdrm_device *sdrm_device_new(struct platform_device *pdev, > + struct sdrm_hw *hw) > +{ > + struct sdrm_device *sdrm; > + int r; > + > + sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL); > + if (!sdrm) > + return ERR_PTR(-ENOMEM); > + > + atomic_set(&sdrm->n_used, INT_MIN); > + > + sdrm->ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev); > + if (!sdrm->ddev) { > + r = -ENOMEM; > + goto error; > + } > + > + sdrm->ddev->dev_private = sdrm; > + sdrm->hw = hw; > + > + return sdrm; > + > +error: > + sdrm_device_free(sdrm); > + return ERR_PTR(r); > +} > + > +static void sdrm_device_unbind(struct sdrm_device *sdrm) > +{ > + if (sdrm) { > + sdrm_kms_unbind(sdrm); > + sdrm_hw_unbind(sdrm->hw); > + sdrm_of_unbind(sdrm); > + } > +} > + > +static int sdrm_device_bind(struct sdrm_device *sdrm) > +{ > + int r; > + > + r = sdrm_of_bind(sdrm); > + if (r < 0) > + goto error; > + > + r = sdrm_hw_bind(sdrm->hw); > + if (r < 0) > + goto error; > + > + r = sdrm_kms_bind(sdrm); > + if (r < 0) > + goto error; > + > + return 0; > + > +error: > + sdrm_device_unbind(sdrm); > + return r; > +} > + > +static int sdrm_device_acquire(struct sdrm_device *sdrm) > +{ > + return (sdrm && atomic_inc_unless_negative(&sdrm->n_used)) > + ? 0 : -ENODEV; > +} > + > +static void sdrm_device_release(struct sdrm_device *sdrm) > +{ > + if (sdrm && atomic_dec_return(&sdrm->n_used) == INT_MIN) { > + sdrm_device_unbind(sdrm); > + sdrm_device_free(sdrm); > + } > +} > + > +static int sdrm_fop_open(struct inode *inode, struct file *file) > +{ > + struct drm_device *ddev; > + int r; > + > + mutex_lock(&sdrm_lock); > + r = drm_open(inode, file); > + if (r >= 0) { > + ddev = file->private_data; > + r = sdrm_device_acquire(ddev->dev_private); > + if (r < 0) > + drm_release(inode, file); > + } > + mutex_unlock(&sdrm_lock); > + > + return r; > +} > + > +static int sdrm_fop_release(struct inode *inode, struct file *file) > +{ > + struct drm_file *dfile = file->private_data; > + struct drm_device *ddev = dfile->minor->dev; > + struct sdrm_device *sdrm = ddev->dev_private; > + int res; > + > + res = drm_release(inode, file); > + sdrm_device_release(sdrm); > + return res; > +} > + > +static int sdrm_fop_mmap(struct file *file, struct vm_area_struct *vma) > +{ > + struct drm_file *dfile = file->private_data; > + struct drm_device *dev = dfile->minor->dev; > + struct drm_gem_object *obj = NULL; > + struct drm_vma_offset_node *node; > + int r; > + > + drm_vma_offset_lock_lookup(dev->vma_offset_manager); > + node = drm_vma_offset_exact_lookup_locked(dev->vma_offset_manager, > + vma->vm_pgoff, > + vma_pages(vma)); > + if (likely(node)) { > + obj = container_of(node, struct drm_gem_object, vma_node); > + if (!kref_get_unless_zero(&obj->refcount)) > + obj = NULL; > + } > + drm_vma_offset_unlock_lookup(dev->vma_offset_manager); > + > + if (!obj) > + return -EINVAL; > + > + if (!drm_vma_node_is_allowed(node, dfile)) { > + drm_gem_object_unreference_unlocked(obj); > + return -EACCES; > + } > + > + if (vma->vm_file) > + fput(vma->vm_file); > + vma->vm_file = get_file(obj->filp); > + vma->vm_pgoff = 0; > + > + r = obj->filp->f_op->mmap(obj->filp, vma); > + drm_gem_object_unreference_unlocked(obj); > + return r; > +} > + > +static int sdrm_simplefb_probe(struct platform_device *pdev) > +{ > + struct simplefb_platform_data hw_mode; > + struct simplefb_format hw_format; > + struct sdrm_device *sdrm = NULL; > + struct sdrm_hw *hw = NULL; > + struct resource *hw_mem; > + u32 hw_bpp; > + int r; > + > + r = sdrm_hw_identify(pdev, &hw_mode, &hw_format, &hw_mem, &hw_bpp); > + if (r < 0) > + goto error; > + > + hw = sdrm_hw_new(&hw_mode, &hw_format, hw_mem, hw_bpp); > + if (IS_ERR(hw)) { > + r = PTR_ERR(hw); > + hw = NULL; > + goto error; > + } > + > + sdrm = sdrm_device_new(pdev, hw); > + if (IS_ERR(sdrm)) { > + r = PTR_ERR(sdrm); > + sdrm = NULL; > + goto error; > + } > + hw = NULL; > + platform_set_drvdata(pdev, sdrm); > + > + r = sdrm_device_bind(sdrm); > + if (r < 0) > + goto error; > + > + /* mark device as enabled and acquire bus ref */ > + WARN_ON(atomic_read(&sdrm->n_used) != INT_MIN); > + atomic_set(&sdrm->n_used, 1); > + > + r = drm_dev_register(sdrm->ddev, 0); > + if (r < 0) { > + /* mark device as disabled and drop bus ref */ > + WARN_ON(atomic_add_return(INT_MIN, &sdrm->n_used) != INT_MIN); > + sdrm_device_release(sdrm); > + return r; > + } > + > + dev_info(sdrm->ddev->dev, "initialized %s on minor %d\n", > + sdrm->ddev->driver->name, sdrm->ddev->primary->index); > + > + return 0; > + > +error: > + sdrm_device_unbind(sdrm); > + sdrm_device_free(sdrm); > + sdrm_hw_free(hw); > + return r; > +} > + > +static int sdrm_simplefb_remove(struct platform_device *pdev) > +{ > + struct sdrm_device *sdrm = platform_get_drvdata(pdev); > + > + /* mark device as disabled */ > + atomic_add(INT_MIN, &sdrm->n_used); > + sdrm_hw_unbind(sdrm->hw); > + > + mutex_lock(&sdrm_lock); > + drm_dev_unregister(sdrm->ddev); > + sdrm_device_release(sdrm); > + mutex_unlock(&sdrm_lock); > + > + return 0; > +} > + > +static const struct file_operations sdrm_drm_fops = { > + .owner = THIS_MODULE, > + .open = sdrm_fop_open, > + .release = sdrm_fop_release, > + .mmap = sdrm_fop_mmap, > + .poll = drm_poll, > + .read = drm_read, > + .unlocked_ioctl = drm_ioctl, > +#ifdef CONFIG_COMPAT > + .compat_ioctl = drm_compat_ioctl, > +#endif > + .llseek = noop_llseek, > +}; > + > +static struct drm_driver sdrm_drm_driver = { > + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, > + .fops = &sdrm_drm_fops, > + > + .gem_free_object = sdrm_bo_free, > + > + .dumb_create = sdrm_dumb_create, > + .dumb_map_offset = sdrm_dumb_map_offset, > + .dumb_destroy = drm_gem_dumb_destroy, > + > + .name = "simpledrm", > + .desc = "Simple firmware framebuffer DRM driver", > + .date = "20160901", > +}; > + > +static const struct of_device_id sdrm_simplefb_of_match[] = { > + { .compatible = "simple-framebuffer", }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, sdrm_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 = sdrm_simplefb_of_match, > + }, > +}; > + > +static int __init sdrm_init(void) > +{ > + int r; > + > + r = platform_driver_register(&sdrm_simplefb_driver); > + if (r < 0) > + return r; > + > + sdrm_of_bootstrap(); > + return 0; > +} > + > +static void __exit sdrm_exit(void) > +{ > + platform_driver_unregister(&sdrm_simplefb_driver); > +} > + > +module_init(sdrm_init); > +module_exit(sdrm_exit); > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("Simple firmware framebuffer DRM driver"); > +MODULE_ALIAS("platform:simple-framebuffer"); > diff --git a/drivers/gpu/drm/simpledrm/simpledrm_gem.c > b/drivers/gpu/drm/simpledrm/simpledrm_gem.c > new file mode 100644 > index 0000000..4aaae6e > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c > @@ -0,0 +1,109 @@ > +/* > + * Copyright (C) 2012-2016 Red Hat, Inc. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU Lesser General Public License as published by > the > + * Free Software Foundation; either version 2.1 of the License, or (at your > + * option) any later version. > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > +#include <drm/drmP.h> > +#include <drm/drm_gem.h> > +#include <linux/err.h> > +#include <linux/kernel.h> > +#include <linux/slab.h> > +#include <linux/vmalloc.h> > +#include "simpledrm.h" > + > +struct sdrm_bo *sdrm_bo_new(struct drm_device *ddev, size_t size) > +{ > + struct sdrm_bo *bo; > + > + WARN_ON(!size || (size & ~PAGE_MASK) != 0); > + > + bo = kzalloc(sizeof(*bo), GFP_KERNEL); > + if (!bo) > + return NULL; > + > + if (drm_gem_object_init(ddev, &bo->base, size)) { > + kfree(bo); > + return NULL; > + } > + > + return bo; > +} > + > +void sdrm_bo_free(struct drm_gem_object *dobj) > +{ > + struct sdrm_bo *bo = container_of(dobj, struct sdrm_bo, base); > + > + if (bo->vmapping) > + vunmap(bo->vmapping); > + if (bo->pages) > + drm_gem_put_pages(dobj, bo->pages, false, false); > + drm_gem_object_release(dobj); > + kfree(bo); > +} > + > +int sdrm_bo_vmap(struct sdrm_bo *bo) > +{ > + int r; > + > + if (!bo->pages) { > + bo->pages = drm_gem_get_pages(&bo->base); > + if (IS_ERR(bo->pages)) { > + r = PTR_ERR(bo->pages); > + bo->pages = NULL; > + return r; > + } > + } > + > + if (!bo->vmapping) { > + bo->vmapping = vmap(bo->pages, bo->base.size / PAGE_SIZE, 0, > + PAGE_KERNEL); > + if (!bo->vmapping) > + return -ENOMEM; > + } > + > + return 0; > +} > + > +int sdrm_dumb_create(struct drm_file *dfile, > + struct drm_device *ddev, > + struct drm_mode_create_dumb *args) > +{ > + struct sdrm_bo *bo; > + int r; > + > + /* overflow checks are done by DRM core */ > + args->pitch = (args->bpp + 7) / 8 * args->width; DIV_ROUND_UP? > + args->size = PAGE_ALIGN(args->pitch * args->height); > + > + bo = sdrm_bo_new(ddev, args->size); > + if (!bo) > + return -ENOMEM; > + > + r = drm_gem_handle_create(dfile, &bo->base, &args->handle); > + drm_gem_object_unreference_unlocked(&bo->base); > + return r; > +} > + > +int sdrm_dumb_map_offset(struct drm_file *dfile, > + struct drm_device *ddev, > + uint32_t handle, > + uint64_t *offset) > +{ > + struct drm_gem_object *dobj; > + int r; > + > + dobj = drm_gem_object_lookup(dfile, handle); > + if (!dobj) > + return -ENOENT; > + > + r = drm_gem_create_mmap_offset(dobj); > + if (r >= 0) > + *offset = drm_vma_node_offset_addr(&dobj->vma_node); > + drm_gem_object_unreference_unlocked(dobj); > + return r; > +} > diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c > b/drivers/gpu/drm/simpledrm/simpledrm_kms.c > new file mode 100644 > index 0000000..00101c9 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c > @@ -0,0 +1,263 @@ > +/* > + * Copyright (C) 2012-2016 Red Hat, Inc. > + * Copyright (C) 2016 Noralf Trønnes > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU Lesser General Public License as published by > the > + * Free Software Foundation; either version 2.1 of the License, or (at your > + * option) any later version. > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > +#include <drm/drmP.h> > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_crtc.h> > +#include <drm/drm_crtc_helper.h> > +#include <drm/drm_gem.h> > +#include <drm/drm_simple_kms_helper.h> > +#include <linux/err.h> > +#include <linux/kernel.h> > +#include <linux/slab.h> > +#include "simpledrm.h" > + > +static const uint32_t sdrm_formats[] = { > + DRM_FORMAT_RGB565, > + DRM_FORMAT_ARGB8888, > + DRM_FORMAT_XRGB8888, > +}; > + > +static int sdrm_conn_get_modes(struct drm_connector *conn) > +{ > + struct sdrm_device *sdrm = conn->dev->dev_private; > + struct drm_display_mode *mode; > + > + mode = drm_cvt_mode(sdrm->ddev, sdrm->hw->width, sdrm->hw->height, > + 60, false, false, false); > + if (!mode) > + return 0; > + > + mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; > + drm_mode_set_name(mode); > + drm_mode_probed_add(conn, mode); > + > + return 1; > +} > + > +static const struct drm_connector_helper_funcs sdrm_conn_hfuncs = { > + .get_modes = sdrm_conn_get_modes, > + .best_encoder = drm_atomic_helper_best_encoder, > +}; > + > +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 const struct drm_connector_funcs sdrm_conn_ops = { > + .dpms = drm_atomic_helper_connector_dpms, > + .reset = drm_atomic_helper_connector_reset, > + .detect = sdrm_conn_detect, > + .fill_modes = drm_helper_probe_single_connector_modes, > + .destroy = drm_connector_cleanup, > + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, > + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, > +}; > + > +static void sdrm_crtc_send_vblank_event(struct drm_crtc *crtc) > +{ > + if (crtc->state && crtc->state->event) { > + spin_lock_irq(&crtc->dev->event_lock); > + drm_crtc_send_vblank_event(crtc, crtc->state->event); > + spin_unlock_irq(&crtc->dev->event_lock); > + crtc->state->event = NULL; > + } > +} > + > +void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe, > + struct drm_plane_state *plane_state) > +{ > + struct drm_framebuffer *dfb = pipe->plane.state->fb; > + struct sdrm_fb *fb; > + > + sdrm_crtc_send_vblank_event(&pipe->crtc); > + > + if (dfb) { > + fb = container_of(dfb, struct sdrm_fb, base); > + pipe->plane.fb = dfb; > + sdrm_dirty(fb, 0, 0, dfb->width, dfb->height); > + } > +} > + > +static void sdrm_display_pipe_enable(struct drm_simple_display_pipe *pipe, > + struct drm_crtc_state *crtc_state) > +{ > + sdrm_crtc_send_vblank_event(&pipe->crtc); > +} > + > +static void sdrm_display_pipe_disable(struct drm_simple_display_pipe *pipe) > +{ > + sdrm_crtc_send_vblank_event(&pipe->crtc); > +} > + > +static const struct drm_simple_display_pipe_funcs sdrm_pipe_funcs = { > + .update = sdrm_display_pipe_update, > + .enable = sdrm_display_pipe_enable, > + .disable = sdrm_display_pipe_disable, > +}; > + > +static int sdrm_fb_create_handle(struct drm_framebuffer *dfb, > + struct drm_file *dfile, > + unsigned int *handle) > +{ > + struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base); > + > + return drm_gem_handle_create(dfile, &fb->bo->base, handle); > +} > + > +static int sdrm_fb_dirty(struct drm_framebuffer *dfb, > + struct drm_file *dfile, > + unsigned int flags, > + unsigned int color, > + struct drm_clip_rect *clips, > + unsigned int n_clips) > +{ > + struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base); > + struct sdrm_device *sdrm = dfb->dev->dev_private; > + unsigned int i; > + > + drm_modeset_lock_all(sdrm->ddev); > + if (dfb == sdrm->pipe.plane.fb) { > + if (!clips || !n_clips) { > + sdrm_dirty(fb, 0, 0, dfb->width, dfb->height); > + } else { > + for (i = 0; i < n_clips; i++) { > + if (clips[i].x1 > clips[i].x2 || > + clips[i].x2 > dfb->width || > + clips[i].y1 > clips[i].y2 || > + clips[i].y2 > dfb->height) As discussed in private, overlapping clip_rects should be cropped to fit the fb, rather than discarded. > + continue; > + > + sdrm_dirty(fb, clips[i].x1, clips[i].y1, > + clips[i].x2 - clips[i].x1, > + clips[i].y2 - clips[i].y1); > + } > + } > + } > + drm_modeset_unlock_all(sdrm->ddev); > + > + return 0; > +} > + > +static void sdrm_fb_destroy(struct drm_framebuffer *dfb) > +{ > + struct sdrm_fb *fb = container_of(dfb, struct sdrm_fb, base); > + > + drm_framebuffer_cleanup(dfb); > + drm_gem_object_unreference_unlocked(&fb->bo->base); > + kfree(fb); > +} > + > +static const struct drm_framebuffer_funcs sdrm_fb_ops = { > + .create_handle = sdrm_fb_create_handle, > + .dirty = sdrm_fb_dirty, > + .destroy = sdrm_fb_destroy, > +}; > + > +static struct drm_framebuffer * > +sdrm_fb_create(struct drm_device *ddev, > + struct drm_file *dfile, > + const struct drm_mode_fb_cmd2 *cmd) > +{ > + struct drm_gem_object *dobj; > + struct sdrm_fb *fb = NULL; > + struct sdrm_bo *bo; > + int r; > + > + if (cmd->flags) > + return ERR_PTR(-EINVAL); > + > + dobj = drm_gem_object_lookup(dfile, cmd->handles[0]); > + if (!dobj) > + return ERR_PTR(-EINVAL); > + > + bo = container_of(dobj, struct sdrm_bo, base); > + > + r = sdrm_bo_vmap(bo); > + if (r < 0) > + goto error; > + > + fb = kzalloc(sizeof(*fb), GFP_KERNEL); > + if (!fb) { > + r = -ENOMEM; > + goto error; > + } > + > + fb->bo = bo; > + drm_helper_mode_fill_fb_struct(&fb->base, cmd); > + > + r = drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops); > + if (r < 0) > + goto error; > + > + DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id, > + drm_get_format_name(fb->base.pixel_format)); > + > + return &fb->base; > + > +error: > + kfree(fb); > + drm_gem_object_unreference_unlocked(dobj); > + return ERR_PTR(r); > +} > + > +static const struct drm_mode_config_funcs sdrm_mode_config_ops = { > + .fb_create = sdrm_fb_create, > + .atomic_check = drm_atomic_helper_check, > + .atomic_commit = drm_atomic_helper_commit, > +}; > + > +int sdrm_kms_bind(struct sdrm_device *sdrm) > +{ > + struct drm_connector *conn = &sdrm->conn; > + struct drm_device *ddev = sdrm->ddev; > + int r; > + > + drm_mode_config_init(ddev); > + ddev->mode_config.min_width = sdrm->hw->width; > + ddev->mode_config.max_width = sdrm->hw->width; > + ddev->mode_config.min_height = sdrm->hw->height; > + ddev->mode_config.max_height = sdrm->hw->height; > + ddev->mode_config.preferred_depth = sdrm->hw->bpp; > + ddev->mode_config.funcs = &sdrm_mode_config_ops; > + drm_connector_helper_add(conn, &sdrm_conn_hfuncs); > + > + r = drm_connector_init(ddev, conn, &sdrm_conn_ops, > + DRM_MODE_CONNECTOR_VIRTUAL); > + if (r < 0) > + goto error; > + > + r = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs, > + sdrm_formats, > + ARRAY_SIZE(sdrm_formats), conn); > + if (r < 0) > + goto error; > + > + drm_mode_config_reset(ddev); > + return 0; > + > +error: > + drm_mode_config_cleanup(ddev); > + return r; > +} > + > +void sdrm_kms_unbind(struct sdrm_device *sdrm) > +{ > + if (sdrm->ddev->mode_config.funcs) > + drm_mode_config_cleanup(sdrm->ddev); > +} > diff --git a/drivers/gpu/drm/simpledrm/simpledrm_of.c > b/drivers/gpu/drm/simpledrm/simpledrm_of.c > new file mode 100644 > index 0000000..5620000 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_of.c > @@ -0,0 +1,265 @@ > +/* > + * Copyright (C) 2012-2016 Red Hat, Inc. > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU Lesser General Public License as published by > the > + * Free Software Foundation; either version 2.1 of the License, or (at your > + * option) any later version. > + */ > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > +#include <drm/drmP.h> > +#include <linux/clk.h> > +#include <linux/clk-provider.h> > +#include <linux/err.h> > +#include <linux/kernel.h> > +#include <linux/of.h> > +#include <linux/of_platform.h> > +#include <linux/regulator/consumer.h> > +#include <linux/slab.h> > +#include "simpledrm.h" > + > +#ifdef CONFIG_COMMON_CLK > + > +static int sdrm_of_bind_clocks(struct sdrm_device *sdrm, > + struct device_node *np) > +{ > + struct clk *clock; > + size_t i, n; > + int r; > + > + n = of_clk_get_parent_count(np); > + if (n < 1) > + return 0; > + > + sdrm->clks = kcalloc(n, sizeof(*sdrm->clks), GFP_KERNEL); > + if (!sdrm->clks) > + return -ENOMEM; > + > + for (i = 0; i < n; ++i) { > + clock = of_clk_get(np, i); > + if (!IS_ERR(clock)) { > + sdrm->clks[sdrm->n_clks++] = clock; > + } else if (PTR_ERR(clock) == -EPROBE_DEFER) { > + r = -EPROBE_DEFER; > + goto error; > + } else { > + dev_err(sdrm->ddev->dev, "cannot find clock %zu: > %ld\n", > + i, PTR_ERR(clock)); > + } > + } > + > + for (i = 0; i < sdrm->n_clks; ++i) { > + if (!sdrm->clks[i]) > + continue; > + > + r = clk_prepare_enable(sdrm->clks[i]); > + if (r < 0) { > + dev_err(sdrm->ddev->dev, > + "cannot find clock %zu: %d\n", i, r); "cannot enable clock" ? > + clk_put(sdrm->clks[i]); > + sdrm->clks[i] = NULL; > + } > + } > + > + return 0; > + > +error: > + for (i = 0; i < sdrm->n_clks; ++i) > + clk_put(sdrm->clks[i]); > + kfree(sdrm->clks); > + sdrm->clks = NULL; > + sdrm->n_clks = 0; > + return r; > +} > + > +static void sdrm_of_unbind_clocks(struct sdrm_device *sdrm) > +{ > + size_t i; > + > + for (i = 0; i < sdrm->n_clks; ++i) { > + clk_disable_unprepare(sdrm->clks[i]); > + clk_put(sdrm->clks[i]); > + } > + > + kfree(sdrm->clks); > + sdrm->clks = NULL; > + sdrm->n_clks = 0; > +} > + > +#else /* CONFIG_COMMON_CLK */ > + > +static int sdrm_of_bind_clocks(struct sdrm_device *sdrm, > + struct device_node *np) > +{ > + return 0; > +} > + > +static void sdrm_of_unbind_clocks(struct sdrm_device *sdrm) > +{ > +} > + > +#endif /* CONFIG_COMMON_CLK */ > + > +#ifdef CONFIG_REGULATOR > + > +static int sdrm_of_bind_regulators(struct sdrm_device *sdrm, > + struct device_node *np) > +{ > + struct regulator *regulator; > + struct property *prop; > + char *p, *name; > + size_t i, n; > + int r; > + > + n = 0; > + for_each_property_of_node(np, prop) { > + p = strstr(prop->name, "-supply"); > + if (p && p != prop->name) > + ++n; > + } > + > + if (n < 1) > + return 0; > + > + sdrm->regulators = kcalloc(n, sizeof(*sdrm->regulators), GFP_KERNEL); > + if (!sdrm->regulators) > + return -ENOMEM; > + > + for_each_property_of_node(np, prop) { > + p = strstr(prop->name, "-supply"); > + if (!p || p == prop->name) > + continue; > + > + name = kstrndup(prop->name, p - prop->name, GFP_TEMPORARY); > + if (!name) > + continue; > + > + regulator = regulator_get_optional(sdrm->ddev->dev, name); > + kfree(name); > + > + if (!IS_ERR(regulator)) { > + sdrm->regulators[sdrm->n_regulators++] = regulator; > + } else if (PTR_ERR(regulator) == -EPROBE_DEFER) { > + r = -EPROBE_DEFER; > + goto error; > + } else { > + dev_warn(sdrm->ddev->dev, > + "cannot find regulator %s: %ld\n", > + prop->name, PTR_ERR(regulator)); > + } > + } > + > + for (i = 0; i < sdrm->n_regulators; ++i) { > + if (!sdrm->regulators[i]) > + continue; > + > + r = regulator_enable(sdrm->regulators[i]); > + if (r < 0) { > + dev_warn(sdrm->ddev->dev, > + "cannot enable regulator %zu: %d\n", i, r); > + regulator_put(sdrm->regulators[i]); > + sdrm->regulators[i] = NULL; > + } > + } > + > + return 0; > + > +error: > + for (i = 0; i < sdrm->n_regulators; ++i) > + if (sdrm->regulators[i]) > + regulator_put(sdrm->regulators[i]); > + kfree(sdrm->regulators); > + sdrm->regulators = NULL; > + sdrm->n_regulators = 0; > + return r; > +} > + > +static void sdrm_of_unbind_regulators(struct sdrm_device *sdrm) > +{ > + size_t i; > + > + for (i = 0; i < sdrm->n_regulators; ++i) { > + if (sdrm->regulators[i]) { > + regulator_disable(sdrm->regulators[i]); > + regulator_put(sdrm->regulators[i]); > + } > + } > + > + kfree(sdrm->regulators); > + sdrm->regulators = NULL; > + sdrm->n_regulators = 0; > +} > + > +#else /* CONFIG_REGULATORS */ > + > +static int sdrm_of_bind_regulators(struct sdrm_device *sdrm, > + struct device_node *np) > +{ > + return 0; > +} > + > +static void sdrm_of_unbind_regulators(struct sdrm_device *sdrm) > +{ > +} > + > +#endif /* CONFIG_REGULATORS */ > + > +#ifdef CONFIG_OF > + > +void sdrm_of_bootstrap(void) > +{ > +#ifdef CONFIG_OF_ADDRESS > + struct device_node *np; > + > + for_each_compatible_node(np, NULL, "simple-framebuffer") > + of_platform_device_create(np, NULL, NULL); > +#endif > +} > + > +int sdrm_of_bind(struct sdrm_device *sdrm) > +{ > + int r; > + > + if (WARN_ON(sdrm->n_clks > 0 || sdrm->n_regulators > 0)) > + return 0; > + if (!sdrm->ddev->dev->of_node) > + return 0; > + > + r = sdrm_of_bind_clocks(sdrm, sdrm->ddev->dev->of_node); > + if (r < 0) > + goto error; > + > + r = sdrm_of_bind_regulators(sdrm, sdrm->ddev->dev->of_node); > + if (r < 0) > + goto error; > + > + return 0; > + > +error: > + sdrm_of_unbind(sdrm); > + return r; > +} > + > +void sdrm_of_unbind(struct sdrm_device *sdrm) > +{ > + sdrm_of_unbind_regulators(sdrm); > + sdrm_of_unbind_clocks(sdrm); > +} > + > +#else /* CONFIG_OF */ > + > +void sdrm_of_bootstrap(void) > +{ > +} > + > +int sdrm_of_bind(struct sdrm_device *sdrm) > +{ > + return 0; > +} > + > +void sdrm_of_unbind(struct sdrm_device *sdrm) > +{ > +} > + > +#endif /* CONFIG_OF */ > -- > 2.9.3 > > _______________________________________________ > dri-devel mailing list > dri-devel at lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/dri-devel