Files
linux/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
Thomas Zimmermann cb71de0925 drm/sysfb: Lookup blit function during atomic check
Some configurations of sysfb outputs require format conversion from
framebuffer to scanout buffer. It is a driver bug if the conversion
helper is missing, yet it might happen on odd scanout formats. The old
code, based on drm_fb_blit(), only detects this situation during the
commit's hardware update, which is too late to abort the update.

Lookup the correct blit helper as part of the check phase. Then store
it in the sysfb plane state. Allows for detection of a missing helper
before the commit has started. Also avoids drm_fb_blit()'s large switch
statement on each updated scanline. Only a single lookup has to be done.

The lookup is in drm_sysfb_get_blit_func(), which only tracks formats
supported by sysfb drivers.

The lookup happens in sysfb's begin_fb_access helper instead of its
atomic_check helper. This allows vesadrm, and possibly other drivers,
to implement their own atomic_check without interfering with blit
lookups. Vesadrm implements XRGB8888 on top of R8 formats with the
help of the atomic_check. Doing the blit lookup in begin_fb_access then
always uses the correct CRTC format on all drivers.

v2:
- vesadrm: use drm_sysfb_plane_helper_begin_fb_access()
- fix type in commit description (Javier)

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://lore.kernel.org/r/20250918154207.84714-3-tzimmermann@suse.de
2025-09-23 15:06:06 +02:00

219 lines
6.6 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef DRM_SYSFB_HELPER_H
#define DRM_SYSFB_HELPER_H
#include <linux/container_of.h>
#include <linux/iosys-map.h>
#include <video/pixel_format.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_modes.h>
struct drm_format_info;
struct drm_scanout_buffer;
struct screen_info;
typedef void (*drm_sysfb_blit_func)(struct iosys_map *, const unsigned int *,
const struct iosys_map *,
const struct drm_framebuffer *,
const struct drm_rect *,
struct drm_format_conv_state *);
/*
* Input parsing
*/
struct drm_sysfb_format {
struct pixel_format pixel;
u32 fourcc;
};
int drm_sysfb_get_validated_int(struct drm_device *dev, const char *name,
u64 value, u32 max);
int drm_sysfb_get_validated_int0(struct drm_device *dev, const char *name,
u64 value, u32 max);
#if defined(CONFIG_SCREEN_INFO)
int drm_sysfb_get_width_si(struct drm_device *dev, const struct screen_info *si);
int drm_sysfb_get_height_si(struct drm_device *dev, const struct screen_info *si);
struct resource *drm_sysfb_get_memory_si(struct drm_device *dev,
const struct screen_info *si,
struct resource *res);
int drm_sysfb_get_stride_si(struct drm_device *dev, const struct screen_info *si,
const struct drm_format_info *format,
unsigned int width, unsigned int height, u64 size);
u64 drm_sysfb_get_visible_size_si(struct drm_device *dev, const struct screen_info *si,
unsigned int height, unsigned int stride, u64 size);
const struct drm_format_info *drm_sysfb_get_format_si(struct drm_device *dev,
const struct drm_sysfb_format *formats,
size_t nformats,
const struct screen_info *si);
#endif
/*
* Input parsing
*/
int drm_sysfb_get_validated_int(struct drm_device *dev, const char *name,
u64 value, u32 max);
int drm_sysfb_get_validated_int0(struct drm_device *dev, const char *name,
u64 value, u32 max);
/*
* Display modes
*/
struct drm_display_mode drm_sysfb_mode(unsigned int width,
unsigned int height,
unsigned int width_mm,
unsigned int height_mm);
/*
* Device
*/
struct drm_sysfb_device {
struct drm_device dev;
const u8 *edid; /* can be NULL */
/* hardware settings */
struct drm_display_mode fb_mode;
const struct drm_format_info *fb_format;
unsigned int fb_pitch;
unsigned int fb_gamma_lut_size;
/* hardware-framebuffer kernel address */
struct iosys_map fb_addr;
};
static inline struct drm_sysfb_device *to_drm_sysfb_device(struct drm_device *dev)
{
return container_of(dev, struct drm_sysfb_device, dev);
}
/*
* Plane
*/
struct drm_sysfb_plane_state {
struct drm_shadow_plane_state base;
/* transfers framebuffer data to scanout buffer in CRTC format */
drm_sysfb_blit_func blit_to_crtc;
};
static inline struct drm_sysfb_plane_state *
to_drm_sysfb_plane_state(struct drm_plane_state *base)
{
return container_of(to_drm_shadow_plane_state(base), struct drm_sysfb_plane_state, base);
}
size_t drm_sysfb_build_fourcc_list(struct drm_device *dev,
const u32 *native_fourccs, size_t native_nfourccs,
u32 *fourccs_out, size_t nfourccs_out);
int drm_sysfb_plane_helper_begin_fb_access(struct drm_plane *plane,
struct drm_plane_state *plane_state);
int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane,
struct drm_atomic_state *new_state);
void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane,
struct drm_atomic_state *state);
void drm_sysfb_plane_helper_atomic_disable(struct drm_plane *plane,
struct drm_atomic_state *state);
int drm_sysfb_plane_helper_get_scanout_buffer(struct drm_plane *plane,
struct drm_scanout_buffer *sb);
#define DRM_SYSFB_PLANE_NFORMATS(_num_native) \
((_num_native) + 1)
#define DRM_SYSFB_PLANE_FORMAT_MODIFIERS \
DRM_FORMAT_MOD_LINEAR, \
DRM_FORMAT_MOD_INVALID
#define DRM_SYSFB_PLANE_HELPER_FUNCS \
.begin_fb_access = drm_sysfb_plane_helper_begin_fb_access, \
.end_fb_access = drm_gem_end_shadow_fb_access, \
.atomic_check = drm_sysfb_plane_helper_atomic_check, \
.atomic_update = drm_sysfb_plane_helper_atomic_update, \
.atomic_disable = drm_sysfb_plane_helper_atomic_disable, \
.get_scanout_buffer = drm_sysfb_plane_helper_get_scanout_buffer
void drm_sysfb_plane_reset(struct drm_plane *plane);
struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane);
void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane,
struct drm_plane_state *plane_state);
#define DRM_SYSFB_PLANE_FUNCS \
.reset = drm_sysfb_plane_reset, \
.update_plane = drm_atomic_helper_update_plane, \
.disable_plane = drm_atomic_helper_disable_plane, \
.atomic_duplicate_state = drm_sysfb_plane_atomic_duplicate_state, \
.atomic_destroy_state = drm_sysfb_plane_atomic_destroy_state
/*
* CRTC
*/
struct drm_sysfb_crtc_state {
struct drm_crtc_state base;
/* CRTC input color format; required for color mgmt. */
const struct drm_format_info *format;
};
static inline struct drm_sysfb_crtc_state *
to_drm_sysfb_crtc_state(struct drm_crtc_state *base)
{
return container_of(base, struct drm_sysfb_crtc_state, base);
}
enum drm_mode_status drm_sysfb_crtc_helper_mode_valid(struct drm_crtc *crtc,
const struct drm_display_mode *mode);
int drm_sysfb_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state);
#define DRM_SYSFB_CRTC_HELPER_FUNCS \
.mode_valid = drm_sysfb_crtc_helper_mode_valid, \
.atomic_check = drm_sysfb_crtc_helper_atomic_check
void drm_sysfb_crtc_reset(struct drm_crtc *crtc);
struct drm_crtc_state *drm_sysfb_crtc_atomic_duplicate_state(struct drm_crtc *crtc);
void drm_sysfb_crtc_atomic_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state);
#define DRM_SYSFB_CRTC_FUNCS \
.reset = drm_sysfb_crtc_reset, \
.set_config = drm_atomic_helper_set_config, \
.page_flip = drm_atomic_helper_page_flip, \
.atomic_duplicate_state = drm_sysfb_crtc_atomic_duplicate_state, \
.atomic_destroy_state = drm_sysfb_crtc_atomic_destroy_state
/*
* Connector
*/
int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector);
#define DRM_SYSFB_CONNECTOR_HELPER_FUNCS \
.get_modes = drm_sysfb_connector_helper_get_modes
#define DRM_SYSFB_CONNECTOR_FUNCS \
.reset = drm_atomic_helper_connector_reset, \
.fill_modes = drm_helper_probe_single_connector_modes, \
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, \
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state
/*
* Mode config
*/
#define DRM_SYSFB_MODE_CONFIG_FUNCS \
.fb_create = drm_gem_fb_create_with_dirty, \
.atomic_check = drm_atomic_helper_check, \
.atomic_commit = drm_atomic_helper_commit
#endif