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 14d7d96c00SMaxime Ripard #include <linux/clk.h> 15d7d96c00SMaxime Ripard 16b7e8e25bSMasahiro Yamada #include <drm/drm_atomic.h> 17b7e8e25bSMasahiro Yamada #include <drm/drm_atomic_helper.h> 18fd6d6d80SSam Ravnborg #include <drm/drm_crtc.h> 199762477cSNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h> 20fcd70cd3SDaniel Vetter #include <drm/drm_plane_helper.h> 21fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h> 22fd6d6d80SSam Ravnborg #include <drm/drm_vblank.h> 23fd6d6d80SSam Ravnborg 24c8b75bcaSEric Anholt #include "vc4_drv.h" 25766cc6b1SStefan Schake #include "vc4_regs.h" 26766cc6b1SStefan Schake 27a9661f27SMaxime Ripard #define HVS_NUM_CHANNELS 3 28a9661f27SMaxime Ripard 29766cc6b1SStefan Schake struct vc4_ctm_state { 30766cc6b1SStefan Schake struct drm_private_state base; 31766cc6b1SStefan Schake struct drm_color_ctm *ctm; 32766cc6b1SStefan Schake int fifo; 33766cc6b1SStefan Schake }; 34766cc6b1SStefan Schake 35766cc6b1SStefan Schake static struct vc4_ctm_state *to_vc4_ctm_state(struct drm_private_state *priv) 36766cc6b1SStefan Schake { 37766cc6b1SStefan Schake return container_of(priv, struct vc4_ctm_state, base); 38766cc6b1SStefan Schake } 39766cc6b1SStefan Schake 40f2df84e0SMaxime Ripard struct vc4_hvs_state { 41f2df84e0SMaxime Ripard struct drm_private_state base; 429ec03d7fSMaxime Ripard 439ec03d7fSMaxime Ripard struct { 449ec03d7fSMaxime Ripard unsigned in_use: 1; 459ec03d7fSMaxime Ripard struct drm_crtc_commit *pending_commit; 469ec03d7fSMaxime Ripard } fifo_state[HVS_NUM_CHANNELS]; 47f2df84e0SMaxime Ripard }; 48f2df84e0SMaxime Ripard 49f2df84e0SMaxime Ripard static struct vc4_hvs_state * 50f2df84e0SMaxime Ripard to_vc4_hvs_state(struct drm_private_state *priv) 51f2df84e0SMaxime Ripard { 52f2df84e0SMaxime Ripard return container_of(priv, struct vc4_hvs_state, base); 53f2df84e0SMaxime Ripard } 54f2df84e0SMaxime Ripard 554686da83SBoris Brezillon struct vc4_load_tracker_state { 564686da83SBoris Brezillon struct drm_private_state base; 574686da83SBoris Brezillon u64 hvs_load; 584686da83SBoris Brezillon u64 membus_load; 594686da83SBoris Brezillon }; 604686da83SBoris Brezillon 614686da83SBoris Brezillon static struct vc4_load_tracker_state * 624686da83SBoris Brezillon to_vc4_load_tracker_state(struct drm_private_state *priv) 634686da83SBoris Brezillon { 644686da83SBoris Brezillon return container_of(priv, struct vc4_load_tracker_state, base); 654686da83SBoris Brezillon } 664686da83SBoris Brezillon 67766cc6b1SStefan Schake static struct vc4_ctm_state *vc4_get_ctm_state(struct drm_atomic_state *state, 68766cc6b1SStefan Schake struct drm_private_obj *manager) 69766cc6b1SStefan Schake { 70766cc6b1SStefan Schake struct drm_device *dev = state->dev; 7188e08589SMaxime Ripard struct vc4_dev *vc4 = to_vc4_dev(dev); 72766cc6b1SStefan Schake struct drm_private_state *priv_state; 73766cc6b1SStefan Schake int ret; 74766cc6b1SStefan Schake 75766cc6b1SStefan Schake ret = drm_modeset_lock(&vc4->ctm_state_lock, state->acquire_ctx); 76766cc6b1SStefan Schake if (ret) 77766cc6b1SStefan Schake return ERR_PTR(ret); 78766cc6b1SStefan Schake 79766cc6b1SStefan Schake priv_state = drm_atomic_get_private_obj_state(state, manager); 80766cc6b1SStefan Schake if (IS_ERR(priv_state)) 81766cc6b1SStefan Schake return ERR_CAST(priv_state); 82766cc6b1SStefan Schake 83766cc6b1SStefan Schake return to_vc4_ctm_state(priv_state); 84766cc6b1SStefan Schake } 85766cc6b1SStefan Schake 86766cc6b1SStefan Schake static struct drm_private_state * 87766cc6b1SStefan Schake vc4_ctm_duplicate_state(struct drm_private_obj *obj) 88766cc6b1SStefan Schake { 89766cc6b1SStefan Schake struct vc4_ctm_state *state; 90766cc6b1SStefan Schake 91766cc6b1SStefan Schake state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); 92766cc6b1SStefan Schake if (!state) 93766cc6b1SStefan Schake return NULL; 94766cc6b1SStefan Schake 95766cc6b1SStefan Schake __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 96766cc6b1SStefan Schake 97766cc6b1SStefan Schake return &state->base; 98766cc6b1SStefan Schake } 99766cc6b1SStefan Schake 100766cc6b1SStefan Schake static void vc4_ctm_destroy_state(struct drm_private_obj *obj, 101766cc6b1SStefan Schake struct drm_private_state *state) 102766cc6b1SStefan Schake { 103766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state = to_vc4_ctm_state(state); 104766cc6b1SStefan Schake 105766cc6b1SStefan Schake kfree(ctm_state); 106766cc6b1SStefan Schake } 107766cc6b1SStefan Schake 108766cc6b1SStefan Schake static const struct drm_private_state_funcs vc4_ctm_state_funcs = { 109766cc6b1SStefan Schake .atomic_duplicate_state = vc4_ctm_duplicate_state, 110766cc6b1SStefan Schake .atomic_destroy_state = vc4_ctm_destroy_state, 111766cc6b1SStefan Schake }; 112766cc6b1SStefan Schake 113dcda7c28SMaxime Ripard static void vc4_ctm_obj_fini(struct drm_device *dev, void *unused) 114dcda7c28SMaxime Ripard { 115dcda7c28SMaxime Ripard struct vc4_dev *vc4 = to_vc4_dev(dev); 116dcda7c28SMaxime Ripard 117dcda7c28SMaxime Ripard drm_atomic_private_obj_fini(&vc4->ctm_manager); 118dcda7c28SMaxime Ripard } 119dcda7c28SMaxime Ripard 120dcda7c28SMaxime Ripard static int vc4_ctm_obj_init(struct vc4_dev *vc4) 121dcda7c28SMaxime Ripard { 122dcda7c28SMaxime Ripard struct vc4_ctm_state *ctm_state; 123dcda7c28SMaxime Ripard 124dcda7c28SMaxime Ripard drm_modeset_lock_init(&vc4->ctm_state_lock); 125dcda7c28SMaxime Ripard 126dcda7c28SMaxime Ripard ctm_state = kzalloc(sizeof(*ctm_state), GFP_KERNEL); 127dcda7c28SMaxime Ripard if (!ctm_state) 128dcda7c28SMaxime Ripard return -ENOMEM; 129dcda7c28SMaxime Ripard 130dcda7c28SMaxime Ripard drm_atomic_private_obj_init(&vc4->base, &vc4->ctm_manager, &ctm_state->base, 131dcda7c28SMaxime Ripard &vc4_ctm_state_funcs); 132dcda7c28SMaxime Ripard 1333c354ed1SMaxime Ripard return drmm_add_action_or_reset(&vc4->base, vc4_ctm_obj_fini, NULL); 134dcda7c28SMaxime Ripard } 135dcda7c28SMaxime Ripard 136766cc6b1SStefan Schake /* Converts a DRM S31.32 value to the HW S0.9 format. */ 137766cc6b1SStefan Schake static u16 vc4_ctm_s31_32_to_s0_9(u64 in) 138766cc6b1SStefan Schake { 139766cc6b1SStefan Schake u16 r; 140766cc6b1SStefan Schake 141766cc6b1SStefan Schake /* Sign bit. */ 142766cc6b1SStefan Schake r = in & BIT_ULL(63) ? BIT(9) : 0; 143766cc6b1SStefan Schake 144766cc6b1SStefan Schake if ((in & GENMASK_ULL(62, 32)) > 0) { 145766cc6b1SStefan Schake /* We have zero integer bits so we can only saturate here. */ 146766cc6b1SStefan Schake r |= GENMASK(8, 0); 147766cc6b1SStefan Schake } else { 148766cc6b1SStefan Schake /* Otherwise take the 9 most important fractional bits. */ 149766cc6b1SStefan Schake r |= (in >> 23) & GENMASK(8, 0); 150766cc6b1SStefan Schake } 151766cc6b1SStefan Schake 152766cc6b1SStefan Schake return r; 153766cc6b1SStefan Schake } 154766cc6b1SStefan Schake 155766cc6b1SStefan Schake static void 156766cc6b1SStefan Schake vc4_ctm_commit(struct vc4_dev *vc4, struct drm_atomic_state *state) 157766cc6b1SStefan Schake { 158766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state = to_vc4_ctm_state(vc4->ctm_manager.state); 159766cc6b1SStefan Schake struct drm_color_ctm *ctm = ctm_state->ctm; 160766cc6b1SStefan Schake 161766cc6b1SStefan Schake if (ctm_state->fifo) { 162766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDCOEF2, 163766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[0]), 164766cc6b1SStefan Schake SCALER_OLEDCOEF2_R_TO_R) | 165766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[3]), 166766cc6b1SStefan Schake SCALER_OLEDCOEF2_R_TO_G) | 167766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[6]), 168766cc6b1SStefan Schake SCALER_OLEDCOEF2_R_TO_B)); 169766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDCOEF1, 170766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[1]), 171766cc6b1SStefan Schake SCALER_OLEDCOEF1_G_TO_R) | 172766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[4]), 173766cc6b1SStefan Schake SCALER_OLEDCOEF1_G_TO_G) | 174766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[7]), 175766cc6b1SStefan Schake SCALER_OLEDCOEF1_G_TO_B)); 176766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDCOEF0, 177766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[2]), 178766cc6b1SStefan Schake SCALER_OLEDCOEF0_B_TO_R) | 179766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[5]), 180766cc6b1SStefan Schake SCALER_OLEDCOEF0_B_TO_G) | 181766cc6b1SStefan Schake VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[8]), 182766cc6b1SStefan Schake SCALER_OLEDCOEF0_B_TO_B)); 183766cc6b1SStefan Schake } 184766cc6b1SStefan Schake 185766cc6b1SStefan Schake HVS_WRITE(SCALER_OLEDOFFS, 186766cc6b1SStefan Schake VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO)); 187766cc6b1SStefan Schake } 188c8b75bcaSEric Anholt 189f2df84e0SMaxime Ripard static struct vc4_hvs_state * 1909ec03d7fSMaxime Ripard vc4_hvs_get_new_global_state(struct drm_atomic_state *state) 1919ec03d7fSMaxime Ripard { 1929ec03d7fSMaxime Ripard struct vc4_dev *vc4 = to_vc4_dev(state->dev); 1939ec03d7fSMaxime Ripard struct drm_private_state *priv_state; 1949ec03d7fSMaxime Ripard 1959ec03d7fSMaxime Ripard priv_state = drm_atomic_get_new_private_obj_state(state, &vc4->hvs_channels); 1969ec03d7fSMaxime Ripard if (IS_ERR(priv_state)) 1979ec03d7fSMaxime Ripard return ERR_CAST(priv_state); 1989ec03d7fSMaxime Ripard 1999ec03d7fSMaxime Ripard return to_vc4_hvs_state(priv_state); 2009ec03d7fSMaxime Ripard } 2019ec03d7fSMaxime Ripard 2029ec03d7fSMaxime Ripard static struct vc4_hvs_state * 2039ec03d7fSMaxime Ripard vc4_hvs_get_old_global_state(struct drm_atomic_state *state) 2049ec03d7fSMaxime Ripard { 2059ec03d7fSMaxime Ripard struct vc4_dev *vc4 = to_vc4_dev(state->dev); 2069ec03d7fSMaxime Ripard struct drm_private_state *priv_state; 2079ec03d7fSMaxime Ripard 2089ec03d7fSMaxime Ripard priv_state = drm_atomic_get_old_private_obj_state(state, &vc4->hvs_channels); 2099ec03d7fSMaxime Ripard if (IS_ERR(priv_state)) 2109ec03d7fSMaxime Ripard return ERR_CAST(priv_state); 2119ec03d7fSMaxime Ripard 2129ec03d7fSMaxime Ripard return to_vc4_hvs_state(priv_state); 2139ec03d7fSMaxime Ripard } 2149ec03d7fSMaxime Ripard 2159ec03d7fSMaxime Ripard static struct vc4_hvs_state * 216f2df84e0SMaxime Ripard vc4_hvs_get_global_state(struct drm_atomic_state *state) 217f2df84e0SMaxime Ripard { 218f2df84e0SMaxime Ripard struct vc4_dev *vc4 = to_vc4_dev(state->dev); 219f2df84e0SMaxime Ripard struct drm_private_state *priv_state; 220f2df84e0SMaxime Ripard 221f2df84e0SMaxime Ripard priv_state = drm_atomic_get_private_obj_state(state, &vc4->hvs_channels); 222f2df84e0SMaxime Ripard if (IS_ERR(priv_state)) 223f2df84e0SMaxime Ripard return ERR_CAST(priv_state); 224f2df84e0SMaxime Ripard 225f2df84e0SMaxime Ripard return to_vc4_hvs_state(priv_state); 226f2df84e0SMaxime Ripard } 227f2df84e0SMaxime Ripard 22887ebcd42SMaxime Ripard static void vc4_hvs_pv_muxing_commit(struct vc4_dev *vc4, 22987ebcd42SMaxime Ripard struct drm_atomic_state *state) 23087ebcd42SMaxime Ripard { 23187ebcd42SMaxime Ripard struct drm_crtc_state *crtc_state; 23287ebcd42SMaxime Ripard struct drm_crtc *crtc; 23387ebcd42SMaxime Ripard unsigned int i; 23487ebcd42SMaxime Ripard 23587ebcd42SMaxime Ripard for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 23687ebcd42SMaxime Ripard struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); 23787ebcd42SMaxime Ripard u32 dispctrl; 23887ebcd42SMaxime Ripard u32 dsp3_mux; 23987ebcd42SMaxime Ripard 24087ebcd42SMaxime Ripard if (!crtc_state->active) 24187ebcd42SMaxime Ripard continue; 24287ebcd42SMaxime Ripard 24387ebcd42SMaxime Ripard if (vc4_state->assigned_channel != 2) 24487ebcd42SMaxime Ripard continue; 24587ebcd42SMaxime Ripard 24687ebcd42SMaxime Ripard /* 24787ebcd42SMaxime Ripard * SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to 24887ebcd42SMaxime Ripard * FIFO X'. 24987ebcd42SMaxime Ripard * SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'. 25087ebcd42SMaxime Ripard * 25187ebcd42SMaxime Ripard * DSP3 is connected to FIFO2 unless the transposer is 25287ebcd42SMaxime Ripard * enabled. In this case, FIFO 2 is directly accessed by the 25387ebcd42SMaxime Ripard * TXP IP, and we need to disable the FIFO2 -> pixelvalve1 25487ebcd42SMaxime Ripard * route. 25587ebcd42SMaxime Ripard */ 25687ebcd42SMaxime Ripard if (vc4_state->feed_txp) 25787ebcd42SMaxime Ripard dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX); 25887ebcd42SMaxime Ripard else 25987ebcd42SMaxime Ripard dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX); 26087ebcd42SMaxime Ripard 26187ebcd42SMaxime Ripard dispctrl = HVS_READ(SCALER_DISPCTRL) & 26287ebcd42SMaxime Ripard ~SCALER_DISPCTRL_DSP3_MUX_MASK; 26387ebcd42SMaxime Ripard HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux); 26487ebcd42SMaxime Ripard } 26587ebcd42SMaxime Ripard } 26687ebcd42SMaxime Ripard 26787ebcd42SMaxime Ripard static void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4, 26887ebcd42SMaxime Ripard struct drm_atomic_state *state) 26987ebcd42SMaxime Ripard { 27087ebcd42SMaxime Ripard struct drm_crtc_state *crtc_state; 27187ebcd42SMaxime Ripard struct drm_crtc *crtc; 2722820526dSMaxime Ripard unsigned char mux; 27387ebcd42SMaxime Ripard unsigned int i; 27487ebcd42SMaxime Ripard u32 reg; 27587ebcd42SMaxime Ripard 27687ebcd42SMaxime Ripard for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 27787ebcd42SMaxime Ripard struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); 27887ebcd42SMaxime Ripard struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); 27987ebcd42SMaxime Ripard 2802820526dSMaxime Ripard if (!vc4_state->update_muxing) 28187ebcd42SMaxime Ripard continue; 28287ebcd42SMaxime Ripard 28387ebcd42SMaxime Ripard switch (vc4_crtc->data->hvs_output) { 28487ebcd42SMaxime Ripard case 2: 2852820526dSMaxime Ripard mux = (vc4_state->assigned_channel == 2) ? 0 : 1; 2862820526dSMaxime Ripard reg = HVS_READ(SCALER_DISPECTRL); 2872820526dSMaxime Ripard HVS_WRITE(SCALER_DISPECTRL, 2882820526dSMaxime Ripard (reg & ~SCALER_DISPECTRL_DSP2_MUX_MASK) | 2892820526dSMaxime Ripard VC4_SET_FIELD(mux, SCALER_DISPECTRL_DSP2_MUX)); 29087ebcd42SMaxime Ripard break; 29187ebcd42SMaxime Ripard 29287ebcd42SMaxime Ripard case 3: 2932820526dSMaxime Ripard if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED) 2942820526dSMaxime Ripard mux = 3; 2952820526dSMaxime Ripard else 2962820526dSMaxime Ripard mux = vc4_state->assigned_channel; 2972820526dSMaxime Ripard 2982820526dSMaxime Ripard reg = HVS_READ(SCALER_DISPCTRL); 2992820526dSMaxime Ripard HVS_WRITE(SCALER_DISPCTRL, 3002820526dSMaxime Ripard (reg & ~SCALER_DISPCTRL_DSP3_MUX_MASK) | 3012820526dSMaxime Ripard VC4_SET_FIELD(mux, SCALER_DISPCTRL_DSP3_MUX)); 30287ebcd42SMaxime Ripard break; 30387ebcd42SMaxime Ripard 30487ebcd42SMaxime Ripard case 4: 3052820526dSMaxime Ripard if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED) 3062820526dSMaxime Ripard mux = 3; 3072820526dSMaxime Ripard else 3082820526dSMaxime Ripard mux = vc4_state->assigned_channel; 3092820526dSMaxime Ripard 3102820526dSMaxime Ripard reg = HVS_READ(SCALER_DISPEOLN); 3112820526dSMaxime Ripard HVS_WRITE(SCALER_DISPEOLN, 3122820526dSMaxime Ripard (reg & ~SCALER_DISPEOLN_DSP4_MUX_MASK) | 3132820526dSMaxime Ripard VC4_SET_FIELD(mux, SCALER_DISPEOLN_DSP4_MUX)); 3142820526dSMaxime Ripard 31587ebcd42SMaxime Ripard break; 31687ebcd42SMaxime Ripard 31787ebcd42SMaxime Ripard case 5: 3182820526dSMaxime Ripard if (vc4_state->assigned_channel == VC4_HVS_CHANNEL_DISABLED) 3192820526dSMaxime Ripard mux = 3; 3202820526dSMaxime Ripard else 3212820526dSMaxime Ripard mux = vc4_state->assigned_channel; 3222820526dSMaxime Ripard 3232820526dSMaxime Ripard reg = HVS_READ(SCALER_DISPDITHER); 3242820526dSMaxime Ripard HVS_WRITE(SCALER_DISPDITHER, 3252820526dSMaxime Ripard (reg & ~SCALER_DISPDITHER_DSP5_MUX_MASK) | 3262820526dSMaxime Ripard VC4_SET_FIELD(mux, SCALER_DISPDITHER_DSP5_MUX)); 32787ebcd42SMaxime Ripard break; 32887ebcd42SMaxime Ripard 32987ebcd42SMaxime Ripard default: 33087ebcd42SMaxime Ripard break; 33187ebcd42SMaxime Ripard } 33287ebcd42SMaxime Ripard } 33387ebcd42SMaxime Ripard } 33487ebcd42SMaxime Ripard 335f3c420feSMaxime Ripard static void vc4_atomic_commit_tail(struct drm_atomic_state *state) 336b501baccSEric Anholt { 337b501baccSEric Anholt struct drm_device *dev = state->dev; 338b501baccSEric Anholt struct vc4_dev *vc4 = to_vc4_dev(dev); 339d7d96c00SMaxime Ripard struct vc4_hvs *hvs = vc4->hvs; 3409ec03d7fSMaxime Ripard struct drm_crtc_state *old_crtc_state; 34159635667SMaxime Ripard struct drm_crtc_state *new_crtc_state; 34259635667SMaxime Ripard struct drm_crtc *crtc; 3439ec03d7fSMaxime Ripard struct vc4_hvs_state *old_hvs_state; 344531a1b62SBoris Brezillon int i; 345531a1b62SBoris Brezillon 34659635667SMaxime Ripard for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { 34787ebcd42SMaxime Ripard struct vc4_crtc_state *vc4_crtc_state; 34859635667SMaxime Ripard 34959635667SMaxime Ripard if (!new_crtc_state->commit) 350531a1b62SBoris Brezillon continue; 351531a1b62SBoris Brezillon 35287ebcd42SMaxime Ripard vc4_crtc_state = to_vc4_crtc_state(new_crtc_state); 35387ebcd42SMaxime Ripard vc4_hvs_mask_underrun(dev, vc4_crtc_state->assigned_channel); 354531a1b62SBoris Brezillon } 355b501baccSEric Anholt 356d7d96c00SMaxime Ripard if (vc4->hvs->hvs5) 357d7d96c00SMaxime Ripard clk_set_min_rate(hvs->core_clk, 500000000); 358d7d96c00SMaxime Ripard 3599ec03d7fSMaxime Ripard old_hvs_state = vc4_hvs_get_old_global_state(state); 3609ec03d7fSMaxime Ripard if (!old_hvs_state) 3619ec03d7fSMaxime Ripard return; 3629ec03d7fSMaxime Ripard 3639ec03d7fSMaxime Ripard for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { 3649ec03d7fSMaxime Ripard struct vc4_crtc_state *vc4_crtc_state = 3659ec03d7fSMaxime Ripard to_vc4_crtc_state(old_crtc_state); 3669ec03d7fSMaxime Ripard unsigned int channel = vc4_crtc_state->assigned_channel; 367*b99c2c95SMaxime Ripard int ret; 3689ec03d7fSMaxime Ripard 3699ec03d7fSMaxime Ripard if (channel == VC4_HVS_CHANNEL_DISABLED) 3709ec03d7fSMaxime Ripard continue; 3719ec03d7fSMaxime Ripard 3729ec03d7fSMaxime Ripard if (!old_hvs_state->fifo_state[channel].in_use) 3739ec03d7fSMaxime Ripard continue; 3749ec03d7fSMaxime Ripard 375*b99c2c95SMaxime Ripard ret = drm_crtc_commit_wait(old_hvs_state->fifo_state[i].pending_commit); 376*b99c2c95SMaxime Ripard if (ret) 377*b99c2c95SMaxime Ripard drm_err(dev, "Timed out waiting for commit\n"); 3789ec03d7fSMaxime Ripard } 3799ec03d7fSMaxime Ripard 380b501baccSEric Anholt drm_atomic_helper_commit_modeset_disables(dev, state); 381b501baccSEric Anholt 382766cc6b1SStefan Schake vc4_ctm_commit(vc4, state); 383766cc6b1SStefan Schake 38487ebcd42SMaxime Ripard if (vc4->hvs->hvs5) 38587ebcd42SMaxime Ripard vc5_hvs_pv_muxing_commit(vc4, state); 38687ebcd42SMaxime Ripard else 38787ebcd42SMaxime Ripard vc4_hvs_pv_muxing_commit(vc4, state); 38887ebcd42SMaxime Ripard 3892b58e98dSLiu Ying drm_atomic_helper_commit_planes(dev, state, 0); 390b501baccSEric Anholt 391b501baccSEric Anholt drm_atomic_helper_commit_modeset_enables(dev, state); 392b501baccSEric Anholt 3931ebe99a7SBoris Brezillon drm_atomic_helper_fake_vblank(state); 3941ebe99a7SBoris Brezillon 39534c8ea40SBoris Brezillon drm_atomic_helper_commit_hw_done(state); 39634c8ea40SBoris Brezillon 397184d3cf4SBoris Brezillon drm_atomic_helper_wait_for_flip_done(dev, state); 398b501baccSEric Anholt 399b501baccSEric Anholt drm_atomic_helper_cleanup_planes(dev, state); 400b501baccSEric Anholt 401d7d96c00SMaxime Ripard if (vc4->hvs->hvs5) 402d7d96c00SMaxime Ripard clk_set_min_rate(hvs->core_clk, 0); 403b501baccSEric Anholt } 404b501baccSEric Anholt 4059ec03d7fSMaxime Ripard static int vc4_atomic_commit_setup(struct drm_atomic_state *state) 4069ec03d7fSMaxime Ripard { 4079ec03d7fSMaxime Ripard struct drm_crtc_state *crtc_state; 4089ec03d7fSMaxime Ripard struct vc4_hvs_state *hvs_state; 4099ec03d7fSMaxime Ripard struct drm_crtc *crtc; 4109ec03d7fSMaxime Ripard unsigned int i; 4119ec03d7fSMaxime Ripard 4129ec03d7fSMaxime Ripard hvs_state = vc4_hvs_get_new_global_state(state); 4139ec03d7fSMaxime Ripard if (!hvs_state) 4149ec03d7fSMaxime Ripard return -EINVAL; 4159ec03d7fSMaxime Ripard 4169ec03d7fSMaxime Ripard for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 4179ec03d7fSMaxime Ripard struct vc4_crtc_state *vc4_crtc_state = 4189ec03d7fSMaxime Ripard to_vc4_crtc_state(crtc_state); 4199ec03d7fSMaxime Ripard unsigned int channel = 4209ec03d7fSMaxime Ripard vc4_crtc_state->assigned_channel; 4219ec03d7fSMaxime Ripard 4229ec03d7fSMaxime Ripard if (channel == VC4_HVS_CHANNEL_DISABLED) 4239ec03d7fSMaxime Ripard continue; 4249ec03d7fSMaxime Ripard 4259ec03d7fSMaxime Ripard if (!hvs_state->fifo_state[channel].in_use) 4269ec03d7fSMaxime Ripard continue; 4279ec03d7fSMaxime Ripard 4289ec03d7fSMaxime Ripard hvs_state->fifo_state[channel].pending_commit = 4299ec03d7fSMaxime Ripard drm_crtc_commit_get(crtc_state->commit); 4309ec03d7fSMaxime Ripard } 4319ec03d7fSMaxime Ripard 4329ec03d7fSMaxime Ripard return 0; 4339ec03d7fSMaxime Ripard } 4349ec03d7fSMaxime Ripard 43583753117SEric Anholt static struct drm_framebuffer *vc4_fb_create(struct drm_device *dev, 43683753117SEric Anholt struct drm_file *file_priv, 43783753117SEric Anholt const struct drm_mode_fb_cmd2 *mode_cmd) 43883753117SEric Anholt { 43983753117SEric Anholt struct drm_mode_fb_cmd2 mode_cmd_local; 44083753117SEric Anholt 44183753117SEric Anholt /* If the user didn't specify a modifier, use the 44283753117SEric Anholt * vc4_set_tiling_ioctl() state for the BO. 44383753117SEric Anholt */ 44483753117SEric Anholt if (!(mode_cmd->flags & DRM_MODE_FB_MODIFIERS)) { 44583753117SEric Anholt struct drm_gem_object *gem_obj; 44683753117SEric Anholt struct vc4_bo *bo; 44783753117SEric Anholt 44883753117SEric Anholt gem_obj = drm_gem_object_lookup(file_priv, 44983753117SEric Anholt mode_cmd->handles[0]); 45083753117SEric Anholt if (!gem_obj) { 451fb95992aSEric Anholt DRM_DEBUG("Failed to look up GEM BO %d\n", 45283753117SEric Anholt mode_cmd->handles[0]); 45383753117SEric Anholt return ERR_PTR(-ENOENT); 45483753117SEric Anholt } 45583753117SEric Anholt bo = to_vc4_bo(gem_obj); 45683753117SEric Anholt 45783753117SEric Anholt mode_cmd_local = *mode_cmd; 45883753117SEric Anholt 45983753117SEric Anholt if (bo->t_format) { 46083753117SEric Anholt mode_cmd_local.modifier[0] = 46183753117SEric Anholt DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED; 46283753117SEric Anholt } else { 46383753117SEric Anholt mode_cmd_local.modifier[0] = DRM_FORMAT_MOD_NONE; 46483753117SEric Anholt } 46583753117SEric Anholt 466f7a8cd30SEmil Velikov drm_gem_object_put(gem_obj); 46783753117SEric Anholt 46883753117SEric Anholt mode_cmd = &mode_cmd_local; 46983753117SEric Anholt } 47083753117SEric Anholt 4719762477cSNoralf Trønnes return drm_gem_fb_create(dev, file_priv, mode_cmd); 47283753117SEric Anholt } 47383753117SEric Anholt 474766cc6b1SStefan Schake /* Our CTM has some peculiar limitations: we can only enable it for one CRTC 475766cc6b1SStefan Schake * at a time and the HW only supports S0.9 scalars. To account for the latter, 476766cc6b1SStefan Schake * we don't allow userland to set a CTM that we have no hope of approximating. 477766cc6b1SStefan Schake */ 478766cc6b1SStefan Schake static int 479766cc6b1SStefan Schake vc4_ctm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) 480766cc6b1SStefan Schake { 481766cc6b1SStefan Schake struct vc4_dev *vc4 = to_vc4_dev(dev); 482766cc6b1SStefan Schake struct vc4_ctm_state *ctm_state = NULL; 483766cc6b1SStefan Schake struct drm_crtc *crtc; 484766cc6b1SStefan Schake struct drm_crtc_state *old_crtc_state, *new_crtc_state; 485766cc6b1SStefan Schake struct drm_color_ctm *ctm; 486766cc6b1SStefan Schake int i; 487766cc6b1SStefan Schake 488766cc6b1SStefan Schake for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 489766cc6b1SStefan Schake /* CTM is being disabled. */ 490766cc6b1SStefan Schake if (!new_crtc_state->ctm && old_crtc_state->ctm) { 491766cc6b1SStefan Schake ctm_state = vc4_get_ctm_state(state, &vc4->ctm_manager); 492766cc6b1SStefan Schake if (IS_ERR(ctm_state)) 493766cc6b1SStefan Schake return PTR_ERR(ctm_state); 494766cc6b1SStefan Schake ctm_state->fifo = 0; 495766cc6b1SStefan Schake } 496766cc6b1SStefan Schake } 497766cc6b1SStefan Schake 498766cc6b1SStefan Schake for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 499766cc6b1SStefan Schake if (new_crtc_state->ctm == old_crtc_state->ctm) 500766cc6b1SStefan Schake continue; 501766cc6b1SStefan Schake 502766cc6b1SStefan Schake if (!ctm_state) { 503766cc6b1SStefan Schake ctm_state = vc4_get_ctm_state(state, &vc4->ctm_manager); 504766cc6b1SStefan Schake if (IS_ERR(ctm_state)) 505766cc6b1SStefan Schake return PTR_ERR(ctm_state); 506766cc6b1SStefan Schake } 507766cc6b1SStefan Schake 508766cc6b1SStefan Schake /* CTM is being enabled or the matrix changed. */ 509766cc6b1SStefan Schake if (new_crtc_state->ctm) { 51087ebcd42SMaxime Ripard struct vc4_crtc_state *vc4_crtc_state = 51187ebcd42SMaxime Ripard to_vc4_crtc_state(new_crtc_state); 51287ebcd42SMaxime Ripard 513766cc6b1SStefan Schake /* fifo is 1-based since 0 disables CTM. */ 51487ebcd42SMaxime Ripard int fifo = vc4_crtc_state->assigned_channel + 1; 515766cc6b1SStefan Schake 516766cc6b1SStefan Schake /* Check userland isn't trying to turn on CTM for more 517766cc6b1SStefan Schake * than one CRTC at a time. 518766cc6b1SStefan Schake */ 519766cc6b1SStefan Schake if (ctm_state->fifo && ctm_state->fifo != fifo) { 520766cc6b1SStefan Schake DRM_DEBUG_DRIVER("Too many CTM configured\n"); 521766cc6b1SStefan Schake return -EINVAL; 522766cc6b1SStefan Schake } 523766cc6b1SStefan Schake 524766cc6b1SStefan Schake /* Check we can approximate the specified CTM. 525766cc6b1SStefan Schake * We disallow scalars |c| > 1.0 since the HW has 526766cc6b1SStefan Schake * no integer bits. 527766cc6b1SStefan Schake */ 528766cc6b1SStefan Schake ctm = new_crtc_state->ctm->data; 529766cc6b1SStefan Schake for (i = 0; i < ARRAY_SIZE(ctm->matrix); i++) { 530766cc6b1SStefan Schake u64 val = ctm->matrix[i]; 531766cc6b1SStefan Schake 532766cc6b1SStefan Schake val &= ~BIT_ULL(63); 533766cc6b1SStefan Schake if (val > BIT_ULL(32)) 534766cc6b1SStefan Schake return -EINVAL; 535766cc6b1SStefan Schake } 536766cc6b1SStefan Schake 537766cc6b1SStefan Schake ctm_state->fifo = fifo; 538766cc6b1SStefan Schake ctm_state->ctm = ctm; 539766cc6b1SStefan Schake } 540766cc6b1SStefan Schake } 541766cc6b1SStefan Schake 542766cc6b1SStefan Schake return 0; 543766cc6b1SStefan Schake } 544766cc6b1SStefan Schake 5454686da83SBoris Brezillon static int vc4_load_tracker_atomic_check(struct drm_atomic_state *state) 5464686da83SBoris Brezillon { 5474686da83SBoris Brezillon struct drm_plane_state *old_plane_state, *new_plane_state; 5484686da83SBoris Brezillon struct vc4_dev *vc4 = to_vc4_dev(state->dev); 5494686da83SBoris Brezillon struct vc4_load_tracker_state *load_state; 5504686da83SBoris Brezillon struct drm_private_state *priv_state; 5514686da83SBoris Brezillon struct drm_plane *plane; 5524686da83SBoris Brezillon int i; 5534686da83SBoris Brezillon 554f437bc1eSMaxime Ripard if (!vc4->load_tracker_available) 555f437bc1eSMaxime Ripard return 0; 556f437bc1eSMaxime Ripard 5574686da83SBoris Brezillon priv_state = drm_atomic_get_private_obj_state(state, 5584686da83SBoris Brezillon &vc4->load_tracker); 5594686da83SBoris Brezillon if (IS_ERR(priv_state)) 5604686da83SBoris Brezillon return PTR_ERR(priv_state); 5614686da83SBoris Brezillon 5624686da83SBoris Brezillon load_state = to_vc4_load_tracker_state(priv_state); 5634686da83SBoris Brezillon for_each_oldnew_plane_in_state(state, plane, old_plane_state, 5644686da83SBoris Brezillon new_plane_state, i) { 5654686da83SBoris Brezillon struct vc4_plane_state *vc4_plane_state; 5664686da83SBoris Brezillon 5674686da83SBoris Brezillon if (old_plane_state->fb && old_plane_state->crtc) { 5684686da83SBoris Brezillon vc4_plane_state = to_vc4_plane_state(old_plane_state); 5694686da83SBoris Brezillon load_state->membus_load -= vc4_plane_state->membus_load; 5704686da83SBoris Brezillon load_state->hvs_load -= vc4_plane_state->hvs_load; 5714686da83SBoris Brezillon } 5724686da83SBoris Brezillon 5734686da83SBoris Brezillon if (new_plane_state->fb && new_plane_state->crtc) { 5744686da83SBoris Brezillon vc4_plane_state = to_vc4_plane_state(new_plane_state); 5754686da83SBoris Brezillon load_state->membus_load += vc4_plane_state->membus_load; 5764686da83SBoris Brezillon load_state->hvs_load += vc4_plane_state->hvs_load; 5774686da83SBoris Brezillon } 5784686da83SBoris Brezillon } 5794686da83SBoris Brezillon 5806b5c029dSPaul Kocialkowski /* Don't check the load when the tracker is disabled. */ 5816b5c029dSPaul Kocialkowski if (!vc4->load_tracker_enabled) 5826b5c029dSPaul Kocialkowski return 0; 5836b5c029dSPaul Kocialkowski 5844686da83SBoris Brezillon /* The absolute limit is 2Gbyte/sec, but let's take a margin to let 5854686da83SBoris Brezillon * the system work when other blocks are accessing the memory. 5864686da83SBoris Brezillon */ 5874686da83SBoris Brezillon if (load_state->membus_load > SZ_1G + SZ_512M) 5884686da83SBoris Brezillon return -ENOSPC; 5894686da83SBoris Brezillon 5904686da83SBoris Brezillon /* HVS clock is supposed to run @ 250Mhz, let's take a margin and 5914686da83SBoris Brezillon * consider the maximum number of cycles is 240M. 5924686da83SBoris Brezillon */ 5934686da83SBoris Brezillon if (load_state->hvs_load > 240000000ULL) 5944686da83SBoris Brezillon return -ENOSPC; 5954686da83SBoris Brezillon 5964686da83SBoris Brezillon return 0; 5974686da83SBoris Brezillon } 5984686da83SBoris Brezillon 5994686da83SBoris Brezillon static struct drm_private_state * 6004686da83SBoris Brezillon vc4_load_tracker_duplicate_state(struct drm_private_obj *obj) 6014686da83SBoris Brezillon { 6024686da83SBoris Brezillon struct vc4_load_tracker_state *state; 6034686da83SBoris Brezillon 6044686da83SBoris Brezillon state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); 6054686da83SBoris Brezillon if (!state) 6064686da83SBoris Brezillon return NULL; 6074686da83SBoris Brezillon 6084686da83SBoris Brezillon __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 6094686da83SBoris Brezillon 6104686da83SBoris Brezillon return &state->base; 6114686da83SBoris Brezillon } 6124686da83SBoris Brezillon 6134686da83SBoris Brezillon static void vc4_load_tracker_destroy_state(struct drm_private_obj *obj, 6144686da83SBoris Brezillon struct drm_private_state *state) 6154686da83SBoris Brezillon { 6164686da83SBoris Brezillon struct vc4_load_tracker_state *load_state; 6174686da83SBoris Brezillon 6184686da83SBoris Brezillon load_state = to_vc4_load_tracker_state(state); 6194686da83SBoris Brezillon kfree(load_state); 6204686da83SBoris Brezillon } 6214686da83SBoris Brezillon 6224686da83SBoris Brezillon static const struct drm_private_state_funcs vc4_load_tracker_state_funcs = { 6234686da83SBoris Brezillon .atomic_duplicate_state = vc4_load_tracker_duplicate_state, 6244686da83SBoris Brezillon .atomic_destroy_state = vc4_load_tracker_destroy_state, 6254686da83SBoris Brezillon }; 6264686da83SBoris Brezillon 627dcda7c28SMaxime Ripard static void vc4_load_tracker_obj_fini(struct drm_device *dev, void *unused) 628dcda7c28SMaxime Ripard { 629dcda7c28SMaxime Ripard struct vc4_dev *vc4 = to_vc4_dev(dev); 630dcda7c28SMaxime Ripard 631dcda7c28SMaxime Ripard if (!vc4->load_tracker_available) 632dcda7c28SMaxime Ripard return; 633dcda7c28SMaxime Ripard 634dcda7c28SMaxime Ripard drm_atomic_private_obj_fini(&vc4->load_tracker); 635dcda7c28SMaxime Ripard } 636dcda7c28SMaxime Ripard 637dcda7c28SMaxime Ripard static int vc4_load_tracker_obj_init(struct vc4_dev *vc4) 638dcda7c28SMaxime Ripard { 639dcda7c28SMaxime Ripard struct vc4_load_tracker_state *load_state; 640dcda7c28SMaxime Ripard 641dcda7c28SMaxime Ripard if (!vc4->load_tracker_available) 642dcda7c28SMaxime Ripard return 0; 643dcda7c28SMaxime Ripard 644dcda7c28SMaxime Ripard load_state = kzalloc(sizeof(*load_state), GFP_KERNEL); 645dcda7c28SMaxime Ripard if (!load_state) 646dcda7c28SMaxime Ripard return -ENOMEM; 647dcda7c28SMaxime Ripard 648dcda7c28SMaxime Ripard drm_atomic_private_obj_init(&vc4->base, &vc4->load_tracker, 649dcda7c28SMaxime Ripard &load_state->base, 650dcda7c28SMaxime Ripard &vc4_load_tracker_state_funcs); 651dcda7c28SMaxime Ripard 6523c354ed1SMaxime Ripard return drmm_add_action_or_reset(&vc4->base, vc4_load_tracker_obj_fini, NULL); 653dcda7c28SMaxime Ripard } 654dcda7c28SMaxime Ripard 655f2df84e0SMaxime Ripard static struct drm_private_state * 656f2df84e0SMaxime Ripard vc4_hvs_channels_duplicate_state(struct drm_private_obj *obj) 657f2df84e0SMaxime Ripard { 658f2df84e0SMaxime Ripard struct vc4_hvs_state *old_state = to_vc4_hvs_state(obj->state); 659f2df84e0SMaxime Ripard struct vc4_hvs_state *state; 6609ec03d7fSMaxime Ripard unsigned int i; 661f2df84e0SMaxime Ripard 662f2df84e0SMaxime Ripard state = kzalloc(sizeof(*state), GFP_KERNEL); 663f2df84e0SMaxime Ripard if (!state) 664f2df84e0SMaxime Ripard return NULL; 665f2df84e0SMaxime Ripard 666f2df84e0SMaxime Ripard __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 667f2df84e0SMaxime Ripard 668f2df84e0SMaxime Ripard 6699ec03d7fSMaxime Ripard for (i = 0; i < HVS_NUM_CHANNELS; i++) { 6709ec03d7fSMaxime Ripard state->fifo_state[i].in_use = old_state->fifo_state[i].in_use; 6719ec03d7fSMaxime Ripard 6729ec03d7fSMaxime Ripard if (!old_state->fifo_state[i].pending_commit) 6739ec03d7fSMaxime Ripard continue; 6749ec03d7fSMaxime Ripard 6759ec03d7fSMaxime Ripard state->fifo_state[i].pending_commit = 6769ec03d7fSMaxime Ripard drm_crtc_commit_get(old_state->fifo_state[i].pending_commit); 6779ec03d7fSMaxime Ripard } 6789ec03d7fSMaxime Ripard 679f2df84e0SMaxime Ripard return &state->base; 680f2df84e0SMaxime Ripard } 681f2df84e0SMaxime Ripard 682f2df84e0SMaxime Ripard static void vc4_hvs_channels_destroy_state(struct drm_private_obj *obj, 683f2df84e0SMaxime Ripard struct drm_private_state *state) 684f2df84e0SMaxime Ripard { 685f2df84e0SMaxime Ripard struct vc4_hvs_state *hvs_state = to_vc4_hvs_state(state); 6869ec03d7fSMaxime Ripard unsigned int i; 6879ec03d7fSMaxime Ripard 6889ec03d7fSMaxime Ripard for (i = 0; i < HVS_NUM_CHANNELS; i++) { 6899ec03d7fSMaxime Ripard if (!hvs_state->fifo_state[i].pending_commit) 6909ec03d7fSMaxime Ripard continue; 6919ec03d7fSMaxime Ripard 6929ec03d7fSMaxime Ripard drm_crtc_commit_put(hvs_state->fifo_state[i].pending_commit); 6939ec03d7fSMaxime Ripard } 694f2df84e0SMaxime Ripard 695f2df84e0SMaxime Ripard kfree(hvs_state); 696f2df84e0SMaxime Ripard } 697f2df84e0SMaxime Ripard 698f2df84e0SMaxime Ripard static const struct drm_private_state_funcs vc4_hvs_state_funcs = { 699f2df84e0SMaxime Ripard .atomic_duplicate_state = vc4_hvs_channels_duplicate_state, 700f2df84e0SMaxime Ripard .atomic_destroy_state = vc4_hvs_channels_destroy_state, 701f2df84e0SMaxime Ripard }; 702f2df84e0SMaxime Ripard 703f2df84e0SMaxime Ripard static void vc4_hvs_channels_obj_fini(struct drm_device *dev, void *unused) 704f2df84e0SMaxime Ripard { 705f2df84e0SMaxime Ripard struct vc4_dev *vc4 = to_vc4_dev(dev); 706f2df84e0SMaxime Ripard 707f2df84e0SMaxime Ripard drm_atomic_private_obj_fini(&vc4->hvs_channels); 708f2df84e0SMaxime Ripard } 709f2df84e0SMaxime Ripard 710f2df84e0SMaxime Ripard static int vc4_hvs_channels_obj_init(struct vc4_dev *vc4) 711f2df84e0SMaxime Ripard { 712f2df84e0SMaxime Ripard struct vc4_hvs_state *state; 713f2df84e0SMaxime Ripard 714f2df84e0SMaxime Ripard state = kzalloc(sizeof(*state), GFP_KERNEL); 715f2df84e0SMaxime Ripard if (!state) 716f2df84e0SMaxime Ripard return -ENOMEM; 717f2df84e0SMaxime Ripard 718f2df84e0SMaxime Ripard drm_atomic_private_obj_init(&vc4->base, &vc4->hvs_channels, 719f2df84e0SMaxime Ripard &state->base, 720f2df84e0SMaxime Ripard &vc4_hvs_state_funcs); 721f2df84e0SMaxime Ripard 722f2df84e0SMaxime Ripard return drmm_add_action_or_reset(&vc4->base, vc4_hvs_channels_obj_fini, NULL); 723f2df84e0SMaxime Ripard } 724f2df84e0SMaxime Ripard 725b5dbc4d3SMaxime Ripard /* 726b5dbc4d3SMaxime Ripard * The BCM2711 HVS has up to 7 outputs connected to the pixelvalves and 727b5dbc4d3SMaxime Ripard * the TXP (and therefore all the CRTCs found on that platform). 728b5dbc4d3SMaxime Ripard * 729b5dbc4d3SMaxime Ripard * The naive (and our initial) implementation would just iterate over 730b5dbc4d3SMaxime Ripard * all the active CRTCs, try to find a suitable FIFO, and then remove it 731b5dbc4d3SMaxime Ripard * from the pool of available FIFOs. However, there are a few corner 732b5dbc4d3SMaxime Ripard * cases that need to be considered: 733b5dbc4d3SMaxime Ripard * 734b5dbc4d3SMaxime Ripard * - When running in a dual-display setup (so with two CRTCs involved), 735b5dbc4d3SMaxime Ripard * we can update the state of a single CRTC (for example by changing 736b5dbc4d3SMaxime Ripard * its mode using xrandr under X11) without affecting the other. In 737b5dbc4d3SMaxime Ripard * this case, the other CRTC wouldn't be in the state at all, so we 738b5dbc4d3SMaxime Ripard * need to consider all the running CRTCs in the DRM device to assign 739b5dbc4d3SMaxime Ripard * a FIFO, not just the one in the state. 740b5dbc4d3SMaxime Ripard * 741f2df84e0SMaxime Ripard * - To fix the above, we can't use drm_atomic_get_crtc_state on all 742f2df84e0SMaxime Ripard * enabled CRTCs to pull their CRTC state into the global state, since 743f2df84e0SMaxime Ripard * a page flip would start considering their vblank to complete. Since 744f2df84e0SMaxime Ripard * we don't have a guarantee that they are actually active, that 745f2df84e0SMaxime Ripard * vblank might never happen, and shouldn't even be considered if we 746f2df84e0SMaxime Ripard * want to do a page flip on a single CRTC. That can be tested by 747f2df84e0SMaxime Ripard * doing a modetest -v first on HDMI1 and then on HDMI0. 748f2df84e0SMaxime Ripard * 749b5dbc4d3SMaxime Ripard * - Since we need the pixelvalve to be disabled and enabled back when 750b5dbc4d3SMaxime Ripard * the FIFO is changed, we should keep the FIFO assigned for as long 751b5dbc4d3SMaxime Ripard * as the CRTC is enabled, only considering it free again once that 752b5dbc4d3SMaxime Ripard * CRTC has been disabled. This can be tested by booting X11 on a 753b5dbc4d3SMaxime Ripard * single display, and changing the resolution down and then back up. 754b5dbc4d3SMaxime Ripard */ 755a72b0458SMaxime Ripard static int vc4_pv_muxing_atomic_check(struct drm_device *dev, 756a72b0458SMaxime Ripard struct drm_atomic_state *state) 757766cc6b1SStefan Schake { 758f2df84e0SMaxime Ripard struct vc4_hvs_state *hvs_new_state; 7598ba0b6d1SMaxime Ripard struct drm_crtc_state *old_crtc_state, *new_crtc_state; 76087ebcd42SMaxime Ripard struct drm_crtc *crtc; 76103b03efeSMaxime Ripard unsigned int unassigned_channels = 0; 762a72b0458SMaxime Ripard unsigned int i; 76387ebcd42SMaxime Ripard 764f2df84e0SMaxime Ripard hvs_new_state = vc4_hvs_get_global_state(state); 765f2df84e0SMaxime Ripard if (!hvs_new_state) 766f2df84e0SMaxime Ripard return -EINVAL; 767089d8341SMaxime Ripard 76803b03efeSMaxime Ripard for (i = 0; i < ARRAY_SIZE(hvs_new_state->fifo_state); i++) 76903b03efeSMaxime Ripard if (!hvs_new_state->fifo_state[i].in_use) 77003b03efeSMaxime Ripard unassigned_channels |= BIT(i); 77103b03efeSMaxime Ripard 7728ba0b6d1SMaxime Ripard for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 773f2df84e0SMaxime Ripard struct vc4_crtc_state *old_vc4_crtc_state = 774f2df84e0SMaxime Ripard to_vc4_crtc_state(old_crtc_state); 7758ba0b6d1SMaxime Ripard struct vc4_crtc_state *new_vc4_crtc_state = 7768ba0b6d1SMaxime Ripard to_vc4_crtc_state(new_crtc_state); 77787ebcd42SMaxime Ripard struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); 77887ebcd42SMaxime Ripard unsigned int matching_channels; 779d62a8ed7SMaxime Ripard unsigned int channel; 78087ebcd42SMaxime Ripard 7812820526dSMaxime Ripard /* Nothing to do here, let's skip it */ 7822820526dSMaxime Ripard if (old_crtc_state->enable == new_crtc_state->enable) 7832820526dSMaxime Ripard continue; 7842820526dSMaxime Ripard 7852820526dSMaxime Ripard /* Muxing will need to be modified, mark it as such */ 7862820526dSMaxime Ripard new_vc4_crtc_state->update_muxing = true; 7872820526dSMaxime Ripard 7882820526dSMaxime Ripard /* If we're disabling our CRTC, we put back our channel */ 7892820526dSMaxime Ripard if (!new_crtc_state->enable) { 7909ec03d7fSMaxime Ripard channel = old_vc4_crtc_state->assigned_channel; 7919ec03d7fSMaxime Ripard hvs_new_state->fifo_state[channel].in_use = false; 7928ba0b6d1SMaxime Ripard new_vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED; 7932820526dSMaxime Ripard continue; 794f2df84e0SMaxime Ripard } 7958ba0b6d1SMaxime Ripard 79687ebcd42SMaxime Ripard /* 79787ebcd42SMaxime Ripard * The problem we have to solve here is that we have 79887ebcd42SMaxime Ripard * up to 7 encoders, connected to up to 6 CRTCs. 79987ebcd42SMaxime Ripard * 80087ebcd42SMaxime Ripard * Those CRTCs, depending on the instance, can be 80187ebcd42SMaxime Ripard * routed to 1, 2 or 3 HVS FIFOs, and we need to set 80287ebcd42SMaxime Ripard * the change the muxing between FIFOs and outputs in 80387ebcd42SMaxime Ripard * the HVS accordingly. 80487ebcd42SMaxime Ripard * 80587ebcd42SMaxime Ripard * It would be pretty hard to come up with an 80687ebcd42SMaxime Ripard * algorithm that would generically solve 80787ebcd42SMaxime Ripard * this. However, the current routing trees we support 80887ebcd42SMaxime Ripard * allow us to simplify a bit the problem. 80987ebcd42SMaxime Ripard * 81087ebcd42SMaxime Ripard * Indeed, with the current supported layouts, if we 81187ebcd42SMaxime Ripard * try to assign in the ascending crtc index order the 81287ebcd42SMaxime Ripard * FIFOs, we can't fall into the situation where an 81387ebcd42SMaxime Ripard * earlier CRTC that had multiple routes is assigned 81487ebcd42SMaxime Ripard * one that was the only option for a later CRTC. 81587ebcd42SMaxime Ripard * 81687ebcd42SMaxime Ripard * If the layout changes and doesn't give us that in 81787ebcd42SMaxime Ripard * the future, we will need to have something smarter, 81887ebcd42SMaxime Ripard * but it works so far. 81987ebcd42SMaxime Ripard */ 82003b03efeSMaxime Ripard matching_channels = unassigned_channels & vc4_crtc->data->hvs_available_channels; 821d62a8ed7SMaxime Ripard if (!matching_channels) 822d62a8ed7SMaxime Ripard return -EINVAL; 82387ebcd42SMaxime Ripard 824d62a8ed7SMaxime Ripard channel = ffs(matching_channels) - 1; 8258ba0b6d1SMaxime Ripard new_vc4_crtc_state->assigned_channel = channel; 82603b03efeSMaxime Ripard unassigned_channels &= ~BIT(channel); 8279ec03d7fSMaxime Ripard hvs_new_state->fifo_state[channel].in_use = true; 82887ebcd42SMaxime Ripard } 829766cc6b1SStefan Schake 830a72b0458SMaxime Ripard return 0; 831a72b0458SMaxime Ripard } 832a72b0458SMaxime Ripard 833a72b0458SMaxime Ripard static int 834a72b0458SMaxime Ripard vc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) 835a72b0458SMaxime Ripard { 836a72b0458SMaxime Ripard int ret; 837a72b0458SMaxime Ripard 838a72b0458SMaxime Ripard ret = vc4_pv_muxing_atomic_check(dev, state); 839a72b0458SMaxime Ripard if (ret) 840a72b0458SMaxime Ripard return ret; 841a72b0458SMaxime Ripard 842766cc6b1SStefan Schake ret = vc4_ctm_atomic_check(dev, state); 843766cc6b1SStefan Schake if (ret < 0) 844766cc6b1SStefan Schake return ret; 845766cc6b1SStefan Schake 8464686da83SBoris Brezillon ret = drm_atomic_helper_check(dev, state); 8474686da83SBoris Brezillon if (ret) 8484686da83SBoris Brezillon return ret; 8494686da83SBoris Brezillon 8504686da83SBoris Brezillon return vc4_load_tracker_atomic_check(state); 851766cc6b1SStefan Schake } 852766cc6b1SStefan Schake 8539ec03d7fSMaxime Ripard static struct drm_mode_config_helper_funcs vc4_mode_config_helpers = { 8549ec03d7fSMaxime Ripard .atomic_commit_setup = vc4_atomic_commit_setup, 855f3c420feSMaxime Ripard .atomic_commit_tail = vc4_atomic_commit_tail, 8569ec03d7fSMaxime Ripard }; 8579ec03d7fSMaxime Ripard 858c8b75bcaSEric Anholt static const struct drm_mode_config_funcs vc4_mode_funcs = { 859766cc6b1SStefan Schake .atomic_check = vc4_atomic_check, 860f3c420feSMaxime Ripard .atomic_commit = drm_atomic_helper_commit, 86183753117SEric Anholt .fb_create = vc4_fb_create, 862c8b75bcaSEric Anholt }; 863c8b75bcaSEric Anholt 864c8b75bcaSEric Anholt int vc4_kms_load(struct drm_device *dev) 865c8b75bcaSEric Anholt { 86648666d56SDerek Foreman struct vc4_dev *vc4 = to_vc4_dev(dev); 867f437bc1eSMaxime Ripard bool is_vc5 = of_device_is_compatible(dev->dev->of_node, 868f437bc1eSMaxime Ripard "brcm,bcm2711-vc5"); 869c8b75bcaSEric Anholt int ret; 870c8b75bcaSEric Anholt 871f437bc1eSMaxime Ripard if (!is_vc5) { 872f437bc1eSMaxime Ripard vc4->load_tracker_available = true; 873f437bc1eSMaxime Ripard 874f437bc1eSMaxime Ripard /* Start with the load tracker enabled. Can be 875f437bc1eSMaxime Ripard * disabled through the debugfs load_tracker file. 8766b5c029dSPaul Kocialkowski */ 8776b5c029dSPaul Kocialkowski vc4->load_tracker_enabled = true; 878f437bc1eSMaxime Ripard } 8796b5c029dSPaul Kocialkowski 8807d2818f5SMario Kleiner /* Set support for vblank irq fast disable, before drm_vblank_init() */ 8817d2818f5SMario Kleiner dev->vblank_disable_immediate = true; 8827d2818f5SMario Kleiner 883ffc26740SEric Anholt dev->irq_enabled = true; 884c8b75bcaSEric Anholt ret = drm_vblank_init(dev, dev->mode_config.num_crtc); 885c8b75bcaSEric Anholt if (ret < 0) { 886c8b75bcaSEric Anholt dev_err(dev->dev, "failed to initialize vblank\n"); 887c8b75bcaSEric Anholt return ret; 888c8b75bcaSEric Anholt } 889c8b75bcaSEric Anholt 890f437bc1eSMaxime Ripard if (is_vc5) { 891f437bc1eSMaxime Ripard dev->mode_config.max_width = 7680; 892f437bc1eSMaxime Ripard dev->mode_config.max_height = 7680; 893f437bc1eSMaxime Ripard } else { 894c8b75bcaSEric Anholt dev->mode_config.max_width = 2048; 895c8b75bcaSEric Anholt dev->mode_config.max_height = 2048; 896f437bc1eSMaxime Ripard } 897f437bc1eSMaxime Ripard 898c8b75bcaSEric Anholt dev->mode_config.funcs = &vc4_mode_funcs; 8999ec03d7fSMaxime Ripard dev->mode_config.helper_private = &vc4_mode_config_helpers; 900c8b75bcaSEric Anholt dev->mode_config.preferred_depth = 24; 901b501baccSEric Anholt dev->mode_config.async_page_flip = true; 902423ad7b3SDaniel Stone dev->mode_config.allow_fb_modifiers = true; 903b501baccSEric Anholt 904dcda7c28SMaxime Ripard ret = vc4_ctm_obj_init(vc4); 905dcda7c28SMaxime Ripard if (ret) 906dcda7c28SMaxime Ripard return ret; 907766cc6b1SStefan Schake 908dcda7c28SMaxime Ripard ret = vc4_load_tracker_obj_init(vc4); 909dcda7c28SMaxime Ripard if (ret) 910dcda7c28SMaxime Ripard return ret; 9114686da83SBoris Brezillon 912f2df84e0SMaxime Ripard ret = vc4_hvs_channels_obj_init(vc4); 913f2df84e0SMaxime Ripard if (ret) 914f2df84e0SMaxime Ripard return ret; 915f2df84e0SMaxime Ripard 916c8b75bcaSEric Anholt drm_mode_config_reset(dev); 917c8b75bcaSEric Anholt 918c8b75bcaSEric Anholt drm_kms_helper_poll_init(dev); 919c8b75bcaSEric Anholt 920c8b75bcaSEric Anholt return 0; 921c8b75bcaSEric Anholt } 922