1/*	$NetBSD: intel_atomic_plane.c,v 1.3 2021/12/19 11:38:03 riastradh Exp $	*/
2
3/*
4 * Copyright �� 2014 Intel Corporation
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 * DEALINGS IN THE SOFTWARE.
24 */
25
26/**
27 * DOC: atomic plane helpers
28 *
29 * The functions here are used by the atomic plane helper functions to
30 * implement legacy plane updates (i.e., drm_plane->update_plane() and
31 * drm_plane->disable_plane()).  This allows plane updates to use the
32 * atomic state infrastructure and perform plane updates as separate
33 * prepare/check/commit/cleanup steps.
34 */
35
36#include <sys/cdefs.h>
37__KERNEL_RCSID(0, "$NetBSD: intel_atomic_plane.c,v 1.3 2021/12/19 11:38:03 riastradh Exp $");
38
39#include <drm/drm_atomic_helper.h>
40#include <drm/drm_fourcc.h>
41#include <drm/drm_plane_helper.h>
42
43#include "i915_trace.h"
44#include "intel_atomic_plane.h"
45#include "intel_display_types.h"
46#include "intel_pm.h"
47#include "intel_sprite.h"
48
49static void intel_plane_state_reset(struct intel_plane_state *plane_state,
50				    struct intel_plane *plane)
51{
52	memset(plane_state, 0, sizeof(*plane_state));
53
54	__drm_atomic_helper_plane_state_reset(&plane_state->uapi, &plane->base);
55
56	plane_state->scaler_id = -1;
57}
58
59struct intel_plane *intel_plane_alloc(void)
60{
61	struct intel_plane_state *plane_state;
62	struct intel_plane *plane;
63
64	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
65	if (!plane)
66		return ERR_PTR(-ENOMEM);
67
68	plane_state = kzalloc(sizeof(*plane_state), GFP_KERNEL);
69	if (!plane_state) {
70		kfree(plane);
71		return ERR_PTR(-ENOMEM);
72	}
73
74	intel_plane_state_reset(plane_state, plane);
75
76	plane->base.state = &plane_state->uapi;
77
78	return plane;
79}
80
81void intel_plane_free(struct intel_plane *plane)
82{
83	intel_plane_destroy_state(&plane->base, plane->base.state);
84	kfree(plane);
85}
86
87/**
88 * intel_plane_duplicate_state - duplicate plane state
89 * @plane: drm plane
90 *
91 * Allocates and returns a copy of the plane state (both common and
92 * Intel-specific) for the specified plane.
93 *
94 * Returns: The newly allocated plane state, or NULL on failure.
95 */
96struct drm_plane_state *
97intel_plane_duplicate_state(struct drm_plane *plane)
98{
99	struct intel_plane_state *intel_state;
100
101	intel_state = to_intel_plane_state(plane->state);
102	intel_state = kmemdup(intel_state, sizeof(*intel_state), GFP_KERNEL);
103
104	if (!intel_state)
105		return NULL;
106
107	__drm_atomic_helper_plane_duplicate_state(plane, &intel_state->uapi);
108
109	intel_state->vma = NULL;
110	intel_state->flags = 0;
111
112	/* add reference to fb */
113	if (intel_state->hw.fb)
114		drm_framebuffer_get(intel_state->hw.fb);
115
116	return &intel_state->uapi;
117}
118
119/**
120 * intel_plane_destroy_state - destroy plane state
121 * @plane: drm plane
122 * @state: state object to destroy
123 *
124 * Destroys the plane state (both common and Intel-specific) for the
125 * specified plane.
126 */
127void
128intel_plane_destroy_state(struct drm_plane *plane,
129			  struct drm_plane_state *state)
130{
131	struct intel_plane_state *plane_state = to_intel_plane_state(state);
132	WARN_ON(plane_state->vma);
133
134	__drm_atomic_helper_plane_destroy_state(&plane_state->uapi);
135	if (plane_state->hw.fb)
136		drm_framebuffer_put(plane_state->hw.fb);
137	kfree(plane_state);
138}
139
140unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state,
141				   const struct intel_plane_state *plane_state)
142{
143	const struct drm_framebuffer *fb = plane_state->hw.fb;
144	unsigned int cpp;
145
146	if (!plane_state->uapi.visible)
147		return 0;
148
149	cpp = fb->format->cpp[0];
150
151	/*
152	 * Based on HSD#:1408715493
153	 * NV12 cpp == 4, P010 cpp == 8
154	 *
155	 * FIXME what is the logic behind this?
156	 */
157	if (fb->format->is_yuv && fb->format->num_planes > 1)
158		cpp *= 4;
159
160	return cpp * crtc_state->pixel_rate;
161}
162
163bool intel_plane_calc_min_cdclk(struct intel_atomic_state *state,
164				struct intel_plane *plane)
165{
166	struct drm_i915_private *dev_priv = to_i915(plane->base.dev);
167	const struct intel_plane_state *plane_state =
168		intel_atomic_get_new_plane_state(state, plane);
169	struct intel_crtc *crtc = to_intel_crtc(plane_state->hw.crtc);
170	struct intel_crtc_state *crtc_state;
171
172	if (!plane_state->uapi.visible || !plane->min_cdclk)
173		return false;
174
175	crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
176
177	crtc_state->min_cdclk[plane->id] =
178		plane->min_cdclk(crtc_state, plane_state);
179
180	/*
181	 * Does the cdclk need to be bumbed up?
182	 *
183	 * Note: we obviously need to be called before the new
184	 * cdclk frequency is calculated so state->cdclk.logical
185	 * hasn't been populated yet. Hence we look at the old
186	 * cdclk state under dev_priv->cdclk.logical. This is
187	 * safe as long we hold at least one crtc mutex (which
188	 * must be true since we have crtc_state).
189	 */
190	if (crtc_state->min_cdclk[plane->id] > dev_priv->cdclk.logical.cdclk) {
191		DRM_DEBUG_KMS("[PLANE:%d:%s] min_cdclk (%d kHz) > logical cdclk (%d kHz)\n",
192			      plane->base.base.id, plane->base.name,
193			      crtc_state->min_cdclk[plane->id],
194			      dev_priv->cdclk.logical.cdclk);
195		return true;
196	}
197
198	return false;
199}
200
201static void intel_plane_clear_hw_state(struct intel_plane_state *plane_state)
202{
203	if (plane_state->hw.fb)
204		drm_framebuffer_put(plane_state->hw.fb);
205
206	memset(&plane_state->hw, 0, sizeof(plane_state->hw));
207}
208
209void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state,
210				       const struct intel_plane_state *from_plane_state)
211{
212	intel_plane_clear_hw_state(plane_state);
213
214	plane_state->hw.crtc = from_plane_state->uapi.crtc;
215	plane_state->hw.fb = from_plane_state->uapi.fb;
216	if (plane_state->hw.fb)
217		drm_framebuffer_get(plane_state->hw.fb);
218
219	plane_state->hw.alpha = from_plane_state->uapi.alpha;
220	plane_state->hw.pixel_blend_mode =
221		from_plane_state->uapi.pixel_blend_mode;
222	plane_state->hw.rotation = from_plane_state->uapi.rotation;
223	plane_state->hw.color_encoding = from_plane_state->uapi.color_encoding;
224	plane_state->hw.color_range = from_plane_state->uapi.color_range;
225}
226
227int intel_plane_atomic_check_with_state(const struct intel_crtc_state *old_crtc_state,
228					struct intel_crtc_state *new_crtc_state,
229					const struct intel_plane_state *old_plane_state,
230					struct intel_plane_state *new_plane_state)
231{
232	struct intel_plane *plane = to_intel_plane(new_plane_state->uapi.plane);
233	const struct drm_framebuffer *fb;
234	int ret;
235
236	intel_plane_copy_uapi_to_hw_state(new_plane_state, new_plane_state);
237	fb = new_plane_state->hw.fb;
238
239	new_crtc_state->active_planes &= ~BIT(plane->id);
240	new_crtc_state->nv12_planes &= ~BIT(plane->id);
241	new_crtc_state->c8_planes &= ~BIT(plane->id);
242	new_crtc_state->data_rate[plane->id] = 0;
243	new_crtc_state->min_cdclk[plane->id] = 0;
244	new_plane_state->uapi.visible = false;
245
246	if (!new_plane_state->hw.crtc && !old_plane_state->hw.crtc)
247		return 0;
248
249	ret = plane->check_plane(new_crtc_state, new_plane_state);
250	if (ret)
251		return ret;
252
253	/* FIXME pre-g4x don't work like this */
254	if (new_plane_state->uapi.visible)
255		new_crtc_state->active_planes |= BIT(plane->id);
256
257	if (new_plane_state->uapi.visible &&
258	    intel_format_info_is_yuv_semiplanar(fb->format, fb->modifier))
259		new_crtc_state->nv12_planes |= BIT(plane->id);
260
261	if (new_plane_state->uapi.visible &&
262	    fb->format->format == DRM_FORMAT_C8)
263		new_crtc_state->c8_planes |= BIT(plane->id);
264
265	if (new_plane_state->uapi.visible || old_plane_state->uapi.visible)
266		new_crtc_state->update_planes |= BIT(plane->id);
267
268	new_crtc_state->data_rate[plane->id] =
269		intel_plane_data_rate(new_crtc_state, new_plane_state);
270
271	return intel_plane_atomic_calc_changes(old_crtc_state, new_crtc_state,
272					       old_plane_state, new_plane_state);
273}
274
275static struct intel_crtc *
276get_crtc_from_states(const struct intel_plane_state *old_plane_state,
277		     const struct intel_plane_state *new_plane_state)
278{
279	if (new_plane_state->uapi.crtc)
280		return to_intel_crtc(new_plane_state->uapi.crtc);
281
282	if (old_plane_state->uapi.crtc)
283		return to_intel_crtc(old_plane_state->uapi.crtc);
284
285	return NULL;
286}
287
288int intel_plane_atomic_check(struct intel_atomic_state *state,
289			     struct intel_plane *plane)
290{
291	struct intel_plane_state *new_plane_state =
292		intel_atomic_get_new_plane_state(state, plane);
293	const struct intel_plane_state *old_plane_state =
294		intel_atomic_get_old_plane_state(state, plane);
295	struct intel_crtc *crtc =
296		get_crtc_from_states(old_plane_state, new_plane_state);
297	const struct intel_crtc_state *old_crtc_state;
298	struct intel_crtc_state *new_crtc_state;
299
300	new_plane_state->uapi.visible = false;
301	if (!crtc)
302		return 0;
303
304	old_crtc_state = intel_atomic_get_old_crtc_state(state, crtc);
305	new_crtc_state = intel_atomic_get_new_crtc_state(state, crtc);
306
307	return intel_plane_atomic_check_with_state(old_crtc_state,
308						   new_crtc_state,
309						   old_plane_state,
310						   new_plane_state);
311}
312
313static struct intel_plane *
314skl_next_plane_to_commit(struct intel_atomic_state *state,
315			 struct intel_crtc *crtc,
316			 struct skl_ddb_entry entries_y[I915_MAX_PLANES],
317			 struct skl_ddb_entry entries_uv[I915_MAX_PLANES],
318			 unsigned int *update_mask)
319{
320	struct intel_crtc_state *crtc_state =
321		intel_atomic_get_new_crtc_state(state, crtc);
322	struct intel_plane_state *plane_state __unused;
323	struct intel_plane *plane;
324	int i;
325
326	if (*update_mask == 0)
327		return NULL;
328
329	for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
330		enum plane_id plane_id = plane->id;
331
332		if (crtc->pipe != plane->pipe ||
333		    !(*update_mask & BIT(plane_id)))
334			continue;
335
336		if (skl_ddb_allocation_overlaps(&crtc_state->wm.skl.plane_ddb_y[plane_id],
337						entries_y,
338						I915_MAX_PLANES, plane_id) ||
339		    skl_ddb_allocation_overlaps(&crtc_state->wm.skl.plane_ddb_uv[plane_id],
340						entries_uv,
341						I915_MAX_PLANES, plane_id))
342			continue;
343
344		*update_mask &= ~BIT(plane_id);
345		entries_y[plane_id] = crtc_state->wm.skl.plane_ddb_y[plane_id];
346		entries_uv[plane_id] = crtc_state->wm.skl.plane_ddb_uv[plane_id];
347
348		return plane;
349	}
350
351	/* should never happen */
352	WARN_ON(1);
353
354	return NULL;
355}
356
357void intel_update_plane(struct intel_plane *plane,
358			const struct intel_crtc_state *crtc_state,
359			const struct intel_plane_state *plane_state)
360{
361	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
362
363	trace_intel_update_plane(&plane->base, crtc);
364	plane->update_plane(plane, crtc_state, plane_state);
365}
366
367void intel_disable_plane(struct intel_plane *plane,
368			 const struct intel_crtc_state *crtc_state)
369{
370	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
371
372	trace_intel_disable_plane(&plane->base, crtc);
373	plane->disable_plane(plane, crtc_state);
374}
375
376void skl_update_planes_on_crtc(struct intel_atomic_state *state,
377			       struct intel_crtc *crtc)
378{
379	struct intel_crtc_state *old_crtc_state =
380		intel_atomic_get_old_crtc_state(state, crtc);
381	struct intel_crtc_state *new_crtc_state =
382		intel_atomic_get_new_crtc_state(state, crtc);
383	struct skl_ddb_entry entries_y[I915_MAX_PLANES];
384	struct skl_ddb_entry entries_uv[I915_MAX_PLANES];
385	u32 update_mask = new_crtc_state->update_planes;
386	struct intel_plane *plane;
387
388	memcpy(entries_y, old_crtc_state->wm.skl.plane_ddb_y,
389	       sizeof(old_crtc_state->wm.skl.plane_ddb_y));
390	memcpy(entries_uv, old_crtc_state->wm.skl.plane_ddb_uv,
391	       sizeof(old_crtc_state->wm.skl.plane_ddb_uv));
392
393	while ((plane = skl_next_plane_to_commit(state, crtc,
394						 entries_y, entries_uv,
395						 &update_mask))) {
396		struct intel_plane_state *new_plane_state =
397			intel_atomic_get_new_plane_state(state, plane);
398
399		if (new_plane_state->uapi.visible ||
400		    new_plane_state->planar_slave) {
401			intel_update_plane(plane, new_crtc_state, new_plane_state);
402		} else {
403			intel_disable_plane(plane, new_crtc_state);
404		}
405	}
406}
407
408void i9xx_update_planes_on_crtc(struct intel_atomic_state *state,
409				struct intel_crtc *crtc)
410{
411	struct intel_crtc_state *new_crtc_state =
412		intel_atomic_get_new_crtc_state(state, crtc);
413	u32 update_mask = new_crtc_state->update_planes;
414	struct intel_plane_state *new_plane_state;
415	struct intel_plane *plane;
416	int i;
417
418	for_each_new_intel_plane_in_state(state, plane, new_plane_state, i) {
419		if (crtc->pipe != plane->pipe ||
420		    !(update_mask & BIT(plane->id)))
421			continue;
422
423		if (new_plane_state->uapi.visible)
424			intel_update_plane(plane, new_crtc_state, new_plane_state);
425		else
426			intel_disable_plane(plane, new_crtc_state);
427	}
428}
429
430const struct drm_plane_helper_funcs intel_plane_helper_funcs = {
431	.prepare_fb = intel_prepare_plane_fb,
432	.cleanup_fb = intel_cleanup_plane_fb,
433};
434