1c8b75bcaSEric Anholt /* 2c8b75bcaSEric Anholt * Copyright (C) 2015 Broadcom 3c8b75bcaSEric Anholt * 4c8b75bcaSEric Anholt * This program is free software; you can redistribute it and/or modify 5c8b75bcaSEric Anholt * it under the terms of the GNU General Public License version 2 as 6c8b75bcaSEric Anholt * published by the Free Software Foundation. 7c8b75bcaSEric Anholt */ 8c8b75bcaSEric Anholt 9c8b75bcaSEric Anholt /** 10c8b75bcaSEric Anholt * DOC: VC4 KMS 11c8b75bcaSEric Anholt * 12c8b75bcaSEric Anholt * This is the general code for implementing KMS mode setting that 13c8b75bcaSEric Anholt * doesn't clearly associate with any of the other objects (plane, 14c8b75bcaSEric Anholt * crtc, HDMI encoder). 15c8b75bcaSEric Anholt */ 16c8b75bcaSEric Anholt 17b7e8e25bSMasahiro Yamada #include <drm/drm_crtc.h> 18b7e8e25bSMasahiro Yamada #include <drm/drm_atomic.h> 19b7e8e25bSMasahiro Yamada #include <drm/drm_atomic_helper.h> 20b7e8e25bSMasahiro Yamada #include <drm/drm_crtc_helper.h> 21b7e8e25bSMasahiro Yamada #include <drm/drm_plane_helper.h> 22b8f429a7SNoralf Trønnes #include <drm/drm_fb_helper.h> 23b7e8e25bSMasahiro Yamada #include <drm/drm_fb_cma_helper.h> 249762477cSNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 25c8b75bcaSEric Anholt #include "vc4_drv.h" 26766cc6b1SStefan Schake #include "vc4_regs.h" 27766cc6b1SStefan Schake 28766cc6b1SStefan Schake struct vc4_ctm_state { 29766cc6b1SStefan Schake struct drm_private_state base; 30766cc6b1SStefan Schake struct drm_color_ctm *ctm; 31766cc6b1SStefan Schake int fifo; 32766cc6b1SStefan Schake }; 33766cc6b1SStefan Schake 34766cc6b1SStefan Schake static struct vc4_ctm_state *to_vc4_ctm_state(struct drm_private_state *priv) 35766cc6b1SStefan Schake { 36766cc6b1SStefan Schake return container_of(priv, struct vc4_ctm_state, base); 37766cc6b1SStefan Schake } 38766cc6b1SStefan Schake 39766cc6b1SStefan Schake static struct vc4_ctm_state *vc4_get_ctm_state(struct drm_atomic_state *state, 40766cc6b1SStefan Schake struct drm_private_obj *manager) 41766cc6b1SStefan Schake { 42766cc6b1SStefan Schake struct drm_device *dev = state->dev; 43766cc6b1SStefan Schake struct vc4_dev *vc4 = dev->dev_private; 44766cc6b1SStefan Schake struct drm_private_state *priv_state; 45766cc6b1SStefan Schake int ret; 46766cc6b1SStefan Schake 47766cc6b1SStefan Schake ret = drm_modeset_lock(&vc4->ctm_state_lock, state->acquire_ctx); 48766cc6b1SStefan Schake if (ret) 49766cc6b1SStefan Schake return ERR_PTR(ret); 50766cc6b1SStefan Schake 51766cc6b1SStefan Schake priv_state = drm_atomic_get_private_obj_state(state, manager); 52766cc6b1SStefan Schake if (IS_ERR(priv_state)) 53766cc6b1SStefan Schake return ERR_CAST(priv_state); 54766cc6b1SStefan Schake 55766cc6b1SStefan Schake return to_vc4_ctm_state(priv_state); 56766cc6b1SStefan Schake } 57766cc6b1SStefan Schake 58766cc6b1SStefan Schake static struct drm_private_state * 59766cc6b1SStefan Schake vc4_ctm_duplicate_state(struct drm_private_obj *obj) 60766cc6b1SStefan Schake { 61766cc6b1SStefan Schake struct vc4_ctm_state *state; 62766cc6b1SStefan Schake 63766cc6b1SStefan Schake state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); 64766cc6b1SStefan Schake if (!state) 65766cc6b1SStefan Schake return NULL; 66766cc6b1SStefan Schake 67766cc6b1SStefan Schake __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 68766cc6b1SStefan Schake 69766cc6b1SStefan Schake return &state->base; 70766cc6b1SStefan Schake } 71766cc6b1SStefan Schake 72766cc6b1SStefan Schake static void vc4_ctm_destroy_state(struct drm_private_obj *obj, 73766cc6b1SStefan Schake struct drm_private_state *state) 74766cc6b1SStefan Schake { 75766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state = to_vc4_ctm_state(state); 76766cc6b1SStefan Schake 77766cc6b1SStefan Schake kfree(ctm_state); 78766cc6b1SStefan Schake } 79766cc6b1SStefan Schake 80766cc6b1SStefan Schake static const struct drm_private_state_funcs vc4_ctm_state_funcs = { 81766cc6b1SStefan Schake .atomic_duplicate_state = vc4_ctm_duplicate_state, 82766cc6b1SStefan Schake .atomic_destroy_state = vc4_ctm_destroy_state, 83766cc6b1SStefan Schake }; 84766cc6b1SStefan Schake 85766cc6b1SStefan Schake /* Converts a DRM S31.32 value to the HW S0.9 format. */ 86766cc6b1SStefan Schake static u16 vc4_ctm_s31_32_to_s0_9(u64 in) 87766cc6b1SStefan Schake { 88766cc6b1SStefan Schake u16 r; 89766cc6b1SStefan Schake 90766cc6b1SStefan Schake /* Sign bit. */ 91766cc6b1SStefan Schake r = in & BIT_ULL(63) ? BIT(9) : 0; 92766cc6b1SStefan Schake 93766cc6b1SStefan Schake if ((in & GENMASK_ULL(62, 32)) > 0) { 94766cc6b1SStefan Schake /* We have zero integer bits so we can only saturate here. */ 95766cc6b1SStefan Schake r |= GENMASK(8, 0); 96766cc6b1SStefan Schake } else { 97766cc6b1SStefan Schake /* Otherwise take the 9 most important fractional bits. */ 98766cc6b1SStefan Schake r |= (in >> 23) & GENMASK(8, 0); 99766cc6b1SStefan Schake } 100766cc6b1SStefan Schake 101766cc6b1SStefan Schake return r; 102766cc6b1SStefan Schake } 103766cc6b1SStefan Schake 104766cc6b1SStefan Schake static void 105766cc6b1SStefan Schake vc4_ctm_commit(struct vc4_dev *vc4, struct drm_atomic_state *state) 106766cc6b1SStefan Schake { 107766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state = to_vc4_ctm_state(vc4->ctm_manager.state); 108766cc6b1SStefan Schake struct drm_color_ctm *ctm = ctm_state->ctm; 109766cc6b1SStefan Schake 110766cc6b1SStefan Schake if (ctm_state->fifo) { 111766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDCOEF2, 112766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[0]), 113766cc6b1SStefan Schake SCALER_OLEDCOEF2_R_TO_R) | 114766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[3]), 115766cc6b1SStefan Schake SCALER_OLEDCOEF2_R_TO_G) | 116766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[6]), 117766cc6b1SStefan Schake SCALER_OLEDCOEF2_R_TO_B)); 118766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDCOEF1, 119766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[1]), 120766cc6b1SStefan Schake SCALER_OLEDCOEF1_G_TO_R) | 121766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[4]), 122766cc6b1SStefan Schake SCALER_OLEDCOEF1_G_TO_G) | 123766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[7]), 124766cc6b1SStefan Schake SCALER_OLEDCOEF1_G_TO_B)); 125766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDCOEF0, 126766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[2]), 127766cc6b1SStefan Schake SCALER_OLEDCOEF0_B_TO_R) | 128766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[5]), 129766cc6b1SStefan Schake SCALER_OLEDCOEF0_B_TO_G) | 130766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[8]), 131766cc6b1SStefan Schake SCALER_OLEDCOEF0_B_TO_B)); 132766cc6b1SStefan Schake } 133766cc6b1SStefan Schake 134766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDOFFS, 135766cc6b1SStefan Schake VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO)); 136766cc6b1SStefan Schake } 137c8b75bcaSEric Anholt 138b501baccSEric Anholt static void 139cf1b372eSEric Anholt vc4_atomic_complete_commit(struct drm_atomic_state *state) 140b501baccSEric Anholt { 141b501baccSEric Anholt struct drm_device *dev = state->dev; 142b501baccSEric Anholt struct vc4_dev *vc4 = to_vc4_dev(dev); 143b501baccSEric Anholt 14434c8ea40SBoris Brezillon drm_atomic_helper_wait_for_fences(dev, state, false); 14534c8ea40SBoris Brezillon 14634c8ea40SBoris Brezillon drm_atomic_helper_wait_for_dependencies(state); 14734c8ea40SBoris Brezillon 148b501baccSEric Anholt drm_atomic_helper_commit_modeset_disables(dev, state); 149b501baccSEric Anholt 150766cc6b1SStefan Schake vc4_ctm_commit(vc4, state); 151766cc6b1SStefan Schake 1522b58e98dSLiu Ying drm_atomic_helper_commit_planes(dev, state, 0); 153b501baccSEric Anholt 154b501baccSEric Anholt drm_atomic_helper_commit_modeset_enables(dev, state); 155b501baccSEric Anholt 1566674a904SEric Anholt /* Make sure that drm_atomic_helper_wait_for_vblanks() 1576674a904SEric Anholt * actually waits for vblank. If we're doing a full atomic 1586674a904SEric Anholt * modeset (as opposed to a vc4_update_plane() short circuit), 1596674a904SEric Anholt * then we need to wait for scanout to be done with our 1606674a904SEric Anholt * display lists before we free it and potentially reallocate 1616674a904SEric Anholt * and overwrite the dlist memory with a new modeset. 1626674a904SEric Anholt */ 1636674a904SEric Anholt state->legacy_cursor_update = false; 1646674a904SEric Anholt 16534c8ea40SBoris Brezillon drm_atomic_helper_commit_hw_done(state); 16634c8ea40SBoris Brezillon 167b501baccSEric Anholt drm_atomic_helper_wait_for_vblanks(dev, state); 168b501baccSEric Anholt 169b501baccSEric Anholt drm_atomic_helper_cleanup_planes(dev, state); 170b501baccSEric Anholt 17134c8ea40SBoris Brezillon drm_atomic_helper_commit_cleanup_done(state); 17234c8ea40SBoris Brezillon 1730853695cSChris Wilson drm_atomic_state_put(state); 174b501baccSEric Anholt 175b501baccSEric Anholt up(&vc4->async_modeset); 176b501baccSEric Anholt } 177b501baccSEric Anholt 178cf1b372eSEric Anholt static void commit_work(struct work_struct *work) 179b501baccSEric Anholt { 180cf1b372eSEric Anholt struct drm_atomic_state *state = container_of(work, 181cf1b372eSEric Anholt struct drm_atomic_state, 182cf1b372eSEric Anholt commit_work); 183cf1b372eSEric Anholt vc4_atomic_complete_commit(state); 184b501baccSEric Anholt } 185b501baccSEric Anholt 186b501baccSEric Anholt /** 187b501baccSEric Anholt * vc4_atomic_commit - commit validated state object 188b501baccSEric Anholt * @dev: DRM device 189b501baccSEric Anholt * @state: the driver state object 190eb63961bSMaarten Lankhorst * @nonblock: nonblocking commit 191b501baccSEric Anholt * 192b501baccSEric Anholt * This function commits a with drm_atomic_helper_check() pre-validated state 193b501baccSEric Anholt * object. This can still fail when e.g. the framebuffer reservation fails. For 194b501baccSEric Anholt * now this doesn't implement asynchronous commits. 195b501baccSEric Anholt * 196b501baccSEric Anholt * RETURNS 197b501baccSEric Anholt * Zero for success or -errno. 198b501baccSEric Anholt */ 199b501baccSEric Anholt static int vc4_atomic_commit(struct drm_device *dev, 200b501baccSEric Anholt struct drm_atomic_state *state, 201eb63961bSMaarten Lankhorst bool nonblock) 202b501baccSEric Anholt { 203b501baccSEric Anholt struct vc4_dev *vc4 = to_vc4_dev(dev); 204b501baccSEric Anholt int ret; 205b501baccSEric Anholt 206539c320bSGustavo Padovan if (state->async_update) { 207539c320bSGustavo Padovan ret = down_interruptible(&vc4->async_modeset); 208539c320bSGustavo Padovan if (ret) 209539c320bSGustavo Padovan return ret; 210539c320bSGustavo Padovan 211539c320bSGustavo Padovan ret = drm_atomic_helper_prepare_planes(dev, state); 212539c320bSGustavo Padovan if (ret) { 213539c320bSGustavo Padovan up(&vc4->async_modeset); 214539c320bSGustavo Padovan return ret; 215539c320bSGustavo Padovan } 216539c320bSGustavo Padovan 217539c320bSGustavo Padovan drm_atomic_helper_async_commit(dev, state); 218539c320bSGustavo Padovan 219539c320bSGustavo Padovan drm_atomic_helper_cleanup_planes(dev, state); 220539c320bSGustavo Padovan 221539c320bSGustavo Padovan up(&vc4->async_modeset); 222539c320bSGustavo Padovan 223539c320bSGustavo Padovan return 0; 224539c320bSGustavo Padovan } 225539c320bSGustavo Padovan 22634c8ea40SBoris Brezillon ret = drm_atomic_helper_setup_commit(state, nonblock); 22734c8ea40SBoris Brezillon if (ret) 22834c8ea40SBoris Brezillon return ret; 22926fc78f6SDerek Foreman 230cf1b372eSEric Anholt INIT_WORK(&state->commit_work, commit_work); 231cf1b372eSEric Anholt 232b501baccSEric Anholt ret = down_interruptible(&vc4->async_modeset); 233cf1b372eSEric Anholt if (ret) 234b501baccSEric Anholt return ret; 235b501baccSEric Anholt 236b501baccSEric Anholt ret = drm_atomic_helper_prepare_planes(dev, state); 237b501baccSEric Anholt if (ret) { 238b501baccSEric Anholt up(&vc4->async_modeset); 239b501baccSEric Anholt return ret; 240b501baccSEric Anholt } 241b501baccSEric Anholt 24253ad0694SEric Anholt if (!nonblock) { 24353ad0694SEric Anholt ret = drm_atomic_helper_wait_for_fences(dev, state, true); 24453ad0694SEric Anholt if (ret) { 24553ad0694SEric Anholt drm_atomic_helper_cleanup_planes(dev, state); 24653ad0694SEric Anholt up(&vc4->async_modeset); 24753ad0694SEric Anholt return ret; 24853ad0694SEric Anholt } 24953ad0694SEric Anholt } 25053ad0694SEric Anholt 251b501baccSEric Anholt /* 252b501baccSEric Anholt * This is the point of no return - everything below never fails except 253b501baccSEric Anholt * when the hw goes bonghits. Which means we can commit the new state on 254b501baccSEric Anholt * the software side now. 255b501baccSEric Anholt */ 256b501baccSEric Anholt 257d68bc0e7SMaarten Lankhorst BUG_ON(drm_atomic_helper_swap_state(state, false) < 0); 258b501baccSEric Anholt 259b501baccSEric Anholt /* 260b501baccSEric Anholt * Everything below can be run asynchronously without the need to grab 261b501baccSEric Anholt * any modeset locks at all under one condition: It must be guaranteed 262b501baccSEric Anholt * that the asynchronous work has either been cancelled (if the driver 263b501baccSEric Anholt * supports it, which at least requires that the framebuffers get 264b501baccSEric Anholt * cleaned up with drm_atomic_helper_cleanup_planes()) or completed 265b501baccSEric Anholt * before the new state gets committed on the software side with 266b501baccSEric Anholt * drm_atomic_helper_swap_state(). 267b501baccSEric Anholt * 268b501baccSEric Anholt * This scheme allows new atomic state updates to be prepared and 269b501baccSEric Anholt * checked in parallel to the asynchronous completion of the previous 270b501baccSEric Anholt * update. Which is important since compositors need to figure out the 271b501baccSEric Anholt * composition of the next frame right after having submitted the 272b501baccSEric Anholt * current layout. 273b501baccSEric Anholt */ 274b501baccSEric Anholt 2750853695cSChris Wilson drm_atomic_state_get(state); 276cf1b372eSEric Anholt if (nonblock) 277cf1b372eSEric Anholt queue_work(system_unbound_wq, &state->commit_work); 278cf1b372eSEric Anholt else 279cf1b372eSEric Anholt vc4_atomic_complete_commit(state); 280b501baccSEric Anholt 281b501baccSEric Anholt return 0; 282b501baccSEric Anholt } 283b501baccSEric Anholt 28483753117SEric Anholt static struct drm_framebuffer *vc4_fb_create(struct drm_device *dev, 28583753117SEric Anholt struct drm_file *file_priv, 28683753117SEric Anholt const struct drm_mode_fb_cmd2 *mode_cmd) 28783753117SEric Anholt { 28883753117SEric Anholt struct drm_mode_fb_cmd2 mode_cmd_local; 28983753117SEric Anholt 29083753117SEric Anholt /* If the user didn't specify a modifier, use the 29183753117SEric Anholt * vc4_set_tiling_ioctl() state for the BO. 29283753117SEric Anholt */ 29383753117SEric Anholt if (!(mode_cmd->flags & DRM_MODE_FB_MODIFIERS)) { 29483753117SEric Anholt struct drm_gem_object *gem_obj; 29583753117SEric Anholt struct vc4_bo *bo; 29683753117SEric Anholt 29783753117SEric Anholt gem_obj = drm_gem_object_lookup(file_priv, 29883753117SEric Anholt mode_cmd->handles[0]); 29983753117SEric Anholt if (!gem_obj) { 300fb95992aSEric Anholt DRM_DEBUG("Failed to look up GEM BO %d\n", 30183753117SEric Anholt mode_cmd->handles[0]); 30283753117SEric Anholt return ERR_PTR(-ENOENT); 30383753117SEric Anholt } 30483753117SEric Anholt bo = to_vc4_bo(gem_obj); 30583753117SEric Anholt 30683753117SEric Anholt mode_cmd_local = *mode_cmd; 30783753117SEric Anholt 30883753117SEric Anholt if (bo->t_format) { 30983753117SEric Anholt mode_cmd_local.modifier[0] = 31083753117SEric Anholt DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED; 31183753117SEric Anholt } else { 31283753117SEric Anholt mode_cmd_local.modifier[0] = DRM_FORMAT_MOD_NONE; 31383753117SEric Anholt } 31483753117SEric Anholt 3151d5494e9SCihangir Akturk drm_gem_object_put_unlocked(gem_obj); 31683753117SEric Anholt 31783753117SEric Anholt mode_cmd = &mode_cmd_local; 31883753117SEric Anholt } 31983753117SEric Anholt 3209762477cSNoralf Trønnes return drm_gem_fb_create(dev, file_priv, mode_cmd); 32183753117SEric Anholt } 32283753117SEric Anholt 323766cc6b1SStefan Schake /* Our CTM has some peculiar limitations: we can only enable it for one CRTC 324766cc6b1SStefan Schake * at a time and the HW only supports S0.9 scalars. To account for the latter, 325766cc6b1SStefan Schake * we don't allow userland to set a CTM that we have no hope of approximating. 326766cc6b1SStefan Schake */ 327766cc6b1SStefan Schake static int 328766cc6b1SStefan Schake vc4_ctm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) 329766cc6b1SStefan Schake { 330766cc6b1SStefan Schake struct vc4_dev *vc4 = to_vc4_dev(dev); 331766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state = NULL; 332766cc6b1SStefan Schake struct drm_crtc *crtc; 333766cc6b1SStefan Schake struct drm_crtc_state *old_crtc_state, *new_crtc_state; 334766cc6b1SStefan Schake struct drm_color_ctm *ctm; 335766cc6b1SStefan Schake int i; 336766cc6b1SStefan Schake 337766cc6b1SStefan Schake for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 338766cc6b1SStefan Schake /* CTM is being disabled. */ 339766cc6b1SStefan Schake if (!new_crtc_state->ctm && old_crtc_state->ctm) { 340766cc6b1SStefan Schake ctm_state = vc4_get_ctm_state(state, &vc4->ctm_manager); 341766cc6b1SStefan Schake if (IS_ERR(ctm_state)) 342766cc6b1SStefan Schake return PTR_ERR(ctm_state); 343766cc6b1SStefan Schake ctm_state->fifo = 0; 344766cc6b1SStefan Schake } 345766cc6b1SStefan Schake } 346766cc6b1SStefan Schake 347766cc6b1SStefan Schake for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 348766cc6b1SStefan Schake if (new_crtc_state->ctm == old_crtc_state->ctm) 349766cc6b1SStefan Schake continue; 350766cc6b1SStefan Schake 351766cc6b1SStefan Schake if (!ctm_state) { 352766cc6b1SStefan Schake ctm_state = vc4_get_ctm_state(state, &vc4->ctm_manager); 353766cc6b1SStefan Schake if (IS_ERR(ctm_state)) 354766cc6b1SStefan Schake return PTR_ERR(ctm_state); 355766cc6b1SStefan Schake } 356766cc6b1SStefan Schake 357766cc6b1SStefan Schake /* CTM is being enabled or the matrix changed. */ 358766cc6b1SStefan Schake if (new_crtc_state->ctm) { 359766cc6b1SStefan Schake /* fifo is 1-based since 0 disables CTM. */ 360766cc6b1SStefan Schake int fifo = to_vc4_crtc(crtc)->channel + 1; 361766cc6b1SStefan Schake 362766cc6b1SStefan Schake /* Check userland isn't trying to turn on CTM for more 363766cc6b1SStefan Schake * than one CRTC at a time. 364766cc6b1SStefan Schake */ 365766cc6b1SStefan Schake if (ctm_state->fifo && ctm_state->fifo != fifo) { 366766cc6b1SStefan Schake DRM_DEBUG_DRIVER("Too many CTM configured\n"); 367766cc6b1SStefan Schake return -EINVAL; 368766cc6b1SStefan Schake } 369766cc6b1SStefan Schake 370766cc6b1SStefan Schake /* Check we can approximate the specified CTM. 371766cc6b1SStefan Schake * We disallow scalars |c| > 1.0 since the HW has 372766cc6b1SStefan Schake * no integer bits. 373766cc6b1SStefan Schake */ 374766cc6b1SStefan Schake ctm = new_crtc_state->ctm->data; 375766cc6b1SStefan Schake for (i = 0; i < ARRAY_SIZE(ctm->matrix); i++) { 376766cc6b1SStefan Schake u64 val = ctm->matrix[i]; 377766cc6b1SStefan Schake 378766cc6b1SStefan Schake val &= ~BIT_ULL(63); 379766cc6b1SStefan Schake if (val > BIT_ULL(32)) 380766cc6b1SStefan Schake return -EINVAL; 381766cc6b1SStefan Schake } 382766cc6b1SStefan Schake 383766cc6b1SStefan Schake ctm_state->fifo = fifo; 384766cc6b1SStefan Schake ctm_state->ctm = ctm; 385766cc6b1SStefan Schake } 386766cc6b1SStefan Schake } 387766cc6b1SStefan Schake 388766cc6b1SStefan Schake return 0; 389766cc6b1SStefan Schake } 390766cc6b1SStefan Schake 391766cc6b1SStefan Schake static int 392766cc6b1SStefan Schake vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) 393766cc6b1SStefan Schake { 394766cc6b1SStefan Schake int ret; 395766cc6b1SStefan Schake 396766cc6b1SStefan Schake ret = vc4_ctm_atomic_check(dev, state); 397766cc6b1SStefan Schake if (ret < 0) 398766cc6b1SStefan Schake return ret; 399766cc6b1SStefan Schake 400766cc6b1SStefan Schake return drm_atomic_helper_check(dev, state); 401766cc6b1SStefan Schake } 402766cc6b1SStefan Schake 403c8b75bcaSEric Anholt static const struct drm_mode_config_funcs vc4_mode_funcs = { 404b8f429a7SNoralf Trønnes .output_poll_changed = drm_fb_helper_output_poll_changed, 405766cc6b1SStefan Schake .atomic_check = vc4_atomic_check, 406b501baccSEric Anholt .atomic_commit = vc4_atomic_commit, 40783753117SEric Anholt .fb_create = vc4_fb_create, 408c8b75bcaSEric Anholt }; 409c8b75bcaSEric Anholt 410c8b75bcaSEric Anholt int vc4_kms_load(struct drm_device *dev) 411c8b75bcaSEric Anholt { 41248666d56SDerek Foreman struct vc4_dev *vc4 = to_vc4_dev(dev); 413766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state; 414c8b75bcaSEric Anholt int ret; 415c8b75bcaSEric Anholt 416b501baccSEric Anholt sema_init(&vc4->async_modeset, 1); 417b501baccSEric Anholt 4187d2818f5SMario Kleiner /* Set support for vblank irq fast disable, before drm_vblank_init() */ 4197d2818f5SMario Kleiner dev->vblank_disable_immediate = true; 4207d2818f5SMario Kleiner 421c8b75bcaSEric Anholt ret = drm_vblank_init(dev, dev->mode_config.num_crtc); 422c8b75bcaSEric Anholt if (ret < 0) { 423c8b75bcaSEric Anholt dev_err(dev->dev, "failed to initialize vblank\n"); 424c8b75bcaSEric Anholt return ret; 425c8b75bcaSEric Anholt } 426c8b75bcaSEric Anholt 427c8b75bcaSEric Anholt dev->mode_config.max_width = 2048; 428c8b75bcaSEric Anholt dev->mode_config.max_height = 2048; 429c8b75bcaSEric Anholt dev->mode_config.funcs = &vc4_mode_funcs; 430c8b75bcaSEric Anholt dev->mode_config.preferred_depth = 24; 431b501baccSEric Anholt dev->mode_config.async_page_flip = true; 432423ad7b3SDaniel Stone dev->mode_config.allow_fb_modifiers = true; 433b501baccSEric Anholt 434766cc6b1SStefan Schake drm_modeset_lock_init(&vc4->ctm_state_lock); 435766cc6b1SStefan Schake 436766cc6b1SStefan Schake ctm_state = kzalloc(sizeof(*ctm_state), GFP_KERNEL); 437766cc6b1SStefan Schake if (!ctm_state) 438766cc6b1SStefan Schake return -ENOMEM; 439766cc6b1SStefan Schake drm_atomic_private_obj_init(&vc4->ctm_manager, &ctm_state->base, 440766cc6b1SStefan Schake &vc4_ctm_state_funcs); 441766cc6b1SStefan Schake 442c8b75bcaSEric Anholt drm_mode_config_reset(dev); 443c8b75bcaSEric Anholt 444b8f429a7SNoralf Trønnes if (dev->mode_config.num_connector) 445b8f429a7SNoralf Trønnes drm_fb_cma_fbdev_init(dev, 32, 0); 446c8b75bcaSEric Anholt 447c8b75bcaSEric Anholt drm_kms_helper_poll_init(dev); 448c8b75bcaSEric Anholt 449c8b75bcaSEric Anholt return 0; 450c8b75bcaSEric Anholt } 451