mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-02 02:36:44 +07:00
356 lines
9.2 KiB
C
356 lines
9.2 KiB
C
|
/*
|
||
|
* Copyright (C) 2014 Red Hat
|
||
|
* Copyright (C) 2014 Intel Corp.
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the "Software"),
|
||
|
* to deal in the Software without restriction, including without limitation
|
||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||
|
* Software is furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
|
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||
|
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||
|
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||
|
*
|
||
|
* Authors:
|
||
|
* Rob Clark <robdclark@gmail.com>
|
||
|
* Daniel Vetter <daniel.vetter@ffwll.ch>
|
||
|
*/
|
||
|
|
||
|
#include <drm/drmP.h>
|
||
|
#include <drm/drm_atomic.h>
|
||
|
#include <drm/drm_plane_helper.h>
|
||
|
#include <drm/drm_crtc_helper.h>
|
||
|
|
||
|
static void
|
||
|
drm_atomic_helper_plane_changed(struct drm_atomic_state *state,
|
||
|
struct drm_plane_state *plane_state,
|
||
|
struct drm_plane *plane)
|
||
|
{
|
||
|
struct drm_crtc_state *crtc_state;
|
||
|
|
||
|
if (plane->state->crtc) {
|
||
|
crtc_state = state->crtc_states[drm_crtc_index(plane->crtc)];
|
||
|
|
||
|
if (WARN_ON(!crtc_state))
|
||
|
return;
|
||
|
|
||
|
crtc_state->planes_changed = true;
|
||
|
}
|
||
|
|
||
|
if (plane_state->crtc) {
|
||
|
crtc_state =
|
||
|
state->crtc_states[drm_crtc_index(plane_state->crtc)];
|
||
|
|
||
|
if (WARN_ON(!crtc_state))
|
||
|
return;
|
||
|
|
||
|
crtc_state->planes_changed = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* drm_atomic_helper_check - validate state object
|
||
|
* @dev: DRM device
|
||
|
* @state: the driver state object
|
||
|
*
|
||
|
* Check the state object to see if the requested state is physically possible.
|
||
|
* Only crtcs and planes have check callbacks, so for any additional (global)
|
||
|
* checking that a driver needs it can simply wrap that around this function.
|
||
|
* Drivers without such needs can directly use this as their ->atomic_check()
|
||
|
* callback.
|
||
|
*
|
||
|
* RETURNS
|
||
|
* Zero for success or -errno
|
||
|
*/
|
||
|
int drm_atomic_helper_check(struct drm_device *dev,
|
||
|
struct drm_atomic_state *state)
|
||
|
{
|
||
|
int nplanes = dev->mode_config.num_total_plane;
|
||
|
int ncrtcs = dev->mode_config.num_crtc;
|
||
|
int i, ret = 0;
|
||
|
|
||
|
for (i = 0; i < nplanes; i++) {
|
||
|
struct drm_plane_helper_funcs *funcs;
|
||
|
struct drm_plane *plane = state->planes[i];
|
||
|
struct drm_plane_state *plane_state = state->plane_states[i];
|
||
|
|
||
|
if (!plane)
|
||
|
continue;
|
||
|
|
||
|
funcs = plane->helper_private;
|
||
|
|
||
|
drm_atomic_helper_plane_changed(state, plane_state, plane);
|
||
|
|
||
|
if (!funcs || !funcs->atomic_check)
|
||
|
continue;
|
||
|
|
||
|
ret = funcs->atomic_check(plane, plane_state);
|
||
|
if (ret) {
|
||
|
DRM_DEBUG_KMS("[PLANE:%d] atomic check failed\n",
|
||
|
plane->base.id);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < ncrtcs; i++) {
|
||
|
struct drm_crtc_helper_funcs *funcs;
|
||
|
struct drm_crtc *crtc = state->crtcs[i];
|
||
|
|
||
|
if (!crtc)
|
||
|
continue;
|
||
|
|
||
|
funcs = crtc->helper_private;
|
||
|
|
||
|
if (!funcs || !funcs->atomic_check)
|
||
|
continue;
|
||
|
|
||
|
ret = funcs->atomic_check(crtc, state->crtc_states[i]);
|
||
|
if (ret) {
|
||
|
DRM_DEBUG_KMS("[CRTC:%d] atomic check failed\n",
|
||
|
crtc->base.id);
|
||
|
return ret;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(drm_atomic_helper_check);
|
||
|
|
||
|
/**
|
||
|
* drm_atomic_helper_prepare_planes - prepare plane resources after commit
|
||
|
* @dev: DRM device
|
||
|
* @state: atomic state object with old state structures
|
||
|
*
|
||
|
* This function prepares plane state, specifically framebuffers, for the new
|
||
|
* configuration. If any failure is encountered this function will call
|
||
|
* ->cleanup_fb on any already successfully prepared framebuffer.
|
||
|
*
|
||
|
* Returns:
|
||
|
* 0 on success, negative error code on failure.
|
||
|
*/
|
||
|
int drm_atomic_helper_prepare_planes(struct drm_device *dev,
|
||
|
struct drm_atomic_state *state)
|
||
|
{
|
||
|
int nplanes = dev->mode_config.num_total_plane;
|
||
|
int ret, i;
|
||
|
|
||
|
for (i = 0; i < nplanes; i++) {
|
||
|
struct drm_plane_helper_funcs *funcs;
|
||
|
struct drm_plane *plane = state->planes[i];
|
||
|
struct drm_framebuffer *fb;
|
||
|
|
||
|
if (!plane)
|
||
|
continue;
|
||
|
|
||
|
funcs = plane->helper_private;
|
||
|
|
||
|
fb = state->plane_states[i]->fb;
|
||
|
|
||
|
if (fb && funcs->prepare_fb) {
|
||
|
ret = funcs->prepare_fb(plane, fb);
|
||
|
if (ret)
|
||
|
goto fail;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
fail:
|
||
|
for (i--; i >= 0; i--) {
|
||
|
struct drm_plane_helper_funcs *funcs;
|
||
|
struct drm_plane *plane = state->planes[i];
|
||
|
struct drm_framebuffer *fb;
|
||
|
|
||
|
if (!plane)
|
||
|
continue;
|
||
|
|
||
|
funcs = plane->helper_private;
|
||
|
|
||
|
fb = state->plane_states[i]->fb;
|
||
|
|
||
|
if (fb && funcs->cleanup_fb)
|
||
|
funcs->cleanup_fb(plane, fb);
|
||
|
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
|
||
|
|
||
|
/**
|
||
|
* drm_atomic_helper_commit_planes - commit plane state
|
||
|
* @dev: DRM device
|
||
|
* @state: atomic state
|
||
|
*
|
||
|
* This function commits the new plane state using the plane and atomic helper
|
||
|
* functions for planes and crtcs. It assumes that the atomic state has already
|
||
|
* been pushed into the relevant object state pointers, since this step can no
|
||
|
* longer fail.
|
||
|
*
|
||
|
* It still requires the global state object @state to know which planes and
|
||
|
* crtcs need to be updated though.
|
||
|
*/
|
||
|
void drm_atomic_helper_commit_planes(struct drm_device *dev,
|
||
|
struct drm_atomic_state *state)
|
||
|
{
|
||
|
int nplanes = dev->mode_config.num_total_plane;
|
||
|
int ncrtcs = dev->mode_config.num_crtc;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < ncrtcs; i++) {
|
||
|
struct drm_crtc_helper_funcs *funcs;
|
||
|
struct drm_crtc *crtc = state->crtcs[i];
|
||
|
|
||
|
if (!crtc)
|
||
|
continue;
|
||
|
|
||
|
funcs = crtc->helper_private;
|
||
|
|
||
|
if (!funcs || !funcs->atomic_begin)
|
||
|
continue;
|
||
|
|
||
|
funcs->atomic_begin(crtc);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < nplanes; i++) {
|
||
|
struct drm_plane_helper_funcs *funcs;
|
||
|
struct drm_plane *plane = state->planes[i];
|
||
|
|
||
|
if (!plane)
|
||
|
continue;
|
||
|
|
||
|
funcs = plane->helper_private;
|
||
|
|
||
|
if (!funcs || !funcs->atomic_update)
|
||
|
continue;
|
||
|
|
||
|
funcs->atomic_update(plane);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < ncrtcs; i++) {
|
||
|
struct drm_crtc_helper_funcs *funcs;
|
||
|
struct drm_crtc *crtc = state->crtcs[i];
|
||
|
|
||
|
if (!crtc)
|
||
|
continue;
|
||
|
|
||
|
funcs = crtc->helper_private;
|
||
|
|
||
|
if (!funcs || !funcs->atomic_flush)
|
||
|
continue;
|
||
|
|
||
|
funcs->atomic_flush(crtc);
|
||
|
}
|
||
|
}
|
||
|
EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
|
||
|
|
||
|
/**
|
||
|
* drm_atomic_helper_cleanup_planes - cleanup plane resources after commit
|
||
|
* @dev: DRM device
|
||
|
* @old_state: atomic state object with old state structures
|
||
|
*
|
||
|
* This function cleans up plane state, specifically framebuffers, from the old
|
||
|
* configuration. Hence the old configuration must be perserved in @old_state to
|
||
|
* be able to call this function.
|
||
|
*
|
||
|
* This function must also be called on the new state when the atomic update
|
||
|
* fails at any point after calling drm_atomic_helper_prepare_planes().
|
||
|
*/
|
||
|
void drm_atomic_helper_cleanup_planes(struct drm_device *dev,
|
||
|
struct drm_atomic_state *old_state)
|
||
|
{
|
||
|
int nplanes = dev->mode_config.num_total_plane;
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < nplanes; i++) {
|
||
|
struct drm_plane_helper_funcs *funcs;
|
||
|
struct drm_plane *plane = old_state->planes[i];
|
||
|
struct drm_framebuffer *old_fb;
|
||
|
|
||
|
if (!plane)
|
||
|
continue;
|
||
|
|
||
|
funcs = plane->helper_private;
|
||
|
|
||
|
old_fb = old_state->plane_states[i]->fb;
|
||
|
|
||
|
if (old_fb && funcs->cleanup_fb)
|
||
|
funcs->cleanup_fb(plane, old_fb);
|
||
|
}
|
||
|
}
|
||
|
EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
|
||
|
|
||
|
/**
|
||
|
* drm_atomic_helper_swap_state - store atomic state into current sw state
|
||
|
* @dev: DRM device
|
||
|
* @state: atomic state
|
||
|
*
|
||
|
* This function stores the atomic state into the current state pointers in all
|
||
|
* driver objects. It should be called after all failing steps have been done
|
||
|
* and succeeded, but before the actual hardware state is committed.
|
||
|
*
|
||
|
* For cleanup and error recovery the current state for all changed objects will
|
||
|
* be swaped into @state.
|
||
|
*
|
||
|
* With that sequence it fits perfectly into the plane prepare/cleanup sequence:
|
||
|
*
|
||
|
* 1. Call drm_atomic_helper_prepare_planes() with the staged atomic state.
|
||
|
*
|
||
|
* 2. Do any other steps that might fail.
|
||
|
*
|
||
|
* 3. Put the staged state into the current state pointers with this function.
|
||
|
*
|
||
|
* 4. Actually commit the hardware state.
|
||
|
*
|
||
|
* 5. Call drm_atomic_helper_cleanup_planes with @state, which since step 3
|
||
|
* contains the old state. Also do any other cleanup required with that state.
|
||
|
*/
|
||
|
void drm_atomic_helper_swap_state(struct drm_device *dev,
|
||
|
struct drm_atomic_state *state)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
for (i = 0; i < dev->mode_config.num_connector; i++) {
|
||
|
struct drm_connector *connector = state->connectors[i];
|
||
|
|
||
|
if (!connector)
|
||
|
continue;
|
||
|
|
||
|
connector->state->state = state;
|
||
|
swap(state->connector_states[i], connector->state);
|
||
|
connector->state->state = NULL;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < dev->mode_config.num_crtc; i++) {
|
||
|
struct drm_crtc *crtc = state->crtcs[i];
|
||
|
|
||
|
if (!crtc)
|
||
|
continue;
|
||
|
|
||
|
crtc->state->state = state;
|
||
|
swap(state->crtc_states[i], crtc->state);
|
||
|
crtc->state->state = NULL;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < dev->mode_config.num_total_plane; i++) {
|
||
|
struct drm_plane *plane = state->planes[i];
|
||
|
|
||
|
if (!plane)
|
||
|
continue;
|
||
|
|
||
|
plane->state->state = state;
|
||
|
swap(state->plane_states[i], plane->state);
|
||
|
plane->state->state = NULL;
|
||
|
}
|
||
|
}
|
||
|
EXPORT_SYMBOL(drm_atomic_helper_swap_state);
|