Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions kernel-open/common/inc/nvkms-kapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,24 @@ struct NvKmsKapiFunctionsTable {
const NvBool commit
);

/*!
* Move the cursor on the specified head.
*
* \param [in] device A device allocated using allocateDevice().
* \param [in] head Target head index.
* \param [in] x New X coordinate in display space.
* \param [in] y New Y coordinate in display space.
*
* \return NV_TRUE on success, NV_FALSE on failure.
*/
NvBool (*moveCursor)
(
struct NvKmsKapiDevice *device,
const NvU32 head,
const NvS16 x,
const NvS16 y
);

/*!
* Return status of flip.
*
Expand Down
106 changes: 106 additions & 0 deletions kernel-open/conftest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1631,6 +1631,24 @@ compile_test() {
compile_check_conftest "$CODE" "NV_DRM_ATOMIC_HELPER_LEGACY_GAMMA_SET_PRESENT" "" "functions"
;;

drm_atomic_helper_unprepare_planes)
#
# Determine if the function drm_atomic_helper_unprepare_planes() is
# present.
#
# Added by commit 456ff66cb87b ("drm/atomic-helper: Add
# drm_atomic_helper_unprepare_planes") in v6.8 (2023-12-19).
# Prior to v6.8, drm_atomic_helper_cleanup_planes was used.
#
CODE="
#include <drm/drm_atomic_helper.h>
void conftest_drm_atomic_helper_unprepare_planes(void) {
drm_atomic_helper_unprepare_planes();
}"

compile_check_conftest "$CODE" "NV_DRM_ATOMIC_HELPER_UNPREPARE_PLANES_PRESENT" "" "functions"
;;

drm_plane_create_color_properties)
#
# Determine if the function drm_plane_create_color_properties() is
Expand Down Expand Up @@ -3292,6 +3310,94 @@ compile_test() {
fi
;;

drm_plane_helper_funcs_has_atomic_async_check)
#
# Determine if drm_plane_helper_funcs has atomic_async_check member.
#
echo "$CONFTEST_PREAMBLE
#include <drm/drm_modeset_helper_vtables.h>
void conftest_drm_plane_helper_funcs_has_atomic_async_check(void) {
struct drm_plane_helper_funcs funcs;
funcs.atomic_async_check = NULL;
}" > conftest$$.c

$CC $CFLAGS -c conftest$$.c > /dev/null 2>&1
rm -f conftest$$.c

if [ -f conftest$$.o ]; then
rm -f conftest$$.o
echo "#define NV_DRM_PLANE_HELPER_FUNCS_HAS_ATOMIC_ASYNC_CHECK" | append_conftest "types"
else
echo "#undef NV_DRM_PLANE_HELPER_FUNCS_HAS_ATOMIC_ASYNC_CHECK" | append_conftest "types"
fi
;;

drm_plane_atomic_async_check_has_atomic_state_arg)
#
# Determine if drm_plane_helper_funcs::atomic_async_check takes 'state'
# argument of 'struct drm_atomic_state' type.
#
# Commit 881db09bc588 / 7c11b99a8e58 in v5.13 passes the full atomic state to
# drm_plane_helper_funcs::atomic_async_check() and atomic_async_update().
#
echo "$CONFTEST_PREAMBLE
#include <drm/drm_modeset_helper_vtables.h>
static const struct drm_plane_helper_funcs *funcs;
typeof(*funcs->atomic_async_check) conftest_drm_plane_atomic_async_check_has_atomic_state_arg;
#if defined(NV_DRM_ATOMIC_COMMIT_STRUCT_PRESENT)
int conftest_drm_plane_atomic_async_check_has_atomic_state_arg(
struct drm_plane *plane, struct drm_atomic_commit *state) {
return 0;
}
#else
int conftest_drm_plane_atomic_async_check_has_atomic_state_arg(
struct drm_plane *plane, struct drm_atomic_state *state) {
return 0;
}
#endif" > conftest$$.c

$CC $CFLAGS -c conftest$$.c > /dev/null 2>&1
rm -f conftest$$.c

if [ -f conftest$$.o ]; then
rm -f conftest$$.o
echo "#define NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_ATOMIC_STATE_ARG" | append_conftest "types"
else
echo "#undef NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_ATOMIC_STATE_ARG" | append_conftest "types"
fi
;;

drm_plane_atomic_async_check_has_flip_arg)
#
# Determine if drm_plane_helper_funcs::atomic_async_check takes 'bool' (flip)
#
echo "$CONFTEST_PREAMBLE
#include <drm/drm_modeset_helper_vtables.h>
static const struct drm_plane_helper_funcs *funcs;
typeof(*funcs->atomic_async_check) conftest_drm_plane_atomic_async_check_has_flip_arg;
#if defined(NV_DRM_ATOMIC_COMMIT_STRUCT_PRESENT)
int conftest_drm_plane_atomic_async_check_has_flip_arg(
struct drm_plane *plane, struct drm_atomic_commit *state, bool flip) {
return 0;
}
#else
int conftest_drm_plane_atomic_async_check_has_flip_arg(
struct drm_plane *plane, struct drm_atomic_state *state, bool flip) {
return 0;
}
#endif" > conftest$$.c

$CC $CFLAGS -c conftest$$.c > /dev/null 2>&1
rm -f conftest$$.c

if [ -f conftest$$.o ]; then
rm -f conftest$$.o
echo "#define NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_FLIP_ARG" | append_conftest "types"
else
echo "#undef NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_FLIP_ARG" | append_conftest "types"
fi
;;

ib_peer_memory_symbols)
#
# Determine if the following symbols exist in Module.symvers:
Expand Down
197 changes: 197 additions & 0 deletions kernel-open/nvidia-drm/nvidia-drm-crtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2205,8 +2205,205 @@ static const struct drm_plane_funcs nv_plane_funcs = {
.format_mod_supported = nv_drm_plane_format_mod_supported,
};

#if defined(NV_DRM_PLANE_HELPER_FUNCS_HAS_ATOMIC_ASYNC_CHECK)

#if defined(NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_FLIP_ARG)
static int nv_drm_plane_atomic_async_check(struct drm_plane *plane,
nv_drm_atomic_state_base_t *state,
bool flip)
#elif defined(NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_ATOMIC_STATE_ARG)
static int nv_drm_plane_atomic_async_check(struct drm_plane *plane,
nv_drm_atomic_state_base_t *state)
#else
static int nv_drm_plane_atomic_async_check(struct drm_plane *plane,
struct drm_plane_state *plane_state)
#endif
{
struct drm_plane_state *old_plane_state = plane->state;
struct nv_drm_device *nv_dev = to_nv_device(plane->dev);
struct drm_crtc *crtc;
struct nv_drm_plane_state *nv_plane_state;
struct nv_drm_plane_state *nv_old_plane_state;

#if defined(NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_FULL_STATE_ARG)
struct drm_plane_state *plane_state =
drm_atomic_get_new_plane_state(state, plane);
#endif

#if defined(NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_FLIP_ARG)
if (flip) {
return -EINVAL;
}
#endif

if (plane->type != DRM_PLANE_TYPE_CURSOR) {
return -EINVAL;
}

if (nv_dev->subOwnershipGranted) {
return -EINVAL;
}

if (plane_state == NULL || old_plane_state == NULL) {
return -EINVAL;
}

if (plane_state->crtc == NULL || plane_state->fb == NULL ||
old_plane_state->crtc == NULL || old_plane_state->fb == NULL) {
return -EINVAL;
}

/* CRTC and Framebuffer must not change */
if (plane_state->crtc != old_plane_state->crtc ||
plane_state->fb != old_plane_state->fb) {
return -EINVAL;
}

/*
* Target CRTC must be active (not in DPMS off / suspend / modeset disable).
* In legacy cursor updates, CRTC is not in the atomic transaction state,
* so reading crtc->state directly is safe because caller holds modeset locks.
*/
crtc = plane_state->crtc;
if (crtc->state == NULL || !crtc->state->active) {
return -EINVAL;
}

/* Destination and Source dimensions must match and cannot change */
if (plane_state->crtc_w != old_plane_state->crtc_w ||
plane_state->crtc_h != old_plane_state->crtc_h ||
plane_state->src_w != old_plane_state->src_w ||
plane_state->src_h != old_plane_state->src_h) {
return -EINVAL;
}

/* Source offset must be (0, 0) - hardware cursor does not support cropping */
if (plane_state->src_x != 0 || plane_state->src_y != 0 ||
plane_state->src_x != old_plane_state->src_x ||
plane_state->src_y != old_plane_state->src_y) {
return -EINVAL;
}

/* Hardware cursor requires 1:1 scaling (no up/down-scaling) */
if ((plane_state->src_w >> 16) != plane_state->crtc_w ||
(plane_state->src_h >> 16) != plane_state->crtc_h) {
return -EINVAL;
}

/* Rotation, Alpha, and Blending mode must not change */
if (plane_state->rotation != old_plane_state->rotation ||
plane_state->alpha != old_plane_state->alpha ||
plane_state->pixel_blend_mode != old_plane_state->pixel_blend_mode) {
return -EINVAL;
}

/* Z-order must not change */
if (plane_state->zpos != old_plane_state->zpos ||
plane_state->normalized_zpos != old_plane_state->normalized_zpos) {
return -EINVAL;
}

/* Color properties (encoding, range) must not change */
if (plane_state->color_encoding != old_plane_state->color_encoding ||
plane_state->color_range != old_plane_state->color_range) {
return -EINVAL;
}

/* If explicit fencing is attached, full atomic commit is required */
if (plane_state->fence != NULL) {
return -EINVAL;
}

/* Check driver-private plane state extensions */
nv_plane_state = to_nv_drm_plane_state(plane_state);
nv_old_plane_state = to_nv_drm_plane_state(old_plane_state);

if (nv_plane_state->input_colorspace != nv_old_plane_state->input_colorspace ||
nv_plane_state->degamma_changed ||
nv_plane_state->tmo_changed ||
nv_plane_state->degamma_tf != nv_old_plane_state->degamma_tf ||
nv_plane_state->degamma_lut != nv_old_plane_state->degamma_lut ||
nv_plane_state->tmo_lut != nv_old_plane_state->tmo_lut) {
return -EINVAL;
}

#if defined(NV_DRM_HAS_HDR_OUTPUT_METADATA)
if (nv_plane_state->hdr_output_metadata != nv_old_plane_state->hdr_output_metadata) {
return -EINVAL;
}
#endif

/* Coordinates must fit in signed 16-bit integers for NvKms */
if (plane_state->crtc_x < S16_MIN || plane_state->crtc_x > S16_MAX ||
plane_state->crtc_y < S16_MIN || plane_state->crtc_y > S16_MAX) {
return -EINVAL;
}

return 0;
}

#if defined(NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_FULL_STATE_ARG)
static void nv_drm_plane_atomic_async_update(struct drm_plane *plane,
nv_drm_atomic_state_base_t *state)
#else
static void nv_drm_plane_atomic_async_update(struct drm_plane *plane,
struct drm_plane_state *plane_state)
#endif
{
#if defined(NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_FULL_STATE_ARG)
struct drm_plane_state *plane_state =
drm_atomic_get_new_plane_state(state, plane);
#endif
struct nv_drm_crtc *nv_crtc;
struct nv_drm_device *nv_dev;

if (plane->state == NULL || plane_state == NULL) {
return;
}

plane->state->crtc_x = plane_state->crtc_x;
plane->state->crtc_y = plane_state->crtc_y;
plane->state->crtc_w = plane_state->crtc_w;
plane->state->crtc_h = plane_state->crtc_h;
plane->state->src_x = plane_state->src_x;
plane->state->src_y = plane_state->src_y;
plane->state->src_w = plane_state->src_w;
plane->state->src_h = plane_state->src_h;

if (WARN_ON_ONCE(plane->state->crtc == NULL ||
plane->state->crtc->state == NULL ||
!plane->state->crtc->state->active)) {
return;
}

nv_crtc = to_nv_crtc(plane->state->crtc);
nv_dev = to_nv_device(plane->dev);

if (nv_crtc != NULL && nv_dev != NULL && nvKms != NULL && nvKms->moveCursor != NULL) {
NvBool status = nvKms->moveCursor(
nv_dev->pDevice,
nv_crtc->head,
(NvS16)plane->state->crtc_x,
(NvS16)plane->state->crtc_y);

if (!status) {
NV_DRM_DEV_LOG_ERR(
nv_dev,
"Failed to move cursor on head %u to (%d, %d)",
nv_crtc->head,
plane->state->crtc_x,
plane->state->crtc_y);
}
}
}
#endif /* NV_DRM_PLANE_HELPER_FUNCS_HAS_ATOMIC_ASYNC_CHECK */

static const struct drm_plane_helper_funcs nv_plane_helper_funcs = {
.atomic_check = nv_drm_plane_atomic_check,
#if defined(NV_DRM_PLANE_HELPER_FUNCS_HAS_ATOMIC_ASYNC_CHECK)
.atomic_async_check = nv_drm_plane_atomic_async_check,
.atomic_async_update = nv_drm_plane_atomic_async_update,
#endif
};

static void nv_drm_crtc_destroy(struct drm_crtc *crtc)
Expand Down
5 changes: 5 additions & 0 deletions kernel-open/nvidia-drm/nvidia-drm-helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ typedef struct drm_atomic_state nv_drm_atomic_state_base_t;
#define NV_DRM_PLANE_ATOMIC_CHECK_HAS_FULL_STATE_ARG
#endif

#if defined(NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_ATOMIC_STATE_ARG) || \
defined(NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_FLIP_ARG)
#define NV_DRM_PLANE_ATOMIC_ASYNC_CHECK_HAS_FULL_STATE_ARG
#endif

static inline nv_drm_atomic_state_base_t *
nv_drm_atomic_state_base_alloc(struct drm_device *dev)
{
Expand Down
18 changes: 18 additions & 0 deletions kernel-open/nvidia-drm/nvidia-drm-modeset.c
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,24 @@ int nv_drm_atomic_commit(struct drm_device *dev,
struct drm_crtc_state *crtc_state = NULL;
struct nv_drm_device *nv_dev = to_nv_device(dev);

#if defined(NV_DRM_PLANE_HELPER_FUNCS_HAS_ATOMIC_ASYNC_CHECK)
if (state->async_update) {
ret = drm_atomic_helper_prepare_planes(dev, state);
if (ret) {
return ret;
}

drm_atomic_helper_async_commit(dev, state);

#if defined(NV_DRM_ATOMIC_HELPER_UNPREPARE_PLANES_PRESENT)
drm_atomic_helper_unprepare_planes(dev, state);
#else
drm_atomic_helper_cleanup_planes(dev, state);
#endif
return 0;
}
#endif

/*
* XXX: drm_mode_config_funcs::atomic_commit() mandates to return -EBUSY
* for nonblocking commit if the commit would need to wait for previous
Expand Down
Loading