root/drivers/phy/rockchip/phy-rockchip-samsung-dcphy.c
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2025 Rockchip Electronics Co.Ltd
 * Author:
 *      Guochun Huang <hero.huang@rock-chips.com>
 */

#include <dt-bindings/phy/phy.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/hw_bitfield.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/reset.h>

#define BIAS_CON0               0x0000
#define I_RES_CNTL_MASK         GENMASK(6, 4)
#define I_RES_CNTL(x)           FIELD_PREP(I_RES_CNTL_MASK, x)
#define I_RES_059_2UA           I_RES_CNTL(0)
#define I_RES_100_2UA           I_RES_CNTL(1)
#define I_RES_094_2UA           I_RES_CNTL(2)
#define I_RES_113_8UA           I_RES_CNTL(3)
#define I_RES_089_7UA           I_RES_CNTL(4)
#define I_RES_111_8UA           I_RES_CNTL(5)
#define I_RES_108_2UA           I_RES_CNTL(6)
#define I_RES_120_8UA           I_RES_CNTL(7)
#define I_DEV_SEL_MASK          GENMASK(1, 0)
#define I_DEV_SEL(x)            FIELD_PREP(I_DEV_SEL_MASK, x)
#define I_DEV_DIV_6             I_DEV_SEL(0)
#define I_DEV_DIV_12            I_DEV_SEL(1)
#define I_DEV_DIV_20            I_DEV_SEL(2)
#define I_DEV_DIV_40            I_DEV_SEL(3)

#define BIAS_CON1               0x0004
#define I_VBG_SEL_MASK          GENMASK(9, 8)
#define I_VBG_SEL(x)            FIELD_PREP(I_VBG_SEL_MASK, x)
#define I_VBG_SEL_780MV         I_VBG_SEL(0)
#define I_VBG_SEL_820MV         I_VBG_SEL(1)
#define I_VBG_SEL_860MV         I_VBG_SEL(2)
#define I_VBG_SEL_900MV         I_VBG_SEL(3)
#define I_BGR_VREF_SEL_MASK     GENMASK(5, 4)
#define I_BGR_VREF_SEL(x)       FIELD_PREP(I_BGR_VREF_SEL_MASK, x)
#define I_BGR_VREF_810MV        I_BGR_VREF_SEL(0)
#define I_BGR_VREF_820MV        I_BGR_VREF_SEL(1)
#define I_BGR_VREF_830MV        I_BGR_VREF_SEL(2)
#define I_BGR_VREF_840MV        I_BGR_VREF_SEL(3)
#define I_LADDER_SEL_MASK       GENMASK(2, 0)
#define I_LADDER_SEL(x)         FIELD_PREP(I_LADDER_SEL_MASK, x)
#define I_LADDER_1_00V          I_LADDER_SEL(0)
#define I_LADDER_0_96V          I_LADDER_SEL(1)
#define I_LADDER_0_92V          I_LADDER_SEL(2)
#define I_LADDER_0_88V          I_LADDER_SEL(3)
#define I_LADDER_0_84V          I_LADDER_SEL(4)
#define I_LADDER_0_80V          I_LADDER_SEL(5)
#define I_LADDER_0_76V          I_LADDER_SEL(6)
#define I_LADDER_0_72V          I_LADDER_SEL(7)

/*
 * Voltage corrections around reference voltages
 * The selection between the 400-based or 200-based values for REG_400M
 * is done by the hw depending on I_MUX below being 400MV or 200MV.
 */
#define BIAS_CON2               0x0008
#define REG_325M_MASK           GENMASK(14, 12)
#define REG_325M(x)             FIELD_PREP(REG_325M_MASK, x)
#define REG_325M_295MV          REG_325M(0)
#define REG_325M_305MV          REG_325M(1)
#define REG_325M_315MV          REG_325M(2)
#define REG_325M_325MV          REG_325M(3)
#define REG_325M_335MV          REG_325M(4)
#define REG_325M_345MV          REG_325M(5)
#define REG_325M_355MV          REG_325M(6)
#define REG_325M_365MV          REG_325M(7)
#define REG_LP_400M_MASK        GENMASK(10, 8)
#define REG_LP_400M(x)          FIELD_PREP(REG_LP_400M_MASK, x)
#define REG_LP_400M_380MV       REG_LP_400M(0)
#define REG_LP_400M_390MV       REG_LP_400M(1)
#define REG_LP_400M_400MV       REG_LP_400M(2)
#define REG_LP_400M_410MV       REG_LP_400M(3)
#define REG_LP_400M_420MV       REG_LP_400M(4)
#define REG_LP_400M_430MV       REG_LP_400M(5)
#define REG_LP_400M_440MV       REG_LP_400M(6)
#define REG_LP_400M_450MV       REG_LP_400M(7)
#define REG_400M_MASK           GENMASK(6, 4)
#define REG_400M(x)             FIELD_PREP(REG_400M_MASK, x)
#define REG_400M_380MV          REG_400M(0)
#define REG_400M_390MV          REG_400M(1)
#define REG_400M_400MV          REG_400M(2)
#define REG_400M_410MV          REG_400M(3)
#define REG_400M_420MV          REG_400M(4)
#define REG_400M_430MV          REG_400M(5)
#define REG_400M_440MV          REG_400M(6)
#define REG_400M_450MV          REG_400M(7)
#define REG_400M_230MV          REG_400M(0)
#define REG_400M_220MV          REG_400M(1)
#define REG_400M_210MV          REG_400M(2)
#define REG_400M_200MV          REG_400M(3)
#define REG_400M_190MV          REG_400M(4)
#define REG_400M_180MV          REG_400M(5)
#define REG_400M_170MV          REG_400M(6)
#define REG_400M_160MV          REG_400M(7)
#define REG_645M_MASK           GENMASK(2, 0)
#define REG_645M(x)             FIELD_PREP(REG_645M_MASK, x)
#define REG_645M_605MV          REG_645M(0)
#define REG_645M_625MV          REG_645M(1)
#define REG_645M_635MV          REG_645M(2)
#define REG_645M_645MV          REG_645M(3)
#define REG_645M_655MV          REG_645M(4)
#define REG_645M_665MV          REG_645M(5)
#define REG_645M_685MV          REG_645M(6)
#define REG_645M_725MV          REG_645M(7)

#define BIAS_CON4               0x0010
#define I_MUX_SEL_MASK          GENMASK(6, 5)
#define I_MUX_SEL(x)            FIELD_PREP(I_MUX_SEL_MASK, x)
#define I_MUX_400MV             I_MUX_SEL(0)
#define I_MUX_200MV             I_MUX_SEL(1)
#define I_MUX_530MV             I_MUX_SEL(2)

#define PLL_CON0                0x0100
#define PLL_EN                  BIT(12)
#define S_MASK                  GENMASK(10, 8)
#define S(x)                    FIELD_PREP(S_MASK, x)
#define P_MASK                  GENMASK(5, 0)
#define P(x)                    FIELD_PREP(P_MASK, x)
#define PLL_CON1                0x0104
#define PLL_CON2                0x0108
#define M_MASK                  GENMASK(9, 0)
#define M(x)                    FIELD_PREP(M_MASK, x)
#define PLL_CON3                0x010c
#define MRR_MASK                GENMASK(13, 8)
#define MRR(x)                  FIELD_PREP(MRR_MASK, x)
#define MFR_MASK                GENMASK(7, 0)
#define MFR(x)                  FIELD_PREP(MFR_MASK, x)
#define PLL_CON4                0x0110
#define SSCG_EN                 BIT(11)
#define PLL_CON5                0x0114
#define RESET_N_SEL             BIT(10)
#define PLL_ENABLE_SEL          BIT(8)
#define PLL_CON6                0x0118
#define PLL_CON7                0x011c
#define PLL_LOCK_CNT(x)         FIELD_PREP(GENMASK(15, 0), x)
#define PLL_CON8                0x0120
#define PLL_STB_CNT(x)          FIELD_PREP(GENMASK(15, 0), x)
#define PLL_STAT0               0x0140
#define PLL_LOCK                BIT(0)

#define DPHY_MC_GNR_CON0        0x0300
#define PHY_READY               BIT(1)
#define PHY_ENABLE              BIT(0)
#define DPHY_MC_GNR_CON1        0x0304
#define T_PHY_READY(x)          FIELD_PREP(GENMASK(15, 0), x)
#define DPHY_MC_ANA_CON0        0x0308
#define EDGE_CON(x)             FIELD_PREP(GENMASK(14, 12), x)
#define EDGE_CON_DIR(x)         FIELD_PREP(BIT(9), x)
#define EDGE_CON_EN             BIT(8)
#define RES_UP(x)               FIELD_PREP(GENMASK(7, 4), x)
#define RES_DN(x)               FIELD_PREP(GENMASK(3, 0), x)
#define DPHY_MC_ANA_CON1        0x030c
#define DPHY_MC_ANA_CON2        0x0310
#define HS_VREG_AMP_ICON(x)     FIELD_PREP(GENMASK(1, 0), x)
#define DPHY_MC_TIME_CON0       0x0330
#define HSTX_CLK_SEL            BIT(12)
#define T_LPX(x)                FIELD_PREP(GENMASK(11, 4), x)
#define DPHY_MC_TIME_CON1       0x0334
#define T_CLK_ZERO(x)           FIELD_PREP(GENMASK(15, 8), x)
#define T_CLK_PREPARE(x)        FIELD_PREP(GENMASK(7, 0), x)
#define DPHY_MC_TIME_CON2       0x0338
#define T_HS_EXIT(x)            FIELD_PREP(GENMASK(15, 8), x)
#define T_CLK_TRAIL(x)          FIELD_PREP(GENMASK(7, 0), x)
#define DPHY_MC_TIME_CON3       0x033c
#define T_CLK_POST(x)           FIELD_PREP(GENMASK(7, 0), x)
#define DPHY_MC_TIME_CON4       0x0340
#define T_ULPS_EXIT(x)          FIELD_PREP(GENMASK(9, 0), x)
#define DPHY_MC_DESKEW_CON0     0x0350
#define SKEW_CAL_RUN_TIME(x)    FIELD_PREP(GENMASK(15, 12), x)

#define SKEW_CAL_INIT_RUN_TIME(x)       FIELD_PREP(GENMASK(11, 8), x)
#define SKEW_CAL_INIT_WAIT_TIME(x)      FIELD_PREP(GENMASK(7, 4), x)
#define SKEW_CAL_EN                     BIT(0)

#define COMBO_MD0_GNR_CON0      0x0400
#define COMBO_MD0_GNR_CON1      0x0404
#define COMBO_MD0_ANA_CON0      0x0408
#define COMBO_MD0_ANA_CON1      0x040c
#define COMBO_MD0_ANA_CON2      0x0410

#define COMBO_MD0_TIME_CON0     0x0430
#define COMBO_MD0_TIME_CON1     0x0434
#define COMBO_MD0_TIME_CON2     0x0438
#define COMBO_MD0_TIME_CON3     0x043c
#define COMBO_MD0_TIME_CON4     0x0440
#define COMBO_MD0_DATA_CON0     0x0444

#define COMBO_MD1_GNR_CON0      0x0500
#define COMBO_MD1_GNR_CON1      0x0504
#define COMBO_MD1_ANA_CON0      0x0508
#define COMBO_MD1_ANA_CON1      0x050c
#define COMBO_MD1_ANA_CON2      0x0510
#define COMBO_MD1_TIME_CON0     0x0530
#define COMBO_MD1_TIME_CON1     0x0534
#define COMBO_MD1_TIME_CON2     0x0538
#define COMBO_MD1_TIME_CON3     0x053c
#define COMBO_MD1_TIME_CON4     0x0540
#define COMBO_MD1_DATA_CON0     0x0544

#define COMBO_MD2_GNR_CON0      0x0600
#define COMBO_MD2_GNR_CON1      0x0604
#define COMBO_MD2_ANA_CON0      0X0608
#define COMBO_MD2_ANA_CON1      0X060c
#define COMBO_MD2_ANA_CON2      0X0610
#define COMBO_MD2_TIME_CON0     0x0630
#define COMBO_MD2_TIME_CON1     0x0634
#define COMBO_MD2_TIME_CON2     0x0638
#define COMBO_MD2_TIME_CON3     0x063c
#define COMBO_MD2_TIME_CON4     0x0640
#define COMBO_MD2_DATA_CON0     0x0644

#define DPHY_MD3_GNR_CON0       0x0700
#define DPHY_MD3_GNR_CON1       0x0704
#define DPHY_MD3_ANA_CON0       0X0708
#define DPHY_MD3_ANA_CON1       0X070c
#define DPHY_MD3_ANA_CON2       0X0710
#define DPHY_MD3_TIME_CON0      0x0730
#define DPHY_MD3_TIME_CON1      0x0734
#define DPHY_MD3_TIME_CON2      0x0738
#define DPHY_MD3_TIME_CON3      0x073c
#define DPHY_MD3_TIME_CON4      0x0740
#define DPHY_MD3_DATA_CON0      0x0744

#define T_LP_EXIT_SKEW(x)       FIELD_PREP(GENMASK(3, 2), x)
#define T_LP_ENTRY_SKEW(x)      FIELD_PREP(GENMASK(1, 0), x)
#define T_HS_ZERO(x)            FIELD_PREP(GENMASK(15, 8), x)
#define T_HS_PREPARE(x)         FIELD_PREP(GENMASK(7, 0), x)
#define T_HS_EXIT(x)            FIELD_PREP(GENMASK(15, 8), x)
#define T_HS_TRAIL(x)           FIELD_PREP(GENMASK(7, 0), x)
#define T_TA_GET(x)             FIELD_PREP(GENMASK(7, 4), x)
#define T_TA_GO(x)              FIELD_PREP(GENMASK(3, 0), x)

/* MIPI_CDPHY_GRF registers */
#define MIPI_DCPHY_GRF_CON0             0x0000
#define S_CPHY_MODE                     FIELD_PREP_WM16(BIT(3), 1)
#define M_CPHY_MODE                     FIELD_PREP_WM16(BIT(0), 1)

enum hs_drv_res_ohm {
        STRENGTH_30_OHM = 0x8,
        STRENGTH_31_2_OHM,
        STRENGTH_32_5_OHM,
        STRENGTH_34_OHM,
        STRENGTH_35_5_OHM,
        STRENGTH_37_OHM,
        STRENGTH_39_OHM,
        STRENGTH_41_OHM,
        STRENGTH_43_OHM = 0x0,
        STRENGTH_46_OHM,
        STRENGTH_49_OHM,
        STRENGTH_52_OHM,
        STRENGTH_56_OHM,
        STRENGTH_60_OHM,
        STRENGTH_66_OHM,
        STRENGTH_73_OHM,
};

struct hs_drv_res_cfg {
        enum hs_drv_res_ohm clk_hs_drv_up_ohm;
        enum hs_drv_res_ohm clk_hs_drv_down_ohm;
        enum hs_drv_res_ohm data_hs_drv_up_ohm;
        enum hs_drv_res_ohm data_hs_drv_down_ohm;
};

struct samsung_mipi_dcphy_plat_data {
        const struct hs_drv_res_cfg *dphy_hs_drv_res_cfg;
        u32 dphy_tx_max_lane_kbps;
};

struct samsung_mipi_dcphy {
        struct device *dev;
        struct clk *ref_clk;
        struct clk *pclk;
        struct regmap *regmap;
        struct regmap *grf_regmap;
        struct reset_control *m_phy_rst;
        struct reset_control *s_phy_rst;
        struct reset_control *apb_rst;
        struct reset_control *grf_apb_rst;
        unsigned int lanes;
        struct phy *phy;
        u8 type;

        const struct samsung_mipi_dcphy_plat_data *pdata;
        struct {
                unsigned long long rate;
                u8 prediv;
                u16 fbdiv;
                long dsm;
                u8 scaler;

                bool ssc_en;
                u8 mfr;
                u8 mrr;
        } pll;
};

struct samsung_mipi_dphy_timing {
        unsigned int max_lane_mbps;
        u8 clk_prepare;
        u8 clk_zero;
        u8 clk_post;
        u8 clk_trail_eot;
        u8 hs_prepare;
        u8 hs_zero;
        u8 hs_trail_eot;
        u8 lpx;
        u8 hs_exit;
        u8 hs_settle;
};

/*
 * Timing values taken from rk3588 vendor kernel.
 * Not documented in hw documentation.
 */
static const
struct samsung_mipi_dphy_timing samsung_mipi_dphy_timing_table[] = {
        {6500, 32, 117, 31, 28, 30, 56, 27, 24, 44, 37},
        {6490, 32, 116, 31, 28, 30, 56, 27, 24, 44, 37},
        {6480, 32, 116, 31, 28, 30, 56, 27, 24, 44, 37},
        {6470, 32, 116, 31, 28, 30, 56, 27, 24, 44, 37},
        {6460, 32, 116, 31, 28, 30, 56, 27, 24, 44, 37},
        {6450, 32, 115, 31, 28, 30, 56, 27, 24, 44, 37},
        {6440, 32, 115, 31, 28, 30, 56, 27, 24, 44, 37},
        {6430, 31, 116, 31, 28, 30, 55, 27, 24, 44, 37},
        {6420, 31, 116, 31, 28, 30, 55, 27, 24, 44, 37},
        {6410, 31, 116, 31, 27, 30, 55, 27, 24, 44, 37},
        {6400, 31, 115, 30, 27, 30, 55, 27, 23, 43, 36},
        {6390, 31, 115, 30, 27, 30, 55, 27, 23, 43, 36},
        {6380, 31, 115, 30, 27, 30, 55, 27, 23, 43, 36},
        {6370, 31, 115, 30, 27, 30, 55, 26, 23, 43, 36},
        {6360, 31, 114, 30, 27, 30, 54, 26, 23, 43, 36},
        {6350, 31, 114, 30, 27, 30, 54, 26, 23, 43, 36},
        {6340, 31, 114, 30, 27, 30, 54, 26, 23, 43, 36},
        {6330, 31, 114, 30, 27, 30, 54, 26, 23, 43, 36},
        {6320, 31, 113, 30, 27, 30, 54, 26, 23, 43, 36},
        {6310, 31, 113, 30, 27, 30, 54, 26, 23, 43, 36},
        {6300, 31, 113, 30, 27, 30, 54, 26, 23, 43, 36},
        {6290, 31, 113, 30, 27, 29, 54, 26, 23, 43, 36},
        {6280, 31, 112, 30, 27, 29, 54, 26, 23, 43, 36},
        {6270, 31, 112, 30, 27, 29, 54, 26, 23, 43, 36},
        {6260, 31, 112, 30, 27, 29, 54, 26, 23, 43, 36},
        {6250, 31, 112, 30, 27, 29, 54, 26, 23, 42, 36},
        {6240, 30, 113, 30, 27, 29, 54, 26, 23, 42, 36},
        {6230, 30, 112, 30, 27, 29, 54, 26, 23, 42, 35},
        {6220, 30, 112, 30, 27, 29, 53, 26, 23, 42, 35},
        {6210, 30, 112, 30, 27, 29, 53, 26, 23, 42, 35},
        {6200, 30, 112, 29, 27, 29, 53, 26, 23, 42, 35},
        {6190, 30, 111, 29, 27, 29, 53, 26, 23, 42, 35},
        {6180, 30, 111, 29, 27, 29, 53, 26, 23, 42, 35},
        {6170, 30, 111, 29, 26, 29, 53, 26, 23, 42, 35},
        {6160, 30, 111, 29, 26, 29, 53, 26, 23, 42, 35},
        {6150, 30, 110, 29, 26, 29, 53, 26, 23, 42, 35},
        {6140, 30, 110, 29, 26, 29, 52, 26, 23, 42, 35},
        {6130, 30, 110, 29, 26, 29, 52, 25, 22, 42, 35},
        {6120, 30, 110, 29, 26, 29, 52, 25, 22, 42, 35},
        {6110, 30, 110, 29, 26, 29, 52, 25, 22, 42, 35},
        {6100, 30, 109, 29, 26, 29, 52, 25, 22, 41, 35},
        {6090, 30, 109, 29, 26, 29, 52, 25, 22, 41, 35},
        {6080, 30, 109, 29, 26, 28, 53, 25, 22, 41, 35},
        {6070, 30, 109, 29, 26, 28, 52, 25, 22, 41, 34},
        {6060, 30, 108, 29, 26, 28, 52, 25, 22, 41, 34},
        {6050, 30, 108, 29, 26, 28, 52, 25, 22, 41, 34},
        {6040, 29, 109, 29, 26, 28, 52, 25, 22, 41, 34},
        {6030, 29, 109, 29, 26, 28, 52, 25, 22, 41, 34},
        {6020, 29, 108, 29, 26, 28, 52, 25, 22, 41, 34},
        {6010, 29, 108, 29, 26, 28, 52, 25, 22, 41, 34},
        {6000, 29, 108, 28, 26, 28, 51, 25, 22, 41, 34},
        {5990, 29, 108, 28, 26, 28, 51, 25, 22, 41, 34},
        {5980, 29, 107, 28, 26, 28, 51, 25, 22, 41, 34},
        {5970, 29, 107, 28, 26, 28, 51, 25, 22, 41, 34},
        {5960, 29, 107, 28, 26, 28, 51, 25, 22, 40, 34},
        {5950, 29, 107, 28, 26, 28, 51, 25, 22, 40, 34},
        {5940, 29, 107, 28, 25, 28, 51, 25, 22, 40, 34},
        {5930, 29, 106, 28, 25, 28, 50, 25, 22, 40, 34},
        {5920, 29, 106, 28, 25, 28, 50, 25, 22, 40, 34},
        {5910, 29, 106, 28, 25, 28, 50, 25, 22, 40, 34},
        {5900, 29, 106, 28, 25, 28, 50, 24, 22, 40, 33},
        {5890, 29, 105, 28, 25, 28, 50, 24, 22, 40, 33},
        {5880, 29, 105, 28, 25, 28, 50, 24, 22, 40, 33},
        {5870, 29, 105, 28, 25, 27, 51, 24, 22, 40, 33},
        {5860, 29, 105, 28, 25, 27, 51, 24, 21, 40, 33},
        {5850, 29, 104, 28, 25, 27, 50, 24, 21, 40, 33},
        {5840, 28, 105, 28, 25, 27, 50, 24, 21, 40, 33},
        {5830, 28, 105, 28, 25, 27, 50, 24, 21, 40, 33},
        {5820, 28, 105, 28, 25, 27, 50, 24, 21, 40, 33},
        {5810, 28, 104, 28, 25, 27, 50, 24, 21, 39, 33},
        {5800, 28, 104, 27, 25, 27, 50, 24, 21, 39, 33},
        {5790, 28, 104, 27, 25, 27, 50, 24, 21, 39, 33},
        {5780, 28, 104, 27, 25, 27, 49, 24, 21, 39, 33},
        {5770, 28, 104, 27, 25, 27, 49, 24, 21, 39, 33},
        {5760, 28, 103, 27, 25, 27, 49, 24, 21, 39, 33},
        {5750, 28, 103, 27, 25, 27, 49, 24, 21, 39, 33},
        {5740, 28, 103, 27, 25, 27, 49, 24, 21, 39, 33},
        {5730, 28, 103, 27, 25, 27, 49, 24, 21, 39, 32},
        {5720, 28, 102, 27, 25, 27, 49, 24, 21, 39, 32},
        {5710, 28, 102, 27, 25, 27, 48, 24, 21, 39, 32},
        {5700, 28, 102, 27, 24, 27, 48, 24, 21, 39, 32},
        {5690, 28, 102, 27, 24, 27, 48, 24, 21, 39, 32},
        {5680, 28, 101, 27, 24, 27, 48, 24, 21, 39, 32},
        {5670, 28, 101, 27, 24, 27, 48, 23, 21, 38, 32},
        {5660, 28, 101, 27, 24, 26, 49, 23, 21, 38, 32},
        {5650, 28, 101, 27, 24, 26, 49, 23, 21, 38, 32},
        {5640, 27, 101, 27, 24, 26, 48, 23, 21, 38, 32},
        {5630, 27, 101, 27, 24, 26, 48, 23, 21, 38, 32},
        {5620, 27, 101, 27, 24, 26, 48, 23, 21, 38, 32},
        {5610, 27, 101, 27, 24, 26, 48, 23, 21, 38, 32},
        {5600, 27, 101, 26, 24, 26, 48, 23, 20, 38, 32},
        {5590, 27, 100, 26, 24, 26, 48, 23, 20, 38, 32},
        {5580, 27, 100, 26, 24, 26, 48, 23, 20, 38, 32},
        {5570, 27, 100, 26, 24, 26, 48, 23, 20, 38, 31},
        {5560, 27, 100, 26, 24, 26, 47, 23, 20, 38, 31},
        {5550, 27,  99, 26, 24, 26, 47, 23, 20, 38, 31},
        {5540, 27,  99, 26, 24, 26, 47, 23, 20, 38, 31},
        {5530, 27,  99, 26, 24, 26, 47, 23, 20, 38, 31},
        {5520, 27,  99, 26, 24, 26, 47, 23, 20, 37, 31},
        {5510, 27,  98, 26, 24, 26, 47, 23, 20, 37, 31},
        {5500, 27,  98, 26, 24, 26, 47, 23, 20, 37, 31},
        {5490, 27,  98, 26, 24, 26, 46, 23, 20, 37, 31},
        {5480, 27,  98, 26, 24, 26, 46, 23, 20, 37, 31},
        {5470, 27,  97, 26, 23, 26, 46, 23, 20, 37, 31},
        {5460, 27,  97, 26, 23, 26, 46, 23, 20, 37, 31},
        {5450, 27,  97, 26, 23, 25, 47, 23, 20, 37, 31},
        {5440, 26,  98, 26, 23, 25, 47, 23, 20, 37, 31},
        {5430, 26,  98, 26, 23, 25, 47, 22, 20, 37, 31},
        {5420, 26,  97, 26, 23, 25, 46, 22, 20, 37, 31},
        {5410, 26,  97, 26, 23, 25, 46, 22, 20, 37, 31},
        {5400, 26,  97, 25, 23, 25, 46, 22, 20, 37, 30},
        {5390, 26,  97, 25, 23, 25, 46, 22, 20, 37, 30},
        {5380, 26,  96, 25, 23, 25, 46, 22, 20, 36, 30},
        {5370, 26,  96, 25, 23, 25, 46, 22, 20, 36, 30},
        {5360, 26,  96, 25, 23, 25, 46, 22, 20, 36, 30},
        {5350, 26,  96, 25, 23, 25, 46, 22, 20, 36, 30},
        {5340, 26,  95, 25, 23, 25, 45, 22, 20, 36, 30},
        {5330, 26,  95, 25, 23, 25, 45, 22, 19, 36, 30},
        {5320, 26,  95, 25, 23, 25, 45, 22, 19, 36, 30},
        {5310, 26,  95, 25, 23, 25, 45, 22, 19, 36, 30},
        {5300, 26,  95, 25, 23, 25, 45, 22, 19, 36, 30},
        {5290, 26,  94, 25, 23, 25, 45, 22, 19, 36, 30},
        {5280, 26,  94, 25, 23, 25, 45, 22, 19, 36, 30},
        {5270, 26,  94, 25, 23, 25, 44, 22, 19, 36, 30},
        {5260, 26,  94, 25, 23, 25, 44, 22, 19, 36, 30},
        {5250, 25,  94, 25, 23, 24, 45, 22, 19, 36, 30},
        {5240, 25,  94, 25, 23, 24, 45, 22, 19, 36, 29},
        {5230, 25,  94, 25, 22, 24, 45, 22, 19, 35, 29},
        {5220, 25,  94, 25, 22, 24, 45, 22, 19, 35, 29},
        {5210, 25,  93, 25, 22, 24, 45, 22, 19, 35, 29},
        {5200, 25,  93, 24, 22, 24, 44, 21, 19, 35, 29},
        {5190, 25,  93, 24, 22, 24, 44, 21, 19, 35, 29},
        {5180, 25,  93, 24, 22, 24, 44, 21, 19, 35, 29},
        {5170, 25,  92, 24, 22, 24, 44, 21, 19, 35, 29},
        {5160, 25,  92, 24, 22, 24, 44, 21, 19, 35, 29},
        {5150, 25,  92, 24, 22, 24, 44, 21, 19, 35, 29},
        {5140, 25,  92, 24, 22, 24, 44, 21, 19, 35, 29},
        {5130, 25,  92, 24, 22, 24, 43, 21, 19, 35, 29},
        {5120, 25,  91, 24, 22, 24, 43, 21, 19, 35, 29},
        {5110, 25,  91, 24, 22, 24, 43, 21, 19, 35, 29},
        {5100, 25,  91, 24, 22, 24, 43, 21, 19, 35, 29},
        {5090, 25,  91, 24, 22, 24, 43, 21, 19, 34, 29},
        {5080, 25,  90, 24, 22, 24, 43, 21, 19, 34, 29},
        {5070, 25,  90, 24, 22, 24, 43, 21, 19, 34, 28},
        {5060, 25,  90, 24, 22, 24, 43, 21, 18, 34, 28},
        {5050, 24,  91, 24, 22, 24, 42, 21, 18, 34, 28},
        {5040, 24,  90, 24, 22, 23, 43, 21, 18, 34, 28},
        {5030, 24,  90, 24, 22, 23, 43, 21, 18, 34, 28},
        {5020, 24,  90, 24, 22, 23, 43, 21, 18, 34, 28},
        {5010, 24,  90, 24, 22, 23, 43, 21, 18, 34, 28},
        {5000, 24,  89, 23, 21, 23, 43, 21, 18, 34, 28},
        {4990, 24,  89, 23, 21, 23, 43, 21, 18, 34, 28},
        {4980, 24,  89, 23, 21, 23, 42, 21, 18, 34, 28},
        {4970, 24,  89, 23, 21, 23, 42, 21, 18, 34, 28},
        {4960, 24,  89, 23, 21, 23, 42, 20, 18, 34, 28},
        {4950, 24,  88, 23, 21, 23, 42, 20, 18, 34, 28},
        {4940, 24,  88, 23, 21, 23, 42, 20, 18, 33, 28},
        {4930, 24,  88, 23, 21, 23, 42, 20, 18, 33, 28},
        {4920, 24,  88, 23, 21, 23, 42, 20, 18, 33, 28},
        {4910, 24,  87, 23, 21, 23, 41, 20, 18, 33, 28},
        {4900, 24,  87, 23, 21, 23, 41, 20, 18, 33, 27},
        {4890, 24,  87, 23, 21, 23, 41, 20, 18, 33, 27},
        {4880, 24,  87, 23, 21, 23, 41, 20, 18, 33, 27},
        {4870, 24,  86, 23, 21, 23, 41, 20, 18, 33, 27},
        {4860, 24,  86, 23, 21, 23, 41, 20, 18, 33, 27},
        {4850, 23,  87, 23, 21, 23, 41, 20, 18, 33, 27},
        {4840, 23,  87, 23, 21, 23, 40, 20, 18, 33, 27},
        {4830, 23,  86, 23, 21, 22, 41, 20, 18, 33, 27},
        {4820, 23,  86, 23, 21, 22, 41, 20, 18, 33, 27},
        {4810, 23,  86, 23, 21, 22, 41, 20, 18, 33, 27},
        {4800, 23,  86, 22, 21, 22, 41, 20, 17, 32, 27},
        {4790, 23,  86, 22, 21, 22, 41, 20, 17, 32, 27},
        {4780, 23,  85, 22, 21, 22, 41, 20, 17, 32, 27},
        {4770, 23,  85, 22, 21, 22, 41, 20, 17, 32, 27},
        {4760, 23,  85, 22, 20, 22, 40, 20, 17, 32, 27},
        {4750, 23,  85, 22, 20, 22, 40, 20, 17, 32, 27},
        {4740, 23,  84, 22, 20, 22, 40, 20, 17, 32, 26},
        {4730, 23,  84, 22, 20, 22, 40, 19, 17, 32, 26},
        {4720, 23,  84, 22, 20, 22, 40, 19, 17, 32, 26},
        {4710, 23,  84, 22, 20, 22, 40, 19, 17, 32, 26},
        {4700, 23,  83, 22, 20, 22, 40, 19, 17, 32, 26},
        {4690, 23,  83, 22, 20, 22, 39, 19, 17, 32, 26},
        {4680, 23,  83, 22, 20, 22, 39, 19, 17, 32, 26},
        {4670, 23,  83, 22, 20, 22, 39, 19, 17, 32, 26},
        {4660, 23,  82, 22, 20, 22, 39, 19, 17, 32, 26},
        {4650, 22,  83, 22, 20, 22, 39, 19, 17, 31, 26},
        {4640, 22,  83, 22, 20, 22, 39, 19, 17, 31, 26},
        {4630, 22,  83, 22, 20, 22, 39, 19, 17, 31, 26},
        {4620, 22,  83, 22, 20, 21, 39, 19, 17, 31, 26},
        {4610, 22,  82, 22, 20, 21, 39, 19, 17, 31, 26},
        {4600, 22,  82, 21, 20, 21, 39, 19, 17, 31, 26},
        {4590, 22,  82, 21, 20, 21, 39, 19, 17, 31, 26},
        {4580, 22,  82, 21, 20, 21, 39, 19, 17, 31, 26},
        {4570, 22,  81, 21, 20, 21, 39, 19, 17, 31, 25},
        {4560, 22,  81, 21, 20, 21, 39, 19, 17, 31, 25},
        {4550, 22,  81, 21, 20, 21, 38, 19, 17, 31, 25},
        {4540, 22,  81, 21, 20, 21, 38, 19, 17, 31, 25},
        {4530, 22,  80, 21, 19, 21, 38, 19, 16, 31, 25},
        {4520, 22,  80, 21, 19, 21, 38, 19, 16, 31, 25},
        {4510, 22,  80, 21, 19, 21, 38, 19, 16, 31, 25},
        {4500, 22,  80, 21, 19, 21, 38, 19, 16, 30, 25},
        {4490, 22,  80, 21, 19, 21, 38, 18, 16, 30, 25},
        {4480, 22,  79, 21, 19, 21, 38, 18, 16, 30, 25},
        {4470, 22,  79, 21, 19, 21, 37, 18, 16, 30, 25},
        {4460, 22,  79, 21, 19, 21, 37, 18, 16, 30, 25},
        {4450, 21,  80, 21, 19, 21, 37, 18, 16, 30, 25},
        {4440, 21,  79, 21, 19, 21, 37, 18, 16, 30, 25},
        {4430, 21,  79, 21, 19, 21, 37, 18, 16, 30, 25},
        {4420, 21,  79, 21, 19, 21, 37, 18, 16, 30, 25},
        {4410, 21,  79, 21, 19, 20, 38, 18, 16, 30, 25},
        {4400, 21,  78, 20, 19, 20, 37, 18, 16, 30, 24},
        {4390, 21,  78, 20, 19, 20, 37, 18, 16, 30, 24},
        {4380, 21,  78, 20, 19, 20, 37, 18, 16, 30, 24},
        {4370, 21,  78, 20, 19, 20, 37, 18, 16, 30, 24},
        {4360, 21,  77, 20, 19, 20, 37, 18, 16, 29, 24},
        {4350, 21,  77, 20, 19, 20, 37, 18, 16, 29, 24},
        {4340, 21,  77, 20, 19, 20, 37, 18, 16, 29, 24},
        {4330, 21,  77, 20, 19, 20, 36, 18, 16, 29, 24},
        {4320, 21,  77, 20, 19, 20, 36, 18, 16, 29, 24},
        {4310, 21,  76, 20, 19, 20, 36, 18, 16, 29, 24},
        {4300, 21,  76, 20, 18, 20, 36, 18, 16, 29, 24},
        {4290, 21,  76, 20, 18, 20, 36, 18, 16, 29, 24},
        {4280, 21,  76, 20, 18, 20, 36, 18, 16, 29, 24},
        {4270, 21,  75, 20, 18, 20, 36, 18, 16, 29, 24},
        {4260, 21,  75, 20, 18, 20, 35, 17, 15, 29, 24},
        {4250, 20,  76, 20, 18, 20, 35, 17, 15, 29, 24},
        {4240, 20,  76, 20, 18, 20, 35, 17, 15, 29, 23},
        {4230, 20,  75, 20, 18, 20, 35, 17, 15, 29, 23},
        {4220, 20,  75, 20, 18, 20, 35, 17, 15, 29, 23},
        {4210, 20,  75, 20, 18, 20, 35, 17, 15, 28, 23},
        {4200, 20,  75, 19, 18, 19, 36, 17, 15, 28, 23},
        {4190, 20,  74, 19, 18, 19, 36, 17, 15, 28, 23},
        {4180, 20,  74, 19, 18, 19, 35, 17, 15, 28, 23},
        {4170, 20,  74, 19, 18, 19, 35, 17, 15, 28, 23},
        {4160, 20,  74, 19, 18, 19, 35, 17, 15, 28, 23},
        {4150, 20,  74, 19, 18, 19, 35, 17, 15, 28, 23},
        {4140, 20,  73, 19, 18, 19, 35, 17, 15, 28, 23},
        {4130, 20,  73, 19, 18, 19, 35, 17, 15, 28, 23},
        {4120, 20,  73, 19, 18, 19, 35, 17, 15, 28, 23},
        {4110, 20,  73, 19, 18, 19, 34, 17, 15, 28, 23},
        {4100, 20,  72, 19, 18, 19, 34, 17, 15, 28, 23},
        {4090, 20,  72, 19, 18, 19, 34, 17, 15, 28, 23},
        {4080, 20,  72, 19, 18, 19, 34, 17, 15, 28, 23},
        {4070, 20,  72, 19, 18, 19, 34, 17, 15, 27, 22},
        {4060, 19,  72, 19, 17, 19, 34, 17, 15, 27, 22},
        {4050, 19,  72, 19, 17, 19, 34, 17, 15, 27, 22},
        {4040, 19,  72, 19, 17, 19, 33, 17, 15, 27, 22},
        {4030, 19,  72, 19, 17, 19, 33, 17, 15, 27, 22},
        {4020, 19,  71, 19, 17, 19, 33, 16, 15, 27, 22},
        {4010, 19,  71, 19, 17, 19, 33, 16, 15, 27, 22},
        {4000, 19,  71, 18, 17, 19, 33, 16, 14, 27, 22},
        {3990, 19,  71, 18, 17, 18, 34, 16, 14, 27, 22},
        {3980, 19,  71, 18, 17, 18, 34, 16, 14, 27, 22},
        {3970, 19,  70, 18, 17, 18, 33, 16, 14, 27, 22},
        {3960, 19,  70, 18, 17, 18, 33, 16, 14, 27, 22},
        {3950, 19,  70, 18, 17, 18, 33, 16, 14, 27, 22},
        {3940, 19,  70, 18, 17, 18, 33, 16, 14, 27, 22},
        {3930, 19,  69, 18, 17, 18, 33, 16, 14, 27, 22},
        {3920, 19,  69, 18, 17, 18, 33, 16, 14, 26, 22},
        {3910, 19,  69, 18, 17, 18, 33, 16, 14, 26, 22},
        {3900, 19,  69, 18, 17, 18, 33, 16, 14, 26, 21},
        {3890, 19,  68, 18, 17, 18, 32, 16, 14, 26, 21},
        {3880, 19,  68, 18, 17, 18, 32, 16, 14, 26, 21},
        {3870, 19,  68, 18, 17, 18, 32, 16, 14, 26, 21},
        {3860, 18,  69, 18, 17, 18, 32, 16, 14, 26, 21},
        {3850, 18,  68, 18, 17, 18, 32, 16, 14, 26, 21},
        {3840, 18,  68, 18, 17, 18, 32, 16, 14, 26, 21},
        {3830, 18,  68, 18, 16, 18, 32, 16, 14, 26, 21},
        {3820, 18,  68, 18, 16, 18, 31, 16, 14, 26, 21},
        {3810, 18,  68, 18, 16, 18, 31, 16, 14, 26, 21},
        {3800, 18,  67, 17, 16, 18, 31, 16, 14, 26, 21},
        {3790, 18,  67, 17, 16, 17, 32, 15, 14, 26, 21},
        {3780, 18,  67, 17, 16, 17, 32, 15, 14, 25, 21},
        {3770, 18,  67, 17, 16, 17, 32, 15, 14, 25, 21},
        {3760, 18,  66, 17, 16, 17, 32, 15, 14, 25, 21},
        {3750, 18,  66, 17, 16, 17, 31, 15, 14, 25, 21},
        {3740, 18,  66, 17, 16, 17, 31, 15, 14, 25, 20},
        {3730, 18,  66, 17, 16, 17, 31, 15, 13, 25, 20},
        {3720, 18,  65, 17, 16, 17, 31, 15, 13, 25, 20},
        {3710, 18,  65, 17, 16, 17, 31, 15, 13, 25, 20},
        {3700, 18,  65, 17, 16, 17, 31, 15, 13, 25, 20},
        {3690, 18,  65, 17, 16, 17, 31, 15, 13, 25, 20},
        {3680, 18,  64, 17, 16, 17, 31, 15, 13, 25, 20},
        {3670, 18,  64, 17, 16, 17, 30, 15, 13, 25, 20},
        {3660, 17,  65, 17, 16, 17, 30, 15, 13, 25, 20},
        {3650, 17,  65, 17, 16, 17, 30, 15, 13, 25, 20},
        {3640, 17,  65, 17, 16, 17, 30, 15, 13, 25, 20},
        {3630, 17,  64, 17, 16, 17, 30, 15, 13, 24, 20},
        {3620, 17,  64, 17, 16, 17, 30, 15, 13, 24, 20},
        {3610, 17,  64, 17, 16, 17, 30, 15, 13, 24, 20},
        {3600, 17,  64, 16, 16, 17, 29, 15, 13, 24, 20},
        {3590, 17,  63, 16, 15, 17, 29, 15, 13, 24, 20},
        {3580, 17,  63, 16, 15, 16, 30, 15, 13, 24, 20},
        {3570, 17,  63, 16, 15, 16, 30, 15, 13, 24, 19},
        {3560, 17,  63, 16, 15, 16, 30, 14, 13, 24, 19},
        {3550, 17,  62, 16, 15, 16, 30, 14, 13, 24, 19},
        {3540, 17,  62, 16, 15, 16, 30, 14, 13, 24, 19},
        {3530, 17,  62, 16, 15, 16, 29, 14, 13, 24, 19},
        {3520, 17,  62, 16, 15, 16, 29, 14, 13, 24, 19},
        {3510, 17,  62, 16, 15, 16, 29, 14, 13, 24, 19},
        {3500, 17,  61, 16, 15, 16, 29, 14, 13, 24, 19},
        {3490, 17,  61, 16, 15, 16, 29, 14, 13, 23, 19},
        {3480, 17,  61, 16, 15, 16, 29, 14, 13, 23, 19},
        {3470, 17,  61, 16, 15, 16, 29, 14, 13, 23, 19},
        {3460, 16,  61, 16, 15, 16, 28, 14, 12, 23, 19},
        {3450, 16,  61, 16, 15, 16, 28, 14, 12, 23, 19},
        {3440, 16,  61, 16, 15, 16, 28, 14, 12, 23, 19},
        {3430, 16,  61, 16, 15, 16, 28, 14, 12, 23, 19},
        {3420, 16,  60, 16, 15, 16, 28, 14, 12, 23, 19},
        {3410, 16,  60, 16, 15, 16, 28, 14, 12, 23, 18},
        {3400, 16,  60, 15, 15, 16, 28, 14, 12, 23, 18},
        {3390, 16,  60, 15, 15, 16, 28, 14, 12, 23, 18},
        {3380, 16,  59, 15, 15, 16, 27, 14, 12, 23, 18},
        {3370, 16,  59, 15, 15, 15, 28, 14, 12, 23, 18},
        {3360, 16,  59, 15, 14, 15, 28, 14, 12, 23, 18},
        {3350, 16,  59, 15, 14, 15, 28, 14, 12, 23, 18},
        {3340, 16,  59, 15, 14, 15, 28, 14, 12, 22, 18},
        {3330, 16,  58, 15, 14, 15, 28, 14, 12, 22, 18},
        {3320, 16,  58, 15, 14, 15, 28, 13, 12, 22, 18},
        {3310, 16,  58, 15, 14, 15, 27, 13, 12, 22, 18},
        {3300, 16,  58, 15, 14, 15, 27, 13, 12, 22, 18},
        {3290, 16,  57, 15, 14, 15, 27, 13, 12, 22, 18},
        {3280, 16,  57, 15, 14, 15, 27, 13, 12, 22, 18},
        {3270, 16,  57, 15, 14, 15, 27, 13, 12, 22, 18},
        {3260, 15,  58, 15, 14, 15, 27, 13, 12, 22, 18},
        {3250, 15,  57, 15, 14, 15, 27, 13, 12, 22, 18},
        {3240, 15,  57, 15, 14, 15, 26, 13, 12, 22, 17},
        {3230, 15,  57, 15, 14, 15, 26, 13, 12, 22, 17},
        {3220, 15,  57, 15, 14, 15, 26, 13, 12, 22, 17},
        {3210, 15,  56, 15, 14, 15, 26, 13, 12, 22, 17},
        {3200, 15,  56, 14, 14, 15, 26, 13, 11, 21, 17},
        {3190, 15,  56, 14, 14, 15, 26, 13, 11, 21, 17},
        {3180, 15,  56, 14, 14, 15, 26, 13, 11, 21, 17},
        {3170, 15,  56, 14, 14, 15, 25, 13, 11, 21, 17},
        {3160, 15,  55, 14, 14, 14, 26, 13, 11, 21, 17},
        {3150, 15,  55, 14, 14, 14, 26, 13, 11, 21, 17},
        {3140, 15,  55, 14, 14, 14, 26, 13, 11, 21, 17},
        {3130, 15,  55, 14, 14, 14, 26, 13, 11, 21, 17},
        {3120, 15,  54, 14, 13, 14, 26, 13, 11, 21, 17},
        {3110, 15,  54, 14, 13, 14, 26, 13, 11, 21, 17},
        {3100, 15,  54, 14, 13, 14, 26, 13, 11, 21, 17},
        {3090, 15,  54, 14, 13, 14, 25, 12, 11, 21, 17},
        {3080, 15,  53, 14, 13, 14, 25, 12, 11, 21, 17},
        {3070, 14,  54, 14, 13, 14, 25, 12, 11, 21, 16},
        {3060, 14,  54, 14, 13, 14, 25, 12, 11, 21, 16},
        {3050, 14,  54, 14, 13, 14, 25, 12, 11, 20, 16},
        {3040, 14,  53, 14, 13, 14, 25, 12, 11, 20, 16},
        {3030, 14,  53, 14, 13, 14, 25, 12, 11, 20, 16},
        {3020, 14,  53, 14, 13, 14, 24, 12, 11, 20, 16},
        {3010, 14,  53, 14, 13, 14, 24, 12, 11, 20, 16},
        {3000, 14,  53, 13, 13, 14, 24, 12, 11, 20, 16},
        {2990, 14,  52, 13, 13, 14, 24, 12, 11, 20, 16},
        {2980, 14,  52, 13, 13, 14, 24, 12, 11, 20, 16},
        {2970, 14,  52, 13, 13, 14, 24, 12, 11, 20, 16},
        {2960, 14,  52, 13, 13, 14, 24, 12, 11, 20, 16},
        {2950, 14,  51, 13, 13, 13, 24, 12, 11, 20, 16},
        {2940, 14,  51, 13, 13, 13, 24, 12, 11, 20, 16},
        {2930, 14,  51, 13, 13, 13, 24, 12, 10, 20, 16},
        {2920, 14,  51, 13, 13, 13, 24, 12, 10, 20, 16},
        {2910, 14,  50, 13, 13, 13, 24, 12, 10, 20, 15},
        {2900, 14,  50, 13, 13, 13, 24, 12, 10, 19, 15},
        {2890, 14,  50, 13, 12, 13, 24, 12, 10, 19, 15},
        {2880, 14,  50, 13, 12, 13, 23, 12, 10, 19, 15},
        {2870, 13,  50, 13, 12, 13, 23, 12, 10, 19, 15},
        {2860, 13,  50, 13, 12, 13, 23, 12, 10, 19, 15},
        {2850, 13,  50, 13, 12, 13, 23, 11, 10, 19, 15},
        {2840, 13,  50, 13, 12, 13, 23, 11, 10, 19, 15},
        {2830, 13,  50, 13, 12, 13, 23, 11, 10, 19, 15},
        {2820, 13,  49, 13, 12, 13, 23, 11, 10, 19, 15},
        {2810, 13,  49, 13, 12, 13, 23, 11, 10, 19, 15},
        {2800, 13,  49, 12, 12, 13, 22, 11, 10, 19, 15},
        {2790, 13,  49, 12, 12, 13, 22, 11, 10, 19, 15},
        {2780, 13,  48, 12, 12, 13, 22, 11, 10, 19, 15},
        {2770, 13,  48, 12, 12, 13, 22, 11, 10, 19, 15},
        {2760, 13,  48, 12, 12, 13, 22, 11, 10, 18, 15},
        {2750, 13,  48, 12, 12, 13, 22, 11, 10, 18, 15},
        {2740, 13,  47, 12, 12, 12, 23, 11, 10, 18, 14},
        {2730, 13,  47, 12, 12, 12, 22, 11, 10, 18, 14},
        {2720, 13,  47, 12, 12, 12, 22, 11, 10, 18, 14},
        {2710, 13,  47, 12, 12, 12, 22, 11, 10, 18, 14},
        {2700, 13,  47, 12, 12, 12, 22, 11, 10, 18, 14},
        {2690, 13,  46, 12, 12, 12, 22, 11, 10, 18, 14},
        {2680, 13,  46, 12, 12, 12, 22, 11, 10, 18, 14},
        {2670, 12,  47, 12, 12, 12, 22, 11, 10, 18, 14},
        {2660, 12,  47, 12, 12, 12, 21, 11,  9, 18, 14},
        {2650, 12,  46, 12, 11, 12, 21, 11,  9, 18, 14},
        {2640, 12,  46, 12, 11, 12, 21, 11,  9, 18, 14},
        {2630, 12,  46, 12, 11, 12, 21, 11,  9, 18, 14},
        {2620, 12,  46, 12, 11, 12, 21, 10,  9, 18, 14},
        {2610, 12,  45, 12, 11, 12, 21, 10,  9, 17, 14},
        {2600, 12,  45, 11, 11, 12, 21, 10,  9, 17, 14},
        {2590, 12,  45, 11, 11, 12, 20, 10,  9, 17, 14},
        {2580, 12,  45, 11, 11, 12, 20, 10,  9, 17, 14},
        {2570, 12,  44, 11, 11, 12, 20, 10,  9, 17, 13},
        {2560, 12,  44, 11, 11, 12, 20, 10,  9, 17, 13},
        {2550, 12,  44, 11, 11, 12, 20, 10,  9, 17, 13},
        {2540, 12,  44, 11, 11, 11, 21, 10,  9, 17, 13},
        {2530, 12,  44, 11, 11, 11, 21, 10,  9, 17, 13},
        {2520, 12,  43, 11, 11, 11, 21, 10,  9, 17, 13},
        {2510, 12,  43, 11, 11, 11, 20, 10,  9, 17, 13},
        {2500, 12,  43, 11, 11, 11, 20, 10,  9, 17, 13},
        {2490, 12,  43, 11, 11, 11, 20, 10,  9, 17, 13},
        {2480, 12,  42, 11, 11, 11, 20, 10,  9, 17, 13},
        {2470, 11,  43, 11, 11, 11, 20, 10,  9, 16, 13},
        {2460, 11,  43, 11, 11, 11, 20, 10,  9, 16, 13},
        {2450, 11,  43, 11, 11, 11, 20, 10,  9, 16, 13},
        {2440, 11,  42, 11, 11, 11, 19, 10,  9, 16, 13},
        {2430, 11,  42, 11, 11, 11, 19, 10,  9, 16, 13},
        {2420, 11,  42, 11, 10, 11, 19, 10,  9, 16, 13},
        {2410, 11,  42, 11, 10, 11, 19, 10,  9, 16, 12},
        {2400, 11,  41, 10, 10, 11, 19, 10,  8, 16, 12},
        {2390, 11,  41, 10, 10, 11, 19, 10,  8, 16, 12},
        {2380, 11,  41, 10, 10, 11, 19,  9,  8, 16, 12},
        {2370, 11,  41, 10, 10, 11, 18,  9,  8, 16, 12},
        {2360, 11,  41, 10, 10, 11, 18,  9,  8, 16, 12},
        {2350, 11,  40, 10, 10, 11, 18,  9,  8, 16, 12},
        {2340, 11,  40, 10, 10, 11, 18,  9,  8, 16, 12},
        {2330, 11,  40, 10, 10, 10, 19,  9,  8, 16, 12},
        {2320, 11,  40, 10, 10, 10, 19,  9,  8, 15, 12},
        {2310, 11,  39, 10, 10, 10, 19,  9,  8, 15, 12},
        {2300, 11,  39, 10, 10, 10, 18,  9,  8, 15, 12},
        {2290, 11,  39, 10, 10, 10, 18,  9,  8, 15, 12},
        {2280, 11,  39, 10, 10, 10, 18,  9,  8, 15, 12},
        {2270, 10,  39, 10, 10, 10, 18,  9,  8, 15, 12},
        {2260, 10,  39, 10, 10, 10, 18,  9,  8, 15, 12},
        {2250, 10,  39, 10, 10, 10, 18,  9,  8, 15, 12},
        {2240, 10,  39, 10, 10, 10, 18,  9,  8, 15, 11},
        {2230, 10,  38, 10, 10, 10, 18,  9,  8, 15, 11},
        {2220, 10,  38, 10, 10, 10, 17,  9,  8, 15, 11},
        {2210, 10,  38, 10, 10, 10, 17,  9,  8, 15, 11},
        {2200, 10,  38,  9, 10, 10, 17,  9,  8, 15, 11},
        {2190, 10,  38,  9,  9, 10, 17,  9,  8, 15, 11},
        {2180, 10,  37,  9,  9, 10, 17,  9,  8, 14, 11},
        {2170, 10,  37,  9,  9, 10, 17,  9,  8, 14, 11},
        {2160, 10,  37,  9,  9, 10, 17,  9,  8, 14, 11},
        {2150, 10,  37,  9,  9, 10, 16,  8,  8, 14, 11},
        {2140, 10,  36,  9,  9, 10, 16,  8,  8, 14, 11},
        {2130, 10,  36,  9,  9, 10, 16,  8,  7, 14, 11},
        {2120, 10,  36,  9,  9,  9, 17,  8,  7, 14, 11},
        {2110, 10,  36,  9,  9,  9, 17,  8,  7, 14, 11},
        {2100, 10,  35,  9,  9,  9, 17,  8,  7, 14, 11},
        {2090, 10,  35,  9,  9,  9, 17,  8,  7, 14, 11},
        {2080,  9,  36,  9,  9,  9, 16,  8,  7, 14, 11},
        {2070,  9,  36,  9,  9,  9, 16,  8,  7, 14, 10},
        {2060,  9,  35,  9,  9,  9, 16,  8,  7, 14, 10},
        {2050,  9,  35,  9,  9,  9, 16,  8,  7, 14, 10},
        {2040,  9,  35,  9,  9,  9, 16,  8,  7, 14, 10},
        {2030,  9,  35,  9,  9,  9, 16,  8,  7, 13, 10},
        {2020,  9,  35,  9,  9,  9, 16,  8,  7, 13, 10},
        {2010,  9,  34,  9,  9,  9, 15,  8,  7, 13, 10},
        {2000,  9,  34,  8,  9,  9, 15,  8,  7, 13, 10},
        {1990,  9,  34,  8,  9,  9, 15,  8,  7, 13, 10},
        {1980,  9,  34,  8,  9,  9, 15,  8,  7, 13, 10},
        {1970,  9,  33,  8,  9,  9, 15,  8,  7, 13, 10},
        {1960,  9,  33,  8,  9,  9, 15,  8,  7, 13, 10},
        {1950,  9,  33,  8,  8,  9, 15,  8,  7, 13, 10},
        {1940,  9,  33,  8,  8,  9, 15,  8,  7, 13, 10},
        {1930,  9,  32,  8,  8,  9, 14,  8,  7, 13, 10},
        {1920,  9,  32,  8,  8,  9, 14,  8,  7, 13, 10},
        {1910,  9,  32,  8,  8,  8, 15,  7,  7, 13,  9},
        {1900,  9,  32,  8,  8,  8, 15,  7,  7, 13,  9},
        {1890,  9,  31,  8,  8,  8, 15,  7,  7, 12,  9},
        {1880,  8,  32,  8,  8,  8, 15,  7,  7, 12,  9},
        {1870,  8,  32,  8,  8,  8, 15,  7,  7, 12,  9},
        {1860,  8,  32,  8,  8,  8, 14,  7,  6, 12,  9},
        {1850,  8,  32,  8,  8,  8, 14,  7,  6, 12,  9},
        {1840,  8,  31,  8,  8,  8, 14,  7,  6, 12,  9},
        {1830,  8,  31,  8,  8,  8, 14,  7,  6, 12,  9},
        {1820,  8,  31,  8,  8,  8, 14,  7,  6, 12,  9},
        {1810,  8,  31,  8,  8,  8, 14,  7,  6, 12,  9},
        {1800,  8,  30,  7,  8,  8, 14,  7,  6, 12,  9},
        {1790,  8,  30,  7,  8,  8, 13,  7,  6, 12,  9},
        {1780,  8,  30,  7,  8,  8, 13,  7,  6, 12,  9},
        {1770,  8,  30,  7,  8,  8, 13,  7,  6, 12,  9},
        {1760,  8,  29,  7,  8,  8, 13,  7,  6, 12,  9},
        {1750,  8,  29,  7,  8,  8, 13,  7,  6, 12,  9},
        {1740,  8,  29,  7,  8,  8, 13,  7,  6, 11,  8},
        {1730,  8,  29,  7,  8,  8, 13,  7,  6, 11,  8},
        {1720,  8,  29,  7,  7,  8, 13,  7,  6, 11,  8},
        {1710,  8,  28,  7,  7,  8, 12,  7,  6, 11,  8},
        {1700,  8,  28,  7,  7,  7, 13,  7,  6, 11,  8},
        {1690,  8,  28,  7,  7,  7, 13,  7,  6, 11,  8},
        {1680,  7,  29,  7,  7,  7, 13,  6,  6, 11,  8},
        {1670,  7,  28,  7,  7,  7, 13,  6,  6, 11,  8},
        {1660,  7,  28,  7,  7,  7, 13,  6,  6, 11,  8},
        {1650,  7,  28,  7,  7,  7, 13,  6,  6, 11,  8},
        {1640,  7,  28,  7,  7,  7, 12,  6,  6, 11,  8},
        {1630,  7,  27,  7,  7,  7, 12,  6,  6, 11,  8},
        {1620,  7,  27,  7,  7,  7, 12,  6,  6, 11,  8},
        {1610,  7,  27,  7,  7,  7, 12,  6,  6, 11,  8},
        {1600,  7,  27,  6,  7,  7, 12,  6,  5, 10,  8},
        {1590,  7,  26,  6,  7,  7, 12,  6,  5, 10,  8},
        {1580,  7,  26,  6,  7,  7, 12,  6,  5, 10,  7},
        {1570,  7,  26,  6,  7,  7, 11,  6,  5, 10,  7},
        {1560,  7,  26,  6,  7,  7, 11,  6,  5, 10,  7},
        {1550,  7,  26,  6,  7,  7, 11,  6,  5, 10,  7},
        {1540,  7,  25,  6,  7,  7, 11,  6,  5, 10,  7},
        {1530,  7,  25,  6,  7,  7, 11,  6,  5, 10,  7},
        {1520,  7,  25,  6,  7,  7, 11,  6,  5, 10,  7},
        {1510,  7,  25,  6,  7,  7, 11,  6,  5, 10,  7},
        {1500,  7,  24,  6,  7,  7, 10,  6,  5, 10,  7},
        {1490, 59,  25,  6, 77, 59, 10, 70, 44,  9, 73},
        {1480, 59,  24,  6, 76, 58, 10, 70, 44,  9, 73},
        {1470, 58,  24,  6, 76, 58, 10, 69, 44,  9, 72},
        {1460, 58,  24,  6, 76, 58, 10, 69, 43,  9, 72},
        {1450, 58,  24,  6, 75, 57, 10, 68, 43,  9, 71},
        {1440, 57,  24,  6, 75, 57, 10, 68, 43,  9, 71},
        {1430, 57,  23,  6, 75, 57, 10, 68, 43,  8, 70},
        {1420, 56,  23,  6, 74, 57,  9, 67, 43,  8, 70},
        {1410, 56,  23,  6, 74, 57,  9, 67, 43,  8, 69},
        {1400, 56,  23,  5, 74, 55,  9, 67, 41,  8, 69},
        {1390, 55,  23,  5, 73, 55,  9, 66, 41,  8, 68},
        {1380, 55,  23,  5, 73, 54,  9, 66, 41,  8, 68},
        {1370, 54,  22,  5, 72, 54,  9, 66, 41,  8, 67},
        {1360, 54,  22,  5, 72, 54,  9, 65, 40,  8, 67},
        {1350, 54,  22,  5, 72, 53,  9, 65, 40,  8, 66},
        {1340, 53,  22,  5, 71, 53,  9, 65, 40,  8, 66},
        {1330, 53,  22,  5, 71, 53,  9, 64, 39,  8, 65},
        {1320, 52,  22,  5, 71, 53,  8, 64, 40,  8, 65},
        {1310, 52,  21,  5, 70, 53,  8, 64, 40,  8, 64},
        {1300, 51,  21,  5, 70, 51,  8, 63, 38,  8, 64},
        {1290, 51,  21,  5, 70, 51,  8, 63, 38,  7, 64},
        {1280, 51,  21,  5, 69, 51,  8, 63, 38,  7, 63},
        {1270, 50,  21,  5, 69, 50,  8, 62, 38,  7, 63},
        {1260, 50,  20,  5, 69, 50,  8, 62, 37,  7, 62},
        {1250, 49,  20,  5, 68, 49,  8, 62, 37,  7, 62},
        {1240, 49,  20,  5, 68, 49,  8, 61, 37,  7, 61},
        {1230, 49,  20,  5, 68, 49,  8, 61, 36,  7, 61},
        {1220, 48,  20,  5, 67, 48,  8, 61, 36,  7, 60},
        {1210, 48,  19,  5, 67, 48,  7, 60, 36,  7, 60},
        {1200, 49,  19,  4, 67, 49,  7, 60, 36,  7, 59},
        {1190, 48,  19,  4, 66, 48,  7, 60, 36,  7, 59},
        {1180, 48,  19,  4, 66, 48,  7, 59, 36,  7, 58},
        {1170, 46,  19,  4, 66, 46,  7, 59, 35,  7, 58},
        {1160, 46,  18,  4, 65, 46,  7, 59, 34,  7, 57},
        {1150, 45,  18,  4, 65, 46,  7, 58, 34,  7, 57},
        {1140, 45,  18,  4, 65, 45,  7, 58, 34,  6, 56},
        {1130, 45,  18,  4, 64, 45,  7, 58, 33,  6, 56},
        {1120, 44,  18,  4, 64, 44,  7, 57, 33,  6, 55},
        {1110, 44,  18,  4, 64, 44,  7, 57, 33,  6, 55},
        {1100, 43,  17,  4, 63, 44,  6, 57, 32,  6, 54},
        {1090, 43,  17,  4, 63, 44,  6, 56, 33,  6, 54},
        {1080, 43,  17,  4, 63, 44,  6, 56, 33,  6, 53},
        {1070, 42,  17,  4, 62, 44,  6, 56, 33,  6, 53},
        {1060, 42,  17,  4, 62, 42,  6, 55, 31,  6, 52},
        {1050, 41,  17,  4, 62, 42,  6, 55, 31,  6, 52},
        {1040, 41,  16,  4, 61, 41,  6, 54, 31,  6, 52},
        {1030, 41,  16,  4, 61, 41,  6, 54, 30,  6, 51},
        {1020, 40,  16,  4, 61, 41,  6, 54, 30,  6, 51},
        {1010, 40,  16,  4, 60, 40,  6, 53, 30,  6, 50},
        {1000, 39,  16,  3, 60, 40,  6, 53, 29,  5, 50},
        { 990, 39,  15,  3, 60, 39,  6, 53, 29,  5, 49},
        { 980, 39,  15,  3, 59, 39,  5, 52, 29,  5, 49},
        { 970, 38,  15,  3, 59, 39,  5, 52, 29,  5, 48},
        { 960, 38,  15,  3, 59, 39,  5, 52, 29,  5, 48},
        { 950, 37,  15,  3, 58, 39,  5, 51, 29,  5, 47},
        { 940, 37,  14,  3, 58, 39,  5, 51, 29,  5, 47},
        { 930, 37,  14,  3, 57, 37,  5, 51, 27,  5, 46},
        { 920, 36,  14,  3, 57, 37,  5, 50, 27,  5, 46},
        { 910, 36,  14,  3, 57, 36,  5, 50, 27,  5, 45},
        { 900, 35,  14,  3, 56, 36,  5, 50, 26,  5, 45},
        { 890, 35,  14,  3, 56, 36,  5, 49, 26,  5, 44},
        { 880, 35,  13,  3, 56, 35,  5, 49, 26,  5, 44},
        { 870, 34,  13,  3, 55, 35,  4, 49, 26,  5, 43},
        { 860, 34,  13,  3, 55, 35,  4, 48, 25,  5, 43},
        { 850, 33,  13,  3, 55, 35,  4, 48, 26,  4, 42},
        { 840, 33,  13,  3, 54, 35,  4, 48, 26,  4, 42},
        { 830, 33,  12,  3, 54, 33,  4, 47, 24,  4, 41},
        { 820, 32,  12,  3, 54, 33,  4, 47, 24,  4, 41},
        { 810, 32,  12,  3, 53, 33,  4, 47, 24,  4, 40},
        { 800, 31,  12,  2, 53, 32,  4, 46, 23,  4, 40},
        { 790, 31,  12,  2, 53, 32,  4, 46, 23,  4, 39},
        { 780, 30,  12,  2, 52, 31,  4, 46, 23,  4, 39},
        { 770, 30,  11,  2, 52, 31,  4, 45, 23,  4, 39},
        { 760, 30,  11,  2, 52, 31,  3, 45, 22,  4, 38},
        { 750, 29,  11,  2, 51, 30,  3, 45, 22,  4, 38},
        { 740, 29,  11,  2, 51, 30,  3, 44, 22,  4, 37},
        { 730, 28,  11,  2, 51, 31,  3, 44, 22,  4, 37},
        { 720, 28,  10,  2, 50, 30,  3, 44, 22,  4, 36},
        { 710, 28,  10,  2, 50, 30,  3, 43, 22,  4, 36},
        { 700, 27,  10,  2, 50, 28,  3, 43, 20,  3, 35},
        { 690, 27,  10,  2, 49, 28,  3, 43, 20,  3, 35},
        { 680, 26,  10,  2, 49, 28,  3, 42, 20,  3, 34},
        { 670, 26,  10,  2, 49, 27,  3, 42, 20,  3, 34},
        { 660, 26,   9,  2, 48, 27,  3, 42, 19,  3, 33},
        { 650, 25,   9,  2, 48, 26,  3, 41, 19,  3, 33},
        { 640, 25,   9,  2, 48, 26,  2, 41, 19,  3, 32},
        { 630, 24,   9,  2, 47, 26,  2, 40, 18,  3, 32},
        { 620, 24,   9,  2, 47, 26,  2, 40, 19,  3, 31},
        { 610, 24,   8,  2, 47, 26,  2, 40, 19,  3, 31},
        { 600, 23,   8,  1, 46, 26,  2, 39, 18,  3, 30},
        { 590, 23,   8,  1, 46, 24,  2, 39, 17,  3, 30},
        { 580, 22,   8,  1, 46, 24,  2, 39, 17,  3, 29},
        { 570, 22,   8,  1, 45, 23,  2, 38, 17,  3, 29},
        { 560, 22,   7,  1, 45, 23,  2, 38, 16,  2, 28},
        { 550, 21,   7,  1, 45, 23,  2, 38, 16,  2, 28},
        { 540, 21,   7,  1, 44, 22,  2, 37, 16,  2, 27},
        { 530, 20,   7,  1, 44, 22,  1, 37, 15,  2, 27},
        { 520, 20,   7,  1, 43, 21,  1, 37, 15,  2, 27},
        { 510, 20,   6,  1, 43, 21,  1, 36, 15,  2, 26},
        { 500, 19,   6,  1, 43, 22,  1, 36, 15,  2, 26},
        { 490, 19,   6,  1, 42, 21,  1, 36, 15,  2, 25},
        { 480, 18,   6,  1, 42, 21,  1, 35, 15,  2, 25},
        { 470, 18,   6,  1, 42, 21,  1, 35, 15,  2, 24},
        { 460, 18,   6,  1, 41, 19,  1, 35, 13,  2, 24},
        { 450, 17,   5,  1, 41, 19,  1, 34, 13,  2, 23},
        { 440, 17,   5,  1, 41, 18,  1, 34, 13,  2, 23},
        { 430, 16,   5,  1, 40, 18,  0, 34, 12,  2, 22},
        { 420, 16,   5,  1, 40, 18,  0, 33, 12,  2, 22},
        { 410, 16,   5,  1, 40, 17,  0, 33, 12,  1, 21},
        { 400, 15,   5,  0, 39, 17,  0, 33, 11,  1, 21},
        { 390, 15,   4,  0, 39, 17,  0, 32, 12,  1, 20},
        { 380, 14,   4,  0, 39, 17,  0, 32, 12,  1, 20},
        { 370, 14,   4,  0, 38, 17,  0, 32, 12,  1, 19},
        { 360, 14,   4,  0, 38, 15,  0, 31, 10,  1, 19},
        { 350, 13,   4,  0, 38, 15,  0, 31, 10,  1, 18},
        { 340, 13,   3,  0, 37, 15,  0, 31, 10,  1, 18},
        { 330, 12,   3,  0, 37, 14,  0, 30,  9,  1, 17},
        { 320, 12,   3,  0, 37, 14,  0, 30,  9,  1, 17},
        { 310, 12,   3,  0, 36, 13,  0, 30,  9,  1, 16},
        { 300, 11,   3,  0, 36, 13,  0, 29,  8,  1, 16},
        { 290, 11,   2,  0, 36, 13,  0, 29,  8,  1, 15},
        { 280, 10,   2,  0, 35, 12,  0, 29,  8,  1, 15},
        { 270, 10,   2,  0, 35, 12,  0, 28,  8,  0, 14},
        { 260,  9,   2,  0, 35, 12,  0, 28,  8,  0, 14},
        { 250,  9,   2,  0, 34, 12,  0, 28,  8,  0, 14},
        { 240,  9,   2,  0, 34, 12,  0, 27,  8,  0, 13},
        { 230,  8,   1,  0, 34, 10,  0, 27,  6,  0, 13},
        { 220,  8,   1,  0, 33, 10,  0, 27,  6,  0, 12},
        { 210,  7,   1,  0, 33, 10,  0, 26,  6,  0, 12},
        { 200,  7,   1,  0, 33,  9,  0, 26,  5,  0, 11},
        { 190,  7,   1,  0, 32,  9,  0, 25,  5,  0, 11},
        { 180,  6,   1,  0, 32,  8,  0, 25,  5,  0, 10},
        { 170,  6,   0,  0, 32,  8,  0, 25,  5,  0, 10},
        { 160,  5,   0,  0, 31,  8,  0, 24,  4,  0,  9},
        { 150,  5,   0,  0, 31,  8,  0, 24,  5,  0,  9},
        { 140,  5,   0,  0, 31,  8,  0, 24,  5,  0,  8},
        { 130,  4,   0,  0, 30,  6,  0, 23,  3,  0,  8},
        { 120,  4,   0,  0, 30,  6,  0, 23,  3,  0,  7},
        { 110,  3,   0,  0, 30,  6,  0, 23,  3,  0,  7},
        { 100,  3,   0,  0, 29,  5,  0, 22,  2,  0,  6},
        {  90,  3,   0,  0, 29,  5,  0, 22,  2,  0,  6},
        {  80,  2,   0,  0, 28,  5,  0, 22,  2,  0,  5},
};

static void samsung_mipi_dcphy_bias_block_enable(struct samsung_mipi_dcphy *samsung)
{
        regmap_write(samsung->regmap, BIAS_CON0, I_DEV_DIV_6 | I_RES_100_2UA);
        regmap_write(samsung->regmap, BIAS_CON1, I_VBG_SEL_820MV | I_BGR_VREF_820MV |
                                                 I_LADDER_1_00V);
        regmap_write(samsung->regmap, BIAS_CON2, REG_325M_325MV | REG_LP_400M_400MV |
                                                 REG_400M_400MV | REG_645M_645MV);

        /* default output voltage select:
         * dphy: 400mv
         * cphy: 530mv
         */
        regmap_update_bits(samsung->regmap, BIAS_CON4,
                           I_MUX_SEL_MASK, I_MUX_400MV);
}

static void samsung_mipi_dphy_lane_enable(struct samsung_mipi_dcphy *samsung)
{
        regmap_write(samsung->regmap, DPHY_MC_GNR_CON1, T_PHY_READY(0x2000));
        regmap_update_bits(samsung->regmap, DPHY_MC_GNR_CON0,
                           PHY_ENABLE, PHY_ENABLE);

        switch (samsung->lanes) {
        case 4:
                regmap_write(samsung->regmap, DPHY_MD3_GNR_CON1,
                             T_PHY_READY(0x2000));
                regmap_update_bits(samsung->regmap, DPHY_MD3_GNR_CON0,
                                   PHY_ENABLE, PHY_ENABLE);
                fallthrough;
        case 3:
                regmap_write(samsung->regmap, COMBO_MD2_GNR_CON1,
                             T_PHY_READY(0x2000));
                regmap_update_bits(samsung->regmap, COMBO_MD2_GNR_CON0,
                                   PHY_ENABLE, PHY_ENABLE);
                fallthrough;
        case 2:
                regmap_write(samsung->regmap, COMBO_MD1_GNR_CON1,
                             T_PHY_READY(0x2000));
                regmap_update_bits(samsung->regmap, COMBO_MD1_GNR_CON0,
                                   PHY_ENABLE, PHY_ENABLE);
                fallthrough;
        case 1:
        default:
                regmap_write(samsung->regmap, COMBO_MD0_GNR_CON1,
                             T_PHY_READY(0x2000));
                regmap_update_bits(samsung->regmap, COMBO_MD0_GNR_CON0,
                                   PHY_ENABLE, PHY_ENABLE);
                break;
        }
}

static void samsung_mipi_dphy_lane_disable(struct samsung_mipi_dcphy *samsung)
{
        switch (samsung->lanes) {
        case 4:
                regmap_update_bits(samsung->regmap, DPHY_MD3_GNR_CON0,
                                   PHY_ENABLE, 0);
                fallthrough;
        case 3:
                regmap_update_bits(samsung->regmap, COMBO_MD2_GNR_CON0,
                                   PHY_ENABLE, 0);
                fallthrough;
        case 2:
                regmap_update_bits(samsung->regmap, COMBO_MD1_GNR_CON0,
                                   PHY_ENABLE, 0);
                fallthrough;
        case 1:
        default:
                regmap_update_bits(samsung->regmap, COMBO_MD0_GNR_CON0,
                                   PHY_ENABLE, 0);
                break;
        }

        regmap_update_bits(samsung->regmap, DPHY_MC_GNR_CON0, PHY_ENABLE, 0);
}

static void samsung_mipi_dcphy_pll_configure(struct samsung_mipi_dcphy *samsung)
{
        regmap_update_bits(samsung->regmap, PLL_CON0, S_MASK | P_MASK,
                           S(samsung->pll.scaler) | P(samsung->pll.prediv));

        if (samsung->pll.dsm < 0) {
                u16 dsm_tmp;

                /* Using opposite number subtraction to find complement */
                dsm_tmp = abs(samsung->pll.dsm);
                dsm_tmp = dsm_tmp - 1;
                dsm_tmp ^= 0xffff;
                regmap_write(samsung->regmap, PLL_CON1, dsm_tmp);
        } else {
                regmap_write(samsung->regmap, PLL_CON1, samsung->pll.dsm);
        }

        regmap_update_bits(samsung->regmap, PLL_CON2,
                           M_MASK, M(samsung->pll.fbdiv));

        if (samsung->pll.ssc_en) {
                regmap_write(samsung->regmap, PLL_CON3,
                             MRR(samsung->pll.mrr) | MFR(samsung->pll.mfr));
                regmap_update_bits(samsung->regmap, PLL_CON4, SSCG_EN, SSCG_EN);
        }

        regmap_write(samsung->regmap, PLL_CON5, RESET_N_SEL | PLL_ENABLE_SEL);
        regmap_write(samsung->regmap, PLL_CON7, PLL_LOCK_CNT(0xf000));
        regmap_write(samsung->regmap, PLL_CON8, PLL_STB_CNT(0xf000));
}

static int samsung_mipi_dcphy_pll_enable(struct samsung_mipi_dcphy *samsung)
{
        u32 sts;
        int ret;

        regmap_update_bits(samsung->regmap, PLL_CON0, PLL_EN, PLL_EN);

        ret = regmap_read_poll_timeout(samsung->regmap, PLL_STAT0,
                                       sts, (sts & PLL_LOCK), 1000, 20000);
        if (ret < 0)
                dev_err(samsung->dev, "DC-PHY pll failed to lock\n");

        return ret;
}

static void samsung_mipi_dcphy_pll_disable(struct samsung_mipi_dcphy *samsung)
{
        regmap_update_bits(samsung->regmap, PLL_CON0, PLL_EN, 0);
}

static const struct samsung_mipi_dphy_timing *
samsung_mipi_dphy_get_timing(struct samsung_mipi_dcphy *samsung)
{
        const struct samsung_mipi_dphy_timing *timings;
        unsigned int num_timings;
        unsigned int lane_mbps = div64_ul(samsung->pll.rate, USEC_PER_SEC);
        unsigned int i;

        timings = samsung_mipi_dphy_timing_table;
        num_timings = ARRAY_SIZE(samsung_mipi_dphy_timing_table);

        for (i = num_timings; i > 1; i--)
                if (lane_mbps <= timings[i - 1].max_lane_mbps)
                        break;

        return &timings[i - 1];
}

static unsigned long
samsung_mipi_dcphy_pll_round_rate(struct samsung_mipi_dcphy *samsung,
                                  unsigned long prate, unsigned long rate,
                                  u8 *prediv, u16 *fbdiv, int *dsm, u8 *scaler)
{
        u32 max_fout = samsung->pdata->dphy_tx_max_lane_kbps;
        u64 best_freq = 0;
        u64 fin, fvco, fout;
        u8 min_prediv, max_prediv;
        u8 _prediv, best_prediv = 1;
        u16 _fbdiv, best_fbdiv = 1;
        u8 _scaler, best_scaler = 0;
        u32 min_delta = UINT_MAX;
        long _dsm, best_dsm = 0;

        if (!prate) {
                dev_err(samsung->dev, "parent rate of PLL can not be zero\n");
                return 0;
        }

        /*
         * The PLL output frequency can be calculated using a simple formula:
         * Fvco = ((m+k/65536) x 2 x Fin) / p
         * Fout = ((m+k/65536) x 2 x Fin) / (p x 2^s)
         */
        fin = div64_ul(prate, MSEC_PER_SEC);

        while (!best_freq) {
                fout = div64_ul(rate, MSEC_PER_SEC);
                if (fout > max_fout)
                        fout = max_fout;

                /* 0 ≤ S[2:0] ≤ 6 */
                for (_scaler = 0; _scaler < 7; _scaler++) {
                        fvco = fout << _scaler;

                        /*
                         * 2600MHz ≤ FVCO ≤ 6600MHz
                         */
                        if (fvco < 2600 * MSEC_PER_SEC || fvco > 6600 * MSEC_PER_SEC)
                                continue;

                        /* 6MHz ≤ Fref(Fin / p) ≤ 30MHz */
                        min_prediv = DIV_ROUND_UP_ULL(fin, 30 * MSEC_PER_SEC);
                        max_prediv = DIV_ROUND_CLOSEST_ULL(fin, 6 * MSEC_PER_SEC);

                        for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
                                u64 delta, tmp;

                                _fbdiv = DIV_ROUND_CLOSEST_ULL(fvco * _prediv, 2 * fin);

                                 /* 64 ≤ M[9:0] ≤ 1023 */
                                if (_fbdiv < 64 || _fbdiv > 1023)
                                        continue;

                                /* -32767 ≤ K[15:0] ≤ 32767 */
                                _dsm = ((_prediv * fvco) - (2 * _fbdiv * fin));
                                _dsm = DIV_ROUND_UP_ULL(_dsm << 15, fin);
                                if (abs(_dsm) > 32767)
                                        continue;

                                tmp = DIV_ROUND_CLOSEST_ULL((_fbdiv * fin * 2 * 1000), _prediv);
                                tmp += DIV_ROUND_CLOSEST_ULL((_dsm * fin * 1000), _prediv << 15);

                                delta = abs(fvco * MSEC_PER_SEC - tmp);
                                if (delta < min_delta) {
                                        best_prediv = _prediv;
                                        best_fbdiv = _fbdiv;
                                        best_dsm = _dsm;
                                        best_scaler = _scaler;
                                        min_delta = delta;
                                        best_freq = DIV_ROUND_CLOSEST_ULL(tmp, 1000) * MSEC_PER_SEC;
                                }
                        }
                }

                rate += 100 * MSEC_PER_SEC;
        }

        *prediv = best_prediv;
        *fbdiv = best_fbdiv;
        *dsm = (int)best_dsm & 0xffff;
        *scaler = best_scaler;
        dev_dbg(samsung->dev, "p: %d, m: %d, dsm:%ld, scaler: %d\n",
                best_prediv, best_fbdiv, best_dsm, best_scaler);

        return best_freq >> best_scaler;
}

static void
samsung_mipi_dphy_clk_lane_timing_init(struct samsung_mipi_dcphy *samsung)
{
        const struct samsung_mipi_dphy_timing *timing;
        unsigned int lane_hs_rate = div64_ul(samsung->pll.rate, USEC_PER_SEC);
        u32 val, res_up, res_down;

        timing = samsung_mipi_dphy_get_timing(samsung);
        regmap_write(samsung->regmap, DPHY_MC_GNR_CON0, 0xf000);

        /*
         * The Drive-Strength / Voltage-Amplitude is adjusted by setting
         * the Driver-Up Resistor and Driver-Down Resistor.
         */
        res_up = samsung->pdata->dphy_hs_drv_res_cfg->clk_hs_drv_up_ohm;
        res_down = samsung->pdata->dphy_hs_drv_res_cfg->clk_hs_drv_down_ohm;
        val = EDGE_CON(7) | EDGE_CON_DIR(0) | EDGE_CON_EN |
              RES_UP(res_up) | RES_DN(res_down);
        regmap_write(samsung->regmap, DPHY_MC_ANA_CON0, val);

        if (lane_hs_rate >= 4500)
                regmap_write(samsung->regmap, DPHY_MC_ANA_CON1, 0x0001);

        val = 0;
        /*
         * Divide-by-2 Clock from Serial Clock. Use this when data rate is under
         * 1500Mbps, otherwise divide-by-16 Clock from Serial Clock
         */
        if (lane_hs_rate < 1500)
                val = HSTX_CLK_SEL;

        val |= T_LPX(timing->lpx);
        /*  T_LP_EXIT_SKEW/T_LP_ENTRY_SKEW unconfig */
        regmap_write(samsung->regmap, DPHY_MC_TIME_CON0, val);

        val = T_CLK_ZERO(timing->clk_zero) | T_CLK_PREPARE(timing->clk_prepare);
        regmap_write(samsung->regmap, DPHY_MC_TIME_CON1, val);

        val = T_HS_EXIT(timing->hs_exit) | T_CLK_TRAIL(timing->clk_trail_eot);
        regmap_write(samsung->regmap, DPHY_MC_TIME_CON2, val);

        val = T_CLK_POST(timing->clk_post);
        regmap_write(samsung->regmap, DPHY_MC_TIME_CON3, val);

        /* Escape Clock is 20.00MHz */
        regmap_write(samsung->regmap, DPHY_MC_TIME_CON4, 0x1f4);

        /*
         * skew calibration should be off, if the operation data rate is
         * under 1.5Gbps or equal to 1.5Gbps.
         */
        if (lane_hs_rate > 1500)
                regmap_write(samsung->regmap, DPHY_MC_DESKEW_CON0, 0x9cb1);
}

static void
samsung_mipi_dphy_data_lane_timing_init(struct samsung_mipi_dcphy *samsung)
{
        const struct samsung_mipi_dphy_timing *timing;
        unsigned int lane_hs_rate = div64_ul(samsung->pll.rate, USEC_PER_SEC);
        u32 val, res_up, res_down;

        timing = samsung_mipi_dphy_get_timing(samsung);

        /*
         * The Drive-Strength / Voltage-Amplitude is adjusted by adjusting the
         *  Driver-Up Resistor and Driver-Down Resistor.
         */
        res_up = samsung->pdata->dphy_hs_drv_res_cfg->data_hs_drv_up_ohm;
        res_down = samsung->pdata->dphy_hs_drv_res_cfg->data_hs_drv_down_ohm;
        val = EDGE_CON(7) | EDGE_CON_DIR(0) | EDGE_CON_EN |
              RES_UP(res_up) | RES_DN(res_down);
        regmap_write(samsung->regmap, COMBO_MD0_ANA_CON0, val);
        regmap_write(samsung->regmap, COMBO_MD1_ANA_CON0, val);
        regmap_write(samsung->regmap, COMBO_MD2_ANA_CON0, val);
        regmap_write(samsung->regmap, DPHY_MD3_ANA_CON0, val);

        if (lane_hs_rate >= 4500) {
                regmap_write(samsung->regmap, COMBO_MD0_ANA_CON1, 0x0001);
                regmap_write(samsung->regmap, COMBO_MD1_ANA_CON1, 0x0001);
                regmap_write(samsung->regmap, COMBO_MD2_ANA_CON1, 0x0001);
                regmap_write(samsung->regmap, DPHY_MD3_ANA_CON1, 0x0001);
        }

        val = 0;
        /*
         * Divide-by-2 Clock from Serial Clock. Use this when data rate is under
         * 1500Mbps, otherwise divide-by-16 Clock from Serial Clock
         */
        if (lane_hs_rate < 1500)
                val = HSTX_CLK_SEL;

        val |= T_LPX(timing->lpx);
        /*  T_LP_EXIT_SKEW/T_LP_ENTRY_SKEW unconfig */
        regmap_write(samsung->regmap, COMBO_MD0_TIME_CON0, val);
        regmap_write(samsung->regmap, COMBO_MD1_TIME_CON0, val);
        regmap_write(samsung->regmap, COMBO_MD2_TIME_CON0, val);
        regmap_write(samsung->regmap, DPHY_MD3_TIME_CON0, val);

        val = T_HS_ZERO(timing->hs_zero) | T_HS_PREPARE(timing->hs_prepare);
        regmap_write(samsung->regmap, COMBO_MD0_TIME_CON1, val);
        regmap_write(samsung->regmap, COMBO_MD1_TIME_CON1, val);
        regmap_write(samsung->regmap, COMBO_MD2_TIME_CON1, val);
        regmap_write(samsung->regmap, DPHY_MD3_TIME_CON1, val);

        val = T_HS_EXIT(timing->hs_exit) | T_HS_TRAIL(timing->hs_trail_eot);
        regmap_write(samsung->regmap, COMBO_MD0_TIME_CON2, val);
        regmap_write(samsung->regmap, COMBO_MD1_TIME_CON2, val);
        regmap_write(samsung->regmap, COMBO_MD2_TIME_CON2, val);
        regmap_write(samsung->regmap, DPHY_MD3_TIME_CON2, val);

        /* TTA-GET/TTA-GO Timing Counter register use default value */
        val = T_TA_GET(0x3) | T_TA_GO(0x0);
        regmap_write(samsung->regmap, COMBO_MD0_TIME_CON3, val);
        regmap_write(samsung->regmap, COMBO_MD1_TIME_CON3, val);
        regmap_write(samsung->regmap, COMBO_MD2_TIME_CON3, val);
        regmap_write(samsung->regmap, DPHY_MD3_TIME_CON3, val);

        /* Escape Clock is 20.00MHz */
        regmap_write(samsung->regmap, COMBO_MD0_TIME_CON4, 0x1f4);
        regmap_write(samsung->regmap, COMBO_MD1_TIME_CON4, 0x1f4);
        regmap_write(samsung->regmap, COMBO_MD2_TIME_CON4, 0x1f4);
        regmap_write(samsung->regmap, DPHY_MD3_TIME_CON4, 0x1f4);
}

static int samsung_mipi_dphy_power_on(struct samsung_mipi_dcphy *samsung)
{
        int ret;

        reset_control_assert(samsung->m_phy_rst);

        samsung_mipi_dcphy_bias_block_enable(samsung);
        samsung_mipi_dcphy_pll_configure(samsung);
        samsung_mipi_dphy_clk_lane_timing_init(samsung);
        samsung_mipi_dphy_data_lane_timing_init(samsung);
        ret = samsung_mipi_dcphy_pll_enable(samsung);
        if (ret < 0)
                return ret;

        samsung_mipi_dphy_lane_enable(samsung);

        reset_control_deassert(samsung->m_phy_rst);

        /* The TSKEWCAL maximum is 100 µsec
         * at initial calibration.
         */
        usleep_range(100, 110);

        return 0;
}

static int samsung_mipi_dcphy_power_on(struct phy *phy)
{
        struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);

        reset_control_assert(samsung->apb_rst);
        udelay(1);
        reset_control_deassert(samsung->apb_rst);

        switch (samsung->type) {
        case PHY_TYPE_DPHY:
                return samsung_mipi_dphy_power_on(samsung);
        default:
                /* CPHY part to be implemented later */
                return -EOPNOTSUPP;
        }

        return 0;
}

static int samsung_mipi_dcphy_power_off(struct phy *phy)
{
        struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);

        switch (samsung->type) {
        case PHY_TYPE_DPHY:
                samsung_mipi_dphy_lane_disable(samsung);
                break;
        default:
                /* CPHY part to be implemented later */
                return -EOPNOTSUPP;
        }

        samsung_mipi_dcphy_pll_disable(samsung);

        return 0;
}

static int
samsung_mipi_dcphy_pll_ssc_modulation_calc(struct samsung_mipi_dcphy *samsung,
                                           u8 *mfr, u8 *mrr)
{
        unsigned long fin = div64_ul(clk_get_rate(samsung->ref_clk), MSEC_PER_SEC);
        u16 prediv = samsung->pll.prediv;
        u16 fbdiv = samsung->pll.fbdiv;
        u16 min_mfr, max_mfr;
        u16 _mfr, best_mfr = 0;
        u16 mr, _mrr, best_mrr = 0;

        /* 20KHz ≤ MF ≤ 150KHz */
        max_mfr = DIV_ROUND_UP(fin, (20 * prediv) << 5);
        min_mfr = div64_ul(fin, ((150 * prediv) << 5));
        /*0 ≤ mfr ≤ 255 */
        if (max_mfr > 256)
                max_mfr = 256;

        for (_mfr = min_mfr; _mfr < max_mfr; _mfr++) {
                /* 1 ≤ mrr ≤ 31 */
                for (_mrr = 1; _mrr < 32; _mrr++) {
                        mr = DIV_ROUND_UP(_mfr * _mrr * 100, fbdiv << 6);
                        /* 0 ≤ MR ≤ 5% */
                        if (mr > 5)
                                continue;

                        if (_mfr * _mrr < 513) {
                                best_mfr = _mfr;
                                best_mrr = _mrr;
                                break;
                        }
                }
        }

        if (best_mrr) {
                *mfr = best_mfr & 0xff;
                *mrr = best_mrr & 0x3f;
        } else {
                dev_err(samsung->dev, "failed to calc ssc parameter mfr and mrr\n");
                return -EINVAL;
        }

        return 0;
}

static void
samsung_mipi_dcphy_pll_calc_rate(struct samsung_mipi_dcphy *samsung,
                                 unsigned long long rate)
{
        unsigned long prate = clk_get_rate(samsung->ref_clk);
        unsigned long fout;
        u8 scaler = 0, mfr = 0, mrr = 0;
        u16 fbdiv = 0;
        u8 prediv = 1;
        int dsm = 0;
        int ret;

        fout = samsung_mipi_dcphy_pll_round_rate(samsung, prate, rate,
                                                 &prediv, &fbdiv, &dsm,
                                                 &scaler);

        dev_dbg(samsung->dev, "%s: fin=%lu, req_rate=%llu\n",
                __func__, prate, rate);
        dev_dbg(samsung->dev, "%s: fout=%lu, prediv=%u, fbdiv=%u\n",
                __func__, fout, prediv, fbdiv);

        samsung->pll.prediv = prediv;
        samsung->pll.fbdiv = fbdiv;
        samsung->pll.dsm = dsm;
        samsung->pll.scaler = scaler;
        samsung->pll.rate = fout;

        /*
         * All DPHY 2.0 compliant Transmitters shall support SSC operating above
         * 2.5 Gbps
         */
        if (fout > 2500000000LL) {
                ret = samsung_mipi_dcphy_pll_ssc_modulation_calc(samsung,
                                                                 &mfr, &mrr);
                if (!ret) {
                        samsung->pll.ssc_en = true;
                        samsung->pll.mfr = mfr;
                        samsung->pll.mrr = mrr;
                }
        }
}

static int samsung_mipi_dcphy_configure(struct phy *phy,
                                        union phy_configure_opts *opts)
{
        struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);
        unsigned long long target_rate = opts->mipi_dphy.hs_clk_rate;

        samsung->lanes = opts->mipi_dphy.lanes > 4 ? 4 : opts->mipi_dphy.lanes;

        samsung_mipi_dcphy_pll_calc_rate(samsung, target_rate);
        opts->mipi_dphy.hs_clk_rate = samsung->pll.rate;

        return 0;
}

static int samsung_mipi_dcphy_init(struct phy *phy)
{
        struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);

        return pm_runtime_resume_and_get(samsung->dev);
}

static int samsung_mipi_dcphy_exit(struct phy *phy)
{
        struct samsung_mipi_dcphy *samsung = phy_get_drvdata(phy);

        pm_runtime_put(samsung->dev);

        return 0;
}

static const struct phy_ops samsung_mipi_dcphy_ops = {
        .configure = samsung_mipi_dcphy_configure,
        .power_on  = samsung_mipi_dcphy_power_on,
        .power_off = samsung_mipi_dcphy_power_off,
        .init = samsung_mipi_dcphy_init,
        .exit = samsung_mipi_dcphy_exit,
        .owner     = THIS_MODULE,
};

static const struct regmap_config samsung_mipi_dcphy_regmap_config = {
        .name = "dcphy",
        .reg_bits = 32,
        .val_bits = 32,
        .reg_stride = 4,
        .max_register = 0x10000,
};

static struct phy *samsung_mipi_dcphy_xlate(struct device *dev,
                                            const struct of_phandle_args *args)
{
        struct samsung_mipi_dcphy *samsung = dev_get_drvdata(dev);

        if (args->args_count != 1) {
                dev_err(dev, "invalid number of arguments\n");
                return ERR_PTR(-EINVAL);
        }

        if (samsung->type != PHY_NONE && samsung->type != args->args[0])
                dev_warn(dev, "phy type select %d overwriting type %d\n",
                         args->args[0], samsung->type);

        samsung->type = args->args[0];

        return samsung->phy;
}

static int samsung_mipi_dcphy_probe(struct platform_device *pdev)
{
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
        struct samsung_mipi_dcphy *samsung;
        struct phy_provider *phy_provider;
        struct resource *res;
        void __iomem *regs;
        int ret;

        samsung = devm_kzalloc(dev, sizeof(*samsung), GFP_KERNEL);
        if (!samsung)
                return -ENOMEM;

        samsung->dev = dev;
        samsung->pdata = device_get_match_data(dev);
        platform_set_drvdata(pdev, samsung);

        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        regs = devm_ioremap_resource(dev, res);
        if (IS_ERR(regs))
                return PTR_ERR(regs);

        samsung->regmap = devm_regmap_init_mmio(dev, regs,
                                                &samsung_mipi_dcphy_regmap_config);
        if (IS_ERR(samsung->regmap))
                return dev_err_probe(dev, PTR_ERR(samsung->regmap), "Failed to init regmap\n");

        samsung->grf_regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
        if (IS_ERR(samsung->grf_regmap))
                return dev_err_probe(dev, PTR_ERR(samsung->grf_regmap),
                                     "Unable to get rockchip,grf\n");

        samsung->ref_clk = devm_clk_get(dev, "ref");
        if (IS_ERR(samsung->ref_clk))
                return dev_err_probe(dev, PTR_ERR(samsung->ref_clk),
                                     "Failed to get reference clock\n");

        samsung->pclk = devm_clk_get(dev, "pclk");
        if (IS_ERR(samsung->pclk))
                return dev_err_probe(dev, PTR_ERR(samsung->pclk), "Failed to get pclk\n");

        samsung->m_phy_rst = devm_reset_control_get(dev, "m_phy");
        if (IS_ERR(samsung->m_phy_rst))
                return dev_err_probe(dev, PTR_ERR(samsung->m_phy_rst),
                                     "Failed to get system m_phy_rst control\n");

        samsung->s_phy_rst = devm_reset_control_get(dev, "s_phy");
        if (IS_ERR(samsung->s_phy_rst))
                return dev_err_probe(dev, PTR_ERR(samsung->s_phy_rst),
                                     "Failed to get system s_phy_rst control\n");

        samsung->apb_rst = devm_reset_control_get(dev, "apb");
        if (IS_ERR(samsung->apb_rst))
                return dev_err_probe(dev, PTR_ERR(samsung->apb_rst),
                                     "Failed to get system apb_rst control\n");

        samsung->grf_apb_rst = devm_reset_control_get(dev, "grf");
        if (IS_ERR(samsung->grf_apb_rst))
                return dev_err_probe(dev, PTR_ERR(samsung->grf_apb_rst),
                                     "Failed to get system grf_apb_rst control\n");

        samsung->phy = devm_phy_create(dev, NULL, &samsung_mipi_dcphy_ops);
        if (IS_ERR(samsung->phy))
                return dev_err_probe(dev, PTR_ERR(samsung->phy), "Failed to create MIPI DC-PHY\n");

        phy_set_drvdata(samsung->phy, samsung);

        ret = devm_pm_runtime_enable(dev);
        if (ret)
                return dev_err_probe(dev, ret, "Failed to enable runtime PM\n");

        phy_provider = devm_of_phy_provider_register(dev, samsung_mipi_dcphy_xlate);
        if (IS_ERR(phy_provider))
                return dev_err_probe(dev, PTR_ERR(phy_provider),
                                     "Failed to register phy provider\n");

        return 0;
}

static __maybe_unused int samsung_mipi_dcphy_runtime_suspend(struct device *dev)
{
        struct samsung_mipi_dcphy *samsung = dev_get_drvdata(dev);

        clk_disable_unprepare(samsung->ref_clk);
        clk_disable_unprepare(samsung->pclk);

        return 0;
}

static __maybe_unused int samsung_mipi_dcphy_runtime_resume(struct device *dev)
{
        struct samsung_mipi_dcphy *samsung = dev_get_drvdata(dev);
        int ret;

        ret = clk_prepare_enable(samsung->pclk);
        if (ret) {
                dev_err(samsung->dev, "Failed to enable pclk, %d\n", ret);
                return ret;
        }

        ret = clk_prepare_enable(samsung->ref_clk);
        if (ret) {
                dev_err(samsung->dev, "Failed to enable reference clock, %d\n", ret);
                clk_disable_unprepare(samsung->pclk);
                return ret;
        }

        return 0;
}

static const struct dev_pm_ops samsung_mipi_dcphy_pm_ops = {
        SET_RUNTIME_PM_OPS(samsung_mipi_dcphy_runtime_suspend,
                           samsung_mipi_dcphy_runtime_resume, NULL)
};

static const struct hs_drv_res_cfg rk3576_dphy_hs_drv_res_cfg = {
        .clk_hs_drv_up_ohm = STRENGTH_52_OHM,
        .clk_hs_drv_down_ohm = STRENGTH_52_OHM,
        .data_hs_drv_up_ohm = STRENGTH_39_OHM,
        .data_hs_drv_down_ohm = STRENGTH_39_OHM,
};

static const struct hs_drv_res_cfg rk3588_dphy_hs_drv_res_cfg = {
        .clk_hs_drv_up_ohm = STRENGTH_34_OHM,
        .clk_hs_drv_down_ohm = STRENGTH_34_OHM,
        .data_hs_drv_up_ohm = STRENGTH_43_OHM,
        .data_hs_drv_down_ohm = STRENGTH_43_OHM,
};

static const struct samsung_mipi_dcphy_plat_data rk3576_samsung_mipi_dcphy_plat_data = {
        .dphy_hs_drv_res_cfg = &rk3576_dphy_hs_drv_res_cfg,
        .dphy_tx_max_lane_kbps = 2500000L,
};

static const struct samsung_mipi_dcphy_plat_data rk3588_samsung_mipi_dcphy_plat_data = {
        .dphy_hs_drv_res_cfg = &rk3588_dphy_hs_drv_res_cfg,
        .dphy_tx_max_lane_kbps = 4500000L,
};

static const struct of_device_id samsung_mipi_dcphy_of_match[] = {
        {
                .compatible = "rockchip,rk3576-mipi-dcphy",
                .data = &rk3576_samsung_mipi_dcphy_plat_data,
        }, {
                .compatible = "rockchip,rk3588-mipi-dcphy",
                .data = &rk3588_samsung_mipi_dcphy_plat_data,
        },
        { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, samsung_mipi_dcphy_of_match);

static struct platform_driver samsung_mipi_dcphy_driver = {
        .driver = {
                .name = "samsung-mipi-dcphy",
                .of_match_table = samsung_mipi_dcphy_of_match,
                .pm = &samsung_mipi_dcphy_pm_ops,
        },
        .probe  = samsung_mipi_dcphy_probe,
};
module_platform_driver(samsung_mipi_dcphy_driver);

MODULE_AUTHOR("Guochun Huang <hero.huang@rock-chips.com>");
MODULE_DESCRIPTION("Samsung MIPI DCPHY Driver");
MODULE_LICENSE("GPL");