diff options
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_fb.c')
| -rw-r--r-- | drivers/gpu/drm/i915/display/intel_fb.c | 606 | 
1 files changed, 574 insertions, 32 deletions
| diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c index c60a81a81c09..fa1f375e696b 100644 --- a/drivers/gpu/drm/i915/display/intel_fb.c +++ b/drivers/gpu/drm/i915/display/intel_fb.c @@ -4,9 +4,11 @@   */  #include <drm/drm_framebuffer.h> +#include <drm/drm_modeset_helper.h>  #include "intel_display.h"  #include "intel_display_types.h" +#include "intel_dpt.h"  #include "intel_fb.h"  #define check_array_bounds(i915, a, i) drm_WARN_ON(&(i915)->drm, (i) >= ARRAY_SIZE(a)) @@ -61,6 +63,38 @@ int skl_ccs_to_main_plane(const struct drm_framebuffer *fb, int ccs_plane)  	return ccs_plane - fb->format->num_planes / 2;  } +static unsigned int gen12_aligned_scanout_stride(const struct intel_framebuffer *fb, +						 int color_plane) +{ +	struct drm_i915_private *i915 = to_i915(fb->base.dev); +	unsigned int stride = fb->base.pitches[color_plane]; + +	if (IS_ALDERLAKE_P(i915)) +		return roundup_pow_of_two(max(stride, +					      8u * intel_tile_width_bytes(&fb->base, color_plane))); + +	return stride; +} + +static unsigned int gen12_ccs_aux_stride(struct intel_framebuffer *fb, int ccs_plane) +{ +	struct drm_i915_private *i915 = to_i915(fb->base.dev); +	int main_plane = skl_ccs_to_main_plane(&fb->base, ccs_plane); +	unsigned int main_stride = fb->base.pitches[main_plane]; +	unsigned int main_tile_width = intel_tile_width_bytes(&fb->base, main_plane); + +	/* +	 * On ADL-P the AUX stride must align with a power-of-two aligned main +	 * surface stride. The stride of the allocated main surface object can +	 * be less than this POT stride, which is then autopadded to the POT +	 * size. +	 */ +	if (IS_ALDERLAKE_P(i915)) +		main_stride = gen12_aligned_scanout_stride(fb, main_plane); + +	return DIV_ROUND_UP(main_stride, 4 * main_tile_width) * 64; +} +  int skl_main_to_aux_plane(const struct drm_framebuffer *fb, int main_plane)  {  	struct drm_i915_private *i915 = to_i915(fb->dev); @@ -79,16 +113,70 @@ unsigned int intel_tile_size(const struct drm_i915_private *i915)  	return DISPLAY_VER(i915) == 2 ? 2048 : 4096;  } -unsigned int intel_tile_height(const struct drm_framebuffer *fb, int color_plane) +unsigned int +intel_tile_width_bytes(const struct drm_framebuffer *fb, int color_plane)  { -	if (is_gen12_ccs_plane(fb, color_plane)) -		return 1; +	struct drm_i915_private *dev_priv = to_i915(fb->dev); +	unsigned int cpp = fb->format->cpp[color_plane]; + +	switch (fb->modifier) { +	case DRM_FORMAT_MOD_LINEAR: +		return intel_tile_size(dev_priv); +	case I915_FORMAT_MOD_X_TILED: +		if (DISPLAY_VER(dev_priv) == 2) +			return 128; +		else +			return 512; +	case I915_FORMAT_MOD_Y_TILED_CCS: +		if (is_ccs_plane(fb, color_plane)) +			return 128; +		fallthrough; +	case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS: +	case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC: +	case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS: +		if (is_ccs_plane(fb, color_plane)) +			return 64; +		fallthrough; +	case I915_FORMAT_MOD_Y_TILED: +		if (DISPLAY_VER(dev_priv) == 2 || HAS_128_BYTE_Y_TILING(dev_priv)) +			return 128; +		else +			return 512; +	case I915_FORMAT_MOD_Yf_TILED_CCS: +		if (is_ccs_plane(fb, color_plane)) +			return 128; +		fallthrough; +	case I915_FORMAT_MOD_Yf_TILED: +		switch (cpp) { +		case 1: +			return 64; +		case 2: +		case 4: +			return 128; +		case 8: +		case 16: +			return 256; +		default: +			MISSING_CASE(cpp); +			return cpp; +		} +		break; +	default: +		MISSING_CASE(fb->modifier); +		return cpp; +	} +} +unsigned int intel_tile_height(const struct drm_framebuffer *fb, int color_plane) +{  	return intel_tile_size(to_i915(fb->dev)) /  		intel_tile_width_bytes(fb, color_plane);  } -/* Return the tile dimensions in pixel units */ +/* + * Return the tile dimensions in pixel units, based on the (2 or 4 kbyte) GTT + * page tile size. + */  static void intel_tile_dims(const struct drm_framebuffer *fb, int color_plane,  			    unsigned int *tile_width,  			    unsigned int *tile_height) @@ -100,6 +188,21 @@ static void intel_tile_dims(const struct drm_framebuffer *fb, int color_plane,  	*tile_height = intel_tile_height(fb, color_plane);  } +/* + * Return the tile dimensions in pixel units, based on the tile block size. + * The block covers the full GTT page sized tile on all tiled surfaces and + * it's a 64 byte portion of the tile on TGL+ CCS surfaces. + */ +static void intel_tile_block_dims(const struct drm_framebuffer *fb, int color_plane, +				  unsigned int *tile_width, +				  unsigned int *tile_height) +{ +	intel_tile_dims(fb, color_plane, tile_width, tile_height); + +	if (is_gen12_ccs_plane(fb, color_plane)) +		*tile_height = 1; +} +  unsigned int intel_tile_row_size(const struct drm_framebuffer *fb, int color_plane)  {  	unsigned int tile_width, tile_height; @@ -109,6 +212,31 @@ unsigned int intel_tile_row_size(const struct drm_framebuffer *fb, int color_pla  	return fb->pitches[color_plane] * tile_height;  } +unsigned int +intel_fb_align_height(const struct drm_framebuffer *fb, +		      int color_plane, unsigned int height) +{ +	unsigned int tile_height = intel_tile_height(fb, color_plane); + +	return ALIGN(height, tile_height); +} + +static unsigned int intel_fb_modifier_to_tiling(u64 fb_modifier) +{ +	switch (fb_modifier) { +	case I915_FORMAT_MOD_X_TILED: +		return I915_TILING_X; +	case I915_FORMAT_MOD_Y_TILED: +	case I915_FORMAT_MOD_Y_TILED_CCS: +	case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS: +	case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC: +	case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS: +		return I915_TILING_Y; +	default: +		return I915_TILING_NONE; +	} +} +  unsigned int intel_cursor_alignment(const struct drm_i915_private *i915)  {  	if (IS_I830(i915)) @@ -121,6 +249,70 @@ unsigned int intel_cursor_alignment(const struct drm_i915_private *i915)  		return 4 * 1024;  } +static unsigned int intel_linear_alignment(const struct drm_i915_private *dev_priv) +{ +	if (DISPLAY_VER(dev_priv) >= 9) +		return 256 * 1024; +	else if (IS_I965G(dev_priv) || IS_I965GM(dev_priv) || +		 IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) +		return 128 * 1024; +	else if (DISPLAY_VER(dev_priv) >= 4) +		return 4 * 1024; +	else +		return 0; +} + +unsigned int intel_surf_alignment(const struct drm_framebuffer *fb, +				  int color_plane) +{ +	struct drm_i915_private *dev_priv = to_i915(fb->dev); + +	if (intel_fb_uses_dpt(fb)) +		return 512 * 4096; + +	/* AUX_DIST needs only 4K alignment */ +	if (is_ccs_plane(fb, color_plane)) +		return 4096; + +	if (is_semiplanar_uv_plane(fb, color_plane)) { +		/* +		 * TODO: cross-check wrt. the bspec stride in bytes * 64 bytes +		 * alignment for linear UV planes on all platforms. +		 */ +		if (DISPLAY_VER(dev_priv) >= 12) { +			if (fb->modifier == DRM_FORMAT_MOD_LINEAR) +				return intel_linear_alignment(dev_priv); + +			return intel_tile_row_size(fb, color_plane); +		} + +		return 4096; +	} + +	drm_WARN_ON(&dev_priv->drm, color_plane != 0); + +	switch (fb->modifier) { +	case DRM_FORMAT_MOD_LINEAR: +		return intel_linear_alignment(dev_priv); +	case I915_FORMAT_MOD_X_TILED: +		if (HAS_ASYNC_FLIPS(dev_priv)) +			return 256 * 1024; +		return 0; +	case I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS: +	case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS: +	case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC: +		return 16 * 1024; +	case I915_FORMAT_MOD_Y_TILED_CCS: +	case I915_FORMAT_MOD_Yf_TILED_CCS: +	case I915_FORMAT_MOD_Y_TILED: +	case I915_FORMAT_MOD_Yf_TILED: +		return 1 * 1024 * 1024; +	default: +		MISSING_CASE(fb->modifier); +		return 0; +	} +} +  void intel_fb_plane_get_subsampling(int *hsub, int *vsub,  				    const struct drm_framebuffer *fb,  				    int color_plane) @@ -165,15 +357,29 @@ void intel_fb_plane_get_subsampling(int *hsub, int *vsub,  static void intel_fb_plane_dims(const struct intel_framebuffer *fb, int color_plane, int *w, int *h)  { +	struct drm_i915_private *i915 = to_i915(fb->base.dev);  	int main_plane = is_ccs_plane(&fb->base, color_plane) ?  			 skl_ccs_to_main_plane(&fb->base, color_plane) : 0; +	unsigned int main_width = fb->base.width; +	unsigned int main_height = fb->base.height;  	int main_hsub, main_vsub;  	int hsub, vsub; +	/* +	 * On ADL-P the CCS AUX surface layout always aligns with the +	 * power-of-two aligned main surface stride. The main surface +	 * stride in the allocated FB object may not be power-of-two +	 * sized, in which case it is auto-padded to the POT size. +	 */ +	if (IS_ALDERLAKE_P(i915) && is_ccs_plane(&fb->base, color_plane)) +		main_width = gen12_aligned_scanout_stride(fb, 0) / +			     fb->base.format->cpp[0]; +  	intel_fb_plane_get_subsampling(&main_hsub, &main_vsub, &fb->base, main_plane);  	intel_fb_plane_get_subsampling(&hsub, &vsub, &fb->base, color_plane); -	*w = fb->base.width / main_hsub / hsub; -	*h = fb->base.height / main_vsub / vsub; + +	*w = main_width / main_hsub / hsub; +	*h = main_height / main_vsub / vsub;  }  static u32 intel_adjust_tile_offset(int *x, int *y, @@ -355,17 +561,8 @@ static int intel_fb_offset_to_xy(int *x, int *y,  	unsigned int height;  	u32 alignment; -	/* -	 * All DPT color planes must be 512*4k aligned (the amount mapped by a -	 * single DPT page). For ADL_P CCS FBs this only works by requiring -	 * the allocated offsets to be 2MB aligned.  Once supoort to remap -	 * such FBs is added we can remove this requirement, as then all the -	 * planes can be remapped to an aligned offset. -	 */ -	if (IS_ALDERLAKE_P(i915) && is_ccs_modifier(fb->modifier)) -		alignment = 512 * 4096; -	else if (DISPLAY_VER(i915) >= 12 && -		 is_semiplanar_uv_plane(fb, color_plane)) +	if (DISPLAY_VER(i915) >= 12 && +	    is_semiplanar_uv_plane(fb, color_plane))  		alignment = intel_tile_row_size(fb, color_plane);  	else if (fb->modifier != DRM_FORMAT_MOD_LINEAR)  		alignment = intel_tile_size(i915); @@ -416,7 +613,12 @@ static int intel_fb_check_ccs_xy(const struct drm_framebuffer *fb, int ccs_plane  	if (!is_ccs_plane(fb, ccs_plane) || is_gen12_ccs_cc_plane(fb, ccs_plane))  		return 0; -	intel_tile_dims(fb, ccs_plane, &tile_width, &tile_height); +	/* +	 * While all the tile dimensions are based on a 2k or 4k GTT page size +	 * here the main and CCS coordinates must match only within a (64 byte +	 * on TGL+) block inside the tile. +	 */ +	intel_tile_block_dims(fb, ccs_plane, &tile_width, &tile_height);  	intel_fb_plane_get_subsampling(&hsub, &vsub, fb, ccs_plane);  	tile_width *= hsub; @@ -491,8 +693,7 @@ bool intel_fb_needs_pot_stride_remap(const struct intel_framebuffer *fb)  {  	struct drm_i915_private *i915 = to_i915(fb->base.dev); -	return IS_ALDERLAKE_P(i915) && fb->base.modifier != DRM_FORMAT_MOD_LINEAR && -	       !is_ccs_modifier(fb->base.modifier); +	return IS_ALDERLAKE_P(i915) && fb->base.modifier != DRM_FORMAT_MOD_LINEAR;  }  static int intel_fb_pitch(const struct intel_framebuffer *fb, int color_plane, unsigned int rotation) @@ -612,14 +813,16 @@ static unsigned int  plane_view_dst_stride_tiles(const struct intel_framebuffer *fb, int color_plane,  			    unsigned int pitch_tiles)  { -	if (intel_fb_needs_pot_stride_remap(fb)) +	if (intel_fb_needs_pot_stride_remap(fb)) { +		unsigned int min_stride = is_ccs_plane(&fb->base, color_plane) ? 2 : 8;  		/*  		 * ADL_P, the only platform needing a POT stride has a minimum -		 * of 8 stride tiles. +		 * of 8 main surface and 2 CCS AUX stride tiles.  		 */ -		return roundup_pow_of_two(max(pitch_tiles, 8u)); -	else +		return roundup_pow_of_two(max(pitch_tiles, min_stride)); +	} else {  		return pitch_tiles; +	}  }  static unsigned int @@ -655,7 +858,7 @@ static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_p  	unsigned int tile_height = dims->tile_height;  	unsigned int tile_size = intel_tile_size(i915);  	struct drm_rect r; -	u32 size; +	u32 size = 0;  	assign_chk_ovf(i915, remap_info->offset, obj_offset);  	assign_chk_ovf(i915, remap_info->src_stride, plane_view_src_stride_tiles(fb, color_plane, dims)); @@ -680,7 +883,7 @@ static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_p  		color_plane_info->stride = remap_info->dst_stride * tile_height; -		size = remap_info->dst_stride * remap_info->width; +		size += remap_info->dst_stride * remap_info->width;  		/* rotate the tile dimensions to match the GTT view */  		swap(tile_width, tile_height); @@ -689,6 +892,14 @@ static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_p  		check_array_bounds(i915, view->gtt.remapped.plane, color_plane); +		if (view->gtt.remapped.plane_alignment) { +			unsigned int aligned_offset = ALIGN(gtt_offset, +							    view->gtt.remapped.plane_alignment); + +			size += aligned_offset - gtt_offset; +			gtt_offset = aligned_offset; +		} +  		assign_chk_ovf(i915, remap_info->dst_stride,  			       plane_view_dst_stride_tiles(fb, color_plane, remap_info->width)); @@ -698,7 +909,7 @@ static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_p  		color_plane_info->stride = remap_info->dst_stride * tile_width *  					   fb->base.format->cpp[color_plane]; -		size = remap_info->dst_stride * remap_info->height; +		size += remap_info->dst_stride * remap_info->height;  	}  	/* @@ -745,10 +956,14 @@ calc_plane_normal_size(const struct intel_framebuffer *fb, int color_plane,  	return tiles;  } -static void intel_fb_view_init(struct intel_fb_view *view, enum i915_ggtt_view_type view_type) +static void intel_fb_view_init(struct drm_i915_private *i915, struct intel_fb_view *view, +			       enum i915_ggtt_view_type view_type)  {  	memset(view, 0, sizeof(*view));  	view->gtt.type = view_type; + +	if (view_type == I915_GGTT_VIEW_REMAPPED && IS_ALDERLAKE_P(i915)) +		view->gtt.remapped.plane_alignment = SZ_2M / PAGE_SIZE;  }  bool intel_fb_supports_90_270_rotation(const struct intel_framebuffer *fb) @@ -769,16 +984,16 @@ int intel_fill_fb_info(struct drm_i915_private *i915, struct intel_framebuffer *  	int i, num_planes = fb->base.format->num_planes;  	unsigned int tile_size = intel_tile_size(i915); -	intel_fb_view_init(&fb->normal_view, I915_GGTT_VIEW_NORMAL); +	intel_fb_view_init(i915, &fb->normal_view, I915_GGTT_VIEW_NORMAL);  	drm_WARN_ON(&i915->drm,  		    intel_fb_supports_90_270_rotation(fb) &&  		    intel_fb_needs_pot_stride_remap(fb));  	if (intel_fb_supports_90_270_rotation(fb)) -		intel_fb_view_init(&fb->rotated_view, I915_GGTT_VIEW_ROTATED); +		intel_fb_view_init(i915, &fb->rotated_view, I915_GGTT_VIEW_ROTATED);  	if (intel_fb_needs_pot_stride_remap(fb)) -		intel_fb_view_init(&fb->remapped_view, I915_GGTT_VIEW_REMAPPED); +		intel_fb_view_init(i915, &fb->remapped_view, I915_GGTT_VIEW_REMAPPED);  	for (i = 0; i < num_planes; i++) {  		struct fb_plane_view_dims view_dims; @@ -856,7 +1071,7 @@ static void intel_plane_remap_gtt(struct intel_plane_state *plane_state)  	unsigned int src_w, src_h;  	u32 gtt_offset = 0; -	intel_fb_view_init(&plane_state->view, +	intel_fb_view_init(i915, &plane_state->view,  			   drm_rotation_90_or_270(rotation) ? I915_GGTT_VIEW_ROTATED :  							      I915_GGTT_VIEW_REMAPPED); @@ -918,6 +1133,79 @@ void intel_fb_fill_view(const struct intel_framebuffer *fb, unsigned int rotatio  		*view = fb->normal_view;  } +static +u32 intel_fb_max_stride(struct drm_i915_private *dev_priv, +			u32 pixel_format, u64 modifier) +{ +	/* +	 * Arbitrary limit for gen4+ chosen to match the +	 * render engine max stride. +	 * +	 * The new CCS hash mode makes remapping impossible +	 */ +	if (DISPLAY_VER(dev_priv) < 4 || is_ccs_modifier(modifier) || +	    intel_modifier_uses_dpt(dev_priv, modifier)) +		return intel_plane_fb_max_stride(dev_priv, pixel_format, modifier); +	else if (DISPLAY_VER(dev_priv) >= 7) +		return 256 * 1024; +	else +		return 128 * 1024; +} + +static u32 +intel_fb_stride_alignment(const struct drm_framebuffer *fb, int color_plane) +{ +	struct drm_i915_private *dev_priv = to_i915(fb->dev); +	u32 tile_width; + +	if (is_surface_linear(fb, color_plane)) { +		u32 max_stride = intel_plane_fb_max_stride(dev_priv, +							   fb->format->format, +							   fb->modifier); + +		/* +		 * To make remapping with linear generally feasible +		 * we need the stride to be page aligned. +		 */ +		if (fb->pitches[color_plane] > max_stride && +		    !is_ccs_modifier(fb->modifier)) +			return intel_tile_size(dev_priv); +		else +			return 64; +	} + +	tile_width = intel_tile_width_bytes(fb, color_plane); +	if (is_ccs_modifier(fb->modifier)) { +		/* +		 * On ADL-P the stride must be either 8 tiles or a stride +		 * that is aligned to 16 tiles, required by the 16 tiles = +		 * 64 kbyte CCS AUX PTE granularity, allowing CCS FBs to be +		 * remapped. +		 */ +		if (IS_ALDERLAKE_P(dev_priv)) +			tile_width *= fb->pitches[0] <= tile_width * 8 ? 8 : 16; +		/* +		 * On TGL the surface stride must be 4 tile aligned, mapped by +		 * one 64 byte cacheline on the CCS AUX surface. +		 */ +		else if (DISPLAY_VER(dev_priv) >= 12) +			tile_width *= 4; +		/* +		 * Display WA #0531: skl,bxt,kbl,glk +		 * +		 * Render decompression and plane width > 3840 +		 * combined with horizontal panning requires the +		 * plane stride to be a multiple of 4. We'll just +		 * require the entire fb to accommodate that to avoid +		 * potential runtime errors at plane configuration time. +		 */ +		else if ((DISPLAY_VER(dev_priv) == 9 || IS_GEMINILAKE(dev_priv)) && +			 color_plane == 0 && fb->width > 3840) +			tile_width *= 4; +	} +	return tile_width; +} +  static int intel_plane_check_stride(const struct intel_plane_state *plane_state)  {  	struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane); @@ -981,3 +1269,257 @@ int intel_plane_compute_gtt(struct intel_plane_state *plane_state)  	return intel_plane_check_stride(plane_state);  } + +static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ +	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb); + +	drm_framebuffer_cleanup(fb); + +	if (intel_fb_uses_dpt(fb)) +		intel_dpt_destroy(intel_fb->dpt_vm); + +	intel_frontbuffer_put(intel_fb->frontbuffer); + +	kfree(intel_fb); +} + +static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb, +						struct drm_file *file, +						unsigned int *handle) +{ +	struct drm_i915_gem_object *obj = intel_fb_obj(fb); +	struct drm_i915_private *i915 = to_i915(obj->base.dev); + +	if (i915_gem_object_is_userptr(obj)) { +		drm_dbg(&i915->drm, +			"attempting to use a userptr for a framebuffer, denied\n"); +		return -EINVAL; +	} + +	return drm_gem_handle_create(file, &obj->base, handle); +} + +static int intel_user_framebuffer_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 drm_i915_gem_object *obj = intel_fb_obj(fb); + +	i915_gem_object_flush_if_display(obj); +	intel_frontbuffer_flush(to_intel_frontbuffer(fb), ORIGIN_DIRTYFB); + +	return 0; +} + +static const struct drm_framebuffer_funcs intel_fb_funcs = { +	.destroy = intel_user_framebuffer_destroy, +	.create_handle = intel_user_framebuffer_create_handle, +	.dirty = intel_user_framebuffer_dirty, +}; + +int intel_framebuffer_init(struct intel_framebuffer *intel_fb, +			   struct drm_i915_gem_object *obj, +			   struct drm_mode_fb_cmd2 *mode_cmd) +{ +	struct drm_i915_private *dev_priv = to_i915(obj->base.dev); +	struct drm_framebuffer *fb = &intel_fb->base; +	u32 max_stride; +	unsigned int tiling, stride; +	int ret = -EINVAL; +	int i; + +	intel_fb->frontbuffer = intel_frontbuffer_get(obj); +	if (!intel_fb->frontbuffer) +		return -ENOMEM; + +	i915_gem_object_lock(obj, NULL); +	tiling = i915_gem_object_get_tiling(obj); +	stride = i915_gem_object_get_stride(obj); +	i915_gem_object_unlock(obj); + +	if (mode_cmd->flags & DRM_MODE_FB_MODIFIERS) { +		/* +		 * If there's a fence, enforce that +		 * the fb modifier and tiling mode match. +		 */ +		if (tiling != I915_TILING_NONE && +		    tiling != intel_fb_modifier_to_tiling(mode_cmd->modifier[0])) { +			drm_dbg_kms(&dev_priv->drm, +				    "tiling_mode doesn't match fb modifier\n"); +			goto err; +		} +	} else { +		if (tiling == I915_TILING_X) { +			mode_cmd->modifier[0] = I915_FORMAT_MOD_X_TILED; +		} else if (tiling == I915_TILING_Y) { +			drm_dbg_kms(&dev_priv->drm, +				    "No Y tiling for legacy addfb\n"); +			goto err; +		} +	} + +	if (!drm_any_plane_has_format(&dev_priv->drm, +				      mode_cmd->pixel_format, +				      mode_cmd->modifier[0])) { +		drm_dbg_kms(&dev_priv->drm, +			    "unsupported pixel format %p4cc / modifier 0x%llx\n", +			    &mode_cmd->pixel_format, mode_cmd->modifier[0]); +		goto err; +	} + +	/* +	 * gen2/3 display engine uses the fence if present, +	 * so the tiling mode must match the fb modifier exactly. +	 */ +	if (DISPLAY_VER(dev_priv) < 4 && +	    tiling != intel_fb_modifier_to_tiling(mode_cmd->modifier[0])) { +		drm_dbg_kms(&dev_priv->drm, +			    "tiling_mode must match fb modifier exactly on gen2/3\n"); +		goto err; +	} + +	max_stride = intel_fb_max_stride(dev_priv, mode_cmd->pixel_format, +					 mode_cmd->modifier[0]); +	if (mode_cmd->pitches[0] > max_stride) { +		drm_dbg_kms(&dev_priv->drm, +			    "%s pitch (%u) must be at most %d\n", +			    mode_cmd->modifier[0] != DRM_FORMAT_MOD_LINEAR ? +			    "tiled" : "linear", +			    mode_cmd->pitches[0], max_stride); +		goto err; +	} + +	/* +	 * If there's a fence, enforce that +	 * the fb pitch and fence stride match. +	 */ +	if (tiling != I915_TILING_NONE && mode_cmd->pitches[0] != stride) { +		drm_dbg_kms(&dev_priv->drm, +			    "pitch (%d) must match tiling stride (%d)\n", +			    mode_cmd->pitches[0], stride); +		goto err; +	} + +	/* FIXME need to adjust LINOFF/TILEOFF accordingly. */ +	if (mode_cmd->offsets[0] != 0) { +		drm_dbg_kms(&dev_priv->drm, +			    "plane 0 offset (0x%08x) must be 0\n", +			    mode_cmd->offsets[0]); +		goto err; +	} + +	drm_helper_mode_fill_fb_struct(&dev_priv->drm, fb, mode_cmd); + +	for (i = 0; i < fb->format->num_planes; i++) { +		u32 stride_alignment; + +		if (mode_cmd->handles[i] != mode_cmd->handles[0]) { +			drm_dbg_kms(&dev_priv->drm, "bad plane %d handle\n", +				    i); +			goto err; +		} + +		stride_alignment = intel_fb_stride_alignment(fb, i); +		if (fb->pitches[i] & (stride_alignment - 1)) { +			drm_dbg_kms(&dev_priv->drm, +				    "plane %d pitch (%d) must be at least %u byte aligned\n", +				    i, fb->pitches[i], stride_alignment); +			goto err; +		} + +		if (is_gen12_ccs_plane(fb, i) && !is_gen12_ccs_cc_plane(fb, i)) { +			int ccs_aux_stride = gen12_ccs_aux_stride(intel_fb, i); + +			if (fb->pitches[i] != ccs_aux_stride) { +				drm_dbg_kms(&dev_priv->drm, +					    "ccs aux plane %d pitch (%d) must be %d\n", +					    i, +					    fb->pitches[i], ccs_aux_stride); +				goto err; +			} +		} + +		fb->obj[i] = &obj->base; +	} + +	ret = intel_fill_fb_info(dev_priv, intel_fb); +	if (ret) +		goto err; + +	if (intel_fb_uses_dpt(fb)) { +		struct i915_address_space *vm; + +		vm = intel_dpt_create(intel_fb); +		if (IS_ERR(vm)) { +			ret = PTR_ERR(vm); +			goto err; +		} + +		intel_fb->dpt_vm = vm; +	} + +	ret = drm_framebuffer_init(&dev_priv->drm, fb, &intel_fb_funcs); +	if (ret) { +		drm_err(&dev_priv->drm, "framebuffer init failed %d\n", ret); +		goto err; +	} + +	return 0; + +err: +	intel_frontbuffer_put(intel_fb->frontbuffer); +	return ret; +} + +struct drm_framebuffer * +intel_user_framebuffer_create(struct drm_device *dev, +			      struct drm_file *filp, +			      const struct drm_mode_fb_cmd2 *user_mode_cmd) +{ +	struct drm_framebuffer *fb; +	struct drm_i915_gem_object *obj; +	struct drm_mode_fb_cmd2 mode_cmd = *user_mode_cmd; +	struct drm_i915_private *i915; + +	obj = i915_gem_object_lookup(filp, mode_cmd.handles[0]); +	if (!obj) +		return ERR_PTR(-ENOENT); + +	/* object is backed with LMEM for discrete */ +	i915 = to_i915(obj->base.dev); +	if (HAS_LMEM(i915) && !i915_gem_object_can_migrate(obj, INTEL_REGION_LMEM)) { +		/* object is "remote", not in local memory */ +		i915_gem_object_put(obj); +		return ERR_PTR(-EREMOTE); +	} + +	fb = intel_framebuffer_create(obj, &mode_cmd); +	i915_gem_object_put(obj); + +	return fb; +} + +struct drm_framebuffer * +intel_framebuffer_create(struct drm_i915_gem_object *obj, +			 struct drm_mode_fb_cmd2 *mode_cmd) +{ +	struct intel_framebuffer *intel_fb; +	int ret; + +	intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL); +	if (!intel_fb) +		return ERR_PTR(-ENOMEM); + +	ret = intel_framebuffer_init(intel_fb, obj, mode_cmd); +	if (ret) +		goto err; + +	return &intel_fb->base; + +err: +	kfree(intel_fb); +	return ERR_PTR(ret); +} |