[PATCH v5 6/7] drm: add SimpleDRM driver

Tom Gundersen teg at jklm.no
Fri Sep 2 12:45:28 UTC 2016


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


More information about the dri-devel mailing list