608 lines · 18.5 KB · C
RawDownload
// 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_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 */ static u32 to_nonalpha_fourcc(u32 fourcc){	/* only handle formats with depth != 0 and alpha channel */	switch (fourcc) {	case DRM_FORMAT_ARGB1555:		return DRM_FORMAT_XRGB1555;	case DRM_FORMAT_ABGR1555:		return DRM_FORMAT_XBGR1555;	case DRM_FORMAT_RGBA5551:		return DRM_FORMAT_RGBX5551;	case DRM_FORMAT_BGRA5551:		return DRM_FORMAT_BGRX5551;	case DRM_FORMAT_ARGB8888:		return DRM_FORMAT_XRGB8888;	case DRM_FORMAT_ABGR8888:		return DRM_FORMAT_XBGR8888;	case DRM_FORMAT_RGBA8888:		return DRM_FORMAT_RGBX8888;	case DRM_FORMAT_BGRA8888:		return DRM_FORMAT_BGRX8888;	case DRM_FORMAT_ARGB2101010:		return DRM_FORMAT_XRGB2101010;	case DRM_FORMAT_ABGR2101010:		return DRM_FORMAT_XBGR2101010;	case DRM_FORMAT_RGBA1010102:		return DRM_FORMAT_RGBX1010102;	case DRM_FORMAT_BGRA1010102:		return DRM_FORMAT_BGRX1010102;	} 	return fourcc;} static bool is_listed_fourcc(const u32 *fourccs, size_t nfourccs, u32 fourcc){	const u32 *fourccs_end = fourccs + nfourccs; 	while (fourccs < fourccs_end) {		if (*fourccs == fourcc)			return true;		++fourccs;	}	return false;} /** * drm_sysfb_build_fourcc_list - Filters a list of supported color formats against *                               the device's native formats * @dev: DRM device * @native_fourccs: 4CC codes of natively supported color formats * @native_nfourccs: The number of entries in @native_fourccs * @fourccs_out: Returns 4CC codes of supported color formats * @nfourccs_out: The number of available entries in @fourccs_out * * This function create a list of supported color format from natively * supported formats and additional emulated formats. * At a minimum, most userspace programs expect at least support for * XRGB8888 on the primary plane. Sysfb devices that have to emulate * the format should use drm_sysfb_build_fourcc_list() to create a list * of supported color formats. The returned list can be handed over to * drm_universal_plane_init() et al. Native formats will go before * emulated formats. Native formats with alpha channel will be replaced * by equal formats without alpha channel, as primary planes usually * don't support alpha. Other heuristics might be applied to optimize * the sorting order. Formats near the beginning of the list are usually * preferred over formats near the end of the list. * * Returns: * The number of color-formats 4CC codes returned in @fourccs_out. */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){	/*	 * XRGB8888 is the default fallback format for most of userspace	 * and it's currently the only format that should be emulated for	 * the primary plane. Only if there's ever another default fallback,	 * it should be added here.	 */	static const u32 extra_fourccs[] = {		DRM_FORMAT_XRGB8888,	};	static const size_t extra_nfourccs = ARRAY_SIZE(extra_fourccs); 	u32 *fourccs = fourccs_out;	const u32 *fourccs_end = fourccs_out + nfourccs_out;	size_t i; 	/*	 * The device's native formats go first.	 */ 	for (i = 0; i < native_nfourccs; ++i) {		/*		 * Several DTs, boot loaders and firmware report native		 * alpha formats that are non-alpha formats instead. So		 * replace alpha formats by non-alpha formats.		 */		u32 fourcc = to_nonalpha_fourcc(native_fourccs[i]); 		if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) {			continue; /* skip duplicate entries */		} else if (fourccs == fourccs_end) {			drm_warn(dev, "Ignoring native format %p4cc\n", &fourcc);			continue; /* end of available output buffer */		} 		drm_dbg_kms(dev, "adding native format %p4cc\n", &fourcc); 		*fourccs = fourcc;		++fourccs;	} 	/*	 * The extra formats, emulated by the driver, go second.	 */ 	for (i = 0; (i < extra_nfourccs) && (fourccs < fourccs_end); ++i) {		u32 fourcc = extra_fourccs[i]; 		if (is_listed_fourcc(fourccs_out, fourccs - fourccs_out, fourcc)) {			continue; /* skip duplicate and native entries */		} else if (fourccs == fourccs_end) {			drm_warn(dev, "Ignoring emulated format %p4cc\n", &fourcc);			continue; /* end of available output buffer */		} 		drm_dbg_kms(dev, "adding emulated format %p4cc\n", &fourcc); 		*fourccs = fourcc;		++fourccs;	} 	return fourccs - fourccs_out;}EXPORT_SYMBOL(drm_sysfb_build_fourcc_list); static void drm_sysfb_plane_state_destroy(struct drm_sysfb_plane_state *sysfb_plane_state){	__drm_gem_destroy_shadow_plane_state(&sysfb_plane_state->base); 	kfree(sysfb_plane_state);} static void drm_sysfb_memcpy(struct iosys_map *dst, const unsigned int *dst_pitch,			     const struct iosys_map *src, const struct drm_framebuffer *fb,			     const struct drm_rect *clip, struct drm_format_conv_state *state){	drm_fb_memcpy(dst, dst_pitch, src, fb, clip);} static drm_sysfb_blit_func drm_sysfb_get_blit_func(u32 dst_format, u32 src_format){	if (src_format == dst_format) {		return drm_sysfb_memcpy;	} else if (src_format == DRM_FORMAT_XRGB8888) {		switch (dst_format) {		case DRM_FORMAT_RGB565:			return drm_fb_xrgb8888_to_rgb565;		case DRM_FORMAT_RGB565 | DRM_FORMAT_BIG_ENDIAN:			return drm_fb_xrgb8888_to_rgb565be;		case DRM_FORMAT_XRGB1555:			return drm_fb_xrgb8888_to_xrgb1555;		case DRM_FORMAT_ARGB1555:			return drm_fb_xrgb8888_to_argb1555;		case DRM_FORMAT_RGBA5551:			return drm_fb_xrgb8888_to_rgba5551;		case DRM_FORMAT_RGB888:			return drm_fb_xrgb8888_to_rgb888;		case DRM_FORMAT_BGR888:			return drm_fb_xrgb8888_to_bgr888;		case DRM_FORMAT_ARGB8888:			return drm_fb_xrgb8888_to_argb8888;		case DRM_FORMAT_XBGR8888:			return drm_fb_xrgb8888_to_xbgr8888;		case DRM_FORMAT_ABGR8888:			return drm_fb_xrgb8888_to_abgr8888;		case DRM_FORMAT_XRGB2101010:			return drm_fb_xrgb8888_to_xrgb2101010;		case DRM_FORMAT_ARGB2101010:			return drm_fb_xrgb8888_to_argb2101010;		case DRM_FORMAT_BGRX8888:			return drm_fb_xrgb8888_to_bgrx8888;		case DRM_FORMAT_RGB332:			return drm_fb_xrgb8888_to_rgb332;		}	} 	return NULL;} int drm_sysfb_plane_helper_begin_fb_access(struct drm_plane *plane,					   struct drm_plane_state *plane_state){	struct drm_device *dev = plane->dev;	struct drm_sysfb_plane_state *sysfb_plane_state = to_drm_sysfb_plane_state(plane_state);	struct drm_framebuffer *fb = plane_state->fb;	struct drm_crtc_state *crtc_state;	struct drm_sysfb_crtc_state *sysfb_crtc_state;	drm_sysfb_blit_func blit_to_crtc;	int ret; 	ret = drm_gem_begin_shadow_fb_access(plane, plane_state);	if (ret)		return ret; 	if (!fb)		return 0; 	ret = -EINVAL; 	crtc_state = drm_atomic_get_new_crtc_state(plane_state->state, plane_state->crtc);	if (drm_WARN_ON_ONCE(dev, !crtc_state))		goto err_drm_gem_end_shadow_fb_access;	sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state); 	if (drm_WARN_ON_ONCE(dev, !sysfb_crtc_state->format))		goto err_drm_gem_end_shadow_fb_access;	blit_to_crtc = drm_sysfb_get_blit_func(sysfb_crtc_state->format->format,					       fb->format->format);	if (!blit_to_crtc) {		drm_warn_once(dev, "No blit helper from %p4cc to %p4cc found.\n",			      &fb->format->format, &sysfb_crtc_state->format->format);		goto err_drm_gem_end_shadow_fb_access;	}	sysfb_plane_state->blit_to_crtc = blit_to_crtc; 	return 0; err_drm_gem_end_shadow_fb_access:	drm_gem_end_shadow_fb_access(plane, plane_state);	return ret;}EXPORT_SYMBOL(drm_sysfb_plane_helper_begin_fb_access); int drm_sysfb_plane_helper_atomic_check(struct drm_plane *plane,					struct drm_atomic_commit *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; 	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 = sysfb->fb_format; 	if (new_fb->format != new_sysfb_crtc_state->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;	} 	return 0;}EXPORT_SYMBOL(drm_sysfb_plane_helper_atomic_check); void drm_sysfb_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_commit *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_sysfb_plane_state *sysfb_plane_state = to_drm_sysfb_plane_state(plane_state);	struct drm_shadow_plane_state *shadow_plane_state = &sysfb_plane_state->base;	struct drm_framebuffer *fb = plane_state->fb;	unsigned int dst_pitch = sysfb->fb_pitch;	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc);	struct drm_sysfb_crtc_state *sysfb_crtc_state = to_drm_sysfb_crtc_state(crtc_state);	const struct drm_format_info *dst_format = sysfb_crtc_state->format;	drm_sysfb_blit_func blit_to_crtc = sysfb_plane_state->blit_to_crtc;	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));		blit_to_crtc(&dst, &dst_pitch, 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_commit *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); void drm_sysfb_plane_reset(struct drm_plane *plane){	struct drm_sysfb_plane_state *sysfb_plane_state; 	if (plane->state)		drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane->state)); 	sysfb_plane_state = kzalloc_obj(*sysfb_plane_state);	if (sysfb_plane_state)		__drm_gem_reset_shadow_plane(plane, &sysfb_plane_state->base);	else		__drm_gem_reset_shadow_plane(plane, NULL);}EXPORT_SYMBOL(drm_sysfb_plane_reset); struct drm_plane_state *drm_sysfb_plane_atomic_duplicate_state(struct drm_plane *plane){	struct drm_device *dev = plane->dev;	struct drm_plane_state *plane_state = plane->state;	struct drm_sysfb_plane_state *sysfb_plane_state;	struct drm_sysfb_plane_state *new_sysfb_plane_state;	struct drm_shadow_plane_state *new_shadow_plane_state; 	if (drm_WARN_ON(dev, !plane_state))		return NULL;	sysfb_plane_state = to_drm_sysfb_plane_state(plane_state); 	new_sysfb_plane_state = kzalloc_obj(*new_sysfb_plane_state);	if (!new_sysfb_plane_state)		return NULL;	new_shadow_plane_state = &new_sysfb_plane_state->base; 	__drm_gem_duplicate_shadow_plane_state(plane, new_shadow_plane_state);	new_sysfb_plane_state->blit_to_crtc = sysfb_plane_state->blit_to_crtc; 	return &new_shadow_plane_state->base;}EXPORT_SYMBOL(drm_sysfb_plane_atomic_duplicate_state); void drm_sysfb_plane_atomic_destroy_state(struct drm_plane *plane,					  struct drm_plane_state *plane_state){	drm_sysfb_plane_state_destroy(to_drm_sysfb_plane_state(plane_state));}EXPORT_SYMBOL(drm_sysfb_plane_atomic_destroy_state); /* * 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_commit *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)

Lines 1–500 of 608.