162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR MIT) 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Rockchip MIPI Synopsys DPHY RX0 driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2019 Collabora, Ltd. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Based on: 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * drivers/media/platform/rockchip/isp1/mipi_dphy_sy.c 1062306a36Sopenharmony_ci * in https://chromium.googlesource.com/chromiumos/third_party/kernel, 1162306a36Sopenharmony_ci * chromeos-4.4 branch. 1262306a36Sopenharmony_ci * 1362306a36Sopenharmony_ci * Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd. 1462306a36Sopenharmony_ci * Jacob Chen <jacob2.chen@rock-chips.com> 1562306a36Sopenharmony_ci * Shunqian Zheng <zhengsq@rock-chips.com> 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/clk.h> 1962306a36Sopenharmony_ci#include <linux/delay.h> 2062306a36Sopenharmony_ci#include <linux/io.h> 2162306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <linux/phy/phy.h> 2562306a36Sopenharmony_ci#include <linux/phy/phy-mipi-dphy.h> 2662306a36Sopenharmony_ci#include <linux/platform_device.h> 2762306a36Sopenharmony_ci#include <linux/regmap.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define RK3399_GRF_SOC_CON9 0x6224 3062306a36Sopenharmony_ci#define RK3399_GRF_SOC_CON21 0x6254 3162306a36Sopenharmony_ci#define RK3399_GRF_SOC_CON22 0x6258 3262306a36Sopenharmony_ci#define RK3399_GRF_SOC_CON23 0x625c 3362306a36Sopenharmony_ci#define RK3399_GRF_SOC_CON24 0x6260 3462306a36Sopenharmony_ci#define RK3399_GRF_SOC_CON25 0x6264 3562306a36Sopenharmony_ci#define RK3399_GRF_SOC_STATUS1 0xe2a4 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#define CLOCK_LANE_HS_RX_CONTROL 0x34 3862306a36Sopenharmony_ci#define LANE0_HS_RX_CONTROL 0x44 3962306a36Sopenharmony_ci#define LANE1_HS_RX_CONTROL 0x54 4062306a36Sopenharmony_ci#define LANE2_HS_RX_CONTROL 0x84 4162306a36Sopenharmony_ci#define LANE3_HS_RX_CONTROL 0x94 4262306a36Sopenharmony_ci#define LANES_THS_SETTLE_CONTROL 0x75 4362306a36Sopenharmony_ci#define THS_SETTLE_COUNTER_THRESHOLD 0x04 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistruct hsfreq_range { 4662306a36Sopenharmony_ci u16 range_h; 4762306a36Sopenharmony_ci u8 cfg_bit; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic const struct hsfreq_range rk3399_mipidphy_hsfreq_ranges[] = { 5162306a36Sopenharmony_ci { 89, 0x00 }, { 99, 0x10 }, { 109, 0x20 }, { 129, 0x01 }, 5262306a36Sopenharmony_ci { 139, 0x11 }, { 149, 0x21 }, { 169, 0x02 }, { 179, 0x12 }, 5362306a36Sopenharmony_ci { 199, 0x22 }, { 219, 0x03 }, { 239, 0x13 }, { 249, 0x23 }, 5462306a36Sopenharmony_ci { 269, 0x04 }, { 299, 0x14 }, { 329, 0x05 }, { 359, 0x15 }, 5562306a36Sopenharmony_ci { 399, 0x25 }, { 449, 0x06 }, { 499, 0x16 }, { 549, 0x07 }, 5662306a36Sopenharmony_ci { 599, 0x17 }, { 649, 0x08 }, { 699, 0x18 }, { 749, 0x09 }, 5762306a36Sopenharmony_ci { 799, 0x19 }, { 849, 0x29 }, { 899, 0x39 }, { 949, 0x0a }, 5862306a36Sopenharmony_ci { 999, 0x1a }, { 1049, 0x2a }, { 1099, 0x3a }, { 1149, 0x0b }, 5962306a36Sopenharmony_ci { 1199, 0x1b }, { 1249, 0x2b }, { 1299, 0x3b }, { 1349, 0x0c }, 6062306a36Sopenharmony_ci { 1399, 0x1c }, { 1449, 0x2c }, { 1500, 0x3c } 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic const char * const rk3399_mipidphy_clks[] = { 6462306a36Sopenharmony_ci "dphy-ref", 6562306a36Sopenharmony_ci "dphy-cfg", 6662306a36Sopenharmony_ci "grf", 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_cienum dphy_reg_id { 7062306a36Sopenharmony_ci GRF_DPHY_RX0_TURNDISABLE = 0, 7162306a36Sopenharmony_ci GRF_DPHY_RX0_FORCERXMODE, 7262306a36Sopenharmony_ci GRF_DPHY_RX0_FORCETXSTOPMODE, 7362306a36Sopenharmony_ci GRF_DPHY_RX0_ENABLE, 7462306a36Sopenharmony_ci GRF_DPHY_RX0_TESTCLR, 7562306a36Sopenharmony_ci GRF_DPHY_RX0_TESTCLK, 7662306a36Sopenharmony_ci GRF_DPHY_RX0_TESTEN, 7762306a36Sopenharmony_ci GRF_DPHY_RX0_TESTDIN, 7862306a36Sopenharmony_ci GRF_DPHY_RX0_TURNREQUEST, 7962306a36Sopenharmony_ci GRF_DPHY_RX0_TESTDOUT, 8062306a36Sopenharmony_ci GRF_DPHY_TX0_TURNDISABLE, 8162306a36Sopenharmony_ci GRF_DPHY_TX0_FORCERXMODE, 8262306a36Sopenharmony_ci GRF_DPHY_TX0_FORCETXSTOPMODE, 8362306a36Sopenharmony_ci GRF_DPHY_TX0_TURNREQUEST, 8462306a36Sopenharmony_ci GRF_DPHY_TX1RX1_TURNDISABLE, 8562306a36Sopenharmony_ci GRF_DPHY_TX1RX1_FORCERXMODE, 8662306a36Sopenharmony_ci GRF_DPHY_TX1RX1_FORCETXSTOPMODE, 8762306a36Sopenharmony_ci GRF_DPHY_TX1RX1_ENABLE, 8862306a36Sopenharmony_ci GRF_DPHY_TX1RX1_MASTERSLAVEZ, 8962306a36Sopenharmony_ci GRF_DPHY_TX1RX1_BASEDIR, 9062306a36Sopenharmony_ci GRF_DPHY_TX1RX1_ENABLECLK, 9162306a36Sopenharmony_ci GRF_DPHY_TX1RX1_TURNREQUEST, 9262306a36Sopenharmony_ci GRF_DPHY_RX1_SRC_SEL, 9362306a36Sopenharmony_ci /* rk3288 only */ 9462306a36Sopenharmony_ci GRF_CON_DISABLE_ISP, 9562306a36Sopenharmony_ci GRF_CON_ISP_DPHY_SEL, 9662306a36Sopenharmony_ci GRF_DSI_CSI_TESTBUS_SEL, 9762306a36Sopenharmony_ci GRF_DVP_V18SEL, 9862306a36Sopenharmony_ci /* below is for rk3399 only */ 9962306a36Sopenharmony_ci GRF_DPHY_RX0_CLK_INV_SEL, 10062306a36Sopenharmony_ci GRF_DPHY_RX1_CLK_INV_SEL, 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistruct dphy_reg { 10462306a36Sopenharmony_ci u16 offset; 10562306a36Sopenharmony_ci u8 mask; 10662306a36Sopenharmony_ci u8 shift; 10762306a36Sopenharmony_ci}; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci#define PHY_REG(_offset, _width, _shift) \ 11062306a36Sopenharmony_ci { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, } 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic const struct dphy_reg rk3399_grf_dphy_regs[] = { 11362306a36Sopenharmony_ci [GRF_DPHY_RX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON9, 4, 0), 11462306a36Sopenharmony_ci [GRF_DPHY_RX0_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 10), 11562306a36Sopenharmony_ci [GRF_DPHY_RX1_CLK_INV_SEL] = PHY_REG(RK3399_GRF_SOC_CON9, 1, 11), 11662306a36Sopenharmony_ci [GRF_DPHY_RX0_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 0), 11762306a36Sopenharmony_ci [GRF_DPHY_RX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 4), 11862306a36Sopenharmony_ci [GRF_DPHY_RX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 8), 11962306a36Sopenharmony_ci [GRF_DPHY_RX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON21, 4, 12), 12062306a36Sopenharmony_ci [GRF_DPHY_TX0_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 0), 12162306a36Sopenharmony_ci [GRF_DPHY_TX0_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 4), 12262306a36Sopenharmony_ci [GRF_DPHY_TX0_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 8), 12362306a36Sopenharmony_ci [GRF_DPHY_TX0_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON22, 4, 12), 12462306a36Sopenharmony_ci [GRF_DPHY_TX1RX1_ENABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 0), 12562306a36Sopenharmony_ci [GRF_DPHY_TX1RX1_FORCERXMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 4), 12662306a36Sopenharmony_ci [GRF_DPHY_TX1RX1_FORCETXSTOPMODE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 8), 12762306a36Sopenharmony_ci [GRF_DPHY_TX1RX1_TURNDISABLE] = PHY_REG(RK3399_GRF_SOC_CON23, 4, 12), 12862306a36Sopenharmony_ci [GRF_DPHY_TX1RX1_TURNREQUEST] = PHY_REG(RK3399_GRF_SOC_CON24, 4, 0), 12962306a36Sopenharmony_ci [GRF_DPHY_RX1_SRC_SEL] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 4), 13062306a36Sopenharmony_ci [GRF_DPHY_TX1RX1_BASEDIR] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 5), 13162306a36Sopenharmony_ci [GRF_DPHY_TX1RX1_ENABLECLK] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 6), 13262306a36Sopenharmony_ci [GRF_DPHY_TX1RX1_MASTERSLAVEZ] = PHY_REG(RK3399_GRF_SOC_CON24, 1, 7), 13362306a36Sopenharmony_ci [GRF_DPHY_RX0_TESTDIN] = PHY_REG(RK3399_GRF_SOC_CON25, 8, 0), 13462306a36Sopenharmony_ci [GRF_DPHY_RX0_TESTEN] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 8), 13562306a36Sopenharmony_ci [GRF_DPHY_RX0_TESTCLK] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 9), 13662306a36Sopenharmony_ci [GRF_DPHY_RX0_TESTCLR] = PHY_REG(RK3399_GRF_SOC_CON25, 1, 10), 13762306a36Sopenharmony_ci [GRF_DPHY_RX0_TESTDOUT] = PHY_REG(RK3399_GRF_SOC_STATUS1, 8, 0), 13862306a36Sopenharmony_ci}; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistruct rk_dphy_drv_data { 14162306a36Sopenharmony_ci const char * const *clks; 14262306a36Sopenharmony_ci unsigned int num_clks; 14362306a36Sopenharmony_ci const struct hsfreq_range *hsfreq_ranges; 14462306a36Sopenharmony_ci unsigned int num_hsfreq_ranges; 14562306a36Sopenharmony_ci const struct dphy_reg *regs; 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistruct rk_dphy { 14962306a36Sopenharmony_ci struct device *dev; 15062306a36Sopenharmony_ci struct regmap *grf; 15162306a36Sopenharmony_ci struct clk_bulk_data *clks; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci const struct rk_dphy_drv_data *drv_data; 15462306a36Sopenharmony_ci struct phy_configure_opts_mipi_dphy config; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci u8 hsfreq; 15762306a36Sopenharmony_ci}; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic inline void rk_dphy_write_grf(struct rk_dphy *priv, 16062306a36Sopenharmony_ci unsigned int index, u8 value) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci const struct dphy_reg *reg = &priv->drv_data->regs[index]; 16362306a36Sopenharmony_ci /* Update high word */ 16462306a36Sopenharmony_ci unsigned int val = (value << reg->shift) | 16562306a36Sopenharmony_ci (reg->mask << (reg->shift + 16)); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci if (WARN_ON(!reg->offset)) 16862306a36Sopenharmony_ci return; 16962306a36Sopenharmony_ci regmap_write(priv->grf, reg->offset, val); 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic void rk_dphy_write(struct rk_dphy *priv, u8 test_code, u8 test_data) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTDIN, test_code); 17562306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTEN, 1); 17662306a36Sopenharmony_ci /* 17762306a36Sopenharmony_ci * With the falling edge on TESTCLK, the TESTDIN[7:0] signal content 17862306a36Sopenharmony_ci * is latched internally as the current test code. Test data is 17962306a36Sopenharmony_ci * programmed internally by rising edge on TESTCLK. 18062306a36Sopenharmony_ci * This code assumes that TESTCLK is already 1. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 0); 18362306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTEN, 0); 18462306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTDIN, test_data); 18562306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 1); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic void rk_dphy_enable(struct rk_dphy *priv) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_FORCERXMODE, 0); 19162306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_FORCETXSTOPMODE, 0); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* Disable lane turn around, which is ignored in receive mode */ 19462306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TURNREQUEST, 0); 19562306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TURNDISABLE, 0xf); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_ENABLE, 19862306a36Sopenharmony_ci GENMASK(priv->config.lanes - 1, 0)); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* dphy start */ 20162306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLK, 1); 20262306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLR, 1); 20362306a36Sopenharmony_ci usleep_range(100, 150); 20462306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_TESTCLR, 0); 20562306a36Sopenharmony_ci usleep_range(100, 150); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* set clock lane */ 20862306a36Sopenharmony_ci /* HS hsfreq_range & lane 0 settle bypass */ 20962306a36Sopenharmony_ci rk_dphy_write(priv, CLOCK_LANE_HS_RX_CONTROL, 0); 21062306a36Sopenharmony_ci /* HS RX Control of lane0 */ 21162306a36Sopenharmony_ci rk_dphy_write(priv, LANE0_HS_RX_CONTROL, priv->hsfreq << 1); 21262306a36Sopenharmony_ci /* HS RX Control of lane1 */ 21362306a36Sopenharmony_ci rk_dphy_write(priv, LANE1_HS_RX_CONTROL, priv->hsfreq << 1); 21462306a36Sopenharmony_ci /* HS RX Control of lane2 */ 21562306a36Sopenharmony_ci rk_dphy_write(priv, LANE2_HS_RX_CONTROL, priv->hsfreq << 1); 21662306a36Sopenharmony_ci /* HS RX Control of lane3 */ 21762306a36Sopenharmony_ci rk_dphy_write(priv, LANE3_HS_RX_CONTROL, priv->hsfreq << 1); 21862306a36Sopenharmony_ci /* HS RX Data Lanes Settle State Time Control */ 21962306a36Sopenharmony_ci rk_dphy_write(priv, LANES_THS_SETTLE_CONTROL, 22062306a36Sopenharmony_ci THS_SETTLE_COUNTER_THRESHOLD); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci /* Normal operation */ 22362306a36Sopenharmony_ci rk_dphy_write(priv, 0x0, 0); 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int rk_dphy_configure(struct phy *phy, union phy_configure_opts *opts) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct rk_dphy *priv = phy_get_drvdata(phy); 22962306a36Sopenharmony_ci const struct rk_dphy_drv_data *drv_data = priv->drv_data; 23062306a36Sopenharmony_ci struct phy_configure_opts_mipi_dphy *config = &opts->mipi_dphy; 23162306a36Sopenharmony_ci unsigned int hsfreq = 0; 23262306a36Sopenharmony_ci unsigned int i; 23362306a36Sopenharmony_ci u64 data_rate_mbps; 23462306a36Sopenharmony_ci int ret; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci /* pass with phy_mipi_dphy_get_default_config (with pixel rate?) */ 23762306a36Sopenharmony_ci ret = phy_mipi_dphy_config_validate(config); 23862306a36Sopenharmony_ci if (ret) 23962306a36Sopenharmony_ci return ret; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci data_rate_mbps = div_u64(config->hs_clk_rate, 1000 * 1000); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci dev_dbg(priv->dev, "lanes %d - data_rate_mbps %llu\n", 24462306a36Sopenharmony_ci config->lanes, data_rate_mbps); 24562306a36Sopenharmony_ci for (i = 0; i < drv_data->num_hsfreq_ranges; i++) { 24662306a36Sopenharmony_ci if (drv_data->hsfreq_ranges[i].range_h >= data_rate_mbps) { 24762306a36Sopenharmony_ci hsfreq = drv_data->hsfreq_ranges[i].cfg_bit; 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci if (!hsfreq) 25262306a36Sopenharmony_ci return -EINVAL; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci priv->hsfreq = hsfreq; 25562306a36Sopenharmony_ci priv->config = *config; 25662306a36Sopenharmony_ci return 0; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int rk_dphy_power_on(struct phy *phy) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct rk_dphy *priv = phy_get_drvdata(phy); 26262306a36Sopenharmony_ci int ret; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ret = clk_bulk_enable(priv->drv_data->num_clks, priv->clks); 26562306a36Sopenharmony_ci if (ret) 26662306a36Sopenharmony_ci return ret; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci rk_dphy_enable(priv); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci return 0; 27162306a36Sopenharmony_ci} 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_cistatic int rk_dphy_power_off(struct phy *phy) 27462306a36Sopenharmony_ci{ 27562306a36Sopenharmony_ci struct rk_dphy *priv = phy_get_drvdata(phy); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci rk_dphy_write_grf(priv, GRF_DPHY_RX0_ENABLE, 0); 27862306a36Sopenharmony_ci clk_bulk_disable(priv->drv_data->num_clks, priv->clks); 27962306a36Sopenharmony_ci return 0; 28062306a36Sopenharmony_ci} 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_cistatic int rk_dphy_init(struct phy *phy) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci struct rk_dphy *priv = phy_get_drvdata(phy); 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci return clk_bulk_prepare(priv->drv_data->num_clks, priv->clks); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_cistatic int rk_dphy_exit(struct phy *phy) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci struct rk_dphy *priv = phy_get_drvdata(phy); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci clk_bulk_unprepare(priv->drv_data->num_clks, priv->clks); 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_cistatic const struct phy_ops rk_dphy_ops = { 29862306a36Sopenharmony_ci .power_on = rk_dphy_power_on, 29962306a36Sopenharmony_ci .power_off = rk_dphy_power_off, 30062306a36Sopenharmony_ci .init = rk_dphy_init, 30162306a36Sopenharmony_ci .exit = rk_dphy_exit, 30262306a36Sopenharmony_ci .configure = rk_dphy_configure, 30362306a36Sopenharmony_ci .owner = THIS_MODULE, 30462306a36Sopenharmony_ci}; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_cistatic const struct rk_dphy_drv_data rk3399_mipidphy_drv_data = { 30762306a36Sopenharmony_ci .clks = rk3399_mipidphy_clks, 30862306a36Sopenharmony_ci .num_clks = ARRAY_SIZE(rk3399_mipidphy_clks), 30962306a36Sopenharmony_ci .hsfreq_ranges = rk3399_mipidphy_hsfreq_ranges, 31062306a36Sopenharmony_ci .num_hsfreq_ranges = ARRAY_SIZE(rk3399_mipidphy_hsfreq_ranges), 31162306a36Sopenharmony_ci .regs = rk3399_grf_dphy_regs, 31262306a36Sopenharmony_ci}; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cistatic const struct of_device_id rk_dphy_dt_ids[] = { 31562306a36Sopenharmony_ci { 31662306a36Sopenharmony_ci .compatible = "rockchip,rk3399-mipi-dphy-rx0", 31762306a36Sopenharmony_ci .data = &rk3399_mipidphy_drv_data, 31862306a36Sopenharmony_ci }, 31962306a36Sopenharmony_ci {} 32062306a36Sopenharmony_ci}; 32162306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rk_dphy_dt_ids); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_cistatic int rk_dphy_probe(struct platform_device *pdev) 32462306a36Sopenharmony_ci{ 32562306a36Sopenharmony_ci struct device *dev = &pdev->dev; 32662306a36Sopenharmony_ci struct device_node *np = dev->of_node; 32762306a36Sopenharmony_ci const struct rk_dphy_drv_data *drv_data; 32862306a36Sopenharmony_ci struct phy_provider *phy_provider; 32962306a36Sopenharmony_ci struct rk_dphy *priv; 33062306a36Sopenharmony_ci struct phy *phy; 33162306a36Sopenharmony_ci unsigned int i; 33262306a36Sopenharmony_ci int ret; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!dev->parent || !dev->parent->of_node) 33562306a36Sopenharmony_ci return -ENODEV; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 33862306a36Sopenharmony_ci if (!priv) 33962306a36Sopenharmony_ci return -ENOMEM; 34062306a36Sopenharmony_ci priv->dev = dev; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci priv->grf = syscon_node_to_regmap(dev->parent->of_node); 34362306a36Sopenharmony_ci if (IS_ERR(priv->grf)) { 34462306a36Sopenharmony_ci dev_err(dev, "Can't find GRF syscon\n"); 34562306a36Sopenharmony_ci return -ENODEV; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci drv_data = of_device_get_match_data(dev); 34962306a36Sopenharmony_ci priv->drv_data = drv_data; 35062306a36Sopenharmony_ci priv->clks = devm_kcalloc(&pdev->dev, drv_data->num_clks, 35162306a36Sopenharmony_ci sizeof(*priv->clks), GFP_KERNEL); 35262306a36Sopenharmony_ci if (!priv->clks) 35362306a36Sopenharmony_ci return -ENOMEM; 35462306a36Sopenharmony_ci for (i = 0; i < drv_data->num_clks; i++) 35562306a36Sopenharmony_ci priv->clks[i].id = drv_data->clks[i]; 35662306a36Sopenharmony_ci ret = devm_clk_bulk_get(&pdev->dev, drv_data->num_clks, priv->clks); 35762306a36Sopenharmony_ci if (ret) 35862306a36Sopenharmony_ci return ret; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci phy = devm_phy_create(dev, np, &rk_dphy_ops); 36162306a36Sopenharmony_ci if (IS_ERR(phy)) { 36262306a36Sopenharmony_ci dev_err(dev, "failed to create phy\n"); 36362306a36Sopenharmony_ci return PTR_ERR(phy); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci phy_set_drvdata(phy, priv); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(phy_provider); 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic struct platform_driver rk_dphy_driver = { 37362306a36Sopenharmony_ci .probe = rk_dphy_probe, 37462306a36Sopenharmony_ci .driver = { 37562306a36Sopenharmony_ci .name = "rockchip-mipi-dphy-rx0", 37662306a36Sopenharmony_ci .of_match_table = rk_dphy_dt_ids, 37762306a36Sopenharmony_ci }, 37862306a36Sopenharmony_ci}; 37962306a36Sopenharmony_cimodule_platform_driver(rk_dphy_driver); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ciMODULE_AUTHOR("Ezequiel Garcia <ezequiel@collabora.com>"); 38262306a36Sopenharmony_ciMODULE_DESCRIPTION("Rockchip MIPI Synopsys DPHY RX0 driver"); 38362306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL"); 384