// SPDX-License-Identifier: GPL-2.0
/******************************************************************************
 *
 * Copyright(c) 2007 - 2016  Realtek Corporation.
 *
 * Contact Information:
 * wlanfae <wlanfae@realtek.com>
 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
 * Hsinchu 300, Taiwan.
 *
 * Larry Finger <Larry.Finger@lwfinger.net>
 *
 *****************************************************************************/

#include "../mp_precomp.h"
#include "../phydm_precomp.h"

static void phydm_dynamic_switch_htstf_mumimo_8822b(struct phy_dm_struct *dm)
{
	/*if rssi > 40dBm, enable HT-STF gain controller,
	 *otherwise, if rssi < 40dBm, disable the controller
	 */
	/*add by Chun-Hung Ho 20160711 */
	if (dm->rssi_min >= 40)
		odm_set_bb_reg(dm, 0x8d8, BIT(17), 0x1);
	else if (dm->rssi_min < 35)
		odm_set_bb_reg(dm, 0x8d8, BIT(17), 0x0);

	ODM_RT_TRACE(dm, ODM_COMP_COMMON, "%s, rssi_min = %d\n", __func__,
		     dm->rssi_min);
}

static void _set_tx_a_cali_value(struct phy_dm_struct *dm, u8 rf_path,
				 u8 offset, u8 tx_a_bias_offset)
{
	u32 modi_tx_a_value = 0;
	u8 tmp1_byte = 0;
	bool is_minus = false;
	u8 comp_value = 0;

	switch (offset) {
	case 0x0:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X10124);
		break;
	case 0x1:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X10524);
		break;
	case 0x2:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X10924);
		break;
	case 0x3:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X10D24);
		break;
	case 0x4:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X30164);
		break;
	case 0x5:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X30564);
		break;
	case 0x6:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X30964);
		break;
	case 0x7:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X30D64);
		break;
	case 0x8:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X50195);
		break;
	case 0x9:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X50595);
		break;
	case 0xa:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X50995);
		break;
	case 0xb:
		odm_set_rf_reg(dm, rf_path, 0x18, 0xFFFFF, 0X50D95);
		break;
	default:
		ODM_RT_TRACE(dm, ODM_COMP_COMMON,
			     "Invalid TxA band offset...\n");
		return;
	}

	/* Get TxA value */
	modi_tx_a_value = odm_get_rf_reg(dm, rf_path, 0x61, 0xFFFFF);
	tmp1_byte = (u8)modi_tx_a_value & (BIT(3) | BIT(2) | BIT(1) | BIT(0));

	/* check how much need to calibration */
	switch (tx_a_bias_offset) {
	case 0xF6:
		is_minus = true;
		comp_value = 3;
		break;

	case 0xF4:
		is_minus = true;
		comp_value = 2;
		break;

	case 0xF2:
		is_minus = true;
		comp_value = 1;
		break;

	case 0xF3:
		is_minus = false;
		comp_value = 1;
		break;

	case 0xF5:
		is_minus = false;
		comp_value = 2;
		break;

	case 0xF7:
		is_minus = false;
		comp_value = 3;
		break;

	case 0xF9:
		is_minus = false;
		comp_value = 4;
		break;

	/* do nothing case */
	case 0xF0:
	default:
		ODM_RT_TRACE(dm, ODM_COMP_COMMON,
			     "No need to do TxA bias current calibration\n");
		return;
	}

	/* calc correct value to calibrate */
	if (is_minus) {
		if (tmp1_byte >= comp_value) {
			tmp1_byte -= comp_value;
			/*modi_tx_a_value += tmp1_byte;*/
		} else {
			tmp1_byte = 0;
		}
	} else {
		tmp1_byte += comp_value;
		if (tmp1_byte >= 7)
			tmp1_byte = 7;
	}

	/* Write back to RF reg */
	odm_set_rf_reg(dm, rf_path, 0x30, 0xFFFF,
		       (offset << 12 | (modi_tx_a_value & 0xFF0) | tmp1_byte));
}

static void _txa_bias_cali_4_each_path(struct phy_dm_struct *dm, u8 rf_path,
				       u8 efuse_value)
{
	/* switch on set TxA bias */
	odm_set_rf_reg(dm, rf_path, 0xEF, 0xFFFFF, 0x200);

	/* Set 12 sets of TxA value */
	_set_tx_a_cali_value(dm, rf_path, 0x0, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0x1, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0x2, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0x3, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0x4, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0x5, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0x6, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0x7, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0x8, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0x9, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0xa, efuse_value);
	_set_tx_a_cali_value(dm, rf_path, 0xb, efuse_value);

	/* switch off set TxA bias */
	odm_set_rf_reg(dm, rf_path, 0xEF, 0xFFFFF, 0x0);
}

/*
 * for 8822B PCIE D-cut patch only
 * Normal driver and MP driver need this patch
 */

void phydm_txcurrentcalibration(struct phy_dm_struct *dm)
{
	u8 efuse0x3D8, efuse0x3D7;
	u32 orig_rf0x18_path_a = 0, orig_rf0x18_path_b = 0;

	/* save original 0x18 value */
	orig_rf0x18_path_a = odm_get_rf_reg(dm, ODM_RF_PATH_A, 0x18, 0xFFFFF);
	orig_rf0x18_path_b = odm_get_rf_reg(dm, ODM_RF_PATH_B, 0x18, 0xFFFFF);

	/* define efuse content */
	efuse0x3D8 = dm->efuse0x3d8;
	efuse0x3D7 = dm->efuse0x3d7;

	/* check efuse content to judge whether need to calibration or not */
	if (efuse0x3D7 == 0xFF) {
		ODM_RT_TRACE(
			dm, ODM_COMP_COMMON,
			"efuse content 0x3D7 == 0xFF, No need to do TxA cali\n");
		return;
	}

	/* write RF register for calibration */
	_txa_bias_cali_4_each_path(dm, ODM_RF_PATH_A, efuse0x3D7);
	_txa_bias_cali_4_each_path(dm, ODM_RF_PATH_B, efuse0x3D8);

	/* restore original 0x18 value */
	odm_set_rf_reg(dm, ODM_RF_PATH_A, 0x18, 0xFFFFF, orig_rf0x18_path_a);
	odm_set_rf_reg(dm, ODM_RF_PATH_B, 0x18, 0xFFFFF, orig_rf0x18_path_b);
}

void phydm_hwsetting_8822b(struct phy_dm_struct *dm)
{
	phydm_dynamic_switch_htstf_mumimo_8822b(dm);
}
