drm/sysfb: Split source file
authorThomas Zimmermann <tzimmermann@suse.de>
Thu, 10 Apr 2025 08:37:23 +0000 (10:37 +0200)
committerThomas Zimmermann <tzimmermann@suse.de>
Mon, 14 Apr 2025 08:16:12 +0000 (10:16 +0200)
Split drm_sysfb_helper.c into two source files. There's now one
source file for the mode-setting pipeline and one source file for
module meta data. Prepares for adding additional source code to
sysfb helpers.

v2:
- fix typo in commit message (Javier)

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://lore.kernel.org/r/20250410083834.10810-2-tzimmermann@suse.de
drivers/gpu/drm/sysfb/Makefile
drivers/gpu/drm/sysfb/drm_sysfb.c [new file with mode: 0644]
drivers/gpu/drm/sysfb/drm_sysfb_helper.c [deleted file]
drivers/gpu/drm/sysfb/drm_sysfb_helper.h
drivers/gpu/drm/sysfb/drm_sysfb_modeset.c [new file with mode: 0644]

index 0d2518c97163474a6bf7a2da528bbfbcda38b512..861b4026f4a6e3066d4819d8065cc1b85d89a840 100644 (file)
@@ -1,5 +1,8 @@
 # SPDX-License-Identifier: GPL-2.0-only
 
+drm_sysfb_helper-y := \
+       drm_sysfb.o \
+       drm_sysfb_modeset.o
 obj-$(CONFIG_DRM_SYSFB_HELPER) += drm_sysfb_helper.o
 
 obj-$(CONFIG_DRM_EFIDRM)       += efidrm.o
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb.c b/drivers/gpu/drm/sysfb/drm_sysfb.c
new file mode 100644 (file)
index 0000000..c083d21
--- /dev/null
@@ -0,0 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/module.h>
+
+#include "drm_sysfb_helper.h"
+
+MODULE_DESCRIPTION("Helpers for DRM sysfb drivers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.c b/drivers/gpu/drm/sysfb/drm_sysfb_helper.c
deleted file mode 100644 (file)
index 262490a..0000000
+++ /dev/null
@@ -1,324 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-
-#include <linux/export.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-
-#include <drm/drm_atomic.h>
-#include <drm/drm_atomic_helper.h>
-#include <drm/drm_atomic_state_helper.h>
-#include <drm/drm_damage_helper.h>
-#include <drm/drm_drv.h>
-#include <drm/drm_edid.h>
-#include <drm/drm_fourcc.h>
-#include <drm/drm_framebuffer.h>
-#include <drm/drm_gem_atomic_helper.h>
-#include <drm/drm_gem_framebuffer_helper.h>
-#include <drm/drm_panic.h>
-#include <drm/drm_print.h>
-#include <drm/drm_probe_helper.h>
-
-#include "drm_sysfb_helper.h"
-
-MODULE_DESCRIPTION("Helpers for DRM sysfb drivers");
-MODULE_LICENSE("GPL");
-
-struct drm_display_mode drm_sysfb_mode(unsigned int width,
-                                      unsigned int height,
-                                      unsigned int width_mm,
-                                      unsigned int height_mm)
-{
-       /*
-        * Assume a monitor resolution of 96 dpi to
-        * get a somewhat reasonable screen size.
-        */
-       if (!width_mm)
-               width_mm = DRM_MODE_RES_MM(width, 96ul);
-       if (!height_mm)
-               height_mm = DRM_MODE_RES_MM(height, 96ul);
-
-       {
-               const struct drm_display_mode mode = {
-                       DRM_MODE_INIT(60, width, height, width_mm, height_mm)
-               };
-
-               return mode;
-       }
-}
-EXPORT_SYMBOL(drm_sysfb_mode);
-
-/*
- * Plane
- */
-
-int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane,
-                                       struct drm_atomic_state *new_state)
-{
-       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
-       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
-       struct drm_shadow_plane_state *new_shadow_plane_state =
-               to_drm_shadow_plane_state(new_plane_state);
-       struct drm_framebuffer *new_fb = new_plane_state->fb;
-       struct drm_crtc *new_crtc = new_plane_state->crtc;
-       struct drm_crtc_state *new_crtc_state = NULL;
-       struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
-       int ret;
-
-       if (new_crtc)
-               new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
-
-       ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
-                                                 DRM_PLANE_NO_SCALING,
-                                                 DRM_PLANE_NO_SCALING,
-                                                 false, false);
-       if (ret)
-               return ret;
-       else if (!new_plane_state->visible)
-               return 0;
-
-       if (new_fb->format != sysfb->fb_format) {
-               void *buf;
-
-               /* format conversion necessary; reserve buffer */
-               buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state,
-                                                   sysfb->fb_pitch, GFP_KERNEL);
-               if (!buf)
-                       return -ENOMEM;
-       }
-
-       new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
-
-       new_sysfb_crtc_state = to_drm_sysfb_crtc_state(new_crtc_state);
-       new_sysfb_crtc_state->format = new_fb->format;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check);
-
-void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state)
-{
-       struct drm_device *dev = plane->dev;
-       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
-       struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
-       struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
-       struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
-       struct drm_framebuffer *fb = plane_state->fb;
-       unsigned int dst_pitch = sysfb->fb_pitch;
-       const struct drm_format_info *dst_format = sysfb->fb_format;
-       struct drm_atomic_helper_damage_iter iter;
-       struct drm_rect damage;
-       int ret, idx;
-
-       ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
-       if (ret)
-               return;
-
-       if (!drm_dev_enter(dev, &idx))
-               goto out_drm_gem_fb_end_cpu_access;
-
-       drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
-       drm_atomic_for_each_plane_damage(&iter, &damage) {
-               struct iosys_map dst = sysfb->fb_addr;
-               struct drm_rect dst_clip = plane_state->dst;
-
-               if (!drm_rect_intersect(&dst_clip, &damage))
-                       continue;
-
-               iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip));
-               drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb,
-                           &damage, &shadow_plane_state->fmtcnv_state);
-       }
-
-       drm_dev_exit(idx);
-out_drm_gem_fb_end_cpu_access:
-       drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
-}
-EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update);
-
-void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane,
-                                          struct drm_atomic_state *state)
-{
-       struct drm_device *dev = plane->dev;
-       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
-       struct iosys_map dst = sysfb->fb_addr;
-       struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
-       void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */
-       unsigned int dst_pitch = sysfb->fb_pitch;
-       const struct drm_format_info *dst_format = sysfb->fb_format;
-       struct drm_rect dst_clip;
-       unsigned long lines, linepixels, i;
-       int idx;
-
-       drm_rect_init(&dst_clip,
-                     plane_state->src_x >> 16, plane_state->src_y >> 16,
-                     plane_state->src_w >> 16, plane_state->src_h >> 16);
-
-       lines = drm_rect_height(&dst_clip);
-       linepixels = drm_rect_width(&dst_clip);
-
-       if (!drm_dev_enter(dev, &idx))
-               return;
-
-       /* Clear buffer to black if disabled */
-       dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip);
-       for (i = 0; i < lines; ++i) {
-               memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]);
-               dst_vmap += dst_pitch;
-       }
-
-       drm_dev_exit(idx);
-}
-EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable);
-
-int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane,
-                                             struct drm_scanout_buffer *sb)
-{
-       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
-
-       sb->width = sysfb->fb_mode.hdisplay;
-       sb->height = sysfb->fb_mode.vdisplay;
-       sb->format = sysfb->fb_format;
-       sb->pitch[0] = sysfb->fb_pitch;
-       sb->map[0] = sysfb->fb_addr;
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer);
-
-/*
- * CRTC
- */
-
-static void drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state *sysfb_crtc_state)
-{
-       __drm_atomic_helper_crtc_destroy_state(&sysfb_crtc_state->base);
-
-       kfree(sysfb_crtc_state);
-}
-
-enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc,
-                                                     const struct drm_display_mode *mode)
-{
-       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev);
-
-       return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sysfb->fb_mode);
-}
-EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid);
-
-int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
-       struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
-       int ret;
-
-       if (!new_crtc_state->enable)
-               return 0;
-
-       ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state);
-       if (ret)
-               return ret;
-
-       if (new_crtc_state->color_mgmt_changed) {
-               const size_t gamma_lut_length =
-                       sysfb->fb_gamma_lut_size * sizeof(struct drm_color_lut);
-               const struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut;
-
-               if (gamma_lut && (gamma_lut->length != gamma_lut_length)) {
-                       drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length);
-                       return -EINVAL;
-               }
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check);
-
-void drm_sysfb_crtc_reset(struct drm_crtc *crtc)
-{
-       struct drm_sysfb_crtc_state *sysfb_crtc_state;
-
-       if (crtc->state)
-               drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state));
-
-       sysfb_crtc_state = kzalloc(sizeof(*sysfb_crtc_state), GFP_KERNEL);
-       if (sysfb_crtc_state)
-               __drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base);
-       else
-               __drm_atomic_helper_crtc_reset(crtc, NULL);
-}
-EXPORT_SYMBOL(drm_sysfb_crtc_reset);
-
-struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
-{
-       struct drm_device *dev = crtc->dev;
-       struct drm_crtc_state *crtc_state = crtc->state;
-       struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
-       struct drm_sysfb_crtc_state *sysfb_crtc_state;
-
-       if (drm_WARN_ON(dev, !crtc_state))
-               return NULL;
-
-       new_sysfb_crtc_state = kzalloc(sizeof(*new_sysfb_crtc_state), GFP_KERNEL);
-       if (!new_sysfb_crtc_state)
-               return NULL;
-
-       sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);
-
-       __drm_atomic_helper_crtc_duplicate_state(crtc, &new_sysfb_crtc_state->base);
-       new_sysfb_crtc_state->format = sysfb_crtc_state->format;
-
-       return &new_sysfb_crtc_state->base;
-}
-EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state);
-
-void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state)
-{
-       drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc_state));
-}
-EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state);
-
-/*
- * Connector
- */
-
-static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
-{
-       struct drm_sysfb_device *sysfb = data;
-       const u8 *edid = sysfb->edid;
-       size_t off = block * EDID_LENGTH;
-       size_t end = off + len;
-
-       if (!edid)
-               return -EINVAL;
-       if (end > EDID_LENGTH)
-               return -EINVAL;
-       memcpy(buf, &edid[off], len);
-
-       /*
-        * We don't have EDID extensions available and reporting them
-        * will upset DRM helpers. Thus clear the extension field and
-        * update the checksum. Adding the extension flag to the checksum
-        * does this.
-        */
-       buf[127] += buf[126];
-       buf[126] = 0;
-
-       return 0;
-}
-
-int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector)
-{
-       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev);
-       const struct drm_edid *drm_edid;
-
-       if (sysfb->edid) {
-               drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb);
-               drm_edid_connector_update(connector, drm_edid);
-               drm_edid_free(drm_edid);
-       }
-
-       /* Return the fixed mode even with EDID */
-       return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode);
-}
-EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes);
index 3684bd0ef08538b977c09eb66bf0a644aa0f7018..ee94d6199b601cebbcca6d7da0009a1ad634a78c 100644 (file)
 #include <drm/drm_modes.h>
 
 struct drm_format_info;
+struct drm_scanout_buffer;
+
+/*
+ * Display modes
+ */
 
 struct drm_display_mode drm_sysfb_mode(unsigned int width,
                                       unsigned int height,
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
new file mode 100644 (file)
index 0000000..ffaa252
--- /dev/null
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/export.h>
+#include <linux/slab.h>
+
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_atomic_state_helper.h>
+#include <drm/drm_damage_helper.h>
+#include <drm/drm_drv.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
+#include <drm/drm_gem_atomic_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_panic.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+
+#include "drm_sysfb_helper.h"
+
+struct drm_display_mode drm_sysfb_mode(unsigned int width,
+                                      unsigned int height,
+                                      unsigned int width_mm,
+                                      unsigned int height_mm)
+{
+       /*
+        * Assume a monitor resolution of 96 dpi to
+        * get a somewhat reasonable screen size.
+        */
+       if (!width_mm)
+               width_mm = DRM_MODE_RES_MM(width, 96ul);
+       if (!height_mm)
+               height_mm = DRM_MODE_RES_MM(height, 96ul);
+
+       {
+               const struct drm_display_mode mode = {
+                       DRM_MODE_INIT(60, width, height, width_mm, height_mm)
+               };
+
+               return mode;
+       }
+}
+EXPORT_SYMBOL(drm_sysfb_mode);
+
+/*
+ * Plane
+ */
+
+int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane,
+                                       struct drm_atomic_state *new_state)
+{
+       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
+       struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
+       struct drm_shadow_plane_state *new_shadow_plane_state =
+               to_drm_shadow_plane_state(new_plane_state);
+       struct drm_framebuffer *new_fb = new_plane_state->fb;
+       struct drm_crtc *new_crtc = new_plane_state->crtc;
+       struct drm_crtc_state *new_crtc_state = NULL;
+       struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
+       int ret;
+
+       if (new_crtc)
+               new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
+
+       ret = drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+                                                 DRM_PLANE_NO_SCALING,
+                                                 DRM_PLANE_NO_SCALING,
+                                                 false, false);
+       if (ret)
+               return ret;
+       else if (!new_plane_state->visible)
+               return 0;
+
+       if (new_fb->format != sysfb->fb_format) {
+               void *buf;
+
+               /* format conversion necessary; reserve buffer */
+               buf = drm_format_conv_state_reserve(&new_shadow_plane_state->fmtcnv_state,
+                                                   sysfb->fb_pitch, GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
+       }
+
+       new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_plane_state->crtc);
+
+       new_sysfb_crtc_state = to_drm_sysfb_crtc_state(new_crtc_state);
+       new_sysfb_crtc_state->format = new_fb->format;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check);
+
+void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
+       struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+       struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane);
+       struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
+       struct drm_framebuffer *fb = plane_state->fb;
+       unsigned int dst_pitch = sysfb->fb_pitch;
+       const struct drm_format_info *dst_format = sysfb->fb_format;
+       struct drm_atomic_helper_damage_iter iter;
+       struct drm_rect damage;
+       int ret, idx;
+
+       ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
+       if (ret)
+               return;
+
+       if (!drm_dev_enter(dev, &idx))
+               goto out_drm_gem_fb_end_cpu_access;
+
+       drm_atomic_helper_damage_iter_init(&iter, old_plane_state, plane_state);
+       drm_atomic_for_each_plane_damage(&iter, &damage) {
+               struct iosys_map dst = sysfb->fb_addr;
+               struct drm_rect dst_clip = plane_state->dst;
+
+               if (!drm_rect_intersect(&dst_clip, &damage))
+                       continue;
+
+               iosys_map_incr(&dst, drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip));
+               drm_fb_blit(&dst, &dst_pitch, dst_format->format, shadow_plane_state->data, fb,
+                           &damage, &shadow_plane_state->fmtcnv_state);
+       }
+
+       drm_dev_exit(idx);
+out_drm_gem_fb_end_cpu_access:
+       drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
+}
+EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_update);
+
+void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane,
+                                          struct drm_atomic_state *state)
+{
+       struct drm_device *dev = plane->dev;
+       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
+       struct iosys_map dst = sysfb->fb_addr;
+       struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
+       void __iomem *dst_vmap = dst.vaddr_iomem; /* TODO: Use mapping abstraction */
+       unsigned int dst_pitch = sysfb->fb_pitch;
+       const struct drm_format_info *dst_format = sysfb->fb_format;
+       struct drm_rect dst_clip;
+       unsigned long lines, linepixels, i;
+       int idx;
+
+       drm_rect_init(&dst_clip,
+                     plane_state->src_x >> 16, plane_state->src_y >> 16,
+                     plane_state->src_w >> 16, plane_state->src_h >> 16);
+
+       lines = drm_rect_height(&dst_clip);
+       linepixels = drm_rect_width(&dst_clip);
+
+       if (!drm_dev_enter(dev, &idx))
+               return;
+
+       /* Clear buffer to black if disabled */
+       dst_vmap += drm_fb_clip_offset(dst_pitch, dst_format, &dst_clip);
+       for (i = 0; i < lines; ++i) {
+               memset_io(dst_vmap, 0, linepixels * dst_format->cpp[0]);
+               dst_vmap += dst_pitch;
+       }
+
+       drm_dev_exit(idx);
+}
+EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_disable);
+
+int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane,
+                                             struct drm_scanout_buffer *sb)
+{
+       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(plane->dev);
+
+       sb->width = sysfb->fb_mode.hdisplay;
+       sb->height = sysfb->fb_mode.vdisplay;
+       sb->format = sysfb->fb_format;
+       sb->pitch[0] = sysfb->fb_pitch;
+       sb->map[0] = sysfb->fb_addr;
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_sysfb_plane_helper_get_scanout_buffer);
+
+/*
+ * CRTC
+ */
+
+static void drm_sysfb_crtc_state_destroy(struct drm_sysfb_crtc_state *sysfb_crtc_state)
+{
+       __drm_atomic_helper_crtc_destroy_state(&sysfb_crtc_state->base);
+
+       kfree(sysfb_crtc_state);
+}
+
+enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc,
+                                                     const struct drm_display_mode *mode)
+{
+       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(crtc->dev);
+
+       return drm_crtc_helper_mode_valid_fixed(crtc, mode, &sysfb->fb_mode);
+}
+EXPORT_SYMBOL(drm_sysfb_crtc_helper_mode_valid);
+
+int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(dev);
+       struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
+       int ret;
+
+       if (!new_crtc_state->enable)
+               return 0;
+
+       ret = drm_atomic_helper_check_crtc_primary_plane(new_crtc_state);
+       if (ret)
+               return ret;
+
+       if (new_crtc_state->color_mgmt_changed) {
+               const size_t gamma_lut_length =
+                       sysfb->fb_gamma_lut_size * sizeof(struct drm_color_lut);
+               const struct drm_property_blob *gamma_lut = new_crtc_state->gamma_lut;
+
+               if (gamma_lut && (gamma_lut->length != gamma_lut_length)) {
+                       drm_dbg(dev, "Incorrect gamma_lut length %zu\n", gamma_lut->length);
+                       return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL(drm_sysfb_crtc_helper_atomic_check);
+
+void drm_sysfb_crtc_reset(struct drm_crtc *crtc)
+{
+       struct drm_sysfb_crtc_state *sysfb_crtc_state;
+
+       if (crtc->state)
+               drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc->state));
+
+       sysfb_crtc_state = kzalloc(sizeof(*sysfb_crtc_state), GFP_KERNEL);
+       if (sysfb_crtc_state)
+               __drm_atomic_helper_crtc_reset(crtc, &sysfb_crtc_state->base);
+       else
+               __drm_atomic_helper_crtc_reset(crtc, NULL);
+}
+EXPORT_SYMBOL(drm_sysfb_crtc_reset);
+
+struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_crtc_state *crtc_state = crtc->state;
+       struct drm_sysfb_crtc_state *new_sysfb_crtc_state;
+       struct drm_sysfb_crtc_state *sysfb_crtc_state;
+
+       if (drm_WARN_ON(dev, !crtc_state))
+               return NULL;
+
+       new_sysfb_crtc_state = kzalloc(sizeof(*new_sysfb_crtc_state), GFP_KERNEL);
+       if (!new_sysfb_crtc_state)
+               return NULL;
+
+       sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);
+
+       __drm_atomic_helper_crtc_duplicate_state(crtc, &new_sysfb_crtc_state->base);
+       new_sysfb_crtc_state->format = sysfb_crtc_state->format;
+
+       return &new_sysfb_crtc_state->base;
+}
+EXPORT_SYMBOL(drm_sysfb_crtc_atomic_duplicate_state);
+
+void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state)
+{
+       drm_sysfb_crtc_state_destroy(to_drm_sysfb_crtc_state(crtc_state));
+}
+EXPORT_SYMBOL(drm_sysfb_crtc_atomic_destroy_state);
+
+/*
+ * Connector
+ */
+
+static int drm_sysfb_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len)
+{
+       struct drm_sysfb_device *sysfb = data;
+       const u8 *edid = sysfb->edid;
+       size_t off = block * EDID_LENGTH;
+       size_t end = off + len;
+
+       if (!edid)
+               return -EINVAL;
+       if (end > EDID_LENGTH)
+               return -EINVAL;
+       memcpy(buf, &edid[off], len);
+
+       /*
+        * We don't have EDID extensions available and reporting them
+        * will upset DRM helpers. Thus clear the extension field and
+        * update the checksum. Adding the extension flag to the checksum
+        * does this.
+        */
+       buf[127] += buf[126];
+       buf[126] = 0;
+
+       return 0;
+}
+
+int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector)
+{
+       struct drm_sysfb_device *sysfb = to_drm_sysfb_device(connector->dev);
+       const struct drm_edid *drm_edid;
+
+       if (sysfb->edid) {
+               drm_edid = drm_edid_read_custom(connector, drm_sysfb_get_edid_block, sysfb);
+               drm_edid_connector_update(connector, drm_edid);
+               drm_edid_free(drm_edid);
+       }
+
+       /* Return the fixed mode even with EDID */
+       return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode);
+}
+EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes);