/* SPDX-License-Identifier: GPL-2.0-only */
/*
 * Copyright (c) 2018, The Linux Foundation
 * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
 */

#ifndef __QCOM_UBWC_H__
#define __QCOM_UBWC_H__

#include <linux/bits.h>
#include <linux/err.h>
#include <linux/printk.h>
#include <linux/types.h>

struct qcom_ubwc_cfg_data {
	u32 ubwc_enc_version;

	/**
	 * @highest_bank_bit: Highest Bank Bit
	 *
	 * The Highest Bank Bit value represents the bit of the highest
	 * DDR bank.  This should ideally use DRAM type detection.
	 */
	int highest_bank_bit;

	unsigned int flags;
#define UBWC_FLAG_DISABLE_SWIZZLE_LVL2	BIT(0)
#define UBWC_FLAG_DISABLE_SWIZZLE_LVL3	BIT(1)
};

#define UBWC_1_0 0x10000000
#define UBWC_2_0 0x20000000
#define UBWC_3_0 0x30000000
#define UBWC_3_1 0x30010000 /* UBWC 3.0 + Macrotile mode */
#define UBWC_4_0 0x40000000
#define UBWC_4_3 0x40030000
#define UBWC_5_0 0x50000000
#define UBWC_6_0 0x60000000

#if IS_ENABLED(CONFIG_QCOM_UBWC_CONFIG)
const struct qcom_ubwc_cfg_data *qcom_ubwc_config_get_data(void);
#else
static inline const struct qcom_ubwc_cfg_data *qcom_ubwc_config_get_data(void)
{
	return ERR_PTR(-EOPNOTSUPP);
}
#endif

static inline bool qcom_ubwc_get_ubwc_mode(const struct qcom_ubwc_cfg_data *cfg)
{
	return cfg->ubwc_enc_version == UBWC_1_0;
}

static inline bool qcom_ubwc_min_acc_length_64b(const struct qcom_ubwc_cfg_data *cfg)
{
	return cfg->ubwc_enc_version == UBWC_1_0;
}

/*
 * @qcom_ubwc_macrotile_mode: whether to use 4-channel or 8-channel macrotiling
 *
 * The 8-channel macrotiling mode was introduced in UBWC 3.1.
 *
 * Returns: false for the 4-channel and true for 8-channel.
 */
static inline bool qcom_ubwc_macrotile_mode(const struct qcom_ubwc_cfg_data *cfg)
{
	return cfg->ubwc_enc_version >= UBWC_3_1;
}

static inline bool qcom_ubwc_bank_spread(const struct qcom_ubwc_cfg_data *cfg)
{
	return true;
}

#define UBWC_SWIZZLE_ENABLE_LVL1	BIT(0)
#define UBWC_SWIZZLE_ENABLE_LVL2	BIT(1)
#define UBWC_SWIZZLE_ENABLE_LVL3	BIT(2)

/**
 * @qcom_ubwc_swizzle: Whether to enable level 1, 2 & 3 bank swizzling.
 *
 * UBWC 1.0 always enables all three levels.
 * UBWC 2.0 removes level 1 bank swizzling, leaving levels 2 & 3.
 * UBWC 4.0 adds the optional ability to disable levels 2 & 3.
 */
static inline u32 qcom_ubwc_swizzle(const struct qcom_ubwc_cfg_data *cfg)
{
	if (cfg->ubwc_enc_version == 0)
		return 0;

	if (cfg->ubwc_enc_version == UBWC_1_0)
		return UBWC_SWIZZLE_ENABLE_LVL1 |
		       UBWC_SWIZZLE_ENABLE_LVL2 |
		       UBWC_SWIZZLE_ENABLE_LVL3;

	u32 ubwc_swizzle = UBWC_SWIZZLE_ENABLE_LVL2 |
			   UBWC_SWIZZLE_ENABLE_LVL3;

	if (cfg->flags & UBWC_FLAG_DISABLE_SWIZZLE_LVL2)
		ubwc_swizzle &= ~UBWC_SWIZZLE_ENABLE_LVL2;

	if (cfg->flags & UBWC_FLAG_DISABLE_SWIZZLE_LVL3)
		ubwc_swizzle &= ~UBWC_SWIZZLE_ENABLE_LVL3;

	return ubwc_swizzle;
}

static inline u32 qcom_ubwc_version_tag(const struct qcom_ubwc_cfg_data *cfg)
{
	if (cfg->ubwc_enc_version >= UBWC_6_0)
		return 5;
	if (cfg->ubwc_enc_version >= UBWC_5_0)
		return 4;
	if (cfg->ubwc_enc_version >= UBWC_4_3)
		return 3;
	if (cfg->ubwc_enc_version >= UBWC_4_0)
		return 2;
	if (cfg->ubwc_enc_version >= UBWC_3_0)
		return 1;

	return 0;
}

static inline bool qcom_ubwc_enable_amsbc(const struct qcom_ubwc_cfg_data *cfg)
{
	return cfg->ubwc_enc_version >= UBWC_3_0;
}

#endif /* __QCOM_UBWC_H__ */
