From 13ad1111a50216861372a3e152148d38f2d91ac4 Mon Sep 17 00:00:00 2001
From: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
Date: Wed, 1 Mar 2023 12:12:51 +0800
Subject: [PATCH 1008/1014] wifi: mt76: mt7996: add normal mode pre-calibration
 support

Signed-off-by: StanleyYP Wang <StanleyYP.Wang@mediatek.com>
---
 mt76_connac_mcu.h |   1 +
 mt7996/eeprom.c   |  23 +++++++
 mt7996/eeprom.h   |   2 +
 mt7996/init.c     |   6 ++
 mt7996/main.c     |   6 ++
 mt7996/mcu.c      | 166 ++++++++++++++++++++++++++++++++++++++++++++++
 mt7996/mt7996.h   |   2 +
 7 files changed, 206 insertions(+)

diff --git a/mt76_connac_mcu.h b/mt76_connac_mcu.h
index 262abf8..42246fb 100644
--- a/mt76_connac_mcu.h
+++ b/mt76_connac_mcu.h
@@ -1229,6 +1229,7 @@ enum {
 	MCU_UNI_CMD_VOW = 0x37,
 	MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40,
 	MCU_UNI_CMD_TESTMODE_CTRL = 0x46,
+	MCU_UNI_CMD_PRECAL_RESULT = 0x47,
 	MCU_UNI_CMD_RRO = 0x57,
 	MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58,
 	MCU_UNI_CMD_ASSERT_DUMP = 0x6f,
diff --git a/mt7996/eeprom.c b/mt7996/eeprom.c
index 85d9e05..bee4a4b 100644
--- a/mt7996/eeprom.c
+++ b/mt7996/eeprom.c
@@ -330,6 +330,25 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy)
 	return mt7996_eeprom_parse_band_config(phy);
 }
 
+static int mt7996_eeprom_load_precal(struct mt7996_dev *dev)
+{
+	struct mt76_dev *mdev = &dev->mt76;
+	u8 *eeprom = mdev->eeprom.data;
+	u32 offs = MT_EE_DO_PRE_CAL;
+	u32 size, val = eeprom[offs];
+
+	if (!dev->flash_mode || !val)
+		return 0;
+
+	size = MT_EE_CAL_GROUP_SIZE + MT_EE_CAL_DPD_SIZE;
+
+	dev->cal = devm_kzalloc(mdev->dev, size, GFP_KERNEL);
+	if (!dev->cal)
+		return -ENOMEM;
+
+	return mt76_get_of_eeprom(mdev, dev->cal, MT_EE_PRECAL, size);
+}
+
 int mt7996_eeprom_init(struct mt7996_dev *dev)
 {
 	int ret;
@@ -347,6 +366,10 @@ int mt7996_eeprom_init(struct mt7996_dev *dev)
 			return ret;
 	}
 
+	ret = mt7996_eeprom_load_precal(dev);
+	if (ret)
+		return ret;
+
 	ret = mt7996_eeprom_parse_hw_cap(dev, &dev->phy);
 	if (ret < 0)
 		return ret;
diff --git a/mt7996/eeprom.h b/mt7996/eeprom.h
index 20dd877..0f3f31d 100644
--- a/mt7996/eeprom.h
+++ b/mt7996/eeprom.h
@@ -25,6 +25,8 @@ enum mt7996_eeprom_field {
 	MT_EE_TX0_POWER_6G =	0x1310,
 
 	__MT_EE_MAX =	0x1dff,
+	/* 0x1e10 ~ 0x2d644 used to save group cal data */
+	MT_EE_PRECAL =		0x1e10,
 };
 
 #define MT_EE_WIFI_CONF0_TX_PATH		GENMASK(2, 0)
diff --git a/mt7996/init.c b/mt7996/init.c
index fe3ba9a..a47654b 100644
--- a/mt7996/init.c
+++ b/mt7996/init.c
@@ -675,6 +675,12 @@ static int mt7996_init_hardware(struct mt7996_dev *dev)
 	if (ret < 0)
 		return ret;
 
+	if (dev->flash_mode) {
+		ret = mt7996_mcu_apply_group_cal(dev);
+		if (ret)
+			return ret;
+	}
+
 	/* Beacon and mgmt frames should occupy wcid 0 */
 	idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA);
 	if (idx)
diff --git a/mt7996/main.c b/mt7996/main.c
index e5627c9..d40d304 100644
--- a/mt7996/main.c
+++ b/mt7996/main.c
@@ -312,6 +312,12 @@ int mt7996_set_channel(struct mt7996_phy *phy)
 
 	mt76_set_channel(phy->mt76);
 
+	if (dev->flash_mode) {
+		ret = mt7996_mcu_apply_tx_dpd(phy);
+		if (ret)
+			goto out;
+	}
+
 	ret = mt7996_mcu_set_chan_info(phy, UNI_CHANNEL_SWITCH);
 	if (ret)
 		goto out;
diff --git a/mt7996/mcu.c b/mt7996/mcu.c
index 2a52efa..d59d677 100644
--- a/mt7996/mcu.c
+++ b/mt7996/mcu.c
@@ -3382,6 +3382,172 @@ int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num)
 	return 0;
 }
 
+static int mt7996_mcu_set_pre_cal(struct mt7996_dev *dev, u32 idx,
+				  u8 *cal, u32 len, u32 cal_id)
+{
+#define PRECAL_CMD_PRE_CAL_RESULT	0x0
+	struct {
+		/* fixed field */
+		u8 action;
+		u8 dest;
+		u8 attribute;
+		u8 tag_num;
+
+		__le16 tag;
+		__le16 len;
+
+		__le32 cal_id;
+		s8 precal;
+		u8 band;
+		u8 rsv[2];
+		__le32 idx;
+		__le32 cal_len;
+	} req = {
+		.tag = cpu_to_le16(PRECAL_CMD_PRE_CAL_RESULT),
+		.len = cpu_to_le16(sizeof(req) - 4 + len),
+		.cal_id = cpu_to_le32(cal_id),
+		.idx = cpu_to_le32(idx),
+		.cal_len = cpu_to_le32(len),
+	};
+	struct sk_buff *skb;
+
+	if (!len)
+		return 0;
+
+	skb = mt76_mcu_msg_alloc(&dev->mt76, NULL, sizeof(req) + len);
+	if (!skb)
+		return -ENOMEM;
+
+	skb_put_data(skb, &req, sizeof(req));
+	skb_put_data(skb, cal, len);
+
+	return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WM_UNI_CMD(PRECAL_RESULT), false);
+}
+
+int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev)
+{
+	u8 *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
+	u32 idx = 0, total_idx = MT_EE_CAL_GROUP_SIZE / MT_EE_CAL_UNIT;
+	u32 offs = MT_EE_DO_PRE_CAL;
+	int ret = 0;
+
+	if (!(eeprom[offs] & MT_EE_WIFI_CAL_GROUP))
+		return 0;
+
+	for (idx = 0; idx < total_idx; idx++, cal += MT_EE_CAL_UNIT) {
+		ret = mt7996_mcu_set_pre_cal(dev, idx, cal, MT_EE_CAL_UNIT, RF_PRE_CAL);
+		if (ret)
+			goto out;
+	}
+
+	ret = mt7996_mcu_set_pre_cal(dev, total_idx, cal,
+				     MT_EE_CAL_GROUP_SIZE % MT_EE_CAL_UNIT, RF_PRE_CAL);
+
+out:
+	return ret;
+}
+
+int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy)
+{
+	struct mt7996_dev *dev = phy->dev;
+	struct mt76_phy *mphy = phy->mt76;
+	struct cfg80211_chan_def *chandef = &phy->mt76->chandef;
+	enum nl80211_band band = chandef->chan->band;
+	enum nl80211_chan_width bw = chandef->width;
+	const struct ieee80211_channel *chan_list;
+	u32 cal_id, chan_list_size, base_offset = 0, offs = MT_EE_DO_PRE_CAL;
+	u32 dpd_size_2g, dpd_size_5g, per_chan_size = DPD_PER_CH_BW20_SIZE;
+	u16 channel = ieee80211_frequency_to_channel(chandef->center_freq1);
+	u8 dpd_mask, *cal = dev->cal, *eeprom = dev->mt76.eeprom.data;
+	int idx, i, ret;
+
+	dpd_size_2g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_2GHZ);
+	dpd_size_5g = mt7996_get_dpd_per_band_size(dev, NL80211_BAND_5GHZ);
+
+	switch (band) {
+	case NL80211_BAND_2GHZ:
+		dpd_mask = MT_EE_WIFI_CAL_DPD_2G;
+		/* channel 14 don't need DPD cal */
+		if (channel >= 1 && channel <= 4)
+			channel = 3;
+		else if (channel >= 5 && channel <= 9)
+			channel = 7;
+		else if (channel >= 10 && channel <= 13)
+			channel = 11;
+		else
+			return 0;
+		cal_id = RF_DPD_FLAT_CAL;
+		chan_list = dpd_2g_ch_list_bw20;
+		chan_list_size = dpd_2g_bw20_ch_num;
+		break;
+	case NL80211_BAND_5GHZ:
+		dpd_mask = MT_EE_WIFI_CAL_DPD_5G;
+		cal_id = RF_DPD_FLAT_5G_CAL;
+		chan_list = mphy->sband_5g.sband.channels;
+		chan_list_size = mphy->sband_5g.sband.n_channels;
+		base_offset += dpd_size_2g;
+		if (bw == NL80211_CHAN_WIDTH_160) {
+			base_offset += mphy->sband_5g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
+			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+			cal_id = RF_DPD_FLAT_5G_MEM_CAL;
+			chan_list = dpd_5g_ch_list_bw160;
+			chan_list_size = dpd_5g_bw160_ch_num;
+		} else if (bw > NL80211_CHAN_WIDTH_20) {
+			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
+			channel -= 2;
+		}
+		break;
+	case NL80211_BAND_6GHZ:
+		dpd_mask = MT_EE_WIFI_CAL_DPD_6G;
+		cal_id = RF_DPD_FLAT_6G_CAL;
+		chan_list = mphy->sband_6g.sband.channels;
+		chan_list_size = mphy->sband_6g.sband.n_channels;
+		base_offset += dpd_size_2g + dpd_size_5g;
+		if (bw == NL80211_CHAN_WIDTH_160) {
+			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE;
+			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
+			chan_list = dpd_6g_ch_list_bw160;
+			chan_list_size = dpd_6g_bw160_ch_num;
+		} else if (bw == NL80211_CHAN_WIDTH_320) {
+			base_offset += mphy->sband_6g.sband.n_channels * DPD_PER_CH_BW20_SIZE +
+				       dpd_6g_bw160_ch_num * DPD_PER_CH_GT_BW20_SIZE;
+			per_chan_size = DPD_PER_CH_GT_BW20_SIZE;
+			cal_id = RF_DPD_FLAT_6G_MEM_CAL;
+			chan_list = dpd_6g_ch_list_bw320;
+			chan_list_size = dpd_6g_bw320_ch_num;
+		} else if (bw > NL80211_CHAN_WIDTH_20) {
+			/* apply (center channel - 2)'s dpd cal data for bw 40/80 channels */
+			channel -= 2;
+		}
+		break;
+	default:
+		dpd_mask = 0;
+		break;
+	}
+
+	if (!(eeprom[offs] & dpd_mask))
+		return 0;
+
+	for (idx = 0; idx < chan_list_size; idx++)
+		if (channel == chan_list[idx].hw_value)
+			break;
+	if (idx == chan_list_size)
+		return -EINVAL;
+
+	cal += MT_EE_CAL_GROUP_SIZE + base_offset + idx * per_chan_size;
+
+	for (i = 0; i < per_chan_size / MT_EE_CAL_UNIT; i++) {
+		ret = mt7996_mcu_set_pre_cal(dev, i, cal, MT_EE_CAL_UNIT, cal_id);
+		if (ret)
+			return ret;
+
+		cal += MT_EE_CAL_UNIT;
+	}
+
+	return ret;
+}
+
 int mt7996_mcu_get_chip_config(struct mt7996_dev *dev, u32 *cap)
 {
 #define NIC_CAP	3
diff --git a/mt7996/mt7996.h b/mt7996/mt7996.h
index 6ef6bad..c16bc8b 100644
--- a/mt7996/mt7996.h
+++ b/mt7996/mt7996.h
@@ -607,6 +607,8 @@ void mt7996_mcu_rx_event(struct mt7996_dev *dev, struct sk_buff *skb);
 void mt7996_mcu_exit(struct mt7996_dev *dev);
 int mt7996_mcu_set_tx_power_ctrl(struct mt7996_phy *phy, u8 power_ctrl_id, u8 data);
 int mt7996_mcu_get_tx_power_info(struct mt7996_phy *phy, u8 category, void *event);
+int mt7996_mcu_apply_group_cal(struct mt7996_dev *dev);
+int mt7996_mcu_apply_tx_dpd(struct mt7996_phy *phy);
 int mt7996_mcu_set_scs(struct mt7996_phy *phy, u8 enable);
 void mt7996_mcu_scs_sta_poll(struct work_struct *work);
 #ifdef CONFIG_NL80211_TESTMODE
-- 
2.18.0

