// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2017, 2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2021, Linaro Ltd.
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-dp.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <dt-bindings/phy/phy.h>
#include "phy-qcom-qmp-dp-phy.h"
#include "phy-qcom-qmp-qserdes-com-v4.h"
#include "phy-qcom-qmp-qserdes-com-v6.h"
/* EDP_PHY registers */
#define DP_PHY_CFG 0x0010
#define DP_PHY_CFG_1 0x0014
#define DP_PHY_PD_CTL 0x001c
#define DP_PHY_MODE 0x0020
#define DP_PHY_AUX_CFG0 0x0024
#define DP_PHY_AUX_CFG1 0x0028
#define DP_PHY_AUX_CFG2 0x002C
#define DP_PHY_AUX_CFG3 0x0030
#define DP_PHY_AUX_CFG4 0x0034
#define DP_PHY_AUX_CFG5 0x0038
#define DP_PHY_AUX_CFG6 0x003C
#define DP_PHY_AUX_CFG7 0x0040
#define DP_PHY_AUX_CFG8 0x0044
#define DP_PHY_AUX_CFG9 0x0048
#define DP_PHY_AUX_INTERRUPT_MASK 0x0058
#define DP_PHY_VCO_DIV 0x0074
#define DP_PHY_TX0_TX1_LANE_CTL 0x007c
#define DP_PHY_TX2_TX3_LANE_CTL 0x00a0
#define DP_PHY_STATUS 0x00e0
/* LANE_TXn registers */
#define TXn_CLKBUF_ENABLE 0x0000
#define TXn_TX_EMP_POST1_LVL 0x0004
#define TXn_TX_DRV_LVL 0x0014
#define TXn_TX_DRV_LVL_OFFSET 0x0018
#define TXn_RESET_TSYNC_EN 0x001c
#define TXn_LDO_CONFIG 0x0084
#define TXn_TX_BAND 0x0028
#define TXn_RES_CODE_LANE_OFFSET_TX0 0x0044
#define TXn_RES_CODE_LANE_OFFSET_TX1 0x0048
#define TXn_TRANSCEIVER_BIAS_EN 0x0054
#define TXn_HIGHZ_DRVR_EN 0x0058
#define TXn_TX_POL_INV 0x005c
#define TXn_LANE_MODE_1 0x0064
#define TXn_TRAN_DRVR_EMP_EN 0x0078
struct qcom_edp_swing_pre_emph_cfg {
const u8 (*swing_hbr_rbr)[4][4];
const u8 (*swing_hbr3_hbr2)[4][4];
const u8 (*pre_emphasis_hbr_rbr)[4][4];
const u8 (*pre_emphasis_hbr3_hbr2)[4][4];
};
struct qcom_edp;
struct phy_ver_ops {
int (*com_power_on)(const struct qcom_edp *edp);
int (*com_resetsm_cntrl)(const struct qcom_edp *edp);
int (*com_bias_en_clkbuflr)(const struct qcom_edp *edp);
int (*com_configure_pll)(const struct qcom_edp *edp);
int (*com_configure_ssc)(const struct qcom_edp *edp);
};
struct qcom_edp_phy_cfg {
bool is_edp;
const struct qcom_edp_swing_pre_emph_cfg *swing_pre_emph_cfg;
const struct phy_ver_ops *ver_ops;
};
struct qcom_edp {
struct device *dev;
const struct qcom_edp_phy_cfg *cfg;
struct phy *phy;
void __iomem *edp;
void __iomem *tx0;
void __iomem *tx1;
void __iomem *pll;
struct clk_hw dp_link_hw;
struct clk_hw dp_pixel_hw;
struct phy_configure_opts_dp dp_opts;
struct clk_bulk_data clks[2];
struct regulator_bulk_data supplies[2];
bool is_edp;
};
static const u8 dp_swing_hbr_rbr[4][4] = {
{ 0x08, 0x0f, 0x16, 0x1f },
{ 0x11, 0x1e, 0x1f, 0xff },
{ 0x16, 0x1f, 0xff, 0xff },
{ 0x1f, 0xff, 0xff, 0xff }
};
static const u8 dp_pre_emp_hbr_rbr[4][4] = {
{ 0x00, 0x0d, 0x14, 0x1a },
{ 0x00, 0x0e, 0x15, 0xff },
{ 0x00, 0x0e, 0xff, 0xff },
{ 0x03, 0xff, 0xff, 0xff }
};
static const u8 dp_swing_hbr2_hbr3[4][4] = {
{ 0x02, 0x12, 0x16, 0x1a },
{ 0x09, 0x19, 0x1f, 0xff },
{ 0x10, 0x1f, 0xff, 0xff },
{ 0x1f, 0xff, 0xff, 0xff }
};
static const u8 dp_pre_emp_hbr2_hbr3[4][4] = {
{ 0x00, 0x0c, 0x15, 0x1b },
{ 0x02, 0x0e, 0x16, 0xff },
{ 0x02, 0x11, 0xff, 0xff },
{ 0x04, 0xff, 0xff, 0xff }
};
static const struct qcom_edp_swing_pre_emph_cfg dp_phy_swing_pre_emph_cfg = {
.swing_hbr_rbr = &dp_swing_hbr_rbr,
.swing_hbr3_hbr2 = &dp_swing_hbr2_hbr3,
.pre_emphasis_hbr_rbr = &dp_pre_emp_hbr_rbr,
.pre_emphasis_hbr3_hbr2 = &dp_pre_emp_hbr2_hbr3,
};
static const u8 edp_swing_hbr_rbr[4][4] = {
{ 0x07, 0x0f, 0x16, 0x1f },
{ 0x0d, 0x16, 0x1e, 0xff },
{ 0x11, 0x1b, 0xff, 0xff },
{ 0x16, 0xff, 0xff, 0xff }
};
static const u8 edp_pre_emp_hbr_rbr[4][4] = {
{ 0x05, 0x12