File Index
Files and directories
Sign in
FILE

dpu_rm.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/msm/disp/dpu1/dpu_rm.c C
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
   4 * Copyright (c) 2023-2024 Qualcomm Innovation Center, Inc. All rights reserved.
   5 */
   6
   7#define pr_fmt(fmt)	"[drm:%s] " fmt, __func__
   8#include "dpu_kms.h"
   9#include "dpu_hw_lm.h"
  10#include "dpu_hw_ctl.h"
  11#include "dpu_hw_cdm.h"
  12#include "dpu_hw_cwb.h"
  13#include "dpu_hw_pingpong.h"
  14#include "dpu_hw_sspp.h"
  15#include "dpu_hw_intf.h"
  16#include "dpu_hw_wb.h"
  17#include "dpu_hw_dspp.h"
  18#include "dpu_hw_merge3d.h"
  19#include "dpu_hw_dsc.h"
  20#include "dpu_encoder.h"
  21#include "dpu_trace.h"
  22
  23
  24static inline bool reserved_by_other(uint32_t *res_map, int idx,
  25				     uint32_t crtc_id)
  26{
  27	return res_map[idx] && res_map[idx] != crtc_id;
  28}
  29
  30/**
  31 * dpu_rm_init - Read hardware catalog and create reservation tracking objects
  32 *	for all HW blocks.
  33 * @dev:  Corresponding device for devres management
  34 * @rm: DPU Resource Manager handle
  35 * @cat: Pointer to hardware catalog
  36 * @mdss_data: Pointer to MDSS / UBWC configuration
  37 * @mmio: mapped register io address of MDP
  38 * @return: 0 on Success otherwise -ERROR
  39 */
  40int dpu_rm_init(struct drm_device *dev,
  41		struct dpu_rm *rm,
  42		const struct dpu_mdss_cfg *cat,
  43		const struct qcom_ubwc_cfg_data *mdss_data,
  44		void __iomem *mmio)
  45{
  46	int rc, i;
  47
  48	if (!rm || !cat || !mmio) {
  49		DPU_ERROR("invalid kms\n");
  50		return -EINVAL;
  51	}
  52
  53	/* Clear, setup lists */
  54	memset(rm, 0, sizeof(*rm));
  55
  56	rm->has_legacy_ctls = (cat->mdss_ver->core_major_ver < 5);
  57
  58	/* Interrogate HW catalog and create tracking items for hw blocks */
  59	for (i = 0; i < cat->mixer_count; i++) {
  60		struct dpu_hw_mixer *hw;
  61		const struct dpu_lm_cfg *lm = &cat->mixer[i];
  62
  63		hw = dpu_hw_lm_init(dev, lm, mmio, cat->mdss_ver);
  64		if (IS_ERR(hw)) {
  65			rc = PTR_ERR(hw);
  66			DPU_ERROR("failed lm object creation: err %d\n", rc);
  67			goto fail;
  68		}
  69		rm->mixer_blks[lm->id - LM_0] = &hw->base;
  70	}
  71
  72	for (i = 0; i < cat->merge_3d_count; i++) {
  73		struct dpu_hw_merge_3d *hw;
  74		const struct dpu_merge_3d_cfg *merge_3d = &cat->merge_3d[i];
  75
  76		hw = dpu_hw_merge_3d_init(dev, merge_3d, mmio);
  77		if (IS_ERR(hw)) {
  78			rc = PTR_ERR(hw);
  79			DPU_ERROR("failed merge_3d object creation: err %d\n",
  80				rc);
  81			goto fail;
  82		}
  83		rm->merge_3d_blks[merge_3d->id - MERGE_3D_0] = &hw->base;
  84	}
  85
  86	for (i = 0; i < cat->pingpong_count; i++) {
  87		struct dpu_hw_pingpong *hw;
  88		const struct dpu_pingpong_cfg *pp = &cat->pingpong[i];
  89
  90		hw = dpu_hw_pingpong_init(dev, pp, mmio, cat->mdss_ver);
  91		if (IS_ERR(hw)) {
  92			rc = PTR_ERR(hw);
  93			DPU_ERROR("failed pingpong object creation: err %d\n",
  94				rc);
  95			goto fail;
  96		}
  97		if (pp->merge_3d && pp->merge_3d < MERGE_3D_MAX)
  98			hw->merge_3d = to_dpu_hw_merge_3d(rm->merge_3d_blks[pp->merge_3d - MERGE_3D_0]);
  99		rm->pingpong_blks[pp->id - PINGPONG_0] = &hw->base;
 100	}
 101
 102	for (i = 0; i < cat->intf_count; i++) {
 103		struct dpu_hw_intf *hw;
 104		const struct dpu_intf_cfg *intf = &cat->intf[i];
 105
 106		hw = dpu_hw_intf_init(dev, intf, mmio, cat->mdss_ver);
 107		if (IS_ERR(hw)) {
 108			rc = PTR_ERR(hw);
 109			DPU_ERROR("failed intf object creation: err %d\n", rc);
 110			goto fail;
 111		}
 112		rm->hw_intf[intf->id - INTF_0] = hw;
 113	}
 114
 115	for (i = 0; i < cat->wb_count; i++) {
 116		struct dpu_hw_wb *hw;
 117		const struct dpu_wb_cfg *wb = &cat->wb[i];
 118
 119		hw = dpu_hw_wb_init(dev, wb, mmio, cat->mdss_ver);
 120		if (IS_ERR(hw)) {
 121			rc = PTR_ERR(hw);
 122			DPU_ERROR("failed wb object creation: err %d\n", rc);
 123			goto fail;
 124		}
 125		rm->hw_wb[wb->id - WB_0] = hw;
 126	}
 127
 128	for (i = 0; i < cat->cwb_count; i++) {
 129		struct dpu_hw_cwb *hw;
 130		const struct dpu_cwb_cfg *cwb = &cat->cwb[i];
 131
 132		hw = dpu_hw_cwb_init(dev, cwb, mmio);
 133		if (IS_ERR(hw)) {
 134			rc = PTR_ERR(hw);
 135			DPU_ERROR("failed cwb object creation: err %d\n", rc);
 136			goto fail;
 137		}
 138		rm->cwb_blks[cwb->id - CWB_0] = &hw->base;
 139	}
 140
 141	for (i = 0; i < cat->ctl_count; i++) {
 142		struct dpu_hw_ctl *hw;
 143		const struct dpu_ctl_cfg *ctl = &cat->ctl[i];
 144
 145		hw = dpu_hw_ctl_init(dev, ctl, mmio, cat->mdss_ver, cat->mixer_count, cat->mixer);
 146		if (IS_ERR(hw)) {
 147			rc = PTR_ERR(hw);
 148			DPU_ERROR("failed ctl object creation: err %d\n", rc);
 149			goto fail;
 150		}
 151		rm->ctl_blks[ctl->id - CTL_0] = &hw->base;
 152	}
 153
 154	for (i = 0; i < cat->dspp_count; i++) {
 155		struct dpu_hw_dspp *hw;
 156		const struct dpu_dspp_cfg *dspp = &cat->dspp[i];
 157
 158		hw = dpu_hw_dspp_init(dev, dspp, mmio);
 159		if (IS_ERR(hw)) {
 160			rc = PTR_ERR(hw);
 161			DPU_ERROR("failed dspp object creation: err %d\n", rc);
 162			goto fail;
 163		}
 164		rm->dspp_blks[dspp->id - DSPP_0] = &hw->base;
 165	}
 166
 167	for (i = 0; i < cat->dsc_count; i++) {
 168		struct dpu_hw_dsc *hw;
 169		const struct dpu_dsc_cfg *dsc = &cat->dsc[i];
 170
 171		if (cat->mdss_ver->core_major_ver >= 7)
 172			hw = dpu_hw_dsc_init_1_2(dev, dsc, mmio);
 173		else
 174			hw = dpu_hw_dsc_init(dev, dsc, mmio, cat->mdss_ver);
 175
 176		if (IS_ERR(hw)) {
 177			rc = PTR_ERR(hw);
 178			DPU_ERROR("failed dsc object creation: err %d\n", rc);
 179			goto fail;
 180		}
 181		rm->dsc_blks[dsc->id - DSC_0] = &hw->base;
 182	}
 183
 184	for (i = 0; i < cat->sspp_count; i++) {
 185		struct dpu_hw_sspp *hw;
 186		const struct dpu_sspp_cfg *sspp = &cat->sspp[i];
 187
 188		hw = dpu_hw_sspp_init(dev, sspp, mmio, mdss_data, cat->mdss_ver);
 189		if (IS_ERR(hw)) {
 190			rc = PTR_ERR(hw);
 191			DPU_ERROR("failed sspp object creation: err %d\n", rc);
 192			goto fail;
 193		}
 194		rm->hw_sspp[sspp->id - SSPP_NONE] = hw;
 195	}
 196
 197	if (cat->cdm) {
 198		struct dpu_hw_cdm *hw;
 199
 200		hw = dpu_hw_cdm_init(dev, cat->cdm, mmio, cat->mdss_ver);
 201		if (IS_ERR(hw)) {
 202			rc = PTR_ERR(hw);
 203			DPU_ERROR("failed cdm object creation: err %d\n", rc);
 204			goto fail;
 205		}
 206		rm->cdm_blk = &hw->base;
 207	}
 208
 209	return 0;
 210
 211fail:
 212	return rc ? rc : -EFAULT;
 213}
 214
 215static bool _dpu_rm_needs_split_display(const struct msm_display_topology *top)
 216{
 217	return top->num_intf > 1;
 218}
 219
 220/**
 221 * _dpu_rm_get_lm_peer - get the id of a mixer which is a peer of the primary
 222 * @rm: dpu resource manager handle
 223 * @primary_idx: index of primary mixer in rm->mixer_blks[]
 224 *
 225 * Returns: lm peer mixed id on success or %-EINVAL on error
 226 */
 227static int _dpu_rm_get_lm_peer(struct dpu_rm *rm, int primary_idx)
 228{
 229	const struct dpu_lm_cfg *prim_lm_cfg;
 230
 231	prim_lm_cfg = to_dpu_hw_mixer(rm->mixer_blks[primary_idx])->cap;
 232
 233	if (prim_lm_cfg->lm_pair >= LM_0 && prim_lm_cfg->lm_pair < LM_MAX)
 234		return prim_lm_cfg->lm_pair - LM_0;
 235	return -EINVAL;
 236}
 237
 238static int _dpu_rm_reserve_cwb_mux_and_pingpongs(struct dpu_rm *rm,
 239						 struct dpu_global_state *global_state,
 240						 uint32_t crtc_id,
 241						 struct msm_display_topology *topology)
 242{
 243	int num_cwb_mux = topology->num_lm, cwb_mux_count = 0;
 244	int cwb_pp_start_idx = PINGPONG_CWB_0 - PINGPONG_0;
 245	int cwb_pp_idx[MAX_BLOCKS];
 246	int cwb_mux_idx[MAX_BLOCKS];
 247
 248	/*
 249	 * Reserve additional dedicated CWB PINGPONG blocks and muxes for each
 250	 * mixer
 251	 *
 252	 * TODO: add support reserving resources for platforms with no
 253	 *       PINGPONG_CWB
 254	 */
 255	for (int i = 0; i < ARRAY_SIZE(rm->mixer_blks) &&
 256	     cwb_mux_count < num_cwb_mux; i++) {
 257		for (int j = 0; j < ARRAY_SIZE(rm->cwb_blks); j++) {
 258			/*
 259			 * Odd LMs must be assigned to odd CWB muxes and even
 260			 * LMs with even CWB muxes.
 261			 *
 262			 * Since the RM HW block array index is based on the HW
 263			 * block ids, we can also use the array index to enforce
 264			 * the odd/even rule. See dpu_rm_init() for more
 265			 * information
 266			 */
 267			if (reserved_by_other(global_state->cwb_to_crtc_id, j, crtc_id) ||
 268			    i % 2 != j % 2)
 269				continue;
 270
 271			cwb_mux_idx[cwb_mux_count] = j;
 272			cwb_pp_idx[cwb_mux_count] = j + cwb_pp_start_idx;
 273			cwb_mux_count++;
 274			break;
 275		}
 276	}
 277
 278	if (cwb_mux_count != num_cwb_mux) {
 279		DPU_ERROR("Unable to reserve all CWB PINGPONGs\n");
 280		return -ENAVAIL;
 281	}
 282
 283	for (int i = 0; i < cwb_mux_count; i++) {
 284		global_state->pingpong_to_crtc_id[cwb_pp_idx[i]] = crtc_id;
 285		global_state->cwb_to_crtc_id[cwb_mux_idx[i]] = crtc_id;
 286	}
 287
 288	return 0;
 289}
 290
 291/**
 292 * _dpu_rm_check_lm_and_get_connected_blks - check if proposed layer mixer meets
 293 *	proposed use case requirements, incl. hardwired dependent blocks like
 294 *	pingpong
 295 * @rm: dpu resource manager handle
 296 * @global_state: resources shared across multiple kms objects
 297 * @crtc_id: crtc id requesting for allocation
 298 * @lm_idx: index of proposed layer mixer in rm->mixer_blks[], function checks
 299 *      if lm, and all other hardwired blocks connected to the lm (pp) is
 300 *      available and appropriate
 301 * @pp_idx: output parameter, index of pingpong block attached to the layer
 302 *      mixer in rm->pingpong_blks[].
 303 * @dspp_idx: output parameter, index of dspp block attached to the layer
 304 *      mixer in rm->dspp_blks[].
 305 * @topology:  selected topology for the display
 306 * Return: true if lm matches all requirements, false otherwise
 307 */
 308static bool _dpu_rm_check_lm_and_get_connected_blks(struct dpu_rm *rm,
 309		struct dpu_global_state *global_state,
 310		uint32_t crtc_id, int lm_idx, int *pp_idx, int *dspp_idx,
 311		struct msm_display_topology *topology)
 312{
 313	const struct dpu_lm_cfg *lm_cfg;
 314	int idx;
 315
 316	/* Already reserved? */
 317	if (reserved_by_other(global_state->mixer_to_crtc_id, lm_idx, crtc_id)) {
 318		DPU_DEBUG("lm %d already reserved\n", lm_idx + LM_0);
 319		return false;
 320	}
 321
 322	lm_cfg = to_dpu_hw_mixer(rm->mixer_blks[lm_idx])->cap;
 323	idx = lm_cfg->pingpong - PINGPONG_0;
 324	if (idx < 0 || idx >= ARRAY_SIZE(rm->pingpong_blks)) {
 325		DPU_ERROR("failed to get pp on lm %d\n", lm_cfg->pingpong);
 326		return false;
 327	}
 328
 329	if (reserved_by_other(global_state->pingpong_to_crtc_id, idx, crtc_id)) {
 330		DPU_DEBUG("lm %d pp %d already reserved\n", lm_cfg->id,
 331				lm_cfg->pingpong);
 332		return false;
 333	}
 334	*pp_idx = idx;
 335
 336	if (!topology->num_dspp)
 337		return true;
 338
 339	idx = lm_cfg->dspp - DSPP_0;
 340	if (idx < 0 || idx >= ARRAY_SIZE(rm->dspp_blks)) {
 341		DPU_ERROR("failed to get dspp on lm %d\n", lm_cfg->dspp);
 342		return false;
 343	}
 344
 345	if (reserved_by_other(global_state->dspp_to_crtc_id, idx, crtc_id)) {
 346		DPU_DEBUG("lm %d dspp %d already reserved\n", lm_cfg->id,
 347				lm_cfg->dspp);
 348		return false;
 349	}
 350	*dspp_idx = idx;
 351
 352	return true;
 353}
 354
 355static int _dpu_rm_reserve_lms(struct dpu_rm *rm,
 356			       struct dpu_global_state *global_state,
 357			       uint32_t crtc_id,
 358			       struct msm_display_topology *topology)
 359
 360{
 361	int lm_idx[MAX_BLOCKS];
 362	int pp_idx[MAX_BLOCKS];
 363	int dspp_idx[MAX_BLOCKS] = {0};
 364	int i, lm_count = 0;
 365
 366	if (!topology->num_lm) {
 367		DPU_ERROR("invalid number of lm: %d\n", topology->num_lm);
 368		return -EINVAL;
 369	}
 370
 371	/* Find a primary mixer */
 372	for (i = 0; i < ARRAY_SIZE(rm->mixer_blks) &&
 373			lm_count < topology->num_lm; i++) {
 374		if (!rm->mixer_blks[i])
 375			continue;
 376
 377		/*
 378		 * Reset lm_count to an even index. This will drop the previous
 379		 * primary mixer if failed to find its peer.
 380		 */
 381		lm_count &= ~1;
 382		lm_idx[lm_count] = i;
 383
 384		if (!_dpu_rm_check_lm_and_get_connected_blks(rm, global_state,
 385				crtc_id, i, &pp_idx[lm_count],
 386				&dspp_idx[lm_count], topology)) {
 387			continue;
 388		}
 389
 390		++lm_count;
 391
 392		/* Valid primary mixer found, find matching peers */
 393		if (lm_count < topology->num_lm) {
 394			int j = _dpu_rm_get_lm_peer(rm, i);
 395
 396			/* ignore the peer if there is an error or if the peer was already processed */
 397			if (j < 0 || j < i)
 398				continue;
 399
 400			if (!rm->mixer_blks[j])
 401				continue;
 402
 403			if (!_dpu_rm_check_lm_and_get_connected_blks(rm,
 404					global_state, crtc_id, j,
 405					&pp_idx[lm_count], &dspp_idx[lm_count],
 406					topology)) {
 407				continue;
 408			}
 409
 410			lm_idx[lm_count] = j;
 411			++lm_count;
 412		}
 413	}
 414
 415	if (lm_count != topology->num_lm) {
 416		DPU_DEBUG("unable to find appropriate mixers\n");
 417		return -ENAVAIL;
 418	}
 419
 420	for (i = 0; i < lm_count; i++) {
 421		global_state->mixer_to_crtc_id[lm_idx[i]] = crtc_id;
 422		global_state->pingpong_to_crtc_id[pp_idx[i]] = crtc_id;
 423		global_state->dspp_to_crtc_id[dspp_idx[i]] =
 424			topology->num_dspp ? crtc_id : 0;
 425
 426		trace_dpu_rm_reserve_lms(lm_idx[i] + LM_0, crtc_id,
 427					 pp_idx[i] + PINGPONG_0);
 428	}
 429
 430	return 0;
 431}
 432
 433static int _dpu_rm_reserve_ctls(
 434		struct dpu_rm *rm,
 435		struct dpu_global_state *global_state,
 436		uint32_t crtc_id,
 437		const struct msm_display_topology *top)
 438{
 439	int ctl_idx[MAX_BLOCKS];
 440	int i = 0, j, num_ctls;
 441	bool needs_split_display;
 442
 443	if (rm->has_legacy_ctls) {
 444		/*
 445		 * TODO: check if there is a need for special handling if
 446		 * DPU < 5.0 get CWB support.
 447		 */
 448		num_ctls = top->num_intf;
 449
 450		needs_split_display = _dpu_rm_needs_split_display(top);
 451	} else {
 452		/* use single CTL */
 453		num_ctls = 1;
 454		needs_split_display = false;
 455	}
 456
 457	for (j = 0; j < ARRAY_SIZE(rm->ctl_blks); j++) {
 458		const struct dpu_hw_ctl *ctl;
 459		unsigned long features;
 460		bool has_split_display;
 461
 462		if (!rm->ctl_blks[j])
 463			continue;
 464		if (reserved_by_other(global_state->ctl_to_crtc_id, j, crtc_id))
 465			continue;
 466
 467		ctl = to_dpu_hw_ctl(rm->ctl_blks[j]);
 468		features = ctl->caps->features;
 469		has_split_display = BIT(DPU_CTL_SPLIT_DISPLAY) & features;
 470
 471		DPU_DEBUG("ctl %d caps 0x%lX\n", j + CTL_0, features);
 472
 473		if (needs_split_display != has_split_display)
 474			continue;
 475
 476		ctl_idx[i] = j;
 477		DPU_DEBUG("ctl %d match\n", j + CTL_0);
 478
 479		if (++i == num_ctls)
 480			break;
 481
 482	}
 483
 484	if (i != num_ctls)
 485		return -ENAVAIL;
 486
 487	for (i = 0; i < ARRAY_SIZE(ctl_idx) && i < num_ctls; i++) {
 488		global_state->ctl_to_crtc_id[ctl_idx[i]] = crtc_id;
 489		trace_dpu_rm_reserve_ctls(i + CTL_0, crtc_id);
 490	}
 491
 492	return 0;
 493}
 494
 495static int _dpu_rm_pingpong_next_index(struct dpu_global_state *global_state,
 496				       int start,
 497				       uint32_t crtc_id)
 498{
 499	int i;
 500
 501	for (i = start; i < (PINGPONG_MAX - PINGPONG_0); i++) {
 502		if (global_state->pingpong_to_crtc_id[i] == crtc_id)
 503			return i;
 504	}
 505
 506	return -ENAVAIL;
 507}
 508
 509static int _dpu_rm_pingpong_dsc_check(int dsc_idx, int pp_idx)
 510{
 511	/*
 512	 * DSC with even index must be used with the PINGPONG with even index
 513	 * DSC with odd index must be used with the PINGPONG with odd index
 514	 */
 515	if ((dsc_idx & 0x01) != (pp_idx & 0x01))
 516		return -ENAVAIL;
 517
 518	return 0;
 519}
 520
 521static int _dpu_rm_dsc_alloc(struct dpu_rm *rm,
 522			     struct dpu_global_state *global_state,
 523			     uint32_t crtc_id,
 524			     const struct msm_display_topology *top)
 525{
 526	int num_dsc = 0;
 527	int pp_idx = 0;
 528	int dsc_idx;
 529	int ret;
 530
 531	for (dsc_idx = 0; dsc_idx < ARRAY_SIZE(rm->dsc_blks) &&
 532	     num_dsc < top->num_dsc; dsc_idx++) {
 533		if (!rm->dsc_blks[dsc_idx])
 534			continue;
 535
 536		if (reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx, crtc_id))
 537			continue;
 538
 539		pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, crtc_id);
 540		if (pp_idx < 0)
 541			return -ENAVAIL;
 542
 543		ret = _dpu_rm_pingpong_dsc_check(dsc_idx, pp_idx);
 544		if (ret)
 545			return -ENAVAIL;
 546
 547		global_state->dsc_to_crtc_id[dsc_idx] = crtc_id;
 548		num_dsc++;
 549		pp_idx++;
 550	}
 551
 552	if (num_dsc < top->num_dsc) {
 553		DPU_ERROR("DSC allocation failed num_dsc=%d required=%d\n",
 554			   num_dsc, top->num_dsc);
 555		return -ENAVAIL;
 556	}
 557
 558	return 0;
 559}
 560
 561static int _dpu_rm_dsc_alloc_pair(struct dpu_rm *rm,
 562				  struct dpu_global_state *global_state,
 563				  uint32_t crtc_id,
 564				  const struct msm_display_topology *top)
 565{
 566	int num_dsc = 0;
 567	int dsc_idx, pp_idx = 0;
 568	int ret;
 569
 570	/* only start from even dsc index */
 571	for (dsc_idx = 0; dsc_idx < ARRAY_SIZE(rm->dsc_blks) &&
 572	     num_dsc < top->num_dsc; dsc_idx += 2) {
 573		if (!rm->dsc_blks[dsc_idx] ||
 574		    !rm->dsc_blks[dsc_idx + 1])
 575			continue;
 576
 577		/* consective dsc index to be paired */
 578		if (reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx, crtc_id) ||
 579		    reserved_by_other(global_state->dsc_to_crtc_id, dsc_idx + 1, crtc_id))
 580			continue;
 581
 582		pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx, crtc_id);
 583		if (pp_idx < 0)
 584			return -ENAVAIL;
 585
 586		ret = _dpu_rm_pingpong_dsc_check(dsc_idx, pp_idx);
 587		if (ret) {
 588			pp_idx = 0;
 589			continue;
 590		}
 591
 592		pp_idx = _dpu_rm_pingpong_next_index(global_state, pp_idx + 1, crtc_id);
 593		if (pp_idx < 0)
 594			return -ENAVAIL;
 595
 596		ret = _dpu_rm_pingpong_dsc_check(dsc_idx + 1, pp_idx);
 597		if (ret) {
 598			pp_idx = 0;
 599			continue;
 600		}
 601
 602		global_state->dsc_to_crtc_id[dsc_idx] = crtc_id;
 603		global_state->dsc_to_crtc_id[dsc_idx + 1] = crtc_id;
 604		num_dsc += 2;
 605		pp_idx++;	/* start for next pair */
 606	}
 607
 608	if (num_dsc < top->num_dsc) {
 609		DPU_ERROR("DSC allocation failed num_dsc=%d required=%d\n",
 610			   num_dsc, top->num_dsc);
 611		return -ENAVAIL;
 612	}
 613
 614	return 0;
 615}
 616
 617static int _dpu_rm_reserve_dsc(struct dpu_rm *rm,
 618			       struct dpu_global_state *global_state,
 619			       uint32_t crtc_id,
 620			       const struct msm_display_topology *top)
 621{
 622	if (!top->num_dsc || !top->num_intf)
 623		return 0;
 624
 625	/*
 626	 * Facts:
 627	 * 1) no pingpong split (two layer mixers shared one pingpong)
 628	 * 2) DSC pair starts from even index, such as index(0,1), (2,3), etc
 629	 * 3) even PINGPONG connects to even DSC
 630	 * 4) odd PINGPONG connects to odd DSC
 631	 * 5) pair: encoder +--> pp_idx_0 --> dsc_idx_0
 632	 *                  +--> pp_idx_1 --> dsc_idx_1
 633	 */
 634
 635	/* num_dsc should be either 1, 2 or 4 */
 636	if (top->num_dsc > top->num_intf)	/* merge mode */
 637		return _dpu_rm_dsc_alloc_pair(rm, global_state, crtc_id, top);
 638	else
 639		return _dpu_rm_dsc_alloc(rm, global_state, crtc_id, top);
 640
 641	return 0;
 642}
 643
 644static int _dpu_rm_reserve_cdm(struct dpu_rm *rm,
 645			       struct dpu_global_state *global_state,
 646			       uint32_t crtc_id,
 647			       int num_cdm)
 648{
 649	/* try allocating only one CDM block */
 650	if (!rm->cdm_blk) {
 651		DPU_ERROR("CDM block does not exist\n");
 652		return -EIO;
 653	}
 654
 655	if (num_cdm > 1) {
 656		DPU_ERROR("More than 1 INTF requesting CDM\n");
 657		return -EINVAL;
 658	}
 659
 660	if (global_state->cdm_to_crtc_id) {
 661		DPU_ERROR("CDM_0 is already allocated\n");
 662		return -EIO;
 663	}
 664
 665	global_state->cdm_to_crtc_id = crtc_id;
 666
 667	return 0;
 668}
 669
 670static int _dpu_rm_make_reservation(
 671		struct dpu_rm *rm,
 672		struct dpu_global_state *global_state,
 673		uint32_t crtc_id,
 674		struct msm_display_topology *topology)
 675{
 676	int ret;
 677
 678	ret = _dpu_rm_reserve_lms(rm, global_state, crtc_id, topology);
 679	if (ret) {
 680		DPU_ERROR("unable to find appropriate mixers\n");
 681		return ret;
 682	}
 683
 684	if (topology->cwb_enabled) {
 685		ret = _dpu_rm_reserve_cwb_mux_and_pingpongs(rm, global_state,
 686							    crtc_id, topology);
 687		if (ret)
 688			return ret;
 689	}
 690
 691	ret = _dpu_rm_reserve_ctls(rm, global_state, crtc_id,
 692			topology);
 693	if (ret) {
 694		DPU_ERROR("unable to find appropriate CTL\n");
 695		return ret;
 696	}
 697
 698	ret  = _dpu_rm_reserve_dsc(rm, global_state, crtc_id, topology);
 699	if (ret)
 700		return ret;
 701
 702	if (topology->num_cdm > 0) {
 703		ret = _dpu_rm_reserve_cdm(rm, global_state, crtc_id, topology->num_cdm);
 704		if (ret) {
 705			DPU_ERROR("unable to find CDM blk\n");
 706			return ret;
 707		}
 708	}
 709
 710	return ret;
 711}
 712
 713static void _dpu_rm_clear_mapping(uint32_t *res_mapping, int cnt,
 714				  uint32_t crtc_id)
 715{
 716	int i;
 717
 718	for (i = 0; i < cnt; i++) {
 719		if (res_mapping[i] == crtc_id)
 720			res_mapping[i] = 0;
 721	}
 722}
 723
 724/**
 725 * dpu_rm_release - Given the encoder for the display chain, release any
 726 *	HW blocks previously reserved for that use case.
 727 * @global_state: resources shared across multiple kms objects
 728 * @crtc: DRM CRTC handle
 729 * @return: 0 on Success otherwise -ERROR
 730 */
 731void dpu_rm_release(struct dpu_global_state *global_state,
 732		    struct drm_crtc *crtc)
 733{
 734	uint32_t crtc_id = crtc->base.id;
 735
 736	_dpu_rm_clear_mapping(global_state->pingpong_to_crtc_id,
 737			ARRAY_SIZE(global_state->pingpong_to_crtc_id), crtc_id);
 738	_dpu_rm_clear_mapping(global_state->mixer_to_crtc_id,
 739			ARRAY_SIZE(global_state->mixer_to_crtc_id), crtc_id);
 740	_dpu_rm_clear_mapping(global_state->ctl_to_crtc_id,
 741			ARRAY_SIZE(global_state->ctl_to_crtc_id), crtc_id);
 742	_dpu_rm_clear_mapping(global_state->dsc_to_crtc_id,
 743			ARRAY_SIZE(global_state->dsc_to_crtc_id), crtc_id);
 744	_dpu_rm_clear_mapping(global_state->dspp_to_crtc_id,
 745			ARRAY_SIZE(global_state->dspp_to_crtc_id), crtc_id);
 746	_dpu_rm_clear_mapping(&global_state->cdm_to_crtc_id, 1, crtc_id);
 747	_dpu_rm_clear_mapping(global_state->cwb_to_crtc_id,
 748			ARRAY_SIZE(global_state->cwb_to_crtc_id), crtc_id);
 749}
 750
 751/**
 752 * dpu_rm_reserve - Given a CRTC->Encoder->Connector display chain, analyze
 753 *	the use connections and user requirements, specified through related
 754 *	topology control properties, and reserve hardware blocks to that
 755 *	display chain.
 756 *	HW blocks can then be accessed through dpu_rm_get_* functions.
 757 *	HW Reservations should be released via dpu_rm_release_hw.
 758 * @rm: DPU Resource Manager handle
 759 * @global_state: resources shared across multiple kms objects
 760 * @crtc: DRM CRTC handle
 761 * @topology: Pointer to topology info for the display
 762 * @return: 0 on Success otherwise -ERROR
 763 */
 764int dpu_rm_reserve(
 765		struct dpu_rm *rm,
 766		struct dpu_global_state *global_state,
 767		struct drm_crtc *crtc,
 768		struct msm_display_topology *topology)
 769{
 770	int ret;
 771
 772	if (IS_ERR(global_state)) {
 773		DPU_ERROR("failed to global state\n");
 774		return PTR_ERR(global_state);
 775	}
 776
 777	DRM_DEBUG_KMS("reserving hw for crtc %d\n", crtc->base.id);
 778
 779	DRM_DEBUG_KMS("num_lm: %d num_dsc: %d num_intf: %d\n",
 780		      topology->num_lm, topology->num_dsc,
 781		      topology->num_intf);
 782
 783	ret = _dpu_rm_make_reservation(rm, global_state, crtc->base.id, topology);
 784	if (ret)
 785		DPU_ERROR("failed to reserve hw resources: %d\n", ret);
 786
 787	return ret;
 788}
 789
 790static struct dpu_hw_sspp *dpu_rm_try_sspp(struct dpu_rm *rm,
 791					   struct dpu_global_state *global_state,
 792					   struct drm_crtc *crtc,
 793					   struct dpu_rm_sspp_requirements *reqs,
 794					   unsigned int type)
 795{
 796	uint32_t crtc_id = crtc->base.id;
 797	struct dpu_hw_sspp *hw_sspp;
 798	int i;
 799
 800	for (i = 0; i < ARRAY_SIZE(rm->hw_sspp); i++) {
 801		if (!rm->hw_sspp[i])
 802			continue;
 803
 804		if (global_state->sspp_to_crtc_id[i])
 805			continue;
 806
 807		hw_sspp = rm->hw_sspp[i];
 808
 809		if (hw_sspp->cap->type != type)
 810			continue;
 811
 812		if (reqs->scale && !hw_sspp->cap->sblk->scaler_blk.len)
 813			continue;
 814
 815		// TODO: QSEED2 and RGB scalers are not yet supported
 816		if (reqs->scale && !hw_sspp->ops.setup_scaler)
 817			continue;
 818
 819		if (reqs->yuv && !hw_sspp->cap->sblk->csc_blk.len)
 820			continue;
 821
 822		if (reqs->rot90 && !(hw_sspp->cap->features & DPU_SSPP_INLINE_ROTATION))
 823			continue;
 824
 825		global_state->sspp_to_crtc_id[i] = crtc_id;
 826
 827		return rm->hw_sspp[i];
 828	}
 829
 830	return NULL;
 831}
 832
 833/**
 834 * dpu_rm_reserve_sspp - Reserve the required SSPP for the provided CRTC
 835 * @rm: DPU Resource Manager handle
 836 * @global_state: private global state
 837 * @crtc: DRM CRTC handle
 838 * @reqs: SSPP required features
 839 */
 840struct dpu_hw_sspp *dpu_rm_reserve_sspp(struct dpu_rm *rm,
 841					struct dpu_global_state *global_state,
 842					struct drm_crtc *crtc,
 843					struct dpu_rm_sspp_requirements *reqs)
 844{
 845	struct dpu_hw_sspp *hw_sspp = NULL;
 846
 847	if (!reqs->scale && !reqs->yuv)
 848		hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_DMA);
 849	if (!hw_sspp && !reqs->yuv)
 850		hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_RGB);
 851	if (!hw_sspp)
 852		hw_sspp = dpu_rm_try_sspp(rm, global_state, crtc, reqs, SSPP_TYPE_VIG);
 853
 854	return hw_sspp;
 855}
 856
 857/**
 858 * dpu_rm_release_all_sspp - Given the CRTC, release all SSPP
 859 *	blocks previously reserved for that use case.
 860 * @global_state: resources shared across multiple kms objects
 861 * @crtc: DRM CRTC handle
 862 */
 863void dpu_rm_release_all_sspp(struct dpu_global_state *global_state,
 864			     struct drm_crtc *crtc)
 865{
 866	uint32_t crtc_id = crtc->base.id;
 867
 868	_dpu_rm_clear_mapping(global_state->sspp_to_crtc_id,
 869		ARRAY_SIZE(global_state->sspp_to_crtc_id), crtc_id);
 870}
 871
 872static char *dpu_hw_blk_type_name[] = {
 873	[DPU_HW_BLK_TOP] = "TOP",
 874	[DPU_HW_BLK_SSPP] = "SSPP",
 875	[DPU_HW_BLK_LM] = "LM",
 876	[DPU_HW_BLK_CTL] = "CTL",
 877	[DPU_HW_BLK_PINGPONG] = "pingpong",
 878	[DPU_HW_BLK_INTF] = "INTF",
 879	[DPU_HW_BLK_WB] = "WB",
 880	[DPU_HW_BLK_DSPP] = "DSPP",
 881	[DPU_HW_BLK_MERGE_3D] = "merge_3d",
 882	[DPU_HW_BLK_DSC] = "DSC",
 883	[DPU_HW_BLK_CDM] = "CDM",
 884	[DPU_HW_BLK_MAX] = "unknown",
 885};
 886
 887/**
 888 * dpu_rm_get_assigned_resources - Get hw resources of the given type that are
 889 *     assigned to this encoder
 890 * @rm: DPU Resource Manager handle
 891 * @global_state: resources shared across multiple kms objects
 892 * @crtc: DRM CRTC handle
 893 * @type: resource type to return data for
 894 * @blks: pointer to the array to be filled by HW resources
 895 * @blks_size: size of the @blks array
 896 */
 897int dpu_rm_get_assigned_resources(struct dpu_rm *rm,
 898	struct dpu_global_state *global_state, struct drm_crtc *crtc,
 899	enum dpu_hw_blk_type type, struct dpu_hw_blk **blks, int blks_size)
 900{
 901	uint32_t crtc_id = crtc->base.id;
 902	struct dpu_hw_blk **hw_blks;
 903	uint32_t *hw_to_crtc_id;
 904	int i, num_blks, max_blks;
 905
 906	switch (type) {
 907	case DPU_HW_BLK_PINGPONG:
 908	case DPU_HW_BLK_DCWB_PINGPONG:
 909		hw_blks = rm->pingpong_blks;
 910		hw_to_crtc_id = global_state->pingpong_to_crtc_id;
 911		max_blks = ARRAY_SIZE(rm->pingpong_blks);
 912		break;
 913	case DPU_HW_BLK_LM:
 914		hw_blks = rm->mixer_blks;
 915		hw_to_crtc_id = global_state->mixer_to_crtc_id;
 916		max_blks = ARRAY_SIZE(rm->mixer_blks);
 917		break;
 918	case DPU_HW_BLK_CTL:
 919		hw_blks = rm->ctl_blks;
 920		hw_to_crtc_id = global_state->ctl_to_crtc_id;
 921		max_blks = ARRAY_SIZE(rm->ctl_blks);
 922		break;
 923	case DPU_HW_BLK_DSPP:
 924		hw_blks = rm->dspp_blks;
 925		hw_to_crtc_id = global_state->dspp_to_crtc_id;
 926		max_blks = ARRAY_SIZE(rm->dspp_blks);
 927		break;
 928	case DPU_HW_BLK_DSC:
 929		hw_blks = rm->dsc_blks;
 930		hw_to_crtc_id = global_state->dsc_to_crtc_id;
 931		max_blks = ARRAY_SIZE(rm->dsc_blks);
 932		break;
 933	case DPU_HW_BLK_CDM:
 934		hw_blks = &rm->cdm_blk;
 935		hw_to_crtc_id = &global_state->cdm_to_crtc_id;
 936		max_blks = 1;
 937		break;
 938	case DPU_HW_BLK_CWB:
 939		hw_blks = rm->cwb_blks;
 940		hw_to_crtc_id = global_state->cwb_to_crtc_id;
 941		max_blks = ARRAY_SIZE(rm->cwb_blks);
 942		break;
 943	default:
 944		DPU_ERROR("blk type %d not managed by rm\n", type);
 945		return 0;
 946	}
 947
 948	num_blks = 0;
 949	for (i = 0; i < max_blks; i++) {
 950		if (hw_to_crtc_id[i] != crtc_id)
 951			continue;
 952
 953		if (type == DPU_HW_BLK_PINGPONG) {
 954			struct dpu_hw_pingpong *pp = to_dpu_hw_pingpong(hw_blks[i]);
 955
 956			if (pp->idx >= PINGPONG_CWB_0)
 957				continue;
 958		}
 959
 960		if (type == DPU_HW_BLK_DCWB_PINGPONG) {
 961			struct dpu_hw_pingpong *pp = to_dpu_hw_pingpong(hw_blks[i]);
 962
 963			if (pp->idx < PINGPONG_CWB_0)
 964				continue;
 965		}
 966
 967		if (num_blks == blks_size) {
 968			DPU_ERROR("More than %d %s assigned to crtc %d\n",
 969				  blks_size, dpu_hw_blk_type_name[type], crtc_id);
 970			break;
 971		}
 972		if (!hw_blks[i]) {
 973			DPU_ERROR("%s unavailable to assign to crtc %d\n",
 974				  dpu_hw_blk_type_name[type], crtc_id);
 975			break;
 976		}
 977		blks[num_blks++] = hw_blks[i];
 978	}
 979
 980	return num_blks;
 981}
 982
 983static void dpu_rm_print_state_helper(struct drm_printer *p,
 984					    struct dpu_hw_blk *blk,
 985					    uint32_t mapping)
 986{
 987	if (!blk)
 988		drm_puts(p, "- ");
 989	else if (!mapping)
 990		drm_puts(p, "# ");
 991	else
 992		drm_printf(p, "%d ", mapping);
 993}
 994
 995
 996/**
 997 * dpu_rm_print_state - output the RM private state
 998 * @p: DRM printer
 999 * @global_state: global state
1000 */
1001void dpu_rm_print_state(struct drm_printer *p,
1002			const struct dpu_global_state *global_state)
1003{
1004	const struct dpu_rm *rm = global_state->rm;
1005	int i;
1006
1007	drm_puts(p, "resource mapping:\n");
1008	drm_puts(p, "\tpingpong=");
1009	for (i = 0; i < ARRAY_SIZE(global_state->pingpong_to_crtc_id); i++)
1010		dpu_rm_print_state_helper(p, rm->pingpong_blks[i],
1011					  global_state->pingpong_to_crtc_id[i]);
1012	drm_puts(p, "\n");
1013
1014	drm_puts(p, "\tmixer=");
1015	for (i = 0; i < ARRAY_SIZE(global_state->mixer_to_crtc_id); i++)
1016		dpu_rm_print_state_helper(p, rm->mixer_blks[i],
1017					  global_state->mixer_to_crtc_id[i]);
1018	drm_puts(p, "\n");
1019
1020	drm_puts(p, "\tctl=");
1021	for (i = 0; i < ARRAY_SIZE(global_state->ctl_to_crtc_id); i++)
1022		dpu_rm_print_state_helper(p, rm->ctl_blks[i],
1023					  global_state->ctl_to_crtc_id[i]);
1024	drm_puts(p, "\n");
1025
1026	drm_puts(p, "\tdspp=");
1027	for (i = 0; i < ARRAY_SIZE(global_state->dspp_to_crtc_id); i++)
1028		dpu_rm_print_state_helper(p, rm->dspp_blks[i],
1029					  global_state->dspp_to_crtc_id[i]);
1030	drm_puts(p, "\n");
1031
1032	drm_puts(p, "\tdsc=");
1033	for (i = 0; i < ARRAY_SIZE(global_state->dsc_to_crtc_id); i++)
1034		dpu_rm_print_state_helper(p, rm->dsc_blks[i],
1035					  global_state->dsc_to_crtc_id[i]);
1036	drm_puts(p, "\n");
1037
1038	drm_puts(p, "\tcdm=");
1039	dpu_rm_print_state_helper(p, rm->cdm_blk,
1040				  global_state->cdm_to_crtc_id);
1041	drm_puts(p, "\n");
1042
1043	drm_puts(p, "\tsspp=");
1044	/* skip SSPP_NONE and start from the next index */
1045	for (i = SSPP_NONE + 1; i < ARRAY_SIZE(global_state->sspp_to_crtc_id); i++)
1046		dpu_rm_print_state_helper(p, rm->hw_sspp[i] ? &rm->hw_sspp[i]->base : NULL,
1047					  global_state->sspp_to_crtc_id[i]);
1048	drm_puts(p, "\n");
1049
1050	drm_puts(p, "\tcwb=");
1051	for (i = 0; i < ARRAY_SIZE(global_state->cwb_to_crtc_id); i++)
1052		dpu_rm_print_state_helper(p, rm->cwb_blks[i],
1053					  global_state->cwb_to_crtc_id[i]);
1054	drm_puts(p, "\n");
1055}
~/ Read-only file preview.