1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2021 Intel Corporation
4  */
5 
6 #include <drm/drm_framebuffer.h>
7 
8 #include "intel_display.h"
9 #include "intel_display_types.h"
10 #include "intel_fb.h"
11 
12 #define check_array_bounds(i915, a, i) drm_WARN_ON(&(i915)->drm, (i) >= ARRAY_SIZE(a))
13 
14 bool is_ccs_plane(const struct drm_framebuffer *fb, int plane)
15 {
16 	if (!is_ccs_modifier(fb->modifier))
17 		return false;
18 
19 	return plane >= fb->format->num_planes / 2;
20 }
21 
22 bool is_gen12_ccs_plane(const struct drm_framebuffer *fb, int plane)
23 {
24 	return is_gen12_ccs_modifier(fb->modifier) && is_ccs_plane(fb, plane);
25 }
26 
27 bool is_gen12_ccs_cc_plane(const struct drm_framebuffer *fb, int plane)
28 {
29 	return fb->modifier == I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC &&
30 	       plane == 2;
31 }
32 
33 bool is_aux_plane(const struct drm_framebuffer *fb, int plane)
34 {
35 	if (is_ccs_modifier(fb->modifier))
36 		return is_ccs_plane(fb, plane);
37 
38 	return plane == 1;
39 }
40 
41 bool is_semiplanar_uv_plane(const struct drm_framebuffer *fb, int color_plane)
42 {
43 	return intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier) &&
44 		color_plane == 1;
45 }
46 
47 bool is_surface_linear(const struct drm_framebuffer *fb, int color_plane)
48 {
49 	return fb->modifier == DRM_FORMAT_MOD_LINEAR ||
50 	       is_gen12_ccs_plane(fb, color_plane);
51 }
52 
53 int main_to_ccs_plane(const struct drm_framebuffer *fb, int main_plane)
54 {
55 	drm_WARN_ON(fb->dev, !is_ccs_modifier(fb->modifier) ||
56 		    (main_plane && main_plane >= fb->format->num_planes / 2));
57 
58 	return fb->format->num_planes / 2 + main_plane;
59 }
60 
61 int skl_ccs_to_main_plane(const struct drm_framebuffer *fb, int ccs_plane)
62 {
63 	drm_WARN_ON(fb->dev, !is_ccs_modifier(fb->modifier) ||
64 		    ccs_plane < fb->format->num_planes / 2);
65 
66 	if (is_gen12_ccs_cc_plane(fb, ccs_plane))
67 		return 0;
68 
69 	return ccs_plane - fb->format->num_planes / 2;
70 }
71 
72 int skl_main_to_aux_plane(const struct drm_framebuffer *fb, int main_plane)
73 {
74 	struct drm_i915_private *i915 = to_i915(fb->dev);
75 
76 	if (is_ccs_modifier(fb->modifier))
77 		return main_to_ccs_plane(fb, main_plane);
78 	else if (DISPLAY_VER(i915) < 11 &&
79 		 intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier))
80 		return 1;
81 	else
82 		return 0;
83 }
84 
85 unsigned int intel_tile_size(const struct drm_i915_private *i915)
86 {
87 	return IS_DISPLAY_VER(i915, 2) ? 2048 : 4096;
88 }
89 
90 unsigned int intel_tile_height(const struct drm_framebuffer *fb, int color_plane)
91 {
92 	if (is_gen12_ccs_plane(fb, color_plane))
93 		return 1;
94 
95 	return intel_tile_size(to_i915(fb->dev)) /
96 		intel_tile_width_bytes(fb, color_plane);
97 }
98 
99 /* Return the tile dimensions in pixel units */
100 static void intel_tile_dims(const struct drm_framebuffer *fb, int color_plane,
101 			    unsigned int *tile_width,
102 			    unsigned int *tile_height)
103 {
104 	unsigned int tile_width_bytes = intel_tile_width_bytes(fb, color_plane);
105 	unsigned int cpp = fb->format->cpp[color_plane];
106 
107 	*tile_width = tile_width_bytes / cpp;
108 	*tile_height = intel_tile_height(fb, color_plane);
109 }
110 
111 unsigned int intel_tile_row_size(const struct drm_framebuffer *fb, int color_plane)
112 {
113 	unsigned int tile_width, tile_height;
114 
115 	intel_tile_dims(fb, color_plane, &tile_width, &tile_height);
116 
117 	return fb->pitches[color_plane] * tile_height;
118 }
119 
120 unsigned int intel_cursor_alignment(const struct drm_i915_private *i915)
121 {
122 	if (IS_I830(i915))
123 		return 16 * 1024;
124 	else if (IS_I85X(i915))
125 		return 256;
126 	else if (IS_I845G(i915) || IS_I865G(i915))
127 		return 32;
128 	else
129 		return 4 * 1024;
130 }
131 
132 void intel_fb_plane_get_subsampling(int *hsub, int *vsub,
133 				    const struct drm_framebuffer *fb,
134 				    int color_plane)
135 {
136 	int main_plane;
137 
138 	if (color_plane == 0) {
139 		*hsub = 1;
140 		*vsub = 1;
141 
142 		return;
143 	}
144 
145 	/*
146 	 * TODO: Deduct the subsampling from the char block for all CCS
147 	 * formats and planes.
148 	 */
149 	if (!is_gen12_ccs_plane(fb, color_plane)) {
150 		*hsub = fb->format->hsub;
151 		*vsub = fb->format->vsub;
152 
153 		return;
154 	}
155 
156 	main_plane = skl_ccs_to_main_plane(fb, color_plane);
157 	*hsub = drm_format_info_block_width(fb->format, color_plane) /
158 		drm_format_info_block_width(fb->format, main_plane);
159 
160 	/*
161 	 * The min stride check in the core framebuffer_check() function
162 	 * assumes that format->hsub applies to every plane except for the
163 	 * first plane. That's incorrect for the CCS AUX plane of the first
164 	 * plane, but for the above check to pass we must define the block
165 	 * width with that subsampling applied to it. Adjust the width here
166 	 * accordingly, so we can calculate the actual subsampling factor.
167 	 */
168 	if (main_plane == 0)
169 		*hsub *= fb->format->hsub;
170 
171 	*vsub = 32;
172 }
173 
174 static void intel_fb_plane_dims(int *w, int *h, struct drm_framebuffer *fb, int color_plane)
175 {
176 	int main_plane = is_ccs_plane(fb, color_plane) ?
177 			 skl_ccs_to_main_plane(fb, color_plane) : 0;
178 	int main_hsub, main_vsub;
179 	int hsub, vsub;
180 
181 	intel_fb_plane_get_subsampling(&main_hsub, &main_vsub, fb, main_plane);
182 	intel_fb_plane_get_subsampling(&hsub, &vsub, fb, color_plane);
183 	*w = fb->width / main_hsub / hsub;
184 	*h = fb->height / main_vsub / vsub;
185 }
186 
187 static u32 intel_adjust_tile_offset(int *x, int *y,
188 				    unsigned int tile_width,
189 				    unsigned int tile_height,
190 				    unsigned int tile_size,
191 				    unsigned int pitch_tiles,
192 				    u32 old_offset,
193 				    u32 new_offset)
194 {
195 	unsigned int pitch_pixels = pitch_tiles * tile_width;
196 	unsigned int tiles;
197 
198 	WARN_ON(old_offset & (tile_size - 1));
199 	WARN_ON(new_offset & (tile_size - 1));
200 	WARN_ON(new_offset > old_offset);
201 
202 	tiles = (old_offset - new_offset) / tile_size;
203 
204 	*y += tiles / pitch_tiles * tile_height;
205 	*x += tiles % pitch_tiles * tile_width;
206 
207 	/* minimize x in case it got needlessly big */
208 	*y += *x / pitch_pixels * tile_height;
209 	*x %= pitch_pixels;
210 
211 	return new_offset;
212 }
213 
214 static u32 intel_adjust_aligned_offset(int *x, int *y,
215 				       const struct drm_framebuffer *fb,
216 				       int color_plane,
217 				       unsigned int rotation,
218 				       unsigned int pitch,
219 				       u32 old_offset, u32 new_offset)
220 {
221 	struct drm_i915_private *i915 = to_i915(fb->dev);
222 	unsigned int cpp = fb->format->cpp[color_plane];
223 
224 	drm_WARN_ON(&i915->drm, new_offset > old_offset);
225 
226 	if (!is_surface_linear(fb, color_plane)) {
227 		unsigned int tile_size, tile_width, tile_height;
228 		unsigned int pitch_tiles;
229 
230 		tile_size = intel_tile_size(i915);
231 		intel_tile_dims(fb, color_plane, &tile_width, &tile_height);
232 
233 		if (drm_rotation_90_or_270(rotation)) {
234 			pitch_tiles = pitch / tile_height;
235 			swap(tile_width, tile_height);
236 		} else {
237 			pitch_tiles = pitch / (tile_width * cpp);
238 		}
239 
240 		intel_adjust_tile_offset(x, y, tile_width, tile_height,
241 					 tile_size, pitch_tiles,
242 					 old_offset, new_offset);
243 	} else {
244 		old_offset += *y * pitch + *x * cpp;
245 
246 		*y = (old_offset - new_offset) / pitch;
247 		*x = ((old_offset - new_offset) - *y * pitch) / cpp;
248 	}
249 
250 	return new_offset;
251 }
252 
253 /*
254  * Adjust the tile offset by moving the difference into
255  * the x/y offsets.
256  */
257 u32 intel_plane_adjust_aligned_offset(int *x, int *y,
258 				      const struct intel_plane_state *state,
259 				      int color_plane,
260 				      u32 old_offset, u32 new_offset)
261 {
262 	return intel_adjust_aligned_offset(x, y, state->hw.fb, color_plane,
263 					   state->hw.rotation,
264 					   state->view.color_plane[color_plane].stride,
265 					   old_offset, new_offset);
266 }
267 
268 /*
269  * Computes the aligned offset to the base tile and adjusts
270  * x, y. bytes per pixel is assumed to be a power-of-two.
271  *
272  * In the 90/270 rotated case, x and y are assumed
273  * to be already rotated to match the rotated GTT view, and
274  * pitch is the tile_height aligned framebuffer height.
275  *
276  * This function is used when computing the derived information
277  * under intel_framebuffer, so using any of that information
278  * here is not allowed. Anything under drm_framebuffer can be
279  * used. This is why the user has to pass in the pitch since it
280  * is specified in the rotated orientation.
281  */
282 static u32 intel_compute_aligned_offset(struct drm_i915_private *i915,
283 					int *x, int *y,
284 					const struct drm_framebuffer *fb,
285 					int color_plane,
286 					unsigned int pitch,
287 					unsigned int rotation,
288 					u32 alignment)
289 {
290 	unsigned int cpp = fb->format->cpp[color_plane];
291 	u32 offset, offset_aligned;
292 
293 	if (!is_surface_linear(fb, color_plane)) {
294 		unsigned int tile_size, tile_width, tile_height;
295 		unsigned int tile_rows, tiles, pitch_tiles;
296 
297 		tile_size = intel_tile_size(i915);
298 		intel_tile_dims(fb, color_plane, &tile_width, &tile_height);
299 
300 		if (drm_rotation_90_or_270(rotation)) {
301 			pitch_tiles = pitch / tile_height;
302 			swap(tile_width, tile_height);
303 		} else {
304 			pitch_tiles = pitch / (tile_width * cpp);
305 		}
306 
307 		tile_rows = *y / tile_height;
308 		*y %= tile_height;
309 
310 		tiles = *x / tile_width;
311 		*x %= tile_width;
312 
313 		offset = (tile_rows * pitch_tiles + tiles) * tile_size;
314 
315 		offset_aligned = offset;
316 		if (alignment)
317 			offset_aligned = rounddown(offset_aligned, alignment);
318 
319 		intel_adjust_tile_offset(x, y, tile_width, tile_height,
320 					 tile_size, pitch_tiles,
321 					 offset, offset_aligned);
322 	} else {
323 		offset = *y * pitch + *x * cpp;
324 		offset_aligned = offset;
325 		if (alignment) {
326 			offset_aligned = rounddown(offset_aligned, alignment);
327 			*y = (offset % alignment) / pitch;
328 			*x = ((offset % alignment) - *y * pitch) / cpp;
329 		} else {
330 			*y = *x = 0;
331 		}
332 	}
333 
334 	return offset_aligned;
335 }
336 
337 u32 intel_plane_compute_aligned_offset(int *x, int *y,
338 				       const struct intel_plane_state *state,
339 				       int color_plane)
340 {
341 	struct intel_plane *intel_plane = to_intel_plane(state->uapi.plane);
342 	struct drm_i915_private *i915 = to_i915(intel_plane->base.dev);
343 	const struct drm_framebuffer *fb = state->hw.fb;
344 	unsigned int rotation = state->hw.rotation;
345 	int pitch = state->view.color_plane[color_plane].stride;
346 	u32 alignment;
347 
348 	if (intel_plane->id == PLANE_CURSOR)
349 		alignment = intel_cursor_alignment(i915);
350 	else
351 		alignment = intel_surf_alignment(fb, color_plane);
352 
353 	return intel_compute_aligned_offset(i915, x, y, fb, color_plane,
354 					    pitch, rotation, alignment);
355 }
356 
357 /* Convert the fb->offset[] into x/y offsets */
358 static int intel_fb_offset_to_xy(int *x, int *y,
359 				 const struct drm_framebuffer *fb,
360 				 int color_plane)
361 {
362 	struct drm_i915_private *i915 = to_i915(fb->dev);
363 	unsigned int height;
364 	u32 alignment;
365 
366 	if (DISPLAY_VER(i915) >= 12 &&
367 	    is_semiplanar_uv_plane(fb, color_plane))
368 		alignment = intel_tile_row_size(fb, color_plane);
369 	else if (fb->modifier != DRM_FORMAT_MOD_LINEAR)
370 		alignment = intel_tile_size(i915);
371 	else
372 		alignment = 0;
373 
374 	if (alignment != 0 && fb->offsets[color_plane] % alignment) {
375 		drm_dbg_kms(&i915->drm,
376 			    "Misaligned offset 0x%08x for color plane %d\n",
377 			    fb->offsets[color_plane], color_plane);
378 		return -EINVAL;
379 	}
380 
381 	height = drm_framebuffer_plane_height(fb->height, fb, color_plane);
382 	height = ALIGN(height, intel_tile_height(fb, color_plane));
383 
384 	/* Catch potential overflows early */
385 	if (add_overflows_t(u32, mul_u32_u32(height, fb->pitches[color_plane]),
386 			    fb->offsets[color_plane])) {
387 		drm_dbg_kms(&i915->drm,
388 			    "Bad offset 0x%08x or pitch %d for color plane %d\n",
389 			    fb->offsets[color_plane], fb->pitches[color_plane],
390 			    color_plane);
391 		return -ERANGE;
392 	}
393 
394 	*x = 0;
395 	*y = 0;
396 
397 	intel_adjust_aligned_offset(x, y,
398 				    fb, color_plane, DRM_MODE_ROTATE_0,
399 				    fb->pitches[color_plane],
400 				    fb->offsets[color_plane], 0);
401 
402 	return 0;
403 }
404 
405 static int intel_fb_check_ccs_xy(const struct drm_framebuffer *fb, int ccs_plane, int x, int y)
406 {
407 	struct drm_i915_private *i915 = to_i915(fb->dev);
408 	const struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
409 	int main_plane;
410 	int hsub, vsub;
411 	int tile_width, tile_height;
412 	int ccs_x, ccs_y;
413 	int main_x, main_y;
414 
415 	if (!is_ccs_plane(fb, ccs_plane) || is_gen12_ccs_cc_plane(fb, ccs_plane))
416 		return 0;
417 
418 	intel_tile_dims(fb, ccs_plane, &tile_width, &tile_height);
419 	intel_fb_plane_get_subsampling(&hsub, &vsub, fb, ccs_plane);
420 
421 	tile_width *= hsub;
422 	tile_height *= vsub;
423 
424 	ccs_x = (x * hsub) % tile_width;
425 	ccs_y = (y * vsub) % tile_height;
426 
427 	main_plane = skl_ccs_to_main_plane(fb, ccs_plane);
428 	main_x = intel_fb->normal_view.color_plane[main_plane].x % tile_width;
429 	main_y = intel_fb->normal_view.color_plane[main_plane].y % tile_height;
430 
431 	/*
432 	 * CCS doesn't have its own x/y offset register, so the intra CCS tile
433 	 * x/y offsets must match between CCS and the main surface.
434 	 */
435 	if (main_x != ccs_x || main_y != ccs_y) {
436 		drm_dbg_kms(&i915->drm,
437 			      "Bad CCS x/y (main %d,%d ccs %d,%d) full (main %d,%d ccs %d,%d)\n",
438 			      main_x, main_y,
439 			      ccs_x, ccs_y,
440 			      intel_fb->normal_view.color_plane[main_plane].x,
441 			      intel_fb->normal_view.color_plane[main_plane].y,
442 			      x, y);
443 		return -EINVAL;
444 	}
445 
446 	return 0;
447 }
448 
449 static bool intel_plane_can_remap(const struct intel_plane_state *plane_state)
450 {
451 	struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
452 	struct drm_i915_private *i915 = to_i915(plane->base.dev);
453 	const struct drm_framebuffer *fb = plane_state->hw.fb;
454 	int i;
455 
456 	/* We don't want to deal with remapping with cursors */
457 	if (plane->id == PLANE_CURSOR)
458 		return false;
459 
460 	/*
461 	 * The display engine limits already match/exceed the
462 	 * render engine limits, so not much point in remapping.
463 	 * Would also need to deal with the fence POT alignment
464 	 * and gen2 2KiB GTT tile size.
465 	 */
466 	if (DISPLAY_VER(i915) < 4)
467 		return false;
468 
469 	/*
470 	 * The new CCS hash mode isn't compatible with remapping as
471 	 * the virtual address of the pages affects the compressed data.
472 	 */
473 	if (is_ccs_modifier(fb->modifier))
474 		return false;
475 
476 	/* Linear needs a page aligned stride for remapping */
477 	if (fb->modifier == DRM_FORMAT_MOD_LINEAR) {
478 		unsigned int alignment = intel_tile_size(i915) - 1;
479 
480 		for (i = 0; i < fb->format->num_planes; i++) {
481 			if (fb->pitches[i] & alignment)
482 				return false;
483 		}
484 	}
485 
486 	return true;
487 }
488 
489 static bool intel_fb_needs_pot_stride_remap(const struct intel_framebuffer *fb)
490 {
491 	return false;
492 }
493 
494 static int intel_fb_pitch(const struct intel_framebuffer *fb, int color_plane, unsigned int rotation)
495 {
496 	if (drm_rotation_90_or_270(rotation))
497 		return fb->rotated_view.color_plane[color_plane].stride;
498 	else if (intel_fb_needs_pot_stride_remap(fb))
499 		return fb->remapped_view.color_plane[color_plane].stride;
500 	else
501 		return fb->normal_view.color_plane[color_plane].stride;
502 }
503 
504 static bool intel_plane_needs_remap(const struct intel_plane_state *plane_state)
505 {
506 	struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
507 	const struct intel_framebuffer *fb = to_intel_framebuffer(plane_state->hw.fb);
508 	unsigned int rotation = plane_state->hw.rotation;
509 	u32 stride, max_stride;
510 
511 	/*
512 	 * No remapping for invisible planes since we don't have
513 	 * an actual source viewport to remap.
514 	 */
515 	if (!plane_state->uapi.visible)
516 		return false;
517 
518 	if (!intel_plane_can_remap(plane_state))
519 		return false;
520 
521 	/*
522 	 * FIXME: aux plane limits on gen9+ are
523 	 * unclear in Bspec, for now no checking.
524 	 */
525 	stride = intel_fb_pitch(fb, 0, rotation);
526 	max_stride = plane->max_stride(plane, fb->base.format->format,
527 				       fb->base.modifier, rotation);
528 
529 	return stride > max_stride;
530 }
531 
532 static int convert_plane_offset_to_xy(const struct intel_framebuffer *fb, int color_plane,
533 				      int plane_width, int *x, int *y)
534 {
535 	struct drm_i915_gem_object *obj = intel_fb_obj(&fb->base);
536 	int ret;
537 
538 	ret = intel_fb_offset_to_xy(x, y, &fb->base, color_plane);
539 	if (ret) {
540 		drm_dbg_kms(fb->base.dev,
541 			    "bad fb plane %d offset: 0x%x\n",
542 			    color_plane, fb->base.offsets[color_plane]);
543 		return ret;
544 	}
545 
546 	ret = intel_fb_check_ccs_xy(&fb->base, color_plane, *x, *y);
547 	if (ret)
548 		return ret;
549 
550 	/*
551 	 * The fence (if used) is aligned to the start of the object
552 	 * so having the framebuffer wrap around across the edge of the
553 	 * fenced region doesn't really work. We have no API to configure
554 	 * the fence start offset within the object (nor could we probably
555 	 * on gen2/3). So it's just easier if we just require that the
556 	 * fb layout agrees with the fence layout. We already check that the
557 	 * fb stride matches the fence stride elsewhere.
558 	 */
559 	if (color_plane == 0 && i915_gem_object_is_tiled(obj) &&
560 	    (*x + plane_width) * fb->base.format->cpp[color_plane] > fb->base.pitches[color_plane]) {
561 		drm_dbg_kms(fb->base.dev,
562 			    "bad fb plane %d offset: 0x%x\n",
563 			    color_plane, fb->base.offsets[color_plane]);
564 		return -EINVAL;
565 	}
566 
567 	return 0;
568 }
569 
570 static u32 calc_plane_aligned_offset(const struct intel_framebuffer *fb, int color_plane, int *x, int *y)
571 {
572 	struct drm_i915_private *i915 = to_i915(fb->base.dev);
573 	unsigned int tile_size = intel_tile_size(i915);
574 	u32 offset;
575 
576 	offset = intel_compute_aligned_offset(i915, x, y, &fb->base, color_plane,
577 					      fb->base.pitches[color_plane],
578 					      DRM_MODE_ROTATE_0,
579 					      tile_size);
580 
581 	return offset / tile_size;
582 }
583 
584 struct fb_plane_view_dims {
585 	unsigned int width, height;
586 	unsigned int tile_width, tile_height;
587 };
588 
589 static void init_plane_view_dims(const struct intel_framebuffer *fb, int color_plane,
590 				 unsigned int width, unsigned int height,
591 				 struct fb_plane_view_dims *dims)
592 {
593 	dims->width = width;
594 	dims->height = height;
595 
596 	intel_tile_dims(&fb->base, color_plane, &dims->tile_width, &dims->tile_height);
597 }
598 
599 static unsigned int
600 plane_view_src_stride_tiles(const struct intel_framebuffer *fb, int color_plane,
601 			    const struct fb_plane_view_dims *dims)
602 {
603 	return DIV_ROUND_UP(fb->base.pitches[color_plane],
604 			    dims->tile_width * fb->base.format->cpp[color_plane]);
605 }
606 
607 static unsigned int
608 plane_view_dst_stride_tiles(const struct intel_framebuffer *fb, int color_plane,
609 			    unsigned int pitch_tiles)
610 {
611 	if (intel_fb_needs_pot_stride_remap(fb))
612 		return roundup_pow_of_two(pitch_tiles);
613 	else
614 		return pitch_tiles;
615 }
616 
617 static unsigned int
618 plane_view_width_tiles(const struct intel_framebuffer *fb, int color_plane,
619 		       const struct fb_plane_view_dims *dims,
620 		       int x)
621 {
622 	return DIV_ROUND_UP(x + dims->width, dims->tile_width);
623 }
624 
625 static unsigned int
626 plane_view_height_tiles(const struct intel_framebuffer *fb, int color_plane,
627 			const struct fb_plane_view_dims *dims,
628 			int y)
629 {
630 	return DIV_ROUND_UP(y + dims->height, dims->tile_height);
631 }
632 
633 #define assign_chk_ovf(i915, var, val) ({ \
634 	drm_WARN_ON(&(i915)->drm, overflows_type(val, var)); \
635 	(var) = (val); \
636 })
637 
638 static u32 calc_plane_remap_info(const struct intel_framebuffer *fb, int color_plane,
639 				 const struct fb_plane_view_dims *dims,
640 				 u32 obj_offset, u32 gtt_offset, int x, int y,
641 				 struct intel_fb_view *view)
642 {
643 	struct drm_i915_private *i915 = to_i915(fb->base.dev);
644 	struct intel_remapped_plane_info *remap_info = &view->gtt.remapped.plane[color_plane];
645 	struct i915_color_plane_view *color_plane_info = &view->color_plane[color_plane];
646 	unsigned int tile_width = dims->tile_width;
647 	unsigned int tile_height = dims->tile_height;
648 	unsigned int tile_size = intel_tile_size(i915);
649 	struct drm_rect r;
650 	u32 size;
651 
652 	assign_chk_ovf(i915, remap_info->offset, obj_offset);
653 	assign_chk_ovf(i915, remap_info->src_stride, plane_view_src_stride_tiles(fb, color_plane, dims));
654 	assign_chk_ovf(i915, remap_info->width, plane_view_width_tiles(fb, color_plane, dims, x));
655 	assign_chk_ovf(i915, remap_info->height, plane_view_height_tiles(fb, color_plane, dims, y));
656 
657 	if (view->gtt.type == I915_GGTT_VIEW_ROTATED) {
658 		check_array_bounds(i915, view->gtt.rotated.plane, color_plane);
659 
660 		assign_chk_ovf(i915, remap_info->dst_stride,
661 			       plane_view_dst_stride_tiles(fb, color_plane, remap_info->height));
662 
663 		/* rotate the x/y offsets to match the GTT view */
664 		drm_rect_init(&r, x, y, dims->width, dims->height);
665 		drm_rect_rotate(&r,
666 				remap_info->width * tile_width,
667 				remap_info->height * tile_height,
668 				DRM_MODE_ROTATE_270);
669 
670 		color_plane_info->x = r.x1;
671 		color_plane_info->y = r.y1;
672 
673 		color_plane_info->stride = remap_info->dst_stride * tile_height;
674 
675 		size = remap_info->dst_stride * remap_info->width;
676 
677 		/* rotate the tile dimensions to match the GTT view */
678 		swap(tile_width, tile_height);
679 	} else {
680 		drm_WARN_ON(&i915->drm, view->gtt.type != I915_GGTT_VIEW_REMAPPED);
681 
682 		check_array_bounds(i915, view->gtt.remapped.plane, color_plane);
683 
684 		assign_chk_ovf(i915, remap_info->dst_stride,
685 			       plane_view_dst_stride_tiles(fb, color_plane, remap_info->width));
686 
687 		color_plane_info->x = x;
688 		color_plane_info->y = y;
689 
690 		color_plane_info->stride = remap_info->dst_stride * tile_width *
691 					   fb->base.format->cpp[color_plane];
692 
693 		size = remap_info->dst_stride * remap_info->height;
694 	}
695 
696 	/*
697 	 * We only keep the x/y offsets, so push all of the gtt offset into
698 	 * the x/y offsets.  x,y will hold the first pixel of the framebuffer
699 	 * plane from the start of the remapped/rotated gtt mapping.
700 	 */
701 	intel_adjust_tile_offset(&color_plane_info->x, &color_plane_info->y,
702 				 tile_width, tile_height,
703 				 tile_size, remap_info->dst_stride,
704 				 gtt_offset * tile_size, 0);
705 
706 	return size;
707 }
708 
709 #undef assign_chk_ovf
710 
711 /* Return number of tiles @color_plane needs. */
712 static unsigned int
713 calc_plane_normal_size(const struct intel_framebuffer *fb, int color_plane,
714 		       const struct fb_plane_view_dims *dims,
715 		       int x, int y)
716 {
717 	struct drm_i915_private *i915 = to_i915(fb->base.dev);
718 	unsigned int tiles;
719 
720 	if (is_surface_linear(&fb->base, color_plane)) {
721 		unsigned int size;
722 
723 		size = (y + dims->height) * fb->base.pitches[color_plane] +
724 		       x * fb->base.format->cpp[color_plane];
725 		tiles = DIV_ROUND_UP(size, intel_tile_size(i915));
726 	} else {
727 		tiles = plane_view_src_stride_tiles(fb, color_plane, dims) *
728 			plane_view_height_tiles(fb, color_plane, dims, y);
729 		/*
730 		 * If the plane isn't horizontally tile aligned,
731 		 * we need one more tile.
732 		 */
733 		if (x != 0)
734 			tiles++;
735 	}
736 
737 	return tiles;
738 }
739 
740 static void intel_fb_view_init(struct intel_fb_view *view, enum i915_ggtt_view_type view_type)
741 {
742 	memset(view, 0, sizeof(*view));
743 	view->gtt.type = view_type;
744 }
745 
746 int intel_fill_fb_info(struct drm_i915_private *i915, struct drm_framebuffer *fb)
747 {
748 	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
749 	struct drm_i915_gem_object *obj = intel_fb_obj(fb);
750 	u32 gtt_offset_rotated = 0;
751 	u32 gtt_offset_remapped = 0;
752 	unsigned int max_size = 0;
753 	int i, num_planes = fb->format->num_planes;
754 	unsigned int tile_size = intel_tile_size(i915);
755 
756 	intel_fb_view_init(&intel_fb->normal_view, I915_GGTT_VIEW_NORMAL);
757 	intel_fb_view_init(&intel_fb->rotated_view, I915_GGTT_VIEW_ROTATED);
758 	intel_fb_view_init(&intel_fb->remapped_view, I915_GGTT_VIEW_REMAPPED);
759 
760 	for (i = 0; i < num_planes; i++) {
761 		struct fb_plane_view_dims view_dims;
762 		unsigned int width, height;
763 		unsigned int cpp, size;
764 		u32 offset;
765 		int x, y;
766 		int ret;
767 
768 		/*
769 		 * Plane 2 of Render Compression with Clear Color fb modifier
770 		 * is consumed by the driver and not passed to DE. Skip the
771 		 * arithmetic related to alignment and offset calculation.
772 		 */
773 		if (is_gen12_ccs_cc_plane(fb, i)) {
774 			if (IS_ALIGNED(fb->offsets[i], PAGE_SIZE))
775 				continue;
776 			else
777 				return -EINVAL;
778 		}
779 
780 		cpp = fb->format->cpp[i];
781 		intel_fb_plane_dims(&width, &height, fb, i);
782 
783 		ret = convert_plane_offset_to_xy(intel_fb, i, width, &x, &y);
784 		if (ret)
785 			return ret;
786 
787 		init_plane_view_dims(intel_fb, i, width, height, &view_dims);
788 
789 		/*
790 		 * First pixel of the framebuffer from
791 		 * the start of the normal gtt mapping.
792 		 */
793 		intel_fb->normal_view.color_plane[i].x = x;
794 		intel_fb->normal_view.color_plane[i].y = y;
795 		intel_fb->normal_view.color_plane[i].stride = intel_fb->base.pitches[i];
796 
797 		offset = calc_plane_aligned_offset(intel_fb, i, &x, &y);
798 
799 		/* Y or Yf modifiers required for 90/270 rotation */
800 		if (fb->modifier == I915_FORMAT_MOD_Y_TILED ||
801 		    fb->modifier == I915_FORMAT_MOD_Yf_TILED)
802 			gtt_offset_rotated += calc_plane_remap_info(intel_fb, i, &view_dims,
803 								    offset, gtt_offset_rotated, x, y,
804 								    &intel_fb->rotated_view);
805 
806 		if (intel_fb_needs_pot_stride_remap(intel_fb))
807 			gtt_offset_remapped += calc_plane_remap_info(intel_fb, i, &view_dims,
808 								     offset, gtt_offset_remapped, x, y,
809 								     &intel_fb->remapped_view);
810 
811 		size = calc_plane_normal_size(intel_fb, i, &view_dims, x, y);
812 		/* how many tiles in total needed in the bo */
813 		max_size = max(max_size, offset + size);
814 	}
815 
816 	if (mul_u32_u32(max_size, tile_size) > obj->base.size) {
817 		drm_dbg_kms(&i915->drm,
818 			    "fb too big for bo (need %llu bytes, have %zu bytes)\n",
819 			    mul_u32_u32(max_size, tile_size), obj->base.size);
820 		return -EINVAL;
821 	}
822 
823 	return 0;
824 }
825 
826 static void intel_plane_remap_gtt(struct intel_plane_state *plane_state)
827 {
828 	struct drm_i915_private *i915 =
829 		to_i915(plane_state->uapi.plane->dev);
830 	struct drm_framebuffer *fb = plane_state->hw.fb;
831 	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
832 	unsigned int rotation = plane_state->hw.rotation;
833 	int i, num_planes = fb->format->num_planes;
834 	unsigned int src_x, src_y;
835 	unsigned int src_w, src_h;
836 	u32 gtt_offset = 0;
837 
838 	intel_fb_view_init(&plane_state->view,
839 			   drm_rotation_90_or_270(rotation) ? I915_GGTT_VIEW_ROTATED :
840 							      I915_GGTT_VIEW_REMAPPED);
841 
842 	src_x = plane_state->uapi.src.x1 >> 16;
843 	src_y = plane_state->uapi.src.y1 >> 16;
844 	src_w = drm_rect_width(&plane_state->uapi.src) >> 16;
845 	src_h = drm_rect_height(&plane_state->uapi.src) >> 16;
846 
847 	drm_WARN_ON(&i915->drm, is_ccs_modifier(fb->modifier));
848 
849 	/* Make src coordinates relative to the viewport */
850 	drm_rect_translate(&plane_state->uapi.src,
851 			   -(src_x << 16), -(src_y << 16));
852 
853 	/* Rotate src coordinates to match rotated GTT view */
854 	if (drm_rotation_90_or_270(rotation))
855 		drm_rect_rotate(&plane_state->uapi.src,
856 				src_w << 16, src_h << 16,
857 				DRM_MODE_ROTATE_270);
858 
859 	for (i = 0; i < num_planes; i++) {
860 		unsigned int hsub = i ? fb->format->hsub : 1;
861 		unsigned int vsub = i ? fb->format->vsub : 1;
862 		struct fb_plane_view_dims view_dims;
863 		unsigned int width, height;
864 		unsigned int x, y;
865 		u32 offset;
866 
867 		x = src_x / hsub;
868 		y = src_y / vsub;
869 		width = src_w / hsub;
870 		height = src_h / vsub;
871 
872 		init_plane_view_dims(intel_fb, i, width, height, &view_dims);
873 
874 		/*
875 		 * First pixel of the src viewport from the
876 		 * start of the normal gtt mapping.
877 		 */
878 		x += intel_fb->normal_view.color_plane[i].x;
879 		y += intel_fb->normal_view.color_plane[i].y;
880 
881 		offset = calc_plane_aligned_offset(intel_fb, i, &x, &y);
882 
883 		gtt_offset += calc_plane_remap_info(intel_fb, i, &view_dims,
884 						    offset, gtt_offset, x, y,
885 						    &plane_state->view);
886 	}
887 }
888 
889 void intel_fb_fill_view(const struct intel_framebuffer *fb, unsigned int rotation,
890 			struct intel_fb_view *view)
891 {
892 	if (drm_rotation_90_or_270(rotation))
893 		*view = fb->rotated_view;
894 	else if (intel_fb_needs_pot_stride_remap(fb))
895 		*view = fb->remapped_view;
896 	else
897 		*view = fb->normal_view;
898 }
899 
900 static int intel_plane_check_stride(const struct intel_plane_state *plane_state)
901 {
902 	struct intel_plane *plane = to_intel_plane(plane_state->uapi.plane);
903 	const struct drm_framebuffer *fb = plane_state->hw.fb;
904 	unsigned int rotation = plane_state->hw.rotation;
905 	u32 stride, max_stride;
906 
907 	/*
908 	 * We ignore stride for all invisible planes that
909 	 * can be remapped. Otherwise we could end up
910 	 * with a false positive when the remapping didn't
911 	 * kick in due the plane being invisible.
912 	 */
913 	if (intel_plane_can_remap(plane_state) &&
914 	    !plane_state->uapi.visible)
915 		return 0;
916 
917 	/* FIXME other color planes? */
918 	stride = plane_state->view.color_plane[0].stride;
919 	max_stride = plane->max_stride(plane, fb->format->format,
920 				       fb->modifier, rotation);
921 
922 	if (stride > max_stride) {
923 		DRM_DEBUG_KMS("[FB:%d] stride (%d) exceeds [PLANE:%d:%s] max stride (%d)\n",
924 			      fb->base.id, stride,
925 			      plane->base.base.id, plane->base.name, max_stride);
926 		return -EINVAL;
927 	}
928 
929 	return 0;
930 }
931 
932 int intel_plane_compute_gtt(struct intel_plane_state *plane_state)
933 {
934 	const struct intel_framebuffer *fb =
935 		to_intel_framebuffer(plane_state->hw.fb);
936 	unsigned int rotation = plane_state->hw.rotation;
937 
938 	if (!fb)
939 		return 0;
940 
941 	if (intel_plane_needs_remap(plane_state)) {
942 		intel_plane_remap_gtt(plane_state);
943 
944 		/*
945 		 * Sometimes even remapping can't overcome
946 		 * the stride limitations :( Can happen with
947 		 * big plane sizes and suitably misaligned
948 		 * offsets.
949 		 */
950 		return intel_plane_check_stride(plane_state);
951 	}
952 
953 	intel_fb_fill_view(fb, rotation, &plane_state->view);
954 
955 	/* Rotate src coordinates to match rotated GTT view */
956 	if (drm_rotation_90_or_270(rotation))
957 		drm_rect_rotate(&plane_state->uapi.src,
958 				fb->base.width << 16, fb->base.height << 16,
959 				DRM_MODE_ROTATE_270);
960 
961 	return intel_plane_check_stride(plane_state);
962 }
963