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