| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * MediaTek DisplayPort PHY driver |
| * |
| * Copyright (c) 2022, BayLibre Inc. |
| * Copyright (c) 2022, MediaTek Inc. |
| */ |
| |
| #include <linux/delay.h> |
| #include <linux/io.h> |
| #include <linux/mfd/syscon.h> |
| #include <linux/of.h> |
| #include <linux/phy/phy.h> |
| #include <linux/platform_device.h> |
| #include <linux/regmap.h> |
| |
| #define PHY_OFFSET 0x1000 |
| |
| #define MTK_DP_PHY_DIG_PLL_CTL_1 (PHY_OFFSET + 0x14) |
| #define TPLL_SSC_EN BIT(3) |
| |
| #define MTK_DP_PHY_DIG_BIT_RATE (PHY_OFFSET + 0x3C) |
| #define BIT_RATE_RBR 0 |
| #define BIT_RATE_HBR 1 |
| #define BIT_RATE_HBR2 2 |
| #define BIT_RATE_HBR3 3 |
| |
| #define MTK_DP_PHY_DIG_SW_RST (PHY_OFFSET + 0x38) |
| #define DP_GLB_SW_RST_PHYD BIT(0) |
| |
| #define MTK_DP_LANE0_DRIVING_PARAM_3 (PHY_OFFSET + 0x138) |
| #define MTK_DP_LANE1_DRIVING_PARAM_3 (PHY_OFFSET + 0x238) |
| #define MTK_DP_LANE2_DRIVING_PARAM_3 (PHY_OFFSET + 0x338) |
| #define MTK_DP_LANE3_DRIVING_PARAM_3 (PHY_OFFSET + 0x438) |
| #define XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT BIT(4) |
| #define XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT (BIT(10) | BIT(12)) |
| #define XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT GENMASK(20, 19) |
| #define XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT GENMASK(29, 29) |
| #define DRIVING_PARAM_3_DEFAULT (XTP_LN_TX_LCTXC0_SW0_PRE0_DEFAULT | \ |
| XTP_LN_TX_LCTXC0_SW0_PRE1_DEFAULT | \ |
| XTP_LN_TX_LCTXC0_SW0_PRE2_DEFAULT | \ |
| XTP_LN_TX_LCTXC0_SW0_PRE3_DEFAULT) |
| |
| #define XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT GENMASK(4, 3) |
| #define XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT GENMASK(12, 9) |
| #define XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT (BIT(18) | BIT(21)) |
| #define XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT GENMASK(29, 29) |
| #define DRIVING_PARAM_4_DEFAULT (XTP_LN_TX_LCTXC0_SW1_PRE0_DEFAULT | \ |
| XTP_LN_TX_LCTXC0_SW1_PRE1_DEFAULT | \ |
| XTP_LN_TX_LCTXC0_SW1_PRE2_DEFAULT | \ |
| XTP_LN_TX_LCTXC0_SW2_PRE0_DEFAULT) |
| |
| #define XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT (BIT(3) | BIT(5)) |
| #define XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT GENMASK(13, 12) |
| #define DRIVING_PARAM_5_DEFAULT (XTP_LN_TX_LCTXC0_SW2_PRE1_DEFAULT | \ |
| XTP_LN_TX_LCTXC0_SW3_PRE0_DEFAULT) |
| |
| #define XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT 0 |
| #define XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT GENMASK(10, 10) |
| #define XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT GENMASK(19, 19) |
| #define XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT GENMASK(28, 28) |
| #define DRIVING_PARAM_6_DEFAULT (XTP_LN_TX_LCTXCP1_SW0_PRE0_DEFAULT | \ |
| XTP_LN_TX_LCTXCP1_SW0_PRE1_DEFAULT | \ |
| XTP_LN_TX_LCTXCP1_SW0_PRE2_DEFAULT | \ |
| XTP_LN_TX_LCTXCP1_SW0_PRE3_DEFAULT) |
| |
| #define XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT 0 |
| #define XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT GENMASK(10, 9) |
| #define XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT GENMASK(19, 18) |
| #define XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT 0 |
| #define DRIVING_PARAM_7_DEFAULT (XTP_LN_TX_LCTXCP1_SW1_PRE0_DEFAULT | \ |
| XTP_LN_TX_LCTXCP1_SW1_PRE1_DEFAULT | \ |
| XTP_LN_TX_LCTXCP1_SW1_PRE2_DEFAULT | \ |
| XTP_LN_TX_LCTXCP1_SW2_PRE0_DEFAULT) |
| |
| #define XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT GENMASK(3, 3) |
| #define XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT 0 |
| #define DRIVING_PARAM_8_DEFAULT (XTP_LN_TX_LCTXCP1_SW2_PRE1_DEFAULT | \ |
| XTP_LN_TX_LCTXCP1_SW3_PRE0_DEFAULT) |
| |
| struct mtk_dp_phy { |
| struct regmap *regs; |
| }; |
| |
| static int mtk_dp_phy_init(struct phy *phy) |
| { |
| struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy); |
| static const u32 driving_params[] = { |
| DRIVING_PARAM_3_DEFAULT, |
| DRIVING_PARAM_4_DEFAULT, |
| DRIVING_PARAM_5_DEFAULT, |
| DRIVING_PARAM_6_DEFAULT, |
| DRIVING_PARAM_7_DEFAULT, |
| DRIVING_PARAM_8_DEFAULT |
| }; |
| |
| regmap_bulk_write(dp_phy->regs, MTK_DP_LANE0_DRIVING_PARAM_3, |
| driving_params, ARRAY_SIZE(driving_params)); |
| regmap_bulk_write(dp_phy->regs, MTK_DP_LANE1_DRIVING_PARAM_3, |
| driving_params, ARRAY_SIZE(driving_params)); |
| regmap_bulk_write(dp_phy->regs, MTK_DP_LANE2_DRIVING_PARAM_3, |
| driving_params, ARRAY_SIZE(driving_params)); |
| regmap_bulk_write(dp_phy->regs, MTK_DP_LANE3_DRIVING_PARAM_3, |
| driving_params, ARRAY_SIZE(driving_params)); |
| |
| return 0; |
| } |
| |
| static int mtk_dp_phy_configure(struct phy *phy, union phy_configure_opts *opts) |
| { |
| struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy); |
| u32 val; |
| |
| if (opts->dp.set_rate) { |
| switch (opts->dp.link_rate) { |
| default: |
| dev_err(&phy->dev, |
| "Implementation error, unknown linkrate %x\n", |
| opts->dp.link_rate); |
| return -EINVAL; |
| case 1620: |
| val = BIT_RATE_RBR; |
| break; |
| case 2700: |
| val = BIT_RATE_HBR; |
| break; |
| case 5400: |
| val = BIT_RATE_HBR2; |
| break; |
| case 8100: |
| val = BIT_RATE_HBR3; |
| break; |
| } |
| regmap_write(dp_phy->regs, MTK_DP_PHY_DIG_BIT_RATE, val); |
| } |
| |
| regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_PLL_CTL_1, |
| TPLL_SSC_EN, opts->dp.ssc ? TPLL_SSC_EN : 0); |
| |
| return 0; |
| } |
| |
| static int mtk_dp_phy_reset(struct phy *phy) |
| { |
| struct mtk_dp_phy *dp_phy = phy_get_drvdata(phy); |
| |
| regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST, |
| DP_GLB_SW_RST_PHYD, 0); |
| usleep_range(50, 200); |
| regmap_update_bits(dp_phy->regs, MTK_DP_PHY_DIG_SW_RST, |
| DP_GLB_SW_RST_PHYD, 1); |
| |
| return 0; |
| } |
| |
| static const struct phy_ops mtk_dp_phy_dev_ops = { |
| .init = mtk_dp_phy_init, |
| .configure = mtk_dp_phy_configure, |
| .reset = mtk_dp_phy_reset, |
| .owner = THIS_MODULE, |
| }; |
| |
| static int mtk_dp_phy_probe(struct platform_device *pdev) |
| { |
| struct device *dev = &pdev->dev; |
| struct mtk_dp_phy *dp_phy; |
| struct phy *phy; |
| struct regmap *regs; |
| |
| regs = *(struct regmap **)dev->platform_data; |
| if (!regs) |
| return dev_err_probe(dev, -EINVAL, |
| "No data passed, requires struct regmap**\n"); |
| |
| dp_phy = devm_kzalloc(dev, sizeof(*dp_phy), GFP_KERNEL); |
| if (!dp_phy) |
| return -ENOMEM; |
| |
| dp_phy->regs = regs; |
| phy = devm_phy_create(dev, NULL, &mtk_dp_phy_dev_ops); |
| if (IS_ERR(phy)) |
| return dev_err_probe(dev, PTR_ERR(phy), |
| "Failed to create DP PHY\n"); |
| |
| phy_set_drvdata(phy, dp_phy); |
| if (!dev->of_node) |
| phy_create_lookup(phy, "dp", dev_name(dev)); |
| |
| return 0; |
| } |
| |
| static struct platform_driver mtk_dp_phy_driver = { |
| .probe = mtk_dp_phy_probe, |
| .driver = { |
| .name = "mediatek-dp-phy", |
| }, |
| }; |
| module_platform_driver(mtk_dp_phy_driver); |
| |
| MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>"); |
| MODULE_DESCRIPTION("MediaTek DP PHY Driver"); |
| MODULE_LICENSE("GPL"); |