From: Thomas Zimmermann Date: Thu, 10 Apr 2025 08:37:23 +0000 (+0200) Subject: drm/sysfb: Split source file X-Git-Tag: v6.16-rc1~33^2~25^2~120 X-Git-Url: https://git.kernel.dk/?a=commitdiff_plain;h=314c45e39e9abcaf2fe5449a11b6d9ad3b2c7dbc;p=linux-block.git drm/sysfb: Split source file 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 Reviewed-by: Javier Martinez Canillas Link: https://lore.kernel.org/r/20250410083834.10810-2-tzimmermann@suse.de --- diff --git a/drivers/gpu/drm/sysfb/Makefile b/drivers/gpu/drm/sysfb/Makefile index 0d2518c97163..861b4026f4a6 100644 --- a/drivers/gpu/drm/sysfb/Makefile +++ b/drivers/gpu/drm/sysfb/Makefile @@ -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 index 000000000000..c083d21fd9ca --- /dev/null +++ b/drivers/gpu/drm/sysfb/drm_sysfb.c @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include + +#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 index 262490a71792..000000000000 --- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.c +++ /dev/null @@ -1,324 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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); diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h index 3684bd0ef085..ee94d6199b60 100644 --- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h +++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h @@ -11,6 +11,11 @@ #include 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 index 000000000000..ffaa2522ab96 --- /dev/null +++ b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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);