Hey On Mon, Aug 22, 2016 at 10:25 PM, Noralf Trønnes <noralf@xxxxxxxxxxx> 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. > > The driver was originally written by David Herrmann in 2014. > My main contribution is to make use of drm_simple_kms_helper and > rework the probe path to avoid use of the deprecated drm_platform_init() > and drm_driver.{load,unload}(). > Additions have also been made for later changes to the Device Tree binding > document, like support for clocks, regulators and having the node under > /chosen. This code was lifted verbatim from simplefb.c. > > Cc: dh.herrmann@xxxxxxxxx > Cc: libv@xxxxxxxxx > Signed-off-by: Noralf Trønnes <noralf@xxxxxxxxxxx> > --- > > Changes from version 3: > - Reworked gem code to match udl > - Dropped PRIME support > - Dropped dirty_info_property, it's gone > - Don't use drm_device.platformdev it's deprecated > - Remove struct sdrm_device #ifdef's > - Split out sdrm_fb_init() from sdrm_fb_create(), needed by fbdev code > - Simplify drm_clip validation by extending the check in > sdrm_dirty() and drop the one in sdrm_blit() > - Removed sdrm_dirty_all_unlocked() which was unused. > > Changes from version 2: > - Remove superfluos module.h includes > - Move includes from header to source files > - Set plane.fb before flushing in pipe update, or else the previous one > gets flushed > - Added check for vblank event in sdrm_display_pipe_update() > > Changes from version 1: > - Move platform_set_drvdata() before drm_dev_register() > - Remove drm_legacy_mmap() call. > - Set mode_config.{min,max}_{width,height} to the actual dimensions > of the native framebuffer > - Remove plane positioning since it won't work with the simple display pipe, > meaning sdrm_display_pipe_check() isn't necessary either > - Support the additions to the Device Tree binding document, including > clocks, regulators and having the node under /chosen > > Changes from previous version: > - Remove FB_SIMPLE=n dependency to avoid kconfig recursive error > - Changed module name to match kconfig help text: sdrm -> simpledrm > - Use drm_simple_display_pipe > - Replace deprecated drm_platform_init() > - sdrm_dumb_create(): drm_gem_object_unreference() -> *_unlocked() > - sdrm_dumb_map_offset(): drm_gem_object_lookup() remove drm_device parameter > - sdrm_drm_mmap() changes: > Remove struct_mutex locking > Add drm_vma_offset_{lock,unlock}_lookup() > drm_mmap() -> drm_legacy_mmap() > - dma_buf_begin_cpu_access() doesn't require start and length anymore > - Use drm_cvt_mode() instead of open coding a mode > - Fix format conversion. In the intermediate step, store the 8/6/5 bit color > value in the upper part of the 16-bit color variable, not the lower. > - Support clips == NULL in sdrm_dirty() > - Set mode_config.preferred_depth > - Attach mode_config.dirty_info_property to connector > > drivers/gpu/drm/Kconfig | 2 + > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/simpledrm/Kconfig | 19 + > drivers/gpu/drm/simpledrm/Makefile | 4 + > drivers/gpu/drm/simpledrm/simpledrm.h | 86 +++++ > drivers/gpu/drm/simpledrm/simpledrm_damage.c | 235 ++++++++++++ > drivers/gpu/drm/simpledrm/simpledrm_drv.c | 543 +++++++++++++++++++++++++++ > drivers/gpu/drm/simpledrm/simpledrm_gem.c | 202 ++++++++++ > drivers/gpu/drm/simpledrm/simpledrm_kms.c | 234 ++++++++++++ > 9 files changed, 1326 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 > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index fc35731..a54cc8d 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -290,3 +290,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 e3dba6f..eba32ad 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-$(CONFIG_DRM_SIMPLEDRM) += 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..f6a62dc > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/Makefile > @@ -0,0 +1,4 @@ > +simpledrm-y := simpledrm_drv.o simpledrm_kms.o simpledrm_gem.o \ > + simpledrm_damage.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..0739581 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm.h > @@ -0,0 +1,86 @@ > +/* > + * SimpleDRM firmware framebuffer driver > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the Free > + * Software Foundation; either version 2 of the License, or (at your option) > + * any later version. > + */ > + > +#ifndef SDRM_DRV_H > +#define SDRM_DRV_H > + > +#include <drm/drm_crtc.h> > +#include <drm/drm_gem.h> > +#include <drm/drm_simple_kms_helper.h> > + > +struct simplefb_format; > +struct regulator; > +struct clk; > + > +struct sdrm_device { > + struct drm_device *ddev; > + struct drm_simple_display_pipe pipe; > + struct drm_connector conn; > + > + /* framebuffer information */ > + const struct simplefb_format *fb_sformat; > + u32 fb_format; > + u32 fb_width; > + u32 fb_height; > + u32 fb_stride; > + u32 fb_bpp; > + unsigned long fb_base; > + unsigned long fb_size; > + void *fb_map; > + > + unsigned int clk_count; > + struct clk **clks; > + > + u32 regulator_count; > + struct regulator **regulators; > +}; > + > +int sdrm_drm_modeset_init(struct sdrm_device *sdrm); > + > +int sdrm_dirty(struct drm_framebuffer *fb, > + struct drm_file *file, > + unsigned int flags, unsigned int color, > + struct drm_clip_rect *clips, > + unsigned int num_clips); > +int sdrm_dirty_all_locked(struct sdrm_device *sdrm); > + > +struct sdrm_gem_object { > + struct drm_gem_object base; > + struct page **pages; > + void *vmapping; > +}; > + > +#define to_sdrm_bo(x) container_of(x, struct sdrm_gem_object, base) > + > +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma); > +int sdrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); > +int sdrm_gem_vmap(struct sdrm_gem_object *sobj); > + > +struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev, > + size_t size); > +void sdrm_gem_free_object(struct drm_gem_object *obj); > + > +int sdrm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev, > + struct drm_mode_create_dumb *arg); > +int sdrm_dumb_map_offset(struct drm_file *file_priv, struct drm_device *ddev, > + uint32_t handle, uint64_t *offset); > + > +struct sdrm_framebuffer { > + struct drm_framebuffer base; > + struct sdrm_gem_object *obj; > +}; > + > +#define to_sdrm_fb(x) container_of(x, struct sdrm_framebuffer, base) > + > +int sdrm_fb_init(struct drm_device *ddev, struct sdrm_framebuffer *fb, > + const struct drm_mode_fb_cmd2 *mode_cmd, > + struct sdrm_gem_object *obj); > + > +#endif /* SDRM_DRV_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..52c845f > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_damage.c > @@ -0,0 +1,235 @@ > +/* > + * SimpleDRM firmware framebuffer driver > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the Free > + * Software Foundation; either version 2 of the License, or (at your option) > + * any later version. > + */ > + > +#include <asm/unaligned.h> > +#include <drm/drmP.h> > +#include <drm/drm_crtc.h> > +#include <linux/dma-buf.h> > +#include <linux/kernel.h> > +#include <linux/string.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_framebuffer *sfb, u32 x, u32 y, > + u32 width, u32 height) > +{ > + struct drm_framebuffer *fb = &sfb->base; > + struct drm_device *ddev = fb->dev; > + struct sdrm_device *sdrm = ddev->dev_private; > + u32 src_bpp, dst_bpp; > + u8 *src, *dst; > + > + /* already unmapped; ongoing handover? */ > + if (!sdrm->fb_map) > + return; > + > + /* get buffer offsets */ > + src = sfb->obj->vmapping; > + dst = sdrm->fb_map; > + > + /* bo is guaranteed to be big enough; size checks not needed */ > + src_bpp = (fb->bits_per_pixel + 7) / 8; > + src += fb->offsets[0] + y * fb->pitches[0] + x * src_bpp; > + > + dst_bpp = (sdrm->fb_bpp + 7) / 8; > + dst += y * sdrm->fb_stride + x * dst_bpp; > + > + /* if formats are identical, do a line-by-line copy.. */ > + if (fb->pixel_format == sdrm->fb_format) { > + sdrm_blit_lines(src, fb->pitches[0], > + dst, sdrm->fb_stride, > + src_bpp, width, height); > + return; > + } > + > + /* ..otherwise call slow blit-function */ > + switch (fb->pixel_format) { > + case DRM_FORMAT_ARGB8888: > + /* fallthrough */ > + case DRM_FORMAT_XRGB8888: > + sdrm_blit_from_xrgb8888(src, fb->pitches[0], src_bpp, > + dst, sdrm->fb_stride, dst_bpp, > + sdrm->fb_format, width, height); > + break; > + case DRM_FORMAT_RGB565: > + sdrm_blit_from_rgb565(src, fb->pitches[0], src_bpp, > + dst, sdrm->fb_stride, dst_bpp, > + sdrm->fb_format, width, height); > + break; > + } > +} > + > +int sdrm_dirty(struct drm_framebuffer *fb, > + struct drm_file *file, > + unsigned int flags, unsigned int color, > + struct drm_clip_rect *clips, > + unsigned int num_clips) > +{ > + struct sdrm_framebuffer *sfb = to_sdrm_fb(fb); > + struct drm_device *ddev = fb->dev; > + struct sdrm_device *sdrm = ddev->dev_private; > + struct drm_clip_rect full_clip; > + unsigned int i; > + int r; > + > + if (!clips || !num_clips) { > + full_clip.x1 = 0; > + full_clip.x2 = fb->width; > + full_clip.y1 = 0; > + full_clip.y2 = fb->height; > + clips = &full_clip; > + num_clips = 1; > + } > + > + drm_modeset_lock_all(ddev); > + > + if (sdrm->pipe.plane.fb != fb) { > + r = 0; > + goto unlock; > + } > + > + for (i = 0; i < num_clips; i++) { > + if (clips[i].x1 > clips[i].x2 || clips[i].x2 > fb->width || > + clips[i].y1 > clips[i].y2 || clips[i].y2 > fb->height) > + continue; > + > + sdrm_blit(sfb, clips[i].x1, clips[i].y1, > + clips[i].x2 - clips[i].x1, > + clips[i].y2 - clips[i].y1); > + } > + > +unlock: > + drm_modeset_unlock_all(ddev); > + return 0; > +} > + > +int sdrm_dirty_all_locked(struct sdrm_device *sdrm) > +{ > + struct drm_framebuffer *fb; > + struct sdrm_framebuffer *sfb; > + > + fb = sdrm->pipe.plane.fb; > + if (!fb) > + return 0; > + > + sfb = to_sdrm_fb(fb); > + > + sdrm_blit(sfb, 0, 0, fb->width, fb->height); > + > + return 0; > +} > diff --git a/drivers/gpu/drm/simpledrm/simpledrm_drv.c b/drivers/gpu/drm/simpledrm/simpledrm_drv.c > new file mode 100644 > index 0000000..17c1b55 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_drv.c > @@ -0,0 +1,543 @@ > +/* > + * SimpleDRM firmware framebuffer driver > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the Free > + * Software Foundation; either version 2 of the License, or (at your option) > + * any later version. > + */ > + > +#include <drm/drmP.h> > +#include <linux/clk.h> > +#include <linux/clk-provider.h> > +#include <linux/errno.h> > +#include <linux/io.h> > +#include <linux/kernel.h> > +#include <linux/mm.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/of_platform.h> > +#include <linux/platform_data/simplefb.h> > +#include <linux/regulator/consumer.h> > +#include <linux/string.h> > + > +#include "simpledrm.h" > + > +static const struct file_operations sdrm_drm_fops = { > + .owner = THIS_MODULE, > + .open = drm_open, > + .mmap = sdrm_drm_mmap, > + .poll = drm_poll, > + .read = drm_read, > + .unlocked_ioctl = drm_ioctl, > + .release = drm_release, > +#ifdef CONFIG_COMPAT > + .compat_ioctl = drm_compat_ioctl, > +#endif > + .llseek = noop_llseek, > +}; > + > +static const struct vm_operations_struct sdrm_gem_vm_ops = { > + .fault = sdrm_gem_fault, > + .open = drm_gem_vm_open, > + .close = drm_gem_vm_close, > +}; > + > +static struct drm_driver sdrm_drm_driver = { > + .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, > + .fops = &sdrm_drm_fops, > + > + .gem_free_object = sdrm_gem_free_object, > + .gem_vm_ops = &sdrm_gem_vm_ops, > + > + .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 = "20130601", > + .major = 0, > + .minor = 0, > + .patchlevel = 1, > +}; > + > +#if defined CONFIG_OF && defined CONFIG_COMMON_CLK > +/* > + * Clock handling code. > + * > + * Here we handle the clocks property of our "simple-framebuffer" dt node. > + * This is necessary so that we can make sure that any clocks needed by > + * the display engine that the bootloader set up for us (and for which it > + * provided a simplefb dt node), stay up, for the life of the simplefb > + * driver. > + * > + * When the driver unloads, we cleanly disable, and then release the clocks. > + * > + * We only complain about errors here, no action is taken as the most likely > + * error can only happen due to a mismatch between the bootloader which set > + * up simplefb, and the clock definitions in the device tree. Chances are > + * that there are no adverse effects, and if there are, a clean teardown of > + * the fb probe will not help us much either. So just complain and carry on, > + * and hope that the user actually gets a working fb at the end of things. > + */ > +static int sdrm_clocks_init(struct sdrm_device *sdrm, > + struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct clk *clock; > + int i, ret; > + > + if (dev_get_platdata(&pdev->dev) || !np) > + return 0; > + > + sdrm->clk_count = of_clk_get_parent_count(np); > + if (!sdrm->clk_count) > + return 0; > + > + sdrm->clks = kcalloc(sdrm->clk_count, sizeof(struct clk *), GFP_KERNEL); > + if (!sdrm->clks) > + return -ENOMEM; > + > + for (i = 0; i < sdrm->clk_count; i++) { > + clock = of_clk_get(np, i); > + if (IS_ERR(clock)) { > + if (PTR_ERR(clock) == -EPROBE_DEFER) { > + while (--i >= 0) { > + if (sdrm->clks[i]) > + clk_put(sdrm->clks[i]); > + } > + kfree(sdrm->clks); > + return -EPROBE_DEFER; > + } > + dev_err(&pdev->dev, "%s: clock %d not found: %ld\n", > + __func__, i, PTR_ERR(clock)); > + continue; > + } > + sdrm->clks[i] = clock; > + } > + > + for (i = 0; i < sdrm->clk_count; i++) { > + if (sdrm->clks[i]) { > + ret = clk_prepare_enable(sdrm->clks[i]); > + if (ret) { > + dev_err(&pdev->dev, > + "%s: failed to enable clock %d: %d\n", > + __func__, i, ret); > + clk_put(sdrm->clks[i]); > + sdrm->clks[i] = NULL; > + } > + } > + } > + > + return 0; > +} > + > +static void sdrm_clocks_destroy(struct sdrm_device *sdrm) > +{ > + int i; > + > + if (!sdrm->clks) > + return; > + > + for (i = 0; i < sdrm->clk_count; i++) { > + if (sdrm->clks[i]) { > + clk_disable_unprepare(sdrm->clks[i]); > + clk_put(sdrm->clks[i]); > + } > + } > + > + kfree(sdrm->clks); > +} > +#else > +static int sdrm_clocks_init(struct sdrm_device *sdrm, > + struct platform_device *pdev) > +{ > + return 0; > +} > + > +static void sdrm_clocks_destroy(struct sdrm_device *sdrm) > +{ > +} > +#endif > + > +#if defined CONFIG_OF && defined CONFIG_REGULATOR > + > +#define SUPPLY_SUFFIX "-supply" > + > +/* > + * Regulator handling code. > + * > + * Here we handle the num-supplies and vin*-supply properties of our > + * "simple-framebuffer" dt node. This is necessary so that we can make sure > + * that any regulators needed by the display hardware that the bootloader > + * set up for us (and for which it provided a simplefb dt node), stay up, > + * for the life of the simplefb driver. > + * > + * When the driver unloads, we cleanly disable, and then release the > + * regulators. > + * > + * We only complain about errors here, no action is taken as the most likely > + * error can only happen due to a mismatch between the bootloader which set > + * up simplefb, and the regulator definitions in the device tree. Chances are > + * that there are no adverse effects, and if there are, a clean teardown of > + * the fb probe will not help us much either. So just complain and carry on, > + * and hope that the user actually gets a working fb at the end of things. > + */ > +static int sdrm_regulators_init(struct sdrm_device *sdrm, > + struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct regulator *regulator; > + int count = 0, i = 0, ret; > + struct property *prop; > + const char *p; > + > + if (dev_get_platdata(&pdev->dev) || !np) > + return 0; > + > + /* Count the number of regulator supplies */ > + for_each_property_of_node(np, prop) { > + p = strstr(prop->name, SUPPLY_SUFFIX); > + if (p && p != prop->name) > + count++; > + } > + > + if (!count) > + return 0; > + > + sdrm->regulators = devm_kcalloc(&pdev->dev, count, > + sizeof(struct regulator *), > + GFP_KERNEL); > + if (!sdrm->regulators) > + return -ENOMEM; > + > + /* Get all the regulators */ > + for_each_property_of_node(np, prop) { > + char name[32]; /* 32 is max size of property name */ > + > + p = strstr(prop->name, SUPPLY_SUFFIX); > + if (!p || p == prop->name) > + continue; > + > + strlcpy(name, prop->name, > + strlen(prop->name) - strlen(SUPPLY_SUFFIX) + 1); > + regulator = devm_regulator_get_optional(&pdev->dev, name); > + if (IS_ERR(regulator)) { > + if (PTR_ERR(regulator) == -EPROBE_DEFER) > + return -EPROBE_DEFER; > + dev_err(&pdev->dev, "regulator %s not found: %ld\n", > + name, PTR_ERR(regulator)); > + continue; > + } > + sdrm->regulators[i++] = regulator; > + } > + sdrm->regulator_count = i; > + > + /* Enable all the regulators */ > + for (i = 0; i < sdrm->regulator_count; i++) { > + ret = regulator_enable(sdrm->regulators[i]); > + if (ret) { > + dev_err(&pdev->dev, > + "failed to enable regulator %d: %d\n", > + i, ret); > + devm_regulator_put(sdrm->regulators[i]); > + sdrm->regulators[i] = NULL; > + } > + } > + > + return 0; > +} > + > +static void sdrm_regulators_destroy(struct sdrm_device *sdrm) > +{ > + int i; > + > + if (!sdrm->regulators) > + return; > + > + for (i = 0; i < sdrm->regulator_count; i++) > + if (sdrm->regulators[i]) > + regulator_disable(sdrm->regulators[i]); > +} > +#else > +static int sdrm_regulators_init(struct sdrm_device *sdrm, > + struct platform_device *pdev) > +{ > + return 0; > +} > + > +static void sdrm_regulators_destroy(struct sdrm_device *sdrm) > +{ > +} > +#endif > + > +static int parse_dt(struct platform_device *pdev, > + struct simplefb_platform_data *mode) > +{ > + struct device_node *np = pdev->dev.of_node; > + const char *format; > + int ret; > + > + if (!np) > + return -ENODEV; > + > + ret = of_property_read_u32(np, "width", &mode->width); > + if (ret) { > + dev_err(&pdev->dev, "Can't parse width property\n"); > + return ret; > + } > + > + ret = of_property_read_u32(np, "height", &mode->height); > + if (ret) { > + dev_err(&pdev->dev, "Can't parse height property\n"); > + return ret; > + } > + > + ret = of_property_read_u32(np, "stride", &mode->stride); > + if (ret) { > + dev_err(&pdev->dev, "Can't parse stride property\n"); > + return ret; > + } > + > + ret = of_property_read_string(np, "format", &format); > + if (ret) { > + dev_err(&pdev->dev, "Can't parse format property\n"); > + return ret; > + } > + mode->format = format; > + > + return 0; > +} > + > +static struct simplefb_format simplefb_formats[] = SIMPLEFB_FORMATS; > + > +static int sdrm_pdev_init(struct sdrm_device *sdrm, > + struct platform_device *pdev) > +{ > + struct simplefb_platform_data *mode = pdev->dev.platform_data; > + struct simplefb_platform_data pmode; > + struct resource *mem; > + unsigned int depth; > + int ret, i, bpp; > + > + if (!mode) { > + mode = &pmode; > + ret = parse_dt(pdev, mode); > + if (ret) > + return ret; > + } > + > + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!mem) { > + dev_err(sdrm->ddev->dev, "No memory resource\n"); > + return -ENODEV; > + } > + > + for (i = 0; i < ARRAY_SIZE(simplefb_formats); ++i) { > + if (strcmp(mode->format, simplefb_formats[i].name)) > + continue; > + > + sdrm->fb_sformat = &simplefb_formats[i]; > + sdrm->fb_format = simplefb_formats[i].fourcc; > + sdrm->fb_width = mode->width; > + sdrm->fb_height = mode->height; > + sdrm->fb_stride = mode->stride; > + sdrm->fb_base = mem->start; > + sdrm->fb_size = resource_size(mem); > + break; > + } > + > + if (i >= ARRAY_SIZE(simplefb_formats)) { > + dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format); > + return -ENODEV; > + } > + > + switch (sdrm->fb_format) { > + 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: > + dev_err(sdrm->ddev->dev, "Unsupported format %s\n", > + mode->format); > + return -ENODEV; > + } > + > + drm_fb_get_bpp_depth(sdrm->fb_format, &depth, &bpp); > + if (!bpp) { > + dev_err(sdrm->ddev->dev, "Unknown format %s\n", mode->format); > + return -ENODEV; > + } > + > + if (sdrm->fb_size < sdrm->fb_stride * sdrm->fb_height) { > + dev_err(sdrm->ddev->dev, "FB too small\n"); > + return -ENODEV; > + } else if ((bpp + 7) / 8 * sdrm->fb_width > sdrm->fb_stride) { > + dev_err(sdrm->ddev->dev, "Invalid stride\n"); > + return -ENODEV; > + } > + > + sdrm->fb_bpp = bpp; > + > + sdrm->fb_map = ioremap_wc(sdrm->fb_base, sdrm->fb_size); > + if (!sdrm->fb_map) { > + dev_err(sdrm->ddev->dev, "cannot remap VMEM\n"); > + return -EIO; > + } > + > + DRM_DEBUG_KMS("format: %s\n", drm_get_format_name(sdrm->fb_format)); > + > + return 0; > +} > + > +static void sdrm_pdev_destroy(struct sdrm_device *sdrm) > +{ > + if (sdrm->fb_map) { > + iounmap(sdrm->fb_map); > + sdrm->fb_map = NULL; > + } > +} > + > +static int sdrm_simplefb_probe(struct platform_device *pdev) > +{ > + struct sdrm_device *sdrm; > + struct drm_device *ddev; > + int ret; > + > + ddev = drm_dev_alloc(&sdrm_drm_driver, &pdev->dev); > + if (!ddev) > + return -ENOMEM; > + > + sdrm = kzalloc(sizeof(*sdrm), GFP_KERNEL); > + if (!sdrm) > + goto err_free; > + > + ddev->dev_private = sdrm; > + sdrm->ddev = ddev; > + > + ret = sdrm_pdev_init(sdrm, pdev); > + if (ret) > + goto err_free; > + > + ret = sdrm_drm_modeset_init(sdrm); > + if (ret) > + goto err_destroy; > + > + ret = sdrm_clocks_init(sdrm, pdev); > + if (ret < 0) > + goto err_cleanup; > + > + ret = sdrm_regulators_init(sdrm, pdev); > + if (ret < 0) > + goto err_clocks; > + > + platform_set_drvdata(pdev, ddev); > + ret = drm_dev_register(ddev, 0); > + if (ret) > + goto err_regulators; > + > + DRM_INFO("Initialized %s on minor %d\n", ddev->driver->name, > + ddev->primary->index); > + > + return 0; > + > +err_regulators: > + sdrm_regulators_destroy(sdrm); > +err_clocks: > + sdrm_clocks_destroy(sdrm); > +err_cleanup: > + drm_mode_config_cleanup(ddev); > +err_destroy: > + sdrm_pdev_destroy(sdrm); > +err_free: > + drm_dev_unref(ddev); > + kfree(sdrm); > + > + return ret; > +} > + > +static int sdrm_simplefb_remove(struct platform_device *pdev) > +{ > + struct drm_device *ddev = platform_get_drvdata(pdev); > + struct sdrm_device *sdrm = ddev->dev_private; > + > + drm_dev_unregister(ddev); > + drm_mode_config_cleanup(ddev); > + > + /* protect fb_map removal against sdrm_blit() */ > + drm_modeset_lock_all(ddev); > + sdrm_pdev_destroy(sdrm); > + drm_modeset_unlock_all(ddev); > + > + sdrm_regulators_destroy(sdrm); > + sdrm_clocks_destroy(sdrm); > + > + drm_dev_unref(ddev); > + kfree(sdrm); > + > + return 0; > +} > + > +static const struct of_device_id simplefb_of_match[] = { > + { .compatible = "simple-framebuffer", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, simplefb_of_match); > + > +static struct platform_driver sdrm_simplefb_driver = { > + .probe = sdrm_simplefb_probe, > + .remove = sdrm_simplefb_remove, > + .driver = { > + .name = "simple-framebuffer", > + .mod_name = KBUILD_MODNAME, > + .owner = THIS_MODULE, > + .of_match_table = simplefb_of_match, > + }, > +}; > + > +static int __init sdrm_init(void) > +{ > + int ret; > + > + ret = platform_driver_register(&sdrm_simplefb_driver); > + if (ret) > + return ret; > + > + if (IS_ENABLED(CONFIG_OF_ADDRESS) && of_chosen) { > + struct device_node *np; > + > + for_each_child_of_node(of_chosen, np) { > + if (of_device_is_compatible(np, "simple-framebuffer")) > + of_platform_device_create(np, NULL, NULL); > + } > + } > + > + return 0; > +} > +module_init(sdrm_init); > + > +static void __exit sdrm_exit(void) > +{ > + platform_driver_unregister(&sdrm_simplefb_driver); > +} > +module_exit(sdrm_exit); > + > +MODULE_LICENSE("GPL"); > +MODULE_AUTHOR("David Herrmann <dh.herrmann@xxxxxxxxx>"); > +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..8cced80 > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_gem.c > @@ -0,0 +1,202 @@ > +/* > + * SimpleDRM firmware framebuffer driver > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the Free > + * Software Foundation; either version 2 of the License, or (at your option) > + * any later version. > + */ > + > +#include <drm/drmP.h> > +#include <linux/errno.h> > +#include <linux/mm.h> > +#include <linux/slab.h> > +#include <linux/vmalloc.h> > + > +#include "simpledrm.h" > + > +struct sdrm_gem_object *sdrm_gem_alloc_object(struct drm_device *ddev, > + size_t size) > +{ > + struct sdrm_gem_object *obj; > + > + WARN_ON(!size || (size & ~PAGE_MASK) != 0); > + > + obj = kzalloc(sizeof(*obj), GFP_KERNEL); > + if (!obj) > + return NULL; > + > + if (drm_gem_object_init(ddev, &obj->base, size)) { > + kfree(obj); > + return NULL; > + } > + > + return obj; > +} > + > +int sdrm_dumb_create(struct drm_file *dfile, struct drm_device *ddev, > + struct drm_mode_create_dumb *args) > +{ > + struct sdrm_gem_object *obj; > + int r; > + > + /* overflow checks are done by DRM core */ > + args->pitch = (args->bpp + 7) / 8 * args->width; > + args->size = PAGE_ALIGN(args->pitch * args->height); > + > + obj = sdrm_gem_alloc_object(ddev, args->size); > + if (!obj) > + return -ENOMEM; > + > + r = drm_gem_handle_create(dfile, &obj->base, &args->handle); > + if (r) { > + drm_gem_object_unreference_unlocked(&obj->base); > + return r; > + } > + > + /* handle owns a reference */ > + drm_gem_object_unreference_unlocked(&obj->base); > + > + return 0; > +} > + > +int sdrm_drm_mmap(struct file *filp, struct vm_area_struct *vma) > +{ > + int ret; > + > + ret = drm_gem_mmap(filp, vma); > + if (ret) > + return ret; > + > + vma->vm_flags &= ~VM_PFNMAP; > + vma->vm_flags |= VM_MIXEDMAP; > + vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); > + > + return 0; > +} > + > +int sdrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) > +{ > + struct drm_gem_object *gobj = vma->vm_private_data; > + struct sdrm_gem_object *obj = to_sdrm_bo(gobj); > + pgoff_t offset; > + int ret; > + > + if (!obj->pages) > + return VM_FAULT_SIGBUS; > + > + offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> > + PAGE_SHIFT; > + > + ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, > + obj->pages[offset]); > + switch (ret) { > + case -EAGAIN: > + case 0: > + case -ERESTARTSYS: > + case -EINTR: > + case -EBUSY: > + return VM_FAULT_NOPAGE; > + > + case -ENOMEM: > + return VM_FAULT_OOM; > + } > + > + return VM_FAULT_SIGBUS; > +} > + > +int sdrm_gem_get_pages(struct sdrm_gem_object *obj) > +{ > + struct page **pages; > + > + if (obj->pages) > + return 0; > + > + pages = drm_gem_get_pages(&obj->base); > + if (IS_ERR(pages)) > + return PTR_ERR(pages); > + > + obj->pages = pages; > + > + return 0; > +} > + > +static void sdrm_gem_put_pages(struct sdrm_gem_object *obj) > +{ > + if (!obj->pages) > + return; > + > + drm_gem_put_pages(&obj->base, obj->pages, false, false); > + obj->pages = NULL; > +} > + > +int sdrm_gem_vmap(struct sdrm_gem_object *obj) > +{ > + int page_count = obj->base.size / PAGE_SIZE; > + int ret; > + > + if (obj->vmapping) > + return 0; > + > + ret = sdrm_gem_get_pages(obj); > + if (ret) > + return ret; > + > + obj->vmapping = vmap(obj->pages, page_count, 0, PAGE_KERNEL); > + if (!obj->vmapping) > + return -ENOMEM; > + > + return 0; > +} > + > +static void sdrm_gem_vunmap(struct sdrm_gem_object *obj) > +{ > + vunmap(obj->vmapping); > + obj->vmapping = NULL; > + > + sdrm_gem_put_pages(obj); > +} > + > +void sdrm_gem_free_object(struct drm_gem_object *gobj) > +{ > + struct sdrm_gem_object *obj = to_sdrm_bo(gobj); > + > + if (obj->vmapping) > + sdrm_gem_vunmap(obj); > + > + if (obj->pages) > + sdrm_gem_put_pages(obj); > + > + drm_gem_object_release(gobj); > + kfree(obj); > +} > + > +int sdrm_dumb_map_offset(struct drm_file *dfile, struct drm_device *ddev, > + uint32_t handle, uint64_t *offset) > +{ > + struct sdrm_gem_object *obj; > + struct drm_gem_object *gobj; > + int ret; > + > + gobj = drm_gem_object_lookup(dfile, handle); > + if (!gobj) > + return -ENOENT; > + > + obj = to_sdrm_bo(gobj); > + > + ret = sdrm_gem_get_pages(obj); > + if (ret) > + goto out_unref; > + > + ret = drm_gem_create_mmap_offset(gobj); > + if (ret) > + goto out_unref; > + > + *offset = drm_vma_node_offset_addr(&gobj->vma_node); > + > +out_unref: > + drm_gem_object_unreference_unlocked(gobj); > + > + return ret; > +} > diff --git a/drivers/gpu/drm/simpledrm/simpledrm_kms.c b/drivers/gpu/drm/simpledrm/simpledrm_kms.c > new file mode 100644 > index 0000000..e6dc3df > --- /dev/null > +++ b/drivers/gpu/drm/simpledrm/simpledrm_kms.c > @@ -0,0 +1,234 @@ > +/* > + * SimpleDRM firmware framebuffer driver > + * Copyright (c) 2012-2014 David Herrmann <dh.herrmann@xxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the Free > + * Software Foundation; either version 2 of the License, or (at your option) > + * any later version. > + */ > + > +#include <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/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->fb_width, sdrm->fb_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 inline struct sdrm_device * > +pipe_to_sdrm(struct drm_simple_display_pipe *pipe) > +{ > + return container_of(pipe, struct sdrm_device, pipe); > +} > + > +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; > + } So either the drm_simple_kms_helpers are buggy, or the SimpleDRM use of it. On DPMS updates, I get: Sep 02 01:00:39 david-t2 kernel: [drm:drm_atomic_helper_commit_cleanup_done [drm_kms_helper]] *ERROR* [CRTC:25:crtc-0] flip_done timed out Sep 02 01:00:39 david-t2 kernel: ------------[ cut here ]------------ Sep 02 01:00:39 david-t2 kernel: WARNING: CPU: 3 PID: 352 at drivers/gpu/drm/drm_atomic_helper.c:1549 drm_atomic_helper_commit_hw_done+0xab/0xb0 [drm_kms_helper] The atomic-commit originates in: drm_atomic_helper_connector_dpms() Any idea what is missing there? I haven't looked much into the atomic-helpers, yet. Thanks David > +} > + > +void sdrm_display_pipe_update(struct drm_simple_display_pipe *pipe, > + struct drm_plane_state *plane_state) > +{ > + struct drm_framebuffer *fb = pipe->plane.state->fb; > + struct sdrm_device *sdrm = pipe_to_sdrm(pipe); > + > + sdrm_crtc_send_vblank_event(&pipe->crtc); > + > + if (fb) { > + pipe->plane.fb = fb; > + sdrm_dirty_all_locked(sdrm); > + } > +} > + > +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 *fb, > + struct drm_file *dfile, > + unsigned int *handle) > +{ > + struct sdrm_framebuffer *sfb = to_sdrm_fb(fb); > + > + return drm_gem_handle_create(dfile, &sfb->obj->base, handle); > +} > + > +static void sdrm_fb_destroy(struct drm_framebuffer *fb) > +{ > + struct sdrm_framebuffer *sfb = to_sdrm_fb(fb); > + > + drm_framebuffer_cleanup(fb); > + drm_gem_object_unreference_unlocked(&sfb->obj->base); > + kfree(sfb); > +} > + > +static const struct drm_framebuffer_funcs sdrm_fb_ops = { > + .create_handle = sdrm_fb_create_handle, > + .dirty = sdrm_dirty, > + .destroy = sdrm_fb_destroy, > +}; > + > +int sdrm_fb_init(struct drm_device *ddev, struct sdrm_framebuffer *fb, > + const struct drm_mode_fb_cmd2 *mode_cmd, > + struct sdrm_gem_object *obj) > +{ > + fb->obj = obj; > + drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd); > + > + return drm_framebuffer_init(ddev, &fb->base, &sdrm_fb_ops); > +} > + > +static struct drm_framebuffer *sdrm_fb_create(struct drm_device *ddev, > + struct drm_file *dfile, > + const struct drm_mode_fb_cmd2 *cmd) > +{ > + struct sdrm_framebuffer *fb; > + struct drm_gem_object *gobj; > + int ret; > + > + if (cmd->flags) > + return ERR_PTR(-EINVAL); > + > + gobj = drm_gem_object_lookup(dfile, cmd->handles[0]); > + if (!gobj) > + return ERR_PTR(-EINVAL); > + > + fb = kzalloc(sizeof(*fb), GFP_KERNEL); > + if (!fb) { > + ret = -ENOMEM; > + goto err_unref; > + } > + > + ret = sdrm_fb_init(ddev, fb, cmd, to_sdrm_bo(gobj)); > + if (ret) > + goto err_free; > + > + ret = sdrm_gem_vmap(fb->obj); > + if (ret) > + goto err_free; > + > + DRM_DEBUG_KMS("[FB:%d] pixel_format: %s\n", fb->base.base.id, > + drm_get_format_name(fb->base.pixel_format)); > + > + return &fb->base; > + > +err_free: > + kfree(fb); > +err_unref: > + drm_gem_object_unreference_unlocked(gobj); > + > + return ERR_PTR(ret); > +} > + > +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_drm_modeset_init(struct sdrm_device *sdrm) > +{ > + struct drm_connector *conn = &sdrm->conn; > + struct drm_device *ddev = sdrm->ddev; > + int ret; > + > + drm_mode_config_init(ddev); > + ddev->mode_config.min_width = sdrm->fb_width; > + ddev->mode_config.max_width = sdrm->fb_width; > + ddev->mode_config.min_height = sdrm->fb_height; > + ddev->mode_config.max_height = sdrm->fb_height; > + ddev->mode_config.preferred_depth = sdrm->fb_bpp; > + ddev->mode_config.funcs = &sdrm_mode_config_ops; > + > + drm_connector_helper_add(conn, &sdrm_conn_hfuncs); > + ret = drm_connector_init(ddev, conn, &sdrm_conn_ops, > + DRM_MODE_CONNECTOR_VIRTUAL); > + if (ret) > + goto err_cleanup; > + > + ret = drm_simple_display_pipe_init(ddev, &sdrm->pipe, &sdrm_pipe_funcs, > + sdrm_formats, > + ARRAY_SIZE(sdrm_formats), conn); > + if (ret) > + goto err_cleanup; > + > + drm_mode_config_reset(ddev); > + > + return 0; > + > +err_cleanup: > + drm_mode_config_cleanup(ddev); > + > + return ret; > +} > -- > 2.8.2 > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html