1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2c8b75bcaSEric Anholt /* 3c8b75bcaSEric Anholt * Copyright (C) 2015 Broadcom 4c8b75bcaSEric Anholt */ 5c8b75bcaSEric Anholt 6c8b75bcaSEric Anholt /** 7c8b75bcaSEric Anholt * DOC: VC4 KMS 8c8b75bcaSEric Anholt * 9c8b75bcaSEric Anholt * This is the general code for implementing KMS mode setting that 10c8b75bcaSEric Anholt * doesn't clearly associate with any of the other objects (plane, 11c8b75bcaSEric Anholt * crtc, HDMI encoder). 12c8b75bcaSEric Anholt */ 13c8b75bcaSEric Anholt 14b7e8e25bSMasahiro Yamada #include <drm/drm_atomic.h> 15b7e8e25bSMasahiro Yamada #include <drm/drm_atomic_helper.h> 16fd6d6d80SSam Ravnborg #include <drm/drm_crtc.h> 179762477cSNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 18fcd70cd3SDaniel Vetter #include <drm/drm_plane_helper.h> 19fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h> 20fd6d6d80SSam Ravnborg #include <drm/drm_vblank.h> 21fd6d6d80SSam Ravnborg 22c8b75bcaSEric Anholt #include "vc4_drv.h" 23766cc6b1SStefan Schake #include "vc4_regs.h" 24766cc6b1SStefan Schake 25766cc6b1SStefan Schake struct vc4_ctm_state { 26766cc6b1SStefan Schake struct drm_private_state base; 27766cc6b1SStefan Schake struct drm_color_ctm *ctm; 28766cc6b1SStefan Schake int fifo; 29766cc6b1SStefan Schake }; 30766cc6b1SStefan Schake 31766cc6b1SStefan Schake static struct vc4_ctm_state *to_vc4_ctm_state(struct drm_private_state *priv) 32766cc6b1SStefan Schake { 33766cc6b1SStefan Schake return container_of(priv, struct vc4_ctm_state, base); 34766cc6b1SStefan Schake } 35766cc6b1SStefan Schake 364686da83SBoris Brezillon struct vc4_load_tracker_state { 374686da83SBoris Brezillon struct drm_private_state base; 384686da83SBoris Brezillon u64 hvs_load; 394686da83SBoris Brezillon u64 membus_load; 404686da83SBoris Brezillon }; 414686da83SBoris Brezillon 424686da83SBoris Brezillon static struct vc4_load_tracker_state * 434686da83SBoris Brezillon to_vc4_load_tracker_state(struct drm_private_state *priv) 444686da83SBoris Brezillon { 454686da83SBoris Brezillon return container_of(priv, struct vc4_load_tracker_state, base); 464686da83SBoris Brezillon } 474686da83SBoris Brezillon 48766cc6b1SStefan Schake static struct vc4_ctm_state *vc4_get_ctm_state(struct drm_atomic_state *state, 49766cc6b1SStefan Schake struct drm_private_obj *manager) 50766cc6b1SStefan Schake { 51766cc6b1SStefan Schake struct drm_device *dev = state->dev; 52766cc6b1SStefan Schake struct vc4_dev *vc4 = dev->dev_private; 53766cc6b1SStefan Schake struct drm_private_state *priv_state; 54766cc6b1SStefan Schake int ret; 55766cc6b1SStefan Schake 56766cc6b1SStefan Schake ret = drm_modeset_lock(&vc4->ctm_state_lock, state->acquire_ctx); 57766cc6b1SStefan Schake if (ret) 58766cc6b1SStefan Schake return ERR_PTR(ret); 59766cc6b1SStefan Schake 60766cc6b1SStefan Schake priv_state = drm_atomic_get_private_obj_state(state, manager); 61766cc6b1SStefan Schake if (IS_ERR(priv_state)) 62766cc6b1SStefan Schake return ERR_CAST(priv_state); 63766cc6b1SStefan Schake 64766cc6b1SStefan Schake return to_vc4_ctm_state(priv_state); 65766cc6b1SStefan Schake } 66766cc6b1SStefan Schake 67766cc6b1SStefan Schake static struct drm_private_state * 68766cc6b1SStefan Schake vc4_ctm_duplicate_state(struct drm_private_obj *obj) 69766cc6b1SStefan Schake { 70766cc6b1SStefan Schake struct vc4_ctm_state *state; 71766cc6b1SStefan Schake 72766cc6b1SStefan Schake state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); 73766cc6b1SStefan Schake if (!state) 74766cc6b1SStefan Schake return NULL; 75766cc6b1SStefan Schake 76766cc6b1SStefan Schake __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 77766cc6b1SStefan Schake 78766cc6b1SStefan Schake return &state->base; 79766cc6b1SStefan Schake } 80766cc6b1SStefan Schake 81766cc6b1SStefan Schake static void vc4_ctm_destroy_state(struct drm_private_obj *obj, 82766cc6b1SStefan Schake struct drm_private_state *state) 83766cc6b1SStefan Schake { 84766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state = to_vc4_ctm_state(state); 85766cc6b1SStefan Schake 86766cc6b1SStefan Schake kfree(ctm_state); 87766cc6b1SStefan Schake } 88766cc6b1SStefan Schake 89766cc6b1SStefan Schake static const struct drm_private_state_funcs vc4_ctm_state_funcs = { 90766cc6b1SStefan Schake .atomic_duplicate_state = vc4_ctm_duplicate_state, 91766cc6b1SStefan Schake .atomic_destroy_state = vc4_ctm_destroy_state, 92766cc6b1SStefan Schake }; 93766cc6b1SStefan Schake 94766cc6b1SStefan Schake /* Converts a DRM S31.32 value to the HW S0.9 format. */ 95766cc6b1SStefan Schake static u16 vc4_ctm_s31_32_to_s0_9(u64 in) 96766cc6b1SStefan Schake { 97766cc6b1SStefan Schake u16 r; 98766cc6b1SStefan Schake 99766cc6b1SStefan Schake /* Sign bit. */ 100766cc6b1SStefan Schake r = in & BIT_ULL(63) ? BIT(9) : 0; 101766cc6b1SStefan Schake 102766cc6b1SStefan Schake if ((in & GENMASK_ULL(62, 32)) > 0) { 103766cc6b1SStefan Schake /* We have zero integer bits so we can only saturate here. */ 104766cc6b1SStefan Schake r |= GENMASK(8, 0); 105766cc6b1SStefan Schake } else { 106766cc6b1SStefan Schake /* Otherwise take the 9 most important fractional bits. */ 107766cc6b1SStefan Schake r |= (in >> 23) & GENMASK(8, 0); 108766cc6b1SStefan Schake } 109766cc6b1SStefan Schake 110766cc6b1SStefan Schake return r; 111766cc6b1SStefan Schake } 112766cc6b1SStefan Schake 113766cc6b1SStefan Schake static void 114766cc6b1SStefan Schake vc4_ctm_commit(struct vc4_dev *vc4, struct drm_atomic_state *state) 115766cc6b1SStefan Schake { 116766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state = to_vc4_ctm_state(vc4->ctm_manager.state); 117766cc6b1SStefan Schake struct drm_color_ctm *ctm = ctm_state->ctm; 118766cc6b1SStefan Schake 119766cc6b1SStefan Schake if (ctm_state->fifo) { 120766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDCOEF2, 121766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[0]), 122766cc6b1SStefan Schake SCALER_OLEDCOEF2_R_TO_R) | 123766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[3]), 124766cc6b1SStefan Schake SCALER_OLEDCOEF2_R_TO_G) | 125766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[6]), 126766cc6b1SStefan Schake SCALER_OLEDCOEF2_R_TO_B)); 127766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDCOEF1, 128766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[1]), 129766cc6b1SStefan Schake SCALER_OLEDCOEF1_G_TO_R) | 130766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[4]), 131766cc6b1SStefan Schake SCALER_OLEDCOEF1_G_TO_G) | 132766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[7]), 133766cc6b1SStefan Schake SCALER_OLEDCOEF1_G_TO_B)); 134766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDCOEF0, 135766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[2]), 136766cc6b1SStefan Schake SCALER_OLEDCOEF0_B_TO_R) | 137766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[5]), 138766cc6b1SStefan Schake SCALER_OLEDCOEF0_B_TO_G) | 139766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[8]), 140766cc6b1SStefan Schake SCALER_OLEDCOEF0_B_TO_B)); 141766cc6b1SStefan Schake } 142766cc6b1SStefan Schake 143766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDOFFS, 144766cc6b1SStefan Schake VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO)); 145766cc6b1SStefan Schake } 146c8b75bcaSEric Anholt 147b501baccSEric Anholt static void 148cf1b372eSEric Anholt vc4_atomic_complete_commit(struct drm_atomic_state *state) 149b501baccSEric Anholt { 150b501baccSEric Anholt struct drm_device *dev = state->dev; 151b501baccSEric Anholt struct vc4_dev *vc4 = to_vc4_dev(dev); 152531a1b62SBoris Brezillon struct vc4_crtc *vc4_crtc; 153531a1b62SBoris Brezillon int i; 154531a1b62SBoris Brezillon 155531a1b62SBoris Brezillon for (i = 0; i < dev->mode_config.num_crtc; i++) { 156531a1b62SBoris Brezillon if (!state->crtcs[i].ptr || !state->crtcs[i].commit) 157531a1b62SBoris Brezillon continue; 158531a1b62SBoris Brezillon 159531a1b62SBoris Brezillon vc4_crtc = to_vc4_crtc(state->crtcs[i].ptr); 160531a1b62SBoris Brezillon vc4_hvs_mask_underrun(dev, vc4_crtc->channel); 161531a1b62SBoris Brezillon } 162b501baccSEric Anholt 16334c8ea40SBoris Brezillon drm_atomic_helper_wait_for_fences(dev, state, false); 16434c8ea40SBoris Brezillon 16534c8ea40SBoris Brezillon drm_atomic_helper_wait_for_dependencies(state); 16634c8ea40SBoris Brezillon 167b501baccSEric Anholt drm_atomic_helper_commit_modeset_disables(dev, state); 168b501baccSEric Anholt 169766cc6b1SStefan Schake vc4_ctm_commit(vc4, state); 170766cc6b1SStefan Schake 1712b58e98dSLiu Ying drm_atomic_helper_commit_planes(dev, state, 0); 172b501baccSEric Anholt 173b501baccSEric Anholt drm_atomic_helper_commit_modeset_enables(dev, state); 174b501baccSEric Anholt 1751ebe99a7SBoris Brezillon drm_atomic_helper_fake_vblank(state); 1761ebe99a7SBoris Brezillon 17734c8ea40SBoris Brezillon drm_atomic_helper_commit_hw_done(state); 17834c8ea40SBoris Brezillon 179184d3cf4SBoris Brezillon drm_atomic_helper_wait_for_flip_done(dev, state); 180b501baccSEric Anholt 181b501baccSEric Anholt drm_atomic_helper_cleanup_planes(dev, state); 182b501baccSEric Anholt 18334c8ea40SBoris Brezillon drm_atomic_helper_commit_cleanup_done(state); 18434c8ea40SBoris Brezillon 1850853695cSChris Wilson drm_atomic_state_put(state); 186b501baccSEric Anholt 187b501baccSEric Anholt up(&vc4->async_modeset); 188b501baccSEric Anholt } 189b501baccSEric Anholt 190cf1b372eSEric Anholt static void commit_work(struct work_struct *work) 191b501baccSEric Anholt { 192cf1b372eSEric Anholt struct drm_atomic_state *state = container_of(work, 193cf1b372eSEric Anholt struct drm_atomic_state, 194cf1b372eSEric Anholt commit_work); 195cf1b372eSEric Anholt vc4_atomic_complete_commit(state); 196b501baccSEric Anholt } 197b501baccSEric Anholt 198b501baccSEric Anholt /** 199b501baccSEric Anholt * vc4_atomic_commit - commit validated state object 200b501baccSEric Anholt * @dev: DRM device 201b501baccSEric Anholt * @state: the driver state object 202eb63961bSMaarten Lankhorst * @nonblock: nonblocking commit 203b501baccSEric Anholt * 204b501baccSEric Anholt * This function commits a with drm_atomic_helper_check() pre-validated state 205b501baccSEric Anholt * object. This can still fail when e.g. the framebuffer reservation fails. For 206b501baccSEric Anholt * now this doesn't implement asynchronous commits. 207b501baccSEric Anholt * 208b501baccSEric Anholt * RETURNS 209b501baccSEric Anholt * Zero for success or -errno. 210b501baccSEric Anholt */ 211b501baccSEric Anholt static int vc4_atomic_commit(struct drm_device *dev, 212b501baccSEric Anholt struct drm_atomic_state *state, 213eb63961bSMaarten Lankhorst bool nonblock) 214b501baccSEric Anholt { 215b501baccSEric Anholt struct vc4_dev *vc4 = to_vc4_dev(dev); 216b501baccSEric Anholt int ret; 217b501baccSEric Anholt 218539c320bSGustavo Padovan if (state->async_update) { 219539c320bSGustavo Padovan ret = down_interruptible(&vc4->async_modeset); 220539c320bSGustavo Padovan if (ret) 221539c320bSGustavo Padovan return ret; 222539c320bSGustavo Padovan 223539c320bSGustavo Padovan ret = drm_atomic_helper_prepare_planes(dev, state); 224539c320bSGustavo Padovan if (ret) { 225539c320bSGustavo Padovan up(&vc4->async_modeset); 226539c320bSGustavo Padovan return ret; 227539c320bSGustavo Padovan } 228539c320bSGustavo Padovan 229539c320bSGustavo Padovan drm_atomic_helper_async_commit(dev, state); 230539c320bSGustavo Padovan 231539c320bSGustavo Padovan drm_atomic_helper_cleanup_planes(dev, state); 232539c320bSGustavo Padovan 233539c320bSGustavo Padovan up(&vc4->async_modeset); 234539c320bSGustavo Padovan 235539c320bSGustavo Padovan return 0; 236539c320bSGustavo Padovan } 237539c320bSGustavo Padovan 238fcc86cb4SBoris Brezillon /* We know for sure we don't want an async update here. Set 239fcc86cb4SBoris Brezillon * state->legacy_cursor_update to false to prevent 240fcc86cb4SBoris Brezillon * drm_atomic_helper_setup_commit() from auto-completing 241fcc86cb4SBoris Brezillon * commit->flip_done. 242fcc86cb4SBoris Brezillon */ 243fcc86cb4SBoris Brezillon state->legacy_cursor_update = false; 24434c8ea40SBoris Brezillon ret = drm_atomic_helper_setup_commit(state, nonblock); 24534c8ea40SBoris Brezillon if (ret) 24634c8ea40SBoris Brezillon return ret; 24726fc78f6SDerek Foreman 248cf1b372eSEric Anholt INIT_WORK(&state->commit_work, commit_work); 249cf1b372eSEric Anholt 250b501baccSEric Anholt ret = down_interruptible(&vc4->async_modeset); 251cf1b372eSEric Anholt if (ret) 252b501baccSEric Anholt return ret; 253b501baccSEric Anholt 254b501baccSEric Anholt ret = drm_atomic_helper_prepare_planes(dev, state); 255b501baccSEric Anholt if (ret) { 256b501baccSEric Anholt up(&vc4->async_modeset); 257b501baccSEric Anholt return ret; 258b501baccSEric Anholt } 259b501baccSEric Anholt 26053ad0694SEric Anholt if (!nonblock) { 26153ad0694SEric Anholt ret = drm_atomic_helper_wait_for_fences(dev, state, true); 26253ad0694SEric Anholt if (ret) { 26353ad0694SEric Anholt drm_atomic_helper_cleanup_planes(dev, state); 26453ad0694SEric Anholt up(&vc4->async_modeset); 26553ad0694SEric Anholt return ret; 26653ad0694SEric Anholt } 26753ad0694SEric Anholt } 26853ad0694SEric Anholt 269b501baccSEric Anholt /* 270b501baccSEric Anholt * This is the point of no return - everything below never fails except 271b501baccSEric Anholt * when the hw goes bonghits. Which means we can commit the new state on 272b501baccSEric Anholt * the software side now. 273b501baccSEric Anholt */ 274b501baccSEric Anholt 275d68bc0e7SMaarten Lankhorst BUG_ON(drm_atomic_helper_swap_state(state, false) < 0); 276b501baccSEric Anholt 277b501baccSEric Anholt /* 278b501baccSEric Anholt * Everything below can be run asynchronously without the need to grab 279b501baccSEric Anholt * any modeset locks at all under one condition: It must be guaranteed 280b501baccSEric Anholt * that the asynchronous work has either been cancelled (if the driver 281b501baccSEric Anholt * supports it, which at least requires that the framebuffers get 282b501baccSEric Anholt * cleaned up with drm_atomic_helper_cleanup_planes()) or completed 283b501baccSEric Anholt * before the new state gets committed on the software side with 284b501baccSEric Anholt * drm_atomic_helper_swap_state(). 285b501baccSEric Anholt * 286b501baccSEric Anholt * This scheme allows new atomic state updates to be prepared and 287b501baccSEric Anholt * checked in parallel to the asynchronous completion of the previous 288b501baccSEric Anholt * update. Which is important since compositors need to figure out the 289b501baccSEric Anholt * composition of the next frame right after having submitted the 290b501baccSEric Anholt * current layout. 291b501baccSEric Anholt */ 292b501baccSEric Anholt 2930853695cSChris Wilson drm_atomic_state_get(state); 294cf1b372eSEric Anholt if (nonblock) 295cf1b372eSEric Anholt queue_work(system_unbound_wq, &state->commit_work); 296cf1b372eSEric Anholt else 297cf1b372eSEric Anholt vc4_atomic_complete_commit(state); 298b501baccSEric Anholt 299b501baccSEric Anholt return 0; 300b501baccSEric Anholt } 301b501baccSEric Anholt 30283753117SEric Anholt static struct drm_framebuffer *vc4_fb_create(struct drm_device *dev, 30383753117SEric Anholt struct drm_file *file_priv, 30483753117SEric Anholt const struct drm_mode_fb_cmd2 *mode_cmd) 30583753117SEric Anholt { 30683753117SEric Anholt struct drm_mode_fb_cmd2 mode_cmd_local; 30783753117SEric Anholt 30883753117SEric Anholt /* If the user didn't specify a modifier, use the 30983753117SEric Anholt * vc4_set_tiling_ioctl() state for the BO. 31083753117SEric Anholt */ 31183753117SEric Anholt if (!(mode_cmd->flags & DRM_MODE_FB_MODIFIERS)) { 31283753117SEric Anholt struct drm_gem_object *gem_obj; 31383753117SEric Anholt struct vc4_bo *bo; 31483753117SEric Anholt 31583753117SEric Anholt gem_obj = drm_gem_object_lookup(file_priv, 31683753117SEric Anholt mode_cmd->handles[0]); 31783753117SEric Anholt if (!gem_obj) { 318fb95992aSEric Anholt DRM_DEBUG("Failed to look up GEM BO %d\n", 31983753117SEric Anholt mode_cmd->handles[0]); 32083753117SEric Anholt return ERR_PTR(-ENOENT); 32183753117SEric Anholt } 32283753117SEric Anholt bo = to_vc4_bo(gem_obj); 32383753117SEric Anholt 32483753117SEric Anholt mode_cmd_local = *mode_cmd; 32583753117SEric Anholt 32683753117SEric Anholt if (bo->t_format) { 32783753117SEric Anholt mode_cmd_local.modifier[0] = 32883753117SEric Anholt DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED; 32983753117SEric Anholt } else { 33083753117SEric Anholt mode_cmd_local.modifier[0] = DRM_FORMAT_MOD_NONE; 33183753117SEric Anholt } 33283753117SEric Anholt 333f7a8cd30SEmil Velikov drm_gem_object_put(gem_obj); 33483753117SEric Anholt 33583753117SEric Anholt mode_cmd = &mode_cmd_local; 33683753117SEric Anholt } 33783753117SEric Anholt 3389762477cSNoralf Trønnes return drm_gem_fb_create(dev, file_priv, mode_cmd); 33983753117SEric Anholt } 34083753117SEric Anholt 341766cc6b1SStefan Schake /* Our CTM has some peculiar limitations: we can only enable it for one CRTC 342766cc6b1SStefan Schake * at a time and the HW only supports S0.9 scalars. To account for the latter, 343766cc6b1SStefan Schake * we don't allow userland to set a CTM that we have no hope of approximating. 344766cc6b1SStefan Schake */ 345766cc6b1SStefan Schake static int 346766cc6b1SStefan Schake vc4_ctm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) 347766cc6b1SStefan Schake { 348766cc6b1SStefan Schake struct vc4_dev *vc4 = to_vc4_dev(dev); 349766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state = NULL; 350766cc6b1SStefan Schake struct drm_crtc *crtc; 351766cc6b1SStefan Schake struct drm_crtc_state *old_crtc_state, *new_crtc_state; 352766cc6b1SStefan Schake struct drm_color_ctm *ctm; 353766cc6b1SStefan Schake int i; 354766cc6b1SStefan Schake 355766cc6b1SStefan Schake for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 356766cc6b1SStefan Schake /* CTM is being disabled. */ 357766cc6b1SStefan Schake if (!new_crtc_state->ctm && old_crtc_state->ctm) { 358766cc6b1SStefan Schake ctm_state = vc4_get_ctm_state(state, &vc4->ctm_manager); 359766cc6b1SStefan Schake if (IS_ERR(ctm_state)) 360766cc6b1SStefan Schake return PTR_ERR(ctm_state); 361766cc6b1SStefan Schake ctm_state->fifo = 0; 362766cc6b1SStefan Schake } 363766cc6b1SStefan Schake } 364766cc6b1SStefan Schake 365766cc6b1SStefan Schake for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 366766cc6b1SStefan Schake if (new_crtc_state->ctm == old_crtc_state->ctm) 367766cc6b1SStefan Schake continue; 368766cc6b1SStefan Schake 369766cc6b1SStefan Schake if (!ctm_state) { 370766cc6b1SStefan Schake ctm_state = vc4_get_ctm_state(state, &vc4->ctm_manager); 371766cc6b1SStefan Schake if (IS_ERR(ctm_state)) 372766cc6b1SStefan Schake return PTR_ERR(ctm_state); 373766cc6b1SStefan Schake } 374766cc6b1SStefan Schake 375766cc6b1SStefan Schake /* CTM is being enabled or the matrix changed. */ 376766cc6b1SStefan Schake if (new_crtc_state->ctm) { 377766cc6b1SStefan Schake /* fifo is 1-based since 0 disables CTM. */ 378766cc6b1SStefan Schake int fifo = to_vc4_crtc(crtc)->channel + 1; 379766cc6b1SStefan Schake 380766cc6b1SStefan Schake /* Check userland isn't trying to turn on CTM for more 381766cc6b1SStefan Schake * than one CRTC at a time. 382766cc6b1SStefan Schake */ 383766cc6b1SStefan Schake if (ctm_state->fifo && ctm_state->fifo != fifo) { 384766cc6b1SStefan Schake DRM_DEBUG_DRIVER("Too many CTM configured\n"); 385766cc6b1SStefan Schake return -EINVAL; 386766cc6b1SStefan Schake } 387766cc6b1SStefan Schake 388766cc6b1SStefan Schake /* Check we can approximate the specified CTM. 389766cc6b1SStefan Schake * We disallow scalars |c| > 1.0 since the HW has 390766cc6b1SStefan Schake * no integer bits. 391766cc6b1SStefan Schake */ 392766cc6b1SStefan Schake ctm = new_crtc_state->ctm->data; 393766cc6b1SStefan Schake for (i = 0; i < ARRAY_SIZE(ctm->matrix); i++) { 394766cc6b1SStefan Schake u64 val = ctm->matrix[i]; 395766cc6b1SStefan Schake 396766cc6b1SStefan Schake val &= ~BIT_ULL(63); 397766cc6b1SStefan Schake if (val > BIT_ULL(32)) 398766cc6b1SStefan Schake return -EINVAL; 399766cc6b1SStefan Schake } 400766cc6b1SStefan Schake 401766cc6b1SStefan Schake ctm_state->fifo = fifo; 402766cc6b1SStefan Schake ctm_state->ctm = ctm; 403766cc6b1SStefan Schake } 404766cc6b1SStefan Schake } 405766cc6b1SStefan Schake 406766cc6b1SStefan Schake return 0; 407766cc6b1SStefan Schake } 408766cc6b1SStefan Schake 4094686da83SBoris Brezillon static int vc4_load_tracker_atomic_check(struct drm_atomic_state *state) 4104686da83SBoris Brezillon { 4114686da83SBoris Brezillon struct drm_plane_state *old_plane_state, *new_plane_state; 4124686da83SBoris Brezillon struct vc4_dev *vc4 = to_vc4_dev(state->dev); 4134686da83SBoris Brezillon struct vc4_load_tracker_state *load_state; 4144686da83SBoris Brezillon struct drm_private_state *priv_state; 4154686da83SBoris Brezillon struct drm_plane *plane; 4164686da83SBoris Brezillon int i; 4174686da83SBoris Brezillon 4184686da83SBoris Brezillon priv_state = drm_atomic_get_private_obj_state(state, 4194686da83SBoris Brezillon &vc4->load_tracker); 4204686da83SBoris Brezillon if (IS_ERR(priv_state)) 4214686da83SBoris Brezillon return PTR_ERR(priv_state); 4224686da83SBoris Brezillon 4234686da83SBoris Brezillon load_state = to_vc4_load_tracker_state(priv_state); 4244686da83SBoris Brezillon for_each_oldnew_plane_in_state(state, plane, old_plane_state, 4254686da83SBoris Brezillon new_plane_state, i) { 4264686da83SBoris Brezillon struct vc4_plane_state *vc4_plane_state; 4274686da83SBoris Brezillon 4284686da83SBoris Brezillon if (old_plane_state->fb && old_plane_state->crtc) { 4294686da83SBoris Brezillon vc4_plane_state = to_vc4_plane_state(old_plane_state); 4304686da83SBoris Brezillon load_state->membus_load -= vc4_plane_state->membus_load; 4314686da83SBoris Brezillon load_state->hvs_load -= vc4_plane_state->hvs_load; 4324686da83SBoris Brezillon } 4334686da83SBoris Brezillon 4344686da83SBoris Brezillon if (new_plane_state->fb && new_plane_state->crtc) { 4354686da83SBoris Brezillon vc4_plane_state = to_vc4_plane_state(new_plane_state); 4364686da83SBoris Brezillon load_state->membus_load += vc4_plane_state->membus_load; 4374686da83SBoris Brezillon load_state->hvs_load += vc4_plane_state->hvs_load; 4384686da83SBoris Brezillon } 4394686da83SBoris Brezillon } 4404686da83SBoris Brezillon 4416b5c029dSPaul Kocialkowski /* Don't check the load when the tracker is disabled. */ 4426b5c029dSPaul Kocialkowski if (!vc4->load_tracker_enabled) 4436b5c029dSPaul Kocialkowski return 0; 4446b5c029dSPaul Kocialkowski 4454686da83SBoris Brezillon /* The absolute limit is 2Gbyte/sec, but let's take a margin to let 4464686da83SBoris Brezillon * the system work when other blocks are accessing the memory. 4474686da83SBoris Brezillon */ 4484686da83SBoris Brezillon if (load_state->membus_load > SZ_1G + SZ_512M) 4494686da83SBoris Brezillon return -ENOSPC; 4504686da83SBoris Brezillon 4514686da83SBoris Brezillon /* HVS clock is supposed to run @ 250Mhz, let's take a margin and 4524686da83SBoris Brezillon * consider the maximum number of cycles is 240M. 4534686da83SBoris Brezillon */ 4544686da83SBoris Brezillon if (load_state->hvs_load > 240000000ULL) 4554686da83SBoris Brezillon return -ENOSPC; 4564686da83SBoris Brezillon 4574686da83SBoris Brezillon return 0; 4584686da83SBoris Brezillon } 4594686da83SBoris Brezillon 4604686da83SBoris Brezillon static struct drm_private_state * 4614686da83SBoris Brezillon vc4_load_tracker_duplicate_state(struct drm_private_obj *obj) 4624686da83SBoris Brezillon { 4634686da83SBoris Brezillon struct vc4_load_tracker_state *state; 4644686da83SBoris Brezillon 4654686da83SBoris Brezillon state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); 4664686da83SBoris Brezillon if (!state) 4674686da83SBoris Brezillon return NULL; 4684686da83SBoris Brezillon 4694686da83SBoris Brezillon __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 4704686da83SBoris Brezillon 4714686da83SBoris Brezillon return &state->base; 4724686da83SBoris Brezillon } 4734686da83SBoris Brezillon 4744686da83SBoris Brezillon static void vc4_load_tracker_destroy_state(struct drm_private_obj *obj, 4754686da83SBoris Brezillon struct drm_private_state *state) 4764686da83SBoris Brezillon { 4774686da83SBoris Brezillon struct vc4_load_tracker_state *load_state; 4784686da83SBoris Brezillon 4794686da83SBoris Brezillon load_state = to_vc4_load_tracker_state(state); 4804686da83SBoris Brezillon kfree(load_state); 4814686da83SBoris Brezillon } 4824686da83SBoris Brezillon 4834686da83SBoris Brezillon static const struct drm_private_state_funcs vc4_load_tracker_state_funcs = { 4844686da83SBoris Brezillon .atomic_duplicate_state = vc4_load_tracker_duplicate_state, 4854686da83SBoris Brezillon .atomic_destroy_state = vc4_load_tracker_destroy_state, 4864686da83SBoris Brezillon }; 4874686da83SBoris Brezillon 488766cc6b1SStefan Schake static int 489766cc6b1SStefan Schake vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) 490766cc6b1SStefan Schake { 491766cc6b1SStefan Schake int ret; 492766cc6b1SStefan Schake 493766cc6b1SStefan Schake ret = vc4_ctm_atomic_check(dev, state); 494766cc6b1SStefan Schake if (ret < 0) 495766cc6b1SStefan Schake return ret; 496766cc6b1SStefan Schake 4974686da83SBoris Brezillon ret = drm_atomic_helper_check(dev, state); 4984686da83SBoris Brezillon if (ret) 4994686da83SBoris Brezillon return ret; 5004686da83SBoris Brezillon 5014686da83SBoris Brezillon return vc4_load_tracker_atomic_check(state); 502766cc6b1SStefan Schake } 503766cc6b1SStefan Schake 504c8b75bcaSEric Anholt static const struct drm_mode_config_funcs vc4_mode_funcs = { 505766cc6b1SStefan Schake .atomic_check = vc4_atomic_check, 506b501baccSEric Anholt .atomic_commit = vc4_atomic_commit, 50783753117SEric Anholt .fb_create = vc4_fb_create, 508c8b75bcaSEric Anholt }; 509c8b75bcaSEric Anholt 510c8b75bcaSEric Anholt int vc4_kms_load(struct drm_device *dev) 511c8b75bcaSEric Anholt { 51248666d56SDerek Foreman struct vc4_dev *vc4 = to_vc4_dev(dev); 513766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state; 5144686da83SBoris Brezillon struct vc4_load_tracker_state *load_state; 515c8b75bcaSEric Anholt int ret; 516c8b75bcaSEric Anholt 5176b5c029dSPaul Kocialkowski /* Start with the load tracker enabled. Can be disabled through the 5186b5c029dSPaul Kocialkowski * debugfs load_tracker file. 5196b5c029dSPaul Kocialkowski */ 5206b5c029dSPaul Kocialkowski vc4->load_tracker_enabled = true; 5216b5c029dSPaul Kocialkowski 522b501baccSEric Anholt sema_init(&vc4->async_modeset, 1); 523b501baccSEric Anholt 5247d2818f5SMario Kleiner /* Set support for vblank irq fast disable, before drm_vblank_init() */ 5257d2818f5SMario Kleiner dev->vblank_disable_immediate = true; 5267d2818f5SMario Kleiner 527ffc26740SEric Anholt dev->irq_enabled = true; 528c8b75bcaSEric Anholt ret = drm_vblank_init(dev, dev->mode_config.num_crtc); 529c8b75bcaSEric Anholt if (ret < 0) { 530c8b75bcaSEric Anholt dev_err(dev->dev, "failed to initialize vblank\n"); 531c8b75bcaSEric Anholt return ret; 532c8b75bcaSEric Anholt } 533c8b75bcaSEric Anholt 534c8b75bcaSEric Anholt dev->mode_config.max_width = 2048; 535c8b75bcaSEric Anholt dev->mode_config.max_height = 2048; 536c8b75bcaSEric Anholt dev->mode_config.funcs = &vc4_mode_funcs; 537c8b75bcaSEric Anholt dev->mode_config.preferred_depth = 24; 538b501baccSEric Anholt dev->mode_config.async_page_flip = true; 539423ad7b3SDaniel Stone dev->mode_config.allow_fb_modifiers = true; 540b501baccSEric Anholt 541766cc6b1SStefan Schake drm_modeset_lock_init(&vc4->ctm_state_lock); 542766cc6b1SStefan Schake 543766cc6b1SStefan Schake ctm_state = kzalloc(sizeof(*ctm_state), GFP_KERNEL); 544766cc6b1SStefan Schake if (!ctm_state) 545766cc6b1SStefan Schake return -ENOMEM; 546b962a120SRob Clark 547b962a120SRob Clark drm_atomic_private_obj_init(dev, &vc4->ctm_manager, &ctm_state->base, 548766cc6b1SStefan Schake &vc4_ctm_state_funcs); 549766cc6b1SStefan Schake 5504686da83SBoris Brezillon load_state = kzalloc(sizeof(*load_state), GFP_KERNEL); 5514686da83SBoris Brezillon if (!load_state) { 5524686da83SBoris Brezillon drm_atomic_private_obj_fini(&vc4->ctm_manager); 5534686da83SBoris Brezillon return -ENOMEM; 5544686da83SBoris Brezillon } 5554686da83SBoris Brezillon 5564686da83SBoris Brezillon drm_atomic_private_obj_init(dev, &vc4->load_tracker, &load_state->base, 5574686da83SBoris Brezillon &vc4_load_tracker_state_funcs); 5584686da83SBoris Brezillon 559c8b75bcaSEric Anholt drm_mode_config_reset(dev); 560c8b75bcaSEric Anholt 561c8b75bcaSEric Anholt drm_kms_helper_poll_init(dev); 562c8b75bcaSEric Anholt 563c8b75bcaSEric Anholt return 0; 564c8b75bcaSEric Anholt } 565