162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2015 Broadcom 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/** 762306a36Sopenharmony_ci * DOC: VC4 KMS 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This is the general code for implementing KMS mode setting that 1062306a36Sopenharmony_ci * doesn't clearly associate with any of the other objects (plane, 1162306a36Sopenharmony_ci * crtc, HDMI encoder). 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/clk.h> 1562306a36Sopenharmony_ci#include <linux/sort.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1862306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 1962306a36Sopenharmony_ci#include <drm/drm_crtc.h> 2062306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2162306a36Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h> 2262306a36Sopenharmony_ci#include <drm/drm_probe_helper.h> 2362306a36Sopenharmony_ci#include <drm/drm_vblank.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "vc4_drv.h" 2662306a36Sopenharmony_ci#include "vc4_regs.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_cistruct vc4_ctm_state { 2962306a36Sopenharmony_ci struct drm_private_state base; 3062306a36Sopenharmony_ci struct drm_color_ctm *ctm; 3162306a36Sopenharmony_ci int fifo; 3262306a36Sopenharmony_ci}; 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define to_vc4_ctm_state(_state) \ 3562306a36Sopenharmony_ci container_of_const(_state, struct vc4_ctm_state, base) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct vc4_load_tracker_state { 3862306a36Sopenharmony_ci struct drm_private_state base; 3962306a36Sopenharmony_ci u64 hvs_load; 4062306a36Sopenharmony_ci u64 membus_load; 4162306a36Sopenharmony_ci}; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define to_vc4_load_tracker_state(_state) \ 4462306a36Sopenharmony_ci container_of_const(_state, struct vc4_load_tracker_state, base) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct vc4_ctm_state *vc4_get_ctm_state(struct drm_atomic_state *state, 4762306a36Sopenharmony_ci struct drm_private_obj *manager) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct drm_device *dev = state->dev; 5062306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 5162306a36Sopenharmony_ci struct drm_private_state *priv_state; 5262306a36Sopenharmony_ci int ret; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci ret = drm_modeset_lock(&vc4->ctm_state_lock, state->acquire_ctx); 5562306a36Sopenharmony_ci if (ret) 5662306a36Sopenharmony_ci return ERR_PTR(ret); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci priv_state = drm_atomic_get_private_obj_state(state, manager); 5962306a36Sopenharmony_ci if (IS_ERR(priv_state)) 6062306a36Sopenharmony_ci return ERR_CAST(priv_state); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return to_vc4_ctm_state(priv_state); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic struct drm_private_state * 6662306a36Sopenharmony_civc4_ctm_duplicate_state(struct drm_private_obj *obj) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct vc4_ctm_state *state; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); 7162306a36Sopenharmony_ci if (!state) 7262306a36Sopenharmony_ci return NULL; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci return &state->base; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic void vc4_ctm_destroy_state(struct drm_private_obj *obj, 8062306a36Sopenharmony_ci struct drm_private_state *state) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct vc4_ctm_state *ctm_state = to_vc4_ctm_state(state); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci kfree(ctm_state); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic const struct drm_private_state_funcs vc4_ctm_state_funcs = { 8862306a36Sopenharmony_ci .atomic_duplicate_state = vc4_ctm_duplicate_state, 8962306a36Sopenharmony_ci .atomic_destroy_state = vc4_ctm_destroy_state, 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic void vc4_ctm_obj_fini(struct drm_device *dev, void *unused) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci drm_atomic_private_obj_fini(&vc4->ctm_manager); 9762306a36Sopenharmony_ci} 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic int vc4_ctm_obj_init(struct vc4_dev *vc4) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct vc4_ctm_state *ctm_state; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci drm_modeset_lock_init(&vc4->ctm_state_lock); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci ctm_state = kzalloc(sizeof(*ctm_state), GFP_KERNEL); 10662306a36Sopenharmony_ci if (!ctm_state) 10762306a36Sopenharmony_ci return -ENOMEM; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci drm_atomic_private_obj_init(&vc4->base, &vc4->ctm_manager, &ctm_state->base, 11062306a36Sopenharmony_ci &vc4_ctm_state_funcs); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci return drmm_add_action_or_reset(&vc4->base, vc4_ctm_obj_fini, NULL); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* Converts a DRM S31.32 value to the HW S0.9 format. */ 11662306a36Sopenharmony_cistatic u16 vc4_ctm_s31_32_to_s0_9(u64 in) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci u16 r; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci /* Sign bit. */ 12162306a36Sopenharmony_ci r = in & BIT_ULL(63) ? BIT(9) : 0; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if ((in & GENMASK_ULL(62, 32)) > 0) { 12462306a36Sopenharmony_ci /* We have zero integer bits so we can only saturate here. */ 12562306a36Sopenharmony_ci r |= GENMASK(8, 0); 12662306a36Sopenharmony_ci } else { 12762306a36Sopenharmony_ci /* Otherwise take the 9 most important fractional bits. */ 12862306a36Sopenharmony_ci r |= (in >> 23) & GENMASK(8, 0); 12962306a36Sopenharmony_ci } 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return r; 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic void 13562306a36Sopenharmony_civc4_ctm_commit(struct vc4_dev *vc4, struct drm_atomic_state *state) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct vc4_hvs *hvs = vc4->hvs; 13862306a36Sopenharmony_ci struct vc4_ctm_state *ctm_state = to_vc4_ctm_state(vc4->ctm_manager.state); 13962306a36Sopenharmony_ci struct drm_color_ctm *ctm = ctm_state->ctm; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci if (ctm_state->fifo) { 14262306a36Sopenharmony_ci HVS_WRITE(SCALER_OLEDCOEF2, 14362306a36Sopenharmony_ci VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[0]), 14462306a36Sopenharmony_ci SCALER_OLEDCOEF2_R_TO_R) | 14562306a36Sopenharmony_ci VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[3]), 14662306a36Sopenharmony_ci SCALER_OLEDCOEF2_R_TO_G) | 14762306a36Sopenharmony_ci VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[6]), 14862306a36Sopenharmony_ci SCALER_OLEDCOEF2_R_TO_B)); 14962306a36Sopenharmony_ci HVS_WRITE(SCALER_OLEDCOEF1, 15062306a36Sopenharmony_ci VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[1]), 15162306a36Sopenharmony_ci SCALER_OLEDCOEF1_G_TO_R) | 15262306a36Sopenharmony_ci VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[4]), 15362306a36Sopenharmony_ci SCALER_OLEDCOEF1_G_TO_G) | 15462306a36Sopenharmony_ci VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[7]), 15562306a36Sopenharmony_ci SCALER_OLEDCOEF1_G_TO_B)); 15662306a36Sopenharmony_ci HVS_WRITE(SCALER_OLEDCOEF0, 15762306a36Sopenharmony_ci VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[2]), 15862306a36Sopenharmony_ci SCALER_OLEDCOEF0_B_TO_R) | 15962306a36Sopenharmony_ci VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[5]), 16062306a36Sopenharmony_ci SCALER_OLEDCOEF0_B_TO_G) | 16162306a36Sopenharmony_ci VC4_SET_FIELD(vc4_ctm_s31_32_to_s0_9(ctm->matrix[8]), 16262306a36Sopenharmony_ci SCALER_OLEDCOEF0_B_TO_B)); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci HVS_WRITE(SCALER_OLEDOFFS, 16662306a36Sopenharmony_ci VC4_SET_FIELD(ctm_state->fifo, SCALER_OLEDOFFS_DISPFIFO)); 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistruct vc4_hvs_state * 17062306a36Sopenharmony_civc4_hvs_get_new_global_state(const struct drm_atomic_state *state) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(state->dev); 17362306a36Sopenharmony_ci struct drm_private_state *priv_state; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci priv_state = drm_atomic_get_new_private_obj_state(state, &vc4->hvs_channels); 17662306a36Sopenharmony_ci if (!priv_state) 17762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci return to_vc4_hvs_state(priv_state); 18062306a36Sopenharmony_ci} 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistruct vc4_hvs_state * 18362306a36Sopenharmony_civc4_hvs_get_old_global_state(const struct drm_atomic_state *state) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(state->dev); 18662306a36Sopenharmony_ci struct drm_private_state *priv_state; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci priv_state = drm_atomic_get_old_private_obj_state(state, &vc4->hvs_channels); 18962306a36Sopenharmony_ci if (!priv_state) 19062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci return to_vc4_hvs_state(priv_state); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistruct vc4_hvs_state * 19662306a36Sopenharmony_civc4_hvs_get_global_state(struct drm_atomic_state *state) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(state->dev); 19962306a36Sopenharmony_ci struct drm_private_state *priv_state; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci priv_state = drm_atomic_get_private_obj_state(state, &vc4->hvs_channels); 20262306a36Sopenharmony_ci if (IS_ERR(priv_state)) 20362306a36Sopenharmony_ci return ERR_CAST(priv_state); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci return to_vc4_hvs_state(priv_state); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void vc4_hvs_pv_muxing_commit(struct vc4_dev *vc4, 20962306a36Sopenharmony_ci struct drm_atomic_state *state) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci struct vc4_hvs *hvs = vc4->hvs; 21262306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 21362306a36Sopenharmony_ci struct drm_crtc *crtc; 21462306a36Sopenharmony_ci unsigned int i; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 21762306a36Sopenharmony_ci struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); 21862306a36Sopenharmony_ci struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); 21962306a36Sopenharmony_ci u32 dispctrl; 22062306a36Sopenharmony_ci u32 dsp3_mux; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci if (!crtc_state->active) 22362306a36Sopenharmony_ci continue; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (vc4_state->assigned_channel != 2) 22662306a36Sopenharmony_ci continue; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * SCALER_DISPCTRL_DSP3 = X, where X < 2 means 'connect DSP3 to 23062306a36Sopenharmony_ci * FIFO X'. 23162306a36Sopenharmony_ci * SCALER_DISPCTRL_DSP3 = 3 means 'disable DSP 3'. 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * DSP3 is connected to FIFO2 unless the transposer is 23462306a36Sopenharmony_ci * enabled. In this case, FIFO 2 is directly accessed by the 23562306a36Sopenharmony_ci * TXP IP, and we need to disable the FIFO2 -> pixelvalve1 23662306a36Sopenharmony_ci * route. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if (vc4_crtc->feeds_txp) 23962306a36Sopenharmony_ci dsp3_mux = VC4_SET_FIELD(3, SCALER_DISPCTRL_DSP3_MUX); 24062306a36Sopenharmony_ci else 24162306a36Sopenharmony_ci dsp3_mux = VC4_SET_FIELD(2, SCALER_DISPCTRL_DSP3_MUX); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci dispctrl = HVS_READ(SCALER_DISPCTRL) & 24462306a36Sopenharmony_ci ~SCALER_DISPCTRL_DSP3_MUX_MASK; 24562306a36Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRL, dispctrl | dsp3_mux); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic void vc5_hvs_pv_muxing_commit(struct vc4_dev *vc4, 25062306a36Sopenharmony_ci struct drm_atomic_state *state) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct vc4_hvs *hvs = vc4->hvs; 25362306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 25462306a36Sopenharmony_ci struct drm_crtc *crtc; 25562306a36Sopenharmony_ci unsigned char mux; 25662306a36Sopenharmony_ci unsigned int i; 25762306a36Sopenharmony_ci u32 reg; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 26062306a36Sopenharmony_ci struct vc4_crtc_state *vc4_state = to_vc4_crtc_state(crtc_state); 26162306a36Sopenharmony_ci struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); 26262306a36Sopenharmony_ci unsigned int channel = vc4_state->assigned_channel; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (!vc4_state->update_muxing) 26562306a36Sopenharmony_ci continue; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci switch (vc4_crtc->data->hvs_output) { 26862306a36Sopenharmony_ci case 2: 26962306a36Sopenharmony_ci drm_WARN_ON(&vc4->base, 27062306a36Sopenharmony_ci VC4_GET_FIELD(HVS_READ(SCALER_DISPCTRL), 27162306a36Sopenharmony_ci SCALER_DISPCTRL_DSP3_MUX) == channel); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci mux = (channel == 2) ? 0 : 1; 27462306a36Sopenharmony_ci reg = HVS_READ(SCALER_DISPECTRL); 27562306a36Sopenharmony_ci HVS_WRITE(SCALER_DISPECTRL, 27662306a36Sopenharmony_ci (reg & ~SCALER_DISPECTRL_DSP2_MUX_MASK) | 27762306a36Sopenharmony_ci VC4_SET_FIELD(mux, SCALER_DISPECTRL_DSP2_MUX)); 27862306a36Sopenharmony_ci break; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci case 3: 28162306a36Sopenharmony_ci if (channel == VC4_HVS_CHANNEL_DISABLED) 28262306a36Sopenharmony_ci mux = 3; 28362306a36Sopenharmony_ci else 28462306a36Sopenharmony_ci mux = channel; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci reg = HVS_READ(SCALER_DISPCTRL); 28762306a36Sopenharmony_ci HVS_WRITE(SCALER_DISPCTRL, 28862306a36Sopenharmony_ci (reg & ~SCALER_DISPCTRL_DSP3_MUX_MASK) | 28962306a36Sopenharmony_ci VC4_SET_FIELD(mux, SCALER_DISPCTRL_DSP3_MUX)); 29062306a36Sopenharmony_ci break; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci case 4: 29362306a36Sopenharmony_ci if (channel == VC4_HVS_CHANNEL_DISABLED) 29462306a36Sopenharmony_ci mux = 3; 29562306a36Sopenharmony_ci else 29662306a36Sopenharmony_ci mux = channel; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci reg = HVS_READ(SCALER_DISPEOLN); 29962306a36Sopenharmony_ci HVS_WRITE(SCALER_DISPEOLN, 30062306a36Sopenharmony_ci (reg & ~SCALER_DISPEOLN_DSP4_MUX_MASK) | 30162306a36Sopenharmony_ci VC4_SET_FIELD(mux, SCALER_DISPEOLN_DSP4_MUX)); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci break; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci case 5: 30662306a36Sopenharmony_ci if (channel == VC4_HVS_CHANNEL_DISABLED) 30762306a36Sopenharmony_ci mux = 3; 30862306a36Sopenharmony_ci else 30962306a36Sopenharmony_ci mux = channel; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci reg = HVS_READ(SCALER_DISPDITHER); 31262306a36Sopenharmony_ci HVS_WRITE(SCALER_DISPDITHER, 31362306a36Sopenharmony_ci (reg & ~SCALER_DISPDITHER_DSP5_MUX_MASK) | 31462306a36Sopenharmony_ci VC4_SET_FIELD(mux, SCALER_DISPDITHER_DSP5_MUX)); 31562306a36Sopenharmony_ci break; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci default: 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci} 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic void vc4_atomic_commit_tail(struct drm_atomic_state *state) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct drm_device *dev = state->dev; 32662306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 32762306a36Sopenharmony_ci struct vc4_hvs *hvs = vc4->hvs; 32862306a36Sopenharmony_ci struct drm_crtc_state *new_crtc_state; 32962306a36Sopenharmony_ci struct vc4_hvs_state *new_hvs_state; 33062306a36Sopenharmony_ci struct drm_crtc *crtc; 33162306a36Sopenharmony_ci struct vc4_hvs_state *old_hvs_state; 33262306a36Sopenharmony_ci unsigned int channel; 33362306a36Sopenharmony_ci int i; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci old_hvs_state = vc4_hvs_get_old_global_state(state); 33662306a36Sopenharmony_ci if (WARN_ON(IS_ERR(old_hvs_state))) 33762306a36Sopenharmony_ci return; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci new_hvs_state = vc4_hvs_get_new_global_state(state); 34062306a36Sopenharmony_ci if (WARN_ON(IS_ERR(new_hvs_state))) 34162306a36Sopenharmony_ci return; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, new_crtc_state, i) { 34462306a36Sopenharmony_ci struct vc4_crtc_state *vc4_crtc_state; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!new_crtc_state->commit) 34762306a36Sopenharmony_ci continue; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci vc4_crtc_state = to_vc4_crtc_state(new_crtc_state); 35062306a36Sopenharmony_ci vc4_hvs_mask_underrun(hvs, vc4_crtc_state->assigned_channel); 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci for (channel = 0; channel < HVS_NUM_CHANNELS; channel++) { 35462306a36Sopenharmony_ci struct drm_crtc_commit *commit; 35562306a36Sopenharmony_ci int ret; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (!old_hvs_state->fifo_state[channel].in_use) 35862306a36Sopenharmony_ci continue; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci commit = old_hvs_state->fifo_state[channel].pending_commit; 36162306a36Sopenharmony_ci if (!commit) 36262306a36Sopenharmony_ci continue; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci ret = drm_crtc_commit_wait(commit); 36562306a36Sopenharmony_ci if (ret) 36662306a36Sopenharmony_ci drm_err(dev, "Timed out waiting for commit\n"); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci drm_crtc_commit_put(commit); 36962306a36Sopenharmony_ci old_hvs_state->fifo_state[channel].pending_commit = NULL; 37062306a36Sopenharmony_ci } 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (vc4->is_vc5) { 37362306a36Sopenharmony_ci unsigned long state_rate = max(old_hvs_state->core_clock_rate, 37462306a36Sopenharmony_ci new_hvs_state->core_clock_rate); 37562306a36Sopenharmony_ci unsigned long core_rate = clamp_t(unsigned long, state_rate, 37662306a36Sopenharmony_ci 500000000, hvs->max_core_rate); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci drm_dbg(dev, "Raising the core clock at %lu Hz\n", core_rate); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* 38162306a36Sopenharmony_ci * Do a temporary request on the core clock during the 38262306a36Sopenharmony_ci * modeset. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci WARN_ON(clk_set_min_rate(hvs->core_clk, core_rate)); 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci drm_atomic_helper_commit_modeset_disables(dev, state); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci vc4_ctm_commit(vc4, state); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (vc4->is_vc5) 39262306a36Sopenharmony_ci vc5_hvs_pv_muxing_commit(vc4, state); 39362306a36Sopenharmony_ci else 39462306a36Sopenharmony_ci vc4_hvs_pv_muxing_commit(vc4, state); 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci drm_atomic_helper_commit_planes(dev, state, 39762306a36Sopenharmony_ci DRM_PLANE_COMMIT_ACTIVE_ONLY); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci drm_atomic_helper_commit_modeset_enables(dev, state); 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci drm_atomic_helper_fake_vblank(state); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci drm_atomic_helper_commit_hw_done(state); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci drm_atomic_helper_wait_for_flip_done(dev, state); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci drm_atomic_helper_cleanup_planes(dev, state); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (vc4->is_vc5) { 41062306a36Sopenharmony_ci unsigned long core_rate = min_t(unsigned long, 41162306a36Sopenharmony_ci hvs->max_core_rate, 41262306a36Sopenharmony_ci new_hvs_state->core_clock_rate); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci drm_dbg(dev, "Running the core clock at %lu Hz\n", core_rate); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* 41762306a36Sopenharmony_ci * Request a clock rate based on the current HVS 41862306a36Sopenharmony_ci * requirements. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci WARN_ON(clk_set_min_rate(hvs->core_clk, core_rate)); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci drm_dbg(dev, "Core clock actual rate: %lu Hz\n", 42362306a36Sopenharmony_ci clk_get_rate(hvs->core_clk)); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic int vc4_atomic_commit_setup(struct drm_atomic_state *state) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci struct drm_crtc_state *crtc_state; 43062306a36Sopenharmony_ci struct vc4_hvs_state *hvs_state; 43162306a36Sopenharmony_ci struct drm_crtc *crtc; 43262306a36Sopenharmony_ci unsigned int i; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci hvs_state = vc4_hvs_get_new_global_state(state); 43562306a36Sopenharmony_ci if (WARN_ON(IS_ERR(hvs_state))) 43662306a36Sopenharmony_ci return PTR_ERR(hvs_state); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci for_each_new_crtc_in_state(state, crtc, crtc_state, i) { 43962306a36Sopenharmony_ci struct vc4_crtc_state *vc4_crtc_state = 44062306a36Sopenharmony_ci to_vc4_crtc_state(crtc_state); 44162306a36Sopenharmony_ci unsigned int channel = 44262306a36Sopenharmony_ci vc4_crtc_state->assigned_channel; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (channel == VC4_HVS_CHANNEL_DISABLED) 44562306a36Sopenharmony_ci continue; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci if (!hvs_state->fifo_state[channel].in_use) 44862306a36Sopenharmony_ci continue; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci hvs_state->fifo_state[channel].pending_commit = 45162306a36Sopenharmony_ci drm_crtc_commit_get(crtc_state->commit); 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic struct drm_framebuffer *vc4_fb_create(struct drm_device *dev, 45862306a36Sopenharmony_ci struct drm_file *file_priv, 45962306a36Sopenharmony_ci const struct drm_mode_fb_cmd2 *mode_cmd) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 46262306a36Sopenharmony_ci struct drm_mode_fb_cmd2 mode_cmd_local; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (WARN_ON_ONCE(vc4->is_vc5)) 46562306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci /* If the user didn't specify a modifier, use the 46862306a36Sopenharmony_ci * vc4_set_tiling_ioctl() state for the BO. 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci if (!(mode_cmd->flags & DRM_MODE_FB_MODIFIERS)) { 47162306a36Sopenharmony_ci struct drm_gem_object *gem_obj; 47262306a36Sopenharmony_ci struct vc4_bo *bo; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci gem_obj = drm_gem_object_lookup(file_priv, 47562306a36Sopenharmony_ci mode_cmd->handles[0]); 47662306a36Sopenharmony_ci if (!gem_obj) { 47762306a36Sopenharmony_ci DRM_DEBUG("Failed to look up GEM BO %d\n", 47862306a36Sopenharmony_ci mode_cmd->handles[0]); 47962306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci bo = to_vc4_bo(gem_obj); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci mode_cmd_local = *mode_cmd; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (bo->t_format) { 48662306a36Sopenharmony_ci mode_cmd_local.modifier[0] = 48762306a36Sopenharmony_ci DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED; 48862306a36Sopenharmony_ci } else { 48962306a36Sopenharmony_ci mode_cmd_local.modifier[0] = DRM_FORMAT_MOD_NONE; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci drm_gem_object_put(gem_obj); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci mode_cmd = &mode_cmd_local; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return drm_gem_fb_create(dev, file_priv, mode_cmd); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci/* Our CTM has some peculiar limitations: we can only enable it for one CRTC 50162306a36Sopenharmony_ci * at a time and the HW only supports S0.9 scalars. To account for the latter, 50262306a36Sopenharmony_ci * we don't allow userland to set a CTM that we have no hope of approximating. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_cistatic int 50562306a36Sopenharmony_civc4_ctm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) 50662306a36Sopenharmony_ci{ 50762306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 50862306a36Sopenharmony_ci struct vc4_ctm_state *ctm_state = NULL; 50962306a36Sopenharmony_ci struct drm_crtc *crtc; 51062306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state, *new_crtc_state; 51162306a36Sopenharmony_ci struct drm_color_ctm *ctm; 51262306a36Sopenharmony_ci int i; 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 51562306a36Sopenharmony_ci /* CTM is being disabled. */ 51662306a36Sopenharmony_ci if (!new_crtc_state->ctm && old_crtc_state->ctm) { 51762306a36Sopenharmony_ci ctm_state = vc4_get_ctm_state(state, &vc4->ctm_manager); 51862306a36Sopenharmony_ci if (IS_ERR(ctm_state)) 51962306a36Sopenharmony_ci return PTR_ERR(ctm_state); 52062306a36Sopenharmony_ci ctm_state->fifo = 0; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci } 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { 52562306a36Sopenharmony_ci if (new_crtc_state->ctm == old_crtc_state->ctm) 52662306a36Sopenharmony_ci continue; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci if (!ctm_state) { 52962306a36Sopenharmony_ci ctm_state = vc4_get_ctm_state(state, &vc4->ctm_manager); 53062306a36Sopenharmony_ci if (IS_ERR(ctm_state)) 53162306a36Sopenharmony_ci return PTR_ERR(ctm_state); 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci /* CTM is being enabled or the matrix changed. */ 53562306a36Sopenharmony_ci if (new_crtc_state->ctm) { 53662306a36Sopenharmony_ci struct vc4_crtc_state *vc4_crtc_state = 53762306a36Sopenharmony_ci to_vc4_crtc_state(new_crtc_state); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* fifo is 1-based since 0 disables CTM. */ 54062306a36Sopenharmony_ci int fifo = vc4_crtc_state->assigned_channel + 1; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci /* Check userland isn't trying to turn on CTM for more 54362306a36Sopenharmony_ci * than one CRTC at a time. 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci if (ctm_state->fifo && ctm_state->fifo != fifo) { 54662306a36Sopenharmony_ci DRM_DEBUG_DRIVER("Too many CTM configured\n"); 54762306a36Sopenharmony_ci return -EINVAL; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Check we can approximate the specified CTM. 55162306a36Sopenharmony_ci * We disallow scalars |c| > 1.0 since the HW has 55262306a36Sopenharmony_ci * no integer bits. 55362306a36Sopenharmony_ci */ 55462306a36Sopenharmony_ci ctm = new_crtc_state->ctm->data; 55562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(ctm->matrix); i++) { 55662306a36Sopenharmony_ci u64 val = ctm->matrix[i]; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci val &= ~BIT_ULL(63); 55962306a36Sopenharmony_ci if (val > BIT_ULL(32)) 56062306a36Sopenharmony_ci return -EINVAL; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ctm_state->fifo = fifo; 56462306a36Sopenharmony_ci ctm_state->ctm = ctm; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci return 0; 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int vc4_load_tracker_atomic_check(struct drm_atomic_state *state) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct drm_plane_state *old_plane_state, *new_plane_state; 57462306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(state->dev); 57562306a36Sopenharmony_ci struct vc4_load_tracker_state *load_state; 57662306a36Sopenharmony_ci struct drm_private_state *priv_state; 57762306a36Sopenharmony_ci struct drm_plane *plane; 57862306a36Sopenharmony_ci int i; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci priv_state = drm_atomic_get_private_obj_state(state, 58162306a36Sopenharmony_ci &vc4->load_tracker); 58262306a36Sopenharmony_ci if (IS_ERR(priv_state)) 58362306a36Sopenharmony_ci return PTR_ERR(priv_state); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci load_state = to_vc4_load_tracker_state(priv_state); 58662306a36Sopenharmony_ci for_each_oldnew_plane_in_state(state, plane, old_plane_state, 58762306a36Sopenharmony_ci new_plane_state, i) { 58862306a36Sopenharmony_ci struct vc4_plane_state *vc4_plane_state; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (old_plane_state->fb && old_plane_state->crtc) { 59162306a36Sopenharmony_ci vc4_plane_state = to_vc4_plane_state(old_plane_state); 59262306a36Sopenharmony_ci load_state->membus_load -= vc4_plane_state->membus_load; 59362306a36Sopenharmony_ci load_state->hvs_load -= vc4_plane_state->hvs_load; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (new_plane_state->fb && new_plane_state->crtc) { 59762306a36Sopenharmony_ci vc4_plane_state = to_vc4_plane_state(new_plane_state); 59862306a36Sopenharmony_ci load_state->membus_load += vc4_plane_state->membus_load; 59962306a36Sopenharmony_ci load_state->hvs_load += vc4_plane_state->hvs_load; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* Don't check the load when the tracker is disabled. */ 60462306a36Sopenharmony_ci if (!vc4->load_tracker_enabled) 60562306a36Sopenharmony_ci return 0; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci /* The absolute limit is 2Gbyte/sec, but let's take a margin to let 60862306a36Sopenharmony_ci * the system work when other blocks are accessing the memory. 60962306a36Sopenharmony_ci */ 61062306a36Sopenharmony_ci if (load_state->membus_load > SZ_1G + SZ_512M) 61162306a36Sopenharmony_ci return -ENOSPC; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_ci /* HVS clock is supposed to run @ 250Mhz, let's take a margin and 61462306a36Sopenharmony_ci * consider the maximum number of cycles is 240M. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci if (load_state->hvs_load > 240000000ULL) 61762306a36Sopenharmony_ci return -ENOSPC; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci return 0; 62062306a36Sopenharmony_ci} 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_cistatic struct drm_private_state * 62362306a36Sopenharmony_civc4_load_tracker_duplicate_state(struct drm_private_obj *obj) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct vc4_load_tracker_state *state; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); 62862306a36Sopenharmony_ci if (!state) 62962306a36Sopenharmony_ci return NULL; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return &state->base; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic void vc4_load_tracker_destroy_state(struct drm_private_obj *obj, 63762306a36Sopenharmony_ci struct drm_private_state *state) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci struct vc4_load_tracker_state *load_state; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci load_state = to_vc4_load_tracker_state(state); 64262306a36Sopenharmony_ci kfree(load_state); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic const struct drm_private_state_funcs vc4_load_tracker_state_funcs = { 64662306a36Sopenharmony_ci .atomic_duplicate_state = vc4_load_tracker_duplicate_state, 64762306a36Sopenharmony_ci .atomic_destroy_state = vc4_load_tracker_destroy_state, 64862306a36Sopenharmony_ci}; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic void vc4_load_tracker_obj_fini(struct drm_device *dev, void *unused) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci drm_atomic_private_obj_fini(&vc4->load_tracker); 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic int vc4_load_tracker_obj_init(struct vc4_dev *vc4) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci struct vc4_load_tracker_state *load_state; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci load_state = kzalloc(sizeof(*load_state), GFP_KERNEL); 66262306a36Sopenharmony_ci if (!load_state) 66362306a36Sopenharmony_ci return -ENOMEM; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci drm_atomic_private_obj_init(&vc4->base, &vc4->load_tracker, 66662306a36Sopenharmony_ci &load_state->base, 66762306a36Sopenharmony_ci &vc4_load_tracker_state_funcs); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci return drmm_add_action_or_reset(&vc4->base, vc4_load_tracker_obj_fini, NULL); 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic struct drm_private_state * 67362306a36Sopenharmony_civc4_hvs_channels_duplicate_state(struct drm_private_obj *obj) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct vc4_hvs_state *old_state = to_vc4_hvs_state(obj->state); 67662306a36Sopenharmony_ci struct vc4_hvs_state *state; 67762306a36Sopenharmony_ci unsigned int i; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 68062306a36Sopenharmony_ci if (!state) 68162306a36Sopenharmony_ci return NULL; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci for (i = 0; i < HVS_NUM_CHANNELS; i++) { 68662306a36Sopenharmony_ci state->fifo_state[i].in_use = old_state->fifo_state[i].in_use; 68762306a36Sopenharmony_ci state->fifo_state[i].fifo_load = old_state->fifo_state[i].fifo_load; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci state->core_clock_rate = old_state->core_clock_rate; 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return &state->base; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic void vc4_hvs_channels_destroy_state(struct drm_private_obj *obj, 69662306a36Sopenharmony_ci struct drm_private_state *state) 69762306a36Sopenharmony_ci{ 69862306a36Sopenharmony_ci struct vc4_hvs_state *hvs_state = to_vc4_hvs_state(state); 69962306a36Sopenharmony_ci unsigned int i; 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci for (i = 0; i < HVS_NUM_CHANNELS; i++) { 70262306a36Sopenharmony_ci if (!hvs_state->fifo_state[i].pending_commit) 70362306a36Sopenharmony_ci continue; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci drm_crtc_commit_put(hvs_state->fifo_state[i].pending_commit); 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci kfree(hvs_state); 70962306a36Sopenharmony_ci} 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic void vc4_hvs_channels_print_state(struct drm_printer *p, 71262306a36Sopenharmony_ci const struct drm_private_state *state) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci const struct vc4_hvs_state *hvs_state = to_vc4_hvs_state(state); 71562306a36Sopenharmony_ci unsigned int i; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci drm_printf(p, "HVS State\n"); 71862306a36Sopenharmony_ci drm_printf(p, "\tCore Clock Rate: %lu\n", hvs_state->core_clock_rate); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci for (i = 0; i < HVS_NUM_CHANNELS; i++) { 72162306a36Sopenharmony_ci drm_printf(p, "\tChannel %d\n", i); 72262306a36Sopenharmony_ci drm_printf(p, "\t\tin use=%d\n", hvs_state->fifo_state[i].in_use); 72362306a36Sopenharmony_ci drm_printf(p, "\t\tload=%lu\n", hvs_state->fifo_state[i].fifo_load); 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic const struct drm_private_state_funcs vc4_hvs_state_funcs = { 72862306a36Sopenharmony_ci .atomic_duplicate_state = vc4_hvs_channels_duplicate_state, 72962306a36Sopenharmony_ci .atomic_destroy_state = vc4_hvs_channels_destroy_state, 73062306a36Sopenharmony_ci .atomic_print_state = vc4_hvs_channels_print_state, 73162306a36Sopenharmony_ci}; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic void vc4_hvs_channels_obj_fini(struct drm_device *dev, void *unused) 73462306a36Sopenharmony_ci{ 73562306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci drm_atomic_private_obj_fini(&vc4->hvs_channels); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic int vc4_hvs_channels_obj_init(struct vc4_dev *vc4) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci struct vc4_hvs_state *state; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci state = kzalloc(sizeof(*state), GFP_KERNEL); 74562306a36Sopenharmony_ci if (!state) 74662306a36Sopenharmony_ci return -ENOMEM; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci drm_atomic_private_obj_init(&vc4->base, &vc4->hvs_channels, 74962306a36Sopenharmony_ci &state->base, 75062306a36Sopenharmony_ci &vc4_hvs_state_funcs); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci return drmm_add_action_or_reset(&vc4->base, vc4_hvs_channels_obj_fini, NULL); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int cmp_vc4_crtc_hvs_output(const void *a, const void *b) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci const struct vc4_crtc *crtc_a = 75862306a36Sopenharmony_ci to_vc4_crtc(*(const struct drm_crtc **)a); 75962306a36Sopenharmony_ci const struct vc4_crtc_data *data_a = 76062306a36Sopenharmony_ci vc4_crtc_to_vc4_crtc_data(crtc_a); 76162306a36Sopenharmony_ci const struct vc4_crtc *crtc_b = 76262306a36Sopenharmony_ci to_vc4_crtc(*(const struct drm_crtc **)b); 76362306a36Sopenharmony_ci const struct vc4_crtc_data *data_b = 76462306a36Sopenharmony_ci vc4_crtc_to_vc4_crtc_data(crtc_b); 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci return data_a->hvs_output - data_b->hvs_output; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci/* 77062306a36Sopenharmony_ci * The BCM2711 HVS has up to 7 outputs connected to the pixelvalves and 77162306a36Sopenharmony_ci * the TXP (and therefore all the CRTCs found on that platform). 77262306a36Sopenharmony_ci * 77362306a36Sopenharmony_ci * The naive (and our initial) implementation would just iterate over 77462306a36Sopenharmony_ci * all the active CRTCs, try to find a suitable FIFO, and then remove it 77562306a36Sopenharmony_ci * from the pool of available FIFOs. However, there are a few corner 77662306a36Sopenharmony_ci * cases that need to be considered: 77762306a36Sopenharmony_ci * 77862306a36Sopenharmony_ci * - When running in a dual-display setup (so with two CRTCs involved), 77962306a36Sopenharmony_ci * we can update the state of a single CRTC (for example by changing 78062306a36Sopenharmony_ci * its mode using xrandr under X11) without affecting the other. In 78162306a36Sopenharmony_ci * this case, the other CRTC wouldn't be in the state at all, so we 78262306a36Sopenharmony_ci * need to consider all the running CRTCs in the DRM device to assign 78362306a36Sopenharmony_ci * a FIFO, not just the one in the state. 78462306a36Sopenharmony_ci * 78562306a36Sopenharmony_ci * - To fix the above, we can't use drm_atomic_get_crtc_state on all 78662306a36Sopenharmony_ci * enabled CRTCs to pull their CRTC state into the global state, since 78762306a36Sopenharmony_ci * a page flip would start considering their vblank to complete. Since 78862306a36Sopenharmony_ci * we don't have a guarantee that they are actually active, that 78962306a36Sopenharmony_ci * vblank might never happen, and shouldn't even be considered if we 79062306a36Sopenharmony_ci * want to do a page flip on a single CRTC. That can be tested by 79162306a36Sopenharmony_ci * doing a modetest -v first on HDMI1 and then on HDMI0. 79262306a36Sopenharmony_ci * 79362306a36Sopenharmony_ci * - Since we need the pixelvalve to be disabled and enabled back when 79462306a36Sopenharmony_ci * the FIFO is changed, we should keep the FIFO assigned for as long 79562306a36Sopenharmony_ci * as the CRTC is enabled, only considering it free again once that 79662306a36Sopenharmony_ci * CRTC has been disabled. This can be tested by booting X11 on a 79762306a36Sopenharmony_ci * single display, and changing the resolution down and then back up. 79862306a36Sopenharmony_ci */ 79962306a36Sopenharmony_cistatic int vc4_pv_muxing_atomic_check(struct drm_device *dev, 80062306a36Sopenharmony_ci struct drm_atomic_state *state) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct vc4_hvs_state *hvs_new_state; 80362306a36Sopenharmony_ci struct drm_crtc **sorted_crtcs; 80462306a36Sopenharmony_ci struct drm_crtc *crtc; 80562306a36Sopenharmony_ci unsigned int unassigned_channels = 0; 80662306a36Sopenharmony_ci unsigned int i; 80762306a36Sopenharmony_ci int ret; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci hvs_new_state = vc4_hvs_get_global_state(state); 81062306a36Sopenharmony_ci if (IS_ERR(hvs_new_state)) 81162306a36Sopenharmony_ci return PTR_ERR(hvs_new_state); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hvs_new_state->fifo_state); i++) 81462306a36Sopenharmony_ci if (!hvs_new_state->fifo_state[i].in_use) 81562306a36Sopenharmony_ci unassigned_channels |= BIT(i); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci /* 81862306a36Sopenharmony_ci * The problem we have to solve here is that we have up to 7 81962306a36Sopenharmony_ci * encoders, connected to up to 6 CRTCs. 82062306a36Sopenharmony_ci * 82162306a36Sopenharmony_ci * Those CRTCs, depending on the instance, can be routed to 1, 2 82262306a36Sopenharmony_ci * or 3 HVS FIFOs, and we need to set the muxing between FIFOs and 82362306a36Sopenharmony_ci * outputs in the HVS accordingly. 82462306a36Sopenharmony_ci * 82562306a36Sopenharmony_ci * It would be pretty hard to come up with an algorithm that 82662306a36Sopenharmony_ci * would generically solve this. However, the current routing 82762306a36Sopenharmony_ci * trees we support allow us to simplify a bit the problem. 82862306a36Sopenharmony_ci * 82962306a36Sopenharmony_ci * Indeed, with the current supported layouts, if we try to 83062306a36Sopenharmony_ci * assign in the ascending crtc index order the FIFOs, we can't 83162306a36Sopenharmony_ci * fall into the situation where an earlier CRTC that had 83262306a36Sopenharmony_ci * multiple routes is assigned one that was the only option for 83362306a36Sopenharmony_ci * a later CRTC. 83462306a36Sopenharmony_ci * 83562306a36Sopenharmony_ci * If the layout changes and doesn't give us that in the future, 83662306a36Sopenharmony_ci * we will need to have something smarter, but it works so far. 83762306a36Sopenharmony_ci */ 83862306a36Sopenharmony_ci sorted_crtcs = kmalloc_array(dev->num_crtcs, sizeof(*sorted_crtcs), GFP_KERNEL); 83962306a36Sopenharmony_ci if (!sorted_crtcs) 84062306a36Sopenharmony_ci return -ENOMEM; 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci i = 0; 84362306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) 84462306a36Sopenharmony_ci sorted_crtcs[i++] = crtc; 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci sort(sorted_crtcs, i, sizeof(*sorted_crtcs), cmp_vc4_crtc_hvs_output, NULL); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci for (i = 0; i < dev->num_crtcs; i++) { 84962306a36Sopenharmony_ci struct vc4_crtc_state *old_vc4_crtc_state, *new_vc4_crtc_state; 85062306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state, *new_crtc_state; 85162306a36Sopenharmony_ci struct vc4_crtc *vc4_crtc; 85262306a36Sopenharmony_ci unsigned int matching_channels; 85362306a36Sopenharmony_ci unsigned int channel; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci crtc = sorted_crtcs[i]; 85662306a36Sopenharmony_ci if (!crtc) 85762306a36Sopenharmony_ci continue; 85862306a36Sopenharmony_ci vc4_crtc = to_vc4_crtc(crtc); 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc); 86162306a36Sopenharmony_ci if (!old_crtc_state) 86262306a36Sopenharmony_ci continue; 86362306a36Sopenharmony_ci old_vc4_crtc_state = to_vc4_crtc_state(old_crtc_state); 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci new_crtc_state = drm_atomic_get_new_crtc_state(state, crtc); 86662306a36Sopenharmony_ci if (!new_crtc_state) 86762306a36Sopenharmony_ci continue; 86862306a36Sopenharmony_ci new_vc4_crtc_state = to_vc4_crtc_state(new_crtc_state); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci drm_dbg(dev, "%s: Trying to find a channel.\n", crtc->name); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci /* Nothing to do here, let's skip it */ 87362306a36Sopenharmony_ci if (old_crtc_state->enable == new_crtc_state->enable) { 87462306a36Sopenharmony_ci if (new_crtc_state->enable) 87562306a36Sopenharmony_ci drm_dbg(dev, "%s: Already enabled, reusing channel %d.\n", 87662306a36Sopenharmony_ci crtc->name, new_vc4_crtc_state->assigned_channel); 87762306a36Sopenharmony_ci else 87862306a36Sopenharmony_ci drm_dbg(dev, "%s: Disabled, ignoring.\n", crtc->name); 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci continue; 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci /* Muxing will need to be modified, mark it as such */ 88462306a36Sopenharmony_ci new_vc4_crtc_state->update_muxing = true; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /* If we're disabling our CRTC, we put back our channel */ 88762306a36Sopenharmony_ci if (!new_crtc_state->enable) { 88862306a36Sopenharmony_ci channel = old_vc4_crtc_state->assigned_channel; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci drm_dbg(dev, "%s: Disabling, Freeing channel %d\n", 89162306a36Sopenharmony_ci crtc->name, channel); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci hvs_new_state->fifo_state[channel].in_use = false; 89462306a36Sopenharmony_ci new_vc4_crtc_state->assigned_channel = VC4_HVS_CHANNEL_DISABLED; 89562306a36Sopenharmony_ci continue; 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci matching_channels = unassigned_channels & vc4_crtc->data->hvs_available_channels; 89962306a36Sopenharmony_ci if (!matching_channels) { 90062306a36Sopenharmony_ci ret = -EINVAL; 90162306a36Sopenharmony_ci goto err_free_crtc_array; 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci channel = ffs(matching_channels) - 1; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci drm_dbg(dev, "Assigned HVS channel %d to CRTC %s\n", channel, crtc->name); 90762306a36Sopenharmony_ci new_vc4_crtc_state->assigned_channel = channel; 90862306a36Sopenharmony_ci unassigned_channels &= ~BIT(channel); 90962306a36Sopenharmony_ci hvs_new_state->fifo_state[channel].in_use = true; 91062306a36Sopenharmony_ci } 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci kfree(sorted_crtcs); 91362306a36Sopenharmony_ci return 0; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cierr_free_crtc_array: 91662306a36Sopenharmony_ci kfree(sorted_crtcs); 91762306a36Sopenharmony_ci return ret; 91862306a36Sopenharmony_ci} 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_cistatic int 92162306a36Sopenharmony_civc4_core_clock_atomic_check(struct drm_atomic_state *state) 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(state->dev); 92462306a36Sopenharmony_ci struct drm_private_state *priv_state; 92562306a36Sopenharmony_ci struct vc4_hvs_state *hvs_new_state; 92662306a36Sopenharmony_ci struct vc4_load_tracker_state *load_state; 92762306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state, *new_crtc_state; 92862306a36Sopenharmony_ci struct drm_crtc *crtc; 92962306a36Sopenharmony_ci unsigned int num_outputs; 93062306a36Sopenharmony_ci unsigned long pixel_rate; 93162306a36Sopenharmony_ci unsigned long cob_rate; 93262306a36Sopenharmony_ci unsigned int i; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci priv_state = drm_atomic_get_private_obj_state(state, 93562306a36Sopenharmony_ci &vc4->load_tracker); 93662306a36Sopenharmony_ci if (IS_ERR(priv_state)) 93762306a36Sopenharmony_ci return PTR_ERR(priv_state); 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci load_state = to_vc4_load_tracker_state(priv_state); 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci hvs_new_state = vc4_hvs_get_global_state(state); 94262306a36Sopenharmony_ci if (IS_ERR(hvs_new_state)) 94362306a36Sopenharmony_ci return PTR_ERR(hvs_new_state); 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci for_each_oldnew_crtc_in_state(state, crtc, 94662306a36Sopenharmony_ci old_crtc_state, 94762306a36Sopenharmony_ci new_crtc_state, 94862306a36Sopenharmony_ci i) { 94962306a36Sopenharmony_ci if (old_crtc_state->active) { 95062306a36Sopenharmony_ci struct vc4_crtc_state *old_vc4_state = 95162306a36Sopenharmony_ci to_vc4_crtc_state(old_crtc_state); 95262306a36Sopenharmony_ci unsigned int channel = old_vc4_state->assigned_channel; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci hvs_new_state->fifo_state[channel].fifo_load = 0; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (new_crtc_state->active) { 95862306a36Sopenharmony_ci struct vc4_crtc_state *new_vc4_state = 95962306a36Sopenharmony_ci to_vc4_crtc_state(new_crtc_state); 96062306a36Sopenharmony_ci unsigned int channel = new_vc4_state->assigned_channel; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci hvs_new_state->fifo_state[channel].fifo_load = 96362306a36Sopenharmony_ci new_vc4_state->hvs_load; 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci } 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci cob_rate = 0; 96862306a36Sopenharmony_ci num_outputs = 0; 96962306a36Sopenharmony_ci for (i = 0; i < HVS_NUM_CHANNELS; i++) { 97062306a36Sopenharmony_ci if (!hvs_new_state->fifo_state[i].in_use) 97162306a36Sopenharmony_ci continue; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci num_outputs++; 97462306a36Sopenharmony_ci cob_rate = max_t(unsigned long, 97562306a36Sopenharmony_ci hvs_new_state->fifo_state[i].fifo_load, 97662306a36Sopenharmony_ci cob_rate); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci pixel_rate = load_state->hvs_load; 98062306a36Sopenharmony_ci if (num_outputs > 1) { 98162306a36Sopenharmony_ci pixel_rate = (pixel_rate * 40) / 100; 98262306a36Sopenharmony_ci } else { 98362306a36Sopenharmony_ci pixel_rate = (pixel_rate * 60) / 100; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci hvs_new_state->core_clock_rate = max(cob_rate, pixel_rate); 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci return 0; 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci 99262306a36Sopenharmony_cistatic int 99362306a36Sopenharmony_civc4_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci int ret; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci ret = vc4_pv_muxing_atomic_check(dev, state); 99862306a36Sopenharmony_ci if (ret) 99962306a36Sopenharmony_ci return ret; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci ret = vc4_ctm_atomic_check(dev, state); 100262306a36Sopenharmony_ci if (ret < 0) 100362306a36Sopenharmony_ci return ret; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci ret = drm_atomic_helper_check(dev, state); 100662306a36Sopenharmony_ci if (ret) 100762306a36Sopenharmony_ci return ret; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci ret = vc4_load_tracker_atomic_check(state); 101062306a36Sopenharmony_ci if (ret) 101162306a36Sopenharmony_ci return ret; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci return vc4_core_clock_atomic_check(state); 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic struct drm_mode_config_helper_funcs vc4_mode_config_helpers = { 101762306a36Sopenharmony_ci .atomic_commit_setup = vc4_atomic_commit_setup, 101862306a36Sopenharmony_ci .atomic_commit_tail = vc4_atomic_commit_tail, 101962306a36Sopenharmony_ci}; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic const struct drm_mode_config_funcs vc4_mode_funcs = { 102262306a36Sopenharmony_ci .atomic_check = vc4_atomic_check, 102362306a36Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 102462306a36Sopenharmony_ci .fb_create = vc4_fb_create, 102562306a36Sopenharmony_ci}; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic const struct drm_mode_config_funcs vc5_mode_funcs = { 102862306a36Sopenharmony_ci .atomic_check = vc4_atomic_check, 102962306a36Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 103062306a36Sopenharmony_ci .fb_create = drm_gem_fb_create, 103162306a36Sopenharmony_ci}; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ciint vc4_kms_load(struct drm_device *dev) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct vc4_dev *vc4 = to_vc4_dev(dev); 103662306a36Sopenharmony_ci int ret; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci /* 103962306a36Sopenharmony_ci * The limits enforced by the load tracker aren't relevant for 104062306a36Sopenharmony_ci * the BCM2711, but the load tracker computations are used for 104162306a36Sopenharmony_ci * the core clock rate calculation. 104262306a36Sopenharmony_ci */ 104362306a36Sopenharmony_ci if (!vc4->is_vc5) { 104462306a36Sopenharmony_ci /* Start with the load tracker enabled. Can be 104562306a36Sopenharmony_ci * disabled through the debugfs load_tracker file. 104662306a36Sopenharmony_ci */ 104762306a36Sopenharmony_ci vc4->load_tracker_enabled = true; 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci /* Set support for vblank irq fast disable, before drm_vblank_init() */ 105162306a36Sopenharmony_ci dev->vblank_disable_immediate = true; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci ret = drm_vblank_init(dev, dev->mode_config.num_crtc); 105462306a36Sopenharmony_ci if (ret < 0) { 105562306a36Sopenharmony_ci dev_err(dev->dev, "failed to initialize vblank\n"); 105662306a36Sopenharmony_ci return ret; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci if (vc4->is_vc5) { 106062306a36Sopenharmony_ci dev->mode_config.max_width = 7680; 106162306a36Sopenharmony_ci dev->mode_config.max_height = 7680; 106262306a36Sopenharmony_ci } else { 106362306a36Sopenharmony_ci dev->mode_config.max_width = 2048; 106462306a36Sopenharmony_ci dev->mode_config.max_height = 2048; 106562306a36Sopenharmony_ci } 106662306a36Sopenharmony_ci 106762306a36Sopenharmony_ci dev->mode_config.funcs = vc4->is_vc5 ? &vc5_mode_funcs : &vc4_mode_funcs; 106862306a36Sopenharmony_ci dev->mode_config.helper_private = &vc4_mode_config_helpers; 106962306a36Sopenharmony_ci dev->mode_config.preferred_depth = 24; 107062306a36Sopenharmony_ci dev->mode_config.async_page_flip = true; 107162306a36Sopenharmony_ci dev->mode_config.normalize_zpos = true; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci ret = vc4_ctm_obj_init(vc4); 107462306a36Sopenharmony_ci if (ret) 107562306a36Sopenharmony_ci return ret; 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci ret = vc4_load_tracker_obj_init(vc4); 107862306a36Sopenharmony_ci if (ret) 107962306a36Sopenharmony_ci return ret; 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci ret = vc4_hvs_channels_obj_init(vc4); 108262306a36Sopenharmony_ci if (ret) 108362306a36Sopenharmony_ci return ret; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci drm_mode_config_reset(dev); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci drm_kms_helper_poll_init(dev); 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci return 0; 109062306a36Sopenharmony_ci} 1091