/* * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. * * 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 AUTHORS OR COPYRIGHT HOLDERS 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. */ #include "nvidia-drm-conftest.h" /* NV_DRM_ATOMIC_MODESET_AVAILABLE */ #if defined(NV_DRM_ATOMIC_MODESET_AVAILABLE) #include "nvidia-drm-helper.h" #include "nvidia-drm-priv.h" #include "nvidia-drm-crtc.h" #include "nvidia-drm-connector.h" #include "nvidia-drm-encoder.h" #include "nvidia-drm-utils.h" #include "nvidia-drm-fb.h" #include "nvidia-drm-ioctl.h" #include "nvidia-drm-format.h" #include "nvmisc.h" #include #include #include #include #if defined(NV_LINUX_NVHOST_H_PRESENT) && defined(CONFIG_TEGRA_GRHOST) #include #endif static void nv_drm_plane_destroy(struct drm_plane *plane) { struct nv_drm_plane *nv_plane = to_nv_plane(plane); /* plane->state gets freed here */ drm_plane_cleanup(plane); nv_drm_free(nv_plane); } static inline void plane_req_config_disable(struct NvKmsKapiLayerRequestedConfig *req_config) { /* Clear layer config */ memset(&req_config->config, 0, sizeof(req_config->config)); /* Set flags to get cleared layer config applied */ req_config->flags.surfaceChanged = NV_TRUE; req_config->flags.srcXYChanged = NV_TRUE; req_config->flags.srcWHChanged = NV_TRUE; req_config->flags.dstXYChanged = NV_TRUE; req_config->flags.dstWHChanged = NV_TRUE; } static inline void cursor_req_config_disable(struct NvKmsKapiCursorRequestedConfig *req_config) { req_config->surface = NULL; req_config->flags.surfaceChanged = NV_TRUE; } static void cursor_plane_req_config_update(struct drm_plane *plane, struct drm_plane_state *plane_state, struct NvKmsKapiCursorRequestedConfig *req_config) { struct nv_drm_plane *nv_plane = to_nv_plane(plane); struct NvKmsKapiCursorRequestedConfig old_config = *req_config; struct nv_drm_device *nv_dev = to_nv_device(plane->dev); struct nv_drm_plane_state *nv_drm_plane_state = to_nv_drm_plane_state(plane_state); if (plane_state->fb == NULL) { cursor_req_config_disable(req_config); return; } *req_config = (struct NvKmsKapiCursorRequestedConfig) { .surface = to_nv_framebuffer(plane_state->fb)->pSurface, .dstX = plane_state->crtc_x, .dstY = plane_state->crtc_y, }; #if defined(NV_DRM_ALPHA_BLENDING_AVAILABLE) if (plane->blend_mode_property != NULL && plane->alpha_property != NULL) { switch (plane_state->pixel_blend_mode) { case DRM_MODE_BLEND_PREMULTI: req_config->compParams.compMode = NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_SURFACE_ALPHA; break; case DRM_MODE_BLEND_COVERAGE: req_config->compParams.compMode = NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_SURFACE_ALPHA; break; default: /* * We should not hit this, because * plane_state->pixel_blend_mode should only have values * registered in * __nv_drm_plane_create_alpha_blending_properties(). */ WARN_ON("Unsupported blending mode"); break; } req_config->compParams.surfaceAlpha = plane_state->alpha >> 8; } else if (plane->blend_mode_property != NULL) { switch (plane_state->pixel_blend_mode) { case DRM_MODE_BLEND_PREMULTI: req_config->compParams.compMode = NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA; break; case DRM_MODE_BLEND_COVERAGE: req_config->compParams.compMode = NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_ALPHA; break; default: /* * We should not hit this, because * plane_state->pixel_blend_mode should only have values * registered in * __nv_drm_plane_create_alpha_blending_properties(). */ WARN_ON("Unsupported blending mode"); break; } } else { req_config->compParams.compMode = nv_plane->defaultCompositionMode; } #else req_config->compParams.compMode = nv_plane->defaultCompositionMode; #endif /* * Unconditionally mark the surface as changed, even if nothing changed, * so that we always get a flip event: a DRM client may flip with * the same surface and wait for a flip event. */ req_config->flags.surfaceChanged = NV_TRUE; if (old_config.surface == NULL && old_config.surface != req_config->surface) { req_config->flags.dstXYChanged = NV_TRUE; return; } req_config->flags.dstXYChanged = old_config.dstX != req_config->dstX || old_config.dstY != req_config->dstY; } static int plane_req_config_update(struct drm_plane *plane, struct drm_plane_state *plane_state, struct NvKmsKapiLayerRequestedConfig *req_config) { struct nv_drm_plane *nv_plane = to_nv_plane(plane); struct NvKmsKapiLayerConfig old_config = req_config->config; struct nv_drm_device *nv_dev = to_nv_device(plane->dev); struct nv_drm_plane_state *nv_drm_plane_state = to_nv_drm_plane_state(plane_state); int ret = 0; if (plane_state->fb == NULL) { plane_req_config_disable(req_config); return 0; } *req_config = (struct NvKmsKapiLayerRequestedConfig) { .config = { .surface = to_nv_framebuffer(plane_state->fb)->pSurface, /* Source values are 16.16 fixed point */ .srcX = plane_state->src_x >> 16, .srcY = plane_state->src_y >> 16, .srcWidth = plane_state->src_w >> 16, .srcHeight = plane_state->src_h >> 16, .dstX = plane_state->crtc_x, .dstY = plane_state->crtc_y, .dstWidth = plane_state->crtc_w, .dstHeight = plane_state->crtc_h, }, }; #if defined(NV_DRM_ROTATION_AVAILABLE) /* * plane_state->rotation is only valid when plane->rotation_property * is non-NULL. */ if (plane->rotation_property != NULL) { if (plane_state->rotation & DRM_MODE_REFLECT_X) { req_config->config.rrParams.reflectionX = true; } if (plane_state->rotation & DRM_MODE_REFLECT_Y) { req_config->config.rrParams.reflectionY = true; } switch (plane_state->rotation & DRM_MODE_ROTATE_MASK) { case DRM_MODE_ROTATE_0: req_config->config.rrParams.rotation = NVKMS_ROTATION_0; break; case DRM_MODE_ROTATE_90: req_config->config.rrParams.rotation = NVKMS_ROTATION_90; break; case DRM_MODE_ROTATE_180: req_config->config.rrParams.rotation = NVKMS_ROTATION_180; break; case DRM_MODE_ROTATE_270: req_config->config.rrParams.rotation = NVKMS_ROTATION_270; break; default: /* * We should not hit this, because * plane_state->rotation should only have values * registered in * __nv_drm_plane_create_rotation_property(). */ WARN_ON("Unsupported rotation"); break; } } #endif #if defined(NV_DRM_ALPHA_BLENDING_AVAILABLE) if (plane->blend_mode_property != NULL && plane->alpha_property != NULL) { switch (plane_state->pixel_blend_mode) { case DRM_MODE_BLEND_PREMULTI: req_config->config.compParams.compMode = NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_SURFACE_ALPHA; break; case DRM_MODE_BLEND_COVERAGE: req_config->config.compParams.compMode = NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_SURFACE_ALPHA; break; default: /* * We should not hit this, because * plane_state->pixel_blend_mode should only have values * registered in * __nv_drm_plane_create_alpha_blending_properties(). */ WARN_ON("Unsupported blending mode"); break; } req_config->config.compParams.surfaceAlpha = plane_state->alpha >> 8; } else if (plane->blend_mode_property != NULL) { switch (plane_state->pixel_blend_mode) { case DRM_MODE_BLEND_PREMULTI: req_config->config.compParams.compMode = NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA; break; case DRM_MODE_BLEND_COVERAGE: req_config->config.compParams.compMode = NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_ALPHA; break; default: /* * We should not hit this, because * plane_state->pixel_blend_mode should only have values * registered in * __nv_drm_plane_create_alpha_blending_properties(). */ WARN_ON("Unsupported blending mode"); break; } } else { req_config->config.compParams.compMode = nv_plane->defaultCompositionMode; } #else req_config->config.compParams.compMode = nv_plane->defaultCompositionMode; #endif req_config->config.syncptParams.preSyncptSpecified = false; req_config->config.syncptParams.postSyncptRequested = false; if (plane_state->fence != NULL || nv_drm_plane_state->fd_user_ptr) { if (!nv_dev->supportsSyncpts) { return -1; } #if defined(NV_LINUX_NVHOST_H_PRESENT) && defined(CONFIG_TEGRA_GRHOST) #if defined(NV_NVHOST_DMA_FENCE_UNPACK_PRESENT) if (plane_state->fence != NULL) { ret = nvhost_dma_fence_unpack( plane_state->fence, &req_config->config.syncptParams.preSyncptId, &req_config->config.syncptParams.preSyncptValue); if (ret != 0) { return ret; } req_config->config.syncptParams.preSyncptSpecified = true; } #endif if (nv_drm_plane_state->fd_user_ptr) { req_config->config.syncptParams.postSyncptRequested = true; } #else return -1; #endif } /* * Unconditionally mark the surface as changed, even if nothing changed, * so that we always get a flip event: a DRM client may flip with * the same surface and wait for a flip event. */ req_config->flags.surfaceChanged = NV_TRUE; if (old_config.surface == NULL && old_config.surface != req_config->config.surface) { req_config->flags.srcXYChanged = NV_TRUE; req_config->flags.srcWHChanged = NV_TRUE; req_config->flags.dstXYChanged = NV_TRUE; req_config->flags.dstWHChanged = NV_TRUE; return 0; } req_config->flags.srcXYChanged = old_config.srcX != req_config->config.srcX || old_config.srcY != req_config->config.srcY; req_config->flags.srcWHChanged = old_config.srcWidth != req_config->config.srcWidth || old_config.srcHeight != req_config->config.srcHeight; req_config->flags.dstXYChanged = old_config.dstX != req_config->config.dstX || old_config.dstY != req_config->config.dstY; req_config->flags.dstWHChanged = old_config.dstWidth != req_config->config.dstWidth || old_config.dstHeight != req_config->config.dstHeight; return 0; } static bool __is_async_flip_requested(const struct drm_plane *plane, const struct drm_crtc_state *crtc_state) { if (plane->type == DRM_PLANE_TYPE_PRIMARY) { #if defined(NV_DRM_CRTC_STATE_HAS_ASYNC_FLIP) return crtc_state->async_flip; #elif defined(NV_DRM_CRTC_STATE_HAS_PAGEFLIP_FLAGS) return !!(crtc_state->pageflip_flags & DRM_MODE_PAGE_FLIP_ASYNC); #endif } return false; } static int __nv_drm_cursor_atomic_check(struct drm_plane *plane, struct drm_plane_state *plane_state) { struct nv_drm_plane *nv_plane = to_nv_plane(plane); int i; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; WARN_ON(nv_plane->layer_idx != NVKMS_KAPI_LAYER_INVALID_IDX); nv_drm_for_each_crtc_in_state(plane_state->state, crtc, crtc_state, i) { struct nv_drm_crtc_state *nv_crtc_state = to_nv_crtc_state(crtc_state); struct NvKmsKapiHeadRequestedConfig *head_req_config = &nv_crtc_state->req_config; struct NvKmsKapiCursorRequestedConfig *cursor_req_config = &head_req_config->cursorRequestedConfig; if (plane->state->crtc == crtc && plane->state->crtc != plane_state->crtc) { cursor_req_config_disable(cursor_req_config); continue; } if (plane_state->crtc == crtc) { cursor_plane_req_config_update(plane, plane_state, cursor_req_config); } } return 0; } #if defined(NV_DRM_PLANE_ATOMIC_CHECK_HAS_ATOMIC_STATE_ARG) static int nv_drm_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state) #else static int nv_drm_plane_atomic_check(struct drm_plane *plane, struct drm_plane_state *plane_state) #endif { struct nv_drm_plane *nv_plane = to_nv_plane(plane); #if defined(NV_DRM_PLANE_ATOMIC_CHECK_HAS_ATOMIC_STATE_ARG) struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); #endif int i; struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; int ret; if (plane->type == DRM_PLANE_TYPE_CURSOR) { return __nv_drm_cursor_atomic_check(plane, plane_state); } WARN_ON(nv_plane->layer_idx == NVKMS_KAPI_LAYER_INVALID_IDX); nv_drm_for_each_crtc_in_state(plane_state->state, crtc, crtc_state, i) { struct nv_drm_crtc_state *nv_crtc_state = to_nv_crtc_state(crtc_state); struct NvKmsKapiHeadRequestedConfig *head_req_config = &nv_crtc_state->req_config; struct NvKmsKapiLayerRequestedConfig *plane_requested_config = &head_req_config->layerRequestedConfig[nv_plane->layer_idx]; if (plane->state->crtc == crtc && plane->state->crtc != plane_state->crtc) { plane_req_config_disable(plane_requested_config); continue; } if (plane_state->crtc == crtc) { ret = plane_req_config_update(plane, plane_state, plane_requested_config); if (ret != 0) { return ret; } if (__is_async_flip_requested(plane, crtc_state)) { /* * Async flip requests that the flip happen 'as soon as * possible', meaning that it not delay waiting for vblank. * This may cause tearing on the screen. */ plane_requested_config->config.minPresentInterval = 0; plane_requested_config->config.tearing = NV_TRUE; } else { plane_requested_config->config.minPresentInterval = 1; plane_requested_config->config.tearing = NV_FALSE; } } } return 0; } #if defined(NV_DRM_UNIVERSAL_PLANE_INIT_HAS_FORMAT_MODIFIERS_ARG) static bool nv_drm_plane_format_mod_supported(struct drm_plane *plane, uint32_t format, uint64_t modifier) { /* All supported modifiers are compatible with all supported formats */ return true; } #endif static int nv_drm_plane_atomic_set_property( struct drm_plane *plane, struct drm_plane_state *state, struct drm_property *property, uint64_t val) { struct nv_drm_device *nv_dev = to_nv_device(plane->dev); struct nv_drm_plane_state *nv_drm_plane_state = to_nv_drm_plane_state(state); if (property == nv_dev->nv_out_fence_property) { #if defined(NV_LINUX_NVHOST_H_PRESENT) && defined(CONFIG_TEGRA_GRHOST) nv_drm_plane_state->fd_user_ptr = u64_to_user_ptr(val); #endif return 0; } else { return -EINVAL; } } static int nv_drm_plane_atomic_get_property( struct drm_plane *plane, const struct drm_plane_state *state, struct drm_property *property, uint64_t *val) { struct nv_drm_device *nv_dev = to_nv_device(plane->dev); if (property == nv_dev->nv_out_fence_property) { return 0; } else { return -EINVAL; } } static struct drm_plane_state * nv_drm_plane_atomic_duplicate_state(struct drm_plane *plane) { struct nv_drm_plane_state *nv_old_plane_state = to_nv_drm_plane_state(plane->state); struct nv_drm_plane_state *nv_plane_state = nv_drm_calloc(1, sizeof(*nv_plane_state)); if (nv_plane_state == NULL) { return NULL; } __drm_atomic_helper_plane_duplicate_state(plane, &nv_plane_state->base); nv_plane_state->fd_user_ptr = nv_old_plane_state->fd_user_ptr; return &nv_plane_state->base; } static inline void __nv_drm_plane_atomic_destroy_state( struct drm_plane *plane, struct drm_plane_state *state) { #if defined(NV_DRM_ATOMIC_HELPER_PLANE_DESTROY_STATE_HAS_PLANE_ARG) __drm_atomic_helper_plane_destroy_state(plane, state); #else __drm_atomic_helper_plane_destroy_state(state); #endif } static void nv_drm_plane_atomic_destroy_state( struct drm_plane *plane, struct drm_plane_state *state) { __nv_drm_plane_atomic_destroy_state(plane, state); nv_drm_free(to_nv_drm_plane_state(state)); } static const struct drm_plane_funcs nv_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = nv_drm_plane_destroy, .reset = drm_atomic_helper_plane_reset, .atomic_get_property = nv_drm_plane_atomic_get_property, .atomic_set_property = nv_drm_plane_atomic_set_property, .atomic_duplicate_state = nv_drm_plane_atomic_duplicate_state, .atomic_destroy_state = nv_drm_plane_atomic_destroy_state, #if defined(NV_DRM_UNIVERSAL_PLANE_INIT_HAS_FORMAT_MODIFIERS_ARG) .format_mod_supported = nv_drm_plane_format_mod_supported, #endif }; static const struct drm_plane_helper_funcs nv_plane_helper_funcs = { .atomic_check = nv_drm_plane_atomic_check, }; static void nv_drm_crtc_destroy(struct drm_crtc *crtc) { struct nv_drm_crtc *nv_crtc = to_nv_crtc(crtc); drm_crtc_cleanup(crtc); nv_drm_free(nv_crtc); } static inline void __nv_drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) { #if defined(NV_DRM_ATOMIC_HELPER_CRTC_DESTROY_STATE_HAS_CRTC_ARG) __drm_atomic_helper_crtc_destroy_state(crtc, crtc_state); #else __drm_atomic_helper_crtc_destroy_state(crtc_state); #endif } static inline void nv_drm_crtc_duplicate_req_head_modeset_config( const struct NvKmsKapiHeadRequestedConfig *old, struct NvKmsKapiHeadRequestedConfig *new) { uint32_t i; /* * Do not duplicate fields like 'modeChanged' flags expressing delta changed * in new configuration with respect to previous/old configuration because * there is no change in new configuration yet with respect * to older one! */ *new = (struct NvKmsKapiHeadRequestedConfig) { .modeSetConfig = old->modeSetConfig, }; for (i = 0; i < ARRAY_SIZE(old->layerRequestedConfig); i++) { new->layerRequestedConfig[i] = (struct NvKmsKapiLayerRequestedConfig) { .config = old->layerRequestedConfig[i].config, }; } } /** * nv_drm_atomic_crtc_duplicate_state - crtc state duplicate hook * @crtc: DRM crtc * * Allocate and accosiate flip state with DRM crtc state, this flip state will * be getting consumed at the time of atomic update commit to hardware by * nv_drm_atomic_helper_commit_tail(). */ static struct drm_crtc_state* nv_drm_atomic_crtc_duplicate_state(struct drm_crtc *crtc) { struct nv_drm_crtc_state *nv_state = nv_drm_calloc(1, sizeof(*nv_state)); if (nv_state == NULL) { return NULL; } if ((nv_state->nv_flip = nv_drm_calloc(1, sizeof(*(nv_state->nv_flip)))) == NULL) { nv_drm_free(nv_state); return NULL; } __drm_atomic_helper_crtc_duplicate_state(crtc, &nv_state->base); INIT_LIST_HEAD(&nv_state->nv_flip->list_entry); INIT_LIST_HEAD(&nv_state->nv_flip->deferred_flip_list); nv_drm_crtc_duplicate_req_head_modeset_config( &(to_nv_crtc_state(crtc->state)->req_config), &nv_state->req_config); return &nv_state->base; } /** * nv_drm_atomic_crtc_destroy_state - crtc state destroy hook * @crtc: DRM crtc * @state: DRM crtc state object to destroy * * Destroy flip state associated with the given crtc state if it haven't get * consumed because failure of atomic commit. */ static void nv_drm_atomic_crtc_destroy_state(struct drm_crtc *crtc, struct drm_crtc_state *state) { struct nv_drm_crtc_state *nv_state = to_nv_crtc_state(state); if (nv_state->nv_flip != NULL) { nv_drm_free(nv_state->nv_flip); nv_state->nv_flip = NULL; } __nv_drm_atomic_helper_crtc_destroy_state(crtc, &nv_state->base); nv_drm_free(nv_state); } static struct drm_crtc_funcs nv_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, .reset = drm_atomic_helper_crtc_reset, .destroy = nv_drm_crtc_destroy, .atomic_duplicate_state = nv_drm_atomic_crtc_duplicate_state, .atomic_destroy_state = nv_drm_atomic_crtc_destroy_state, }; /* * In kernel versions before the addition of * drm_crtc_state::connectors_changed, connector changes were * reflected in drm_crtc_state::mode_changed. */ static inline bool nv_drm_crtc_state_connectors_changed(struct drm_crtc_state *crtc_state) { #if defined(NV_DRM_CRTC_STATE_HAS_CONNECTORS_CHANGED) return crtc_state->connectors_changed; #else return crtc_state->mode_changed; #endif } static int head_modeset_config_attach_connector( struct nv_drm_connector *nv_connector, struct NvKmsKapiHeadModeSetConfig *head_modeset_config) { struct nv_drm_encoder *nv_encoder = nv_connector->nv_detected_encoder; if (NV_DRM_WARN(nv_encoder == NULL || head_modeset_config->numDisplays >= ARRAY_SIZE(head_modeset_config->displays))) { return -EINVAL; } head_modeset_config->displays[head_modeset_config->numDisplays++] = nv_encoder->hDisplay; return 0; } /** * nv_drm_crtc_atomic_check() can fail after it has modified * the 'nv_drm_crtc_state::req_config', that is fine because 'nv_drm_crtc_state' * will be discarded if ->atomic_check() fails. */ #if defined(NV_DRM_CRTC_ATOMIC_CHECK_HAS_ATOMIC_STATE_ARG) static int nv_drm_crtc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *state) #else static int nv_drm_crtc_atomic_check(struct drm_crtc *crtc, struct drm_crtc_state *crtc_state) #endif { #if defined(NV_DRM_CRTC_ATOMIC_CHECK_HAS_ATOMIC_STATE_ARG) struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); #endif struct nv_drm_crtc_state *nv_crtc_state = to_nv_crtc_state(crtc_state); struct NvKmsKapiHeadRequestedConfig *req_config = &nv_crtc_state->req_config; int ret = 0; if (crtc_state->mode_changed) { drm_mode_to_nvkms_display_mode(&crtc_state->mode, &req_config->modeSetConfig.mode); req_config->flags.modeChanged = NV_TRUE; } if (nv_drm_crtc_state_connectors_changed(crtc_state)) { struct NvKmsKapiHeadModeSetConfig *config = &req_config->modeSetConfig; struct drm_connector *connector; struct drm_connector_state *connector_state; int j; config->numDisplays = 0; memset(config->displays, 0, sizeof(config->displays)); req_config->flags.displaysChanged = NV_TRUE; nv_drm_for_each_connector_in_state(crtc_state->state, connector, connector_state, j) { if (connector_state->crtc != crtc) { continue; } if ((ret = head_modeset_config_attach_connector( to_nv_connector(connector), config)) != 0) { return ret; } } } if (crtc_state->active_changed) { req_config->modeSetConfig.bActive = crtc_state->active; req_config->flags.activeChanged = NV_TRUE; } return ret; } static bool nv_drm_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { return true; } static const struct drm_crtc_helper_funcs nv_crtc_helper_funcs = { .atomic_check = nv_drm_crtc_atomic_check, .mode_fixup = nv_drm_crtc_mode_fixup, }; static void nv_drm_plane_install_properties( struct drm_plane *plane) { struct nv_drm_device *nv_dev = to_nv_device(plane->dev); if (nv_dev->nv_out_fence_property) { drm_object_attach_property( &plane->base, nv_dev->nv_out_fence_property, 0); } } static void __nv_drm_plane_create_alpha_blending_properties(struct drm_plane *plane, NvU32 validCompModes) { #if defined(NV_DRM_ALPHA_BLENDING_AVAILABLE) if ((validCompModes & NVBIT(NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_SURFACE_ALPHA)) != 0x0 && (validCompModes & NVBIT(NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_SURFACE_ALPHA)) != 0x0) { drm_plane_create_alpha_property(plane); drm_plane_create_blend_mode_property(plane, NVBIT(DRM_MODE_BLEND_PREMULTI) | NVBIT(DRM_MODE_BLEND_COVERAGE)); } else if ((validCompModes & NVBIT(NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA)) != 0x0 && (validCompModes & NVBIT(NVKMS_COMPOSITION_BLENDING_MODE_NON_PREMULT_ALPHA)) != 0x0) { drm_plane_create_blend_mode_property(plane, NVBIT(DRM_MODE_BLEND_PREMULTI) | NVBIT(DRM_MODE_BLEND_COVERAGE)); } #endif } static void __nv_drm_plane_create_rotation_property(struct drm_plane *plane, NvU16 validLayerRRTransforms) { #if defined(NV_DRM_ROTATION_AVAILABLE) enum NvKmsRotation curRotation; NvU32 supported_rotations = 0; struct NvKmsRRParams rrParams = { .rotation = NVKMS_ROTATION_0, .reflectionX = true, .reflectionY = true, }; if ((NVBIT(NvKmsRRParamsToCapBit(&rrParams)) & validLayerRRTransforms) != 0) { supported_rotations |= DRM_MODE_REFLECT_X; supported_rotations |= DRM_MODE_REFLECT_Y; } rrParams.reflectionX = false; rrParams.reflectionY = false; for (curRotation = NVKMS_ROTATION_MIN; curRotation <= NVKMS_ROTATION_MAX; curRotation++) { rrParams.rotation = curRotation; if ((NVBIT(NvKmsRRParamsToCapBit(&rrParams)) & validLayerRRTransforms) == 0) { continue; } switch (curRotation) { case NVKMS_ROTATION_0: supported_rotations |= DRM_MODE_ROTATE_0; break; case NVKMS_ROTATION_90: supported_rotations |= DRM_MODE_ROTATE_90; break; case NVKMS_ROTATION_180: supported_rotations |= DRM_MODE_ROTATE_180; break; case NVKMS_ROTATION_270: supported_rotations |= DRM_MODE_ROTATE_270; break; default: break; } } if (supported_rotations != 0) { drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0, supported_rotations); } #endif } static struct drm_plane* nv_drm_plane_create(struct drm_device *dev, enum drm_plane_type plane_type, uint32_t layer_idx, NvU32 head, const struct NvKmsKapiDeviceResourcesInfo *pResInfo) { #if defined(NV_DRM_UNIVERSAL_PLANE_INIT_HAS_FORMAT_MODIFIERS_ARG) struct nv_drm_device *nv_dev = to_nv_device(dev); const NvU64 linear_modifiers[] = { DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID, }; #endif enum NvKmsCompositionBlendingMode defaultCompositionMode; struct nv_drm_plane *nv_plane = NULL; struct nv_drm_plane_state *nv_plane_state = NULL; struct drm_plane *plane = NULL; int ret = -ENOMEM; uint32_t *formats = NULL; unsigned int formats_count = 0; const NvU32 validCompositionModes = (plane_type == DRM_PLANE_TYPE_CURSOR) ? pResInfo->caps.validCursorCompositionModes : pResInfo->caps.layer[layer_idx].validCompositionModes; const long unsigned int nvkms_formats_mask = (plane_type == DRM_PLANE_TYPE_CURSOR) ? pResInfo->caps.supportedCursorSurfaceMemoryFormats : pResInfo->supportedSurfaceMemoryFormats[layer_idx]; const NvU16 validLayerRRTransforms = (plane_type == DRM_PLANE_TYPE_CURSOR) ? 0x0 : pResInfo->caps.layer[layer_idx].validRRTransforms; if ((validCompositionModes & NVBIT(NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE)) != 0x0) { defaultCompositionMode = NVKMS_COMPOSITION_BLENDING_MODE_OPAQUE; } else if ((validCompositionModes & NVBIT(NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA)) != 0x0) { defaultCompositionMode = NVKMS_COMPOSITION_BLENDING_MODE_PREMULT_ALPHA; } else { goto failed; } formats = nv_drm_format_array_alloc(&formats_count, nvkms_formats_mask); if (formats == NULL) { goto failed; } if ((nv_plane = nv_drm_calloc(1, sizeof(*nv_plane))) == NULL) { goto failed_plane_alloc; } plane = &nv_plane->base; nv_plane->defaultCompositionMode = defaultCompositionMode; nv_plane->layer_idx = layer_idx; if ((nv_plane_state = nv_drm_calloc(1, sizeof(*nv_plane_state))) == NULL) { goto failed_state_alloc; } plane->state = &nv_plane_state->base; plane->state->plane = plane; /* * Possible_crtcs for primary and cursor plane is zero because * drm_crtc_init_with_planes() will assign the plane's possible_crtcs * after the crtc is successfully initialized. */ ret = drm_universal_plane_init( dev, plane, (plane_type == DRM_PLANE_TYPE_OVERLAY) ? (1 << head) : 0, &nv_plane_funcs, formats, formats_count, #if defined(NV_DRM_UNIVERSAL_PLANE_INIT_HAS_FORMAT_MODIFIERS_ARG) (plane_type == DRM_PLANE_TYPE_CURSOR) ? linear_modifiers : nv_dev->modifiers, #endif plane_type #if defined(NV_DRM_UNIVERSAL_PLANE_INIT_HAS_NAME_ARG) , NULL #endif ); if (ret != 0) { goto failed_plane_init; } drm_plane_helper_add(plane, &nv_plane_helper_funcs); if (plane_type != DRM_PLANE_TYPE_CURSOR) { nv_drm_plane_install_properties(plane); } __nv_drm_plane_create_alpha_blending_properties( plane, validCompositionModes); __nv_drm_plane_create_rotation_property( plane, validLayerRRTransforms); return plane; failed_plane_init: nv_drm_free(nv_plane_state); failed_state_alloc: nv_drm_free(nv_plane); failed_plane_alloc: nv_drm_free(formats); failed: return ERR_PTR(ret); } /* * Add drm crtc for given head and supported enum NvKmsSurfaceMemoryFormats. */ static struct drm_crtc *__nv_drm_crtc_create(struct nv_drm_device *nv_dev, struct drm_plane *primary_plane, struct drm_plane *cursor_plane, unsigned int head) { struct nv_drm_crtc *nv_crtc = NULL; struct nv_drm_crtc_state *nv_state = NULL; int ret = -ENOMEM; if ((nv_crtc = nv_drm_calloc(1, sizeof(*nv_crtc))) == NULL) { goto failed; } nv_state = nv_drm_calloc(1, sizeof(*nv_state)); if (nv_state == NULL) { goto failed_state_alloc; } nv_crtc->base.state = &nv_state->base; nv_crtc->base.state->crtc = &nv_crtc->base; nv_crtc->head = head; INIT_LIST_HEAD(&nv_crtc->flip_list); spin_lock_init(&nv_crtc->flip_list_lock); ret = drm_crtc_init_with_planes(nv_dev->dev, &nv_crtc->base, primary_plane, cursor_plane, &nv_crtc_funcs #if defined(NV_DRM_CRTC_INIT_WITH_PLANES_HAS_NAME_ARG) , NULL #endif ); if (ret != 0) { NV_DRM_DEV_LOG_ERR( nv_dev, "Failed to init crtc for head %u with planes", head); goto failed_init_crtc; } /* Add crtc to drm sub-system */ drm_crtc_helper_add(&nv_crtc->base, &nv_crtc_helper_funcs); return &nv_crtc->base; failed_init_crtc: nv_drm_free(nv_state); failed_state_alloc: nv_drm_free(nv_crtc); failed: return ERR_PTR(ret); } void nv_drm_enumerate_crtcs_and_planes( struct nv_drm_device *nv_dev, const struct NvKmsKapiDeviceResourcesInfo *pResInfo) { unsigned int i; for (i = 0; i < pResInfo->numHeads; i++) { struct drm_plane *primary_plane = NULL, *cursor_plane = NULL; NvU32 layer; if (pResInfo->numLayers[i] <= NVKMS_KAPI_LAYER_PRIMARY_IDX) { continue; } primary_plane = nv_drm_plane_create(nv_dev->dev, DRM_PLANE_TYPE_PRIMARY, NVKMS_KAPI_LAYER_PRIMARY_IDX, i, pResInfo); if (IS_ERR(primary_plane)) { NV_DRM_DEV_LOG_ERR( nv_dev, "Failed to create primary plane for head %u, error = %ld", i, PTR_ERR(primary_plane)); continue; } cursor_plane = nv_drm_plane_create(nv_dev->dev, DRM_PLANE_TYPE_CURSOR, NVKMS_KAPI_LAYER_INVALID_IDX, i, pResInfo); if (IS_ERR(cursor_plane)) { NV_DRM_DEV_LOG_ERR( nv_dev, "Failed to create cursor plane for head %u, error = %ld", i, PTR_ERR(cursor_plane)); cursor_plane = NULL; } /* Create crtc with the primary and cursor planes */ { struct drm_crtc *crtc = __nv_drm_crtc_create(nv_dev, primary_plane, cursor_plane, i); if (IS_ERR(crtc)) { nv_drm_plane_destroy(primary_plane); if (cursor_plane != NULL) { nv_drm_plane_destroy(cursor_plane); } NV_DRM_DEV_LOG_ERR( nv_dev, "Failed to add DRM CRTC for head %u, error = %ld", i, PTR_ERR(crtc)); continue; } } for (layer = 0; layer < pResInfo->numLayers[i]; layer++) { if (layer == NVKMS_KAPI_LAYER_PRIMARY_IDX) { continue; } struct drm_plane *overlay_plane = nv_drm_plane_create(nv_dev->dev, DRM_PLANE_TYPE_OVERLAY, layer, i, pResInfo); if (IS_ERR(overlay_plane)) { NV_DRM_DEV_LOG_ERR( nv_dev, "Failed to create plane for layer-%u of head %u, error = %ld", layer, i, PTR_ERR(overlay_plane)); } } } } /* * Helper function to convert NvKmsKapiCrcs to drm_nvidia_crtc_crc32_out. */ static void NvKmsKapiCrcsToDrm(const struct NvKmsKapiCrcs *crcs, struct drm_nvidia_crtc_crc32_v2_out *drmCrcs) { drmCrcs->outputCrc32.value = crcs->outputCrc32.value; drmCrcs->outputCrc32.supported = crcs->outputCrc32.supported; drmCrcs->rasterGeneratorCrc32.value = crcs->rasterGeneratorCrc32.value; drmCrcs->rasterGeneratorCrc32.supported = crcs->rasterGeneratorCrc32.supported; drmCrcs->compositorCrc32.value = crcs->compositorCrc32.value; drmCrcs->compositorCrc32.supported = crcs->compositorCrc32.supported; } int nv_drm_get_crtc_crc32_v2_ioctl(struct drm_device *dev, void *data, struct drm_file *filep) { struct drm_nvidia_get_crtc_crc32_v2_params *params = data; struct nv_drm_device *nv_dev = to_nv_device(dev); struct drm_crtc *crtc = NULL; struct nv_drm_crtc *nv_crtc = NULL; struct NvKmsKapiCrcs crc32; if (!drm_core_check_feature(dev, DRIVER_MODESET)) { return -ENOENT; } crtc = nv_drm_crtc_find(dev, params->crtc_id); if (!crtc) { return -ENOENT; } nv_crtc = to_nv_crtc(crtc); if (!nvKms->getCRC32(nv_dev->pDevice, nv_crtc->head, &crc32)) { return -ENODEV; } NvKmsKapiCrcsToDrm(&crc32, ¶ms->crc32); return 0; } int nv_drm_get_crtc_crc32_ioctl(struct drm_device *dev, void *data, struct drm_file *filep) { struct drm_nvidia_get_crtc_crc32_params *params = data; struct nv_drm_device *nv_dev = to_nv_device(dev); struct drm_crtc *crtc = NULL; struct nv_drm_crtc *nv_crtc = NULL; struct NvKmsKapiCrcs crc32; if (!drm_core_check_feature(dev, DRIVER_MODESET)) { return -ENOENT; } crtc = nv_drm_crtc_find(dev, params->crtc_id); if (!crtc) { return -ENOENT; } nv_crtc = to_nv_crtc(crtc); if (!nvKms->getCRC32(nv_dev->pDevice, nv_crtc->head, &crc32)) { return -ENODEV; } params->crc32 = crc32.outputCrc32.value; return 0; } #endif