12a56dc65SNeil Armstrong // SPDX-License-Identifier: GPL-2.0
22a56dc65SNeil Armstrong /*
32a56dc65SNeil Armstrong * Meson G12A MIPI DSI Analog PHY
42a56dc65SNeil Armstrong *
52a56dc65SNeil Armstrong * Copyright (C) 2018 Amlogic, Inc. All rights reserved
62a56dc65SNeil Armstrong * Copyright (C) 2022 BayLibre, SAS
72a56dc65SNeil Armstrong * Author: Neil Armstrong <narmstrong@baylibre.com>
82a56dc65SNeil Armstrong */
92a56dc65SNeil Armstrong #include <linux/bitfield.h>
102a56dc65SNeil Armstrong #include <linux/bitops.h>
112a56dc65SNeil Armstrong #include <linux/module.h>
122a56dc65SNeil Armstrong #include <linux/phy/phy.h>
132a56dc65SNeil Armstrong #include <linux/regmap.h>
142a56dc65SNeil Armstrong #include <linux/delay.h>
152a56dc65SNeil Armstrong #include <linux/mfd/syscon.h>
16*7559e757SRob Herring #include <linux/of.h>
172a56dc65SNeil Armstrong #include <linux/platform_device.h>
182a56dc65SNeil Armstrong #include <dt-bindings/phy/phy.h>
192a56dc65SNeil Armstrong
202a56dc65SNeil Armstrong #define HHI_MIPI_CNTL0 0x00
212a56dc65SNeil Armstrong #define HHI_MIPI_CNTL0_DIF_REF_CTL1 GENMASK(31, 16)
222a56dc65SNeil Armstrong #define HHI_MIPI_CNTL0_DIF_REF_CTL0 GENMASK(15, 0)
232a56dc65SNeil Armstrong
242a56dc65SNeil Armstrong #define HHI_MIPI_CNTL1 0x04
252a56dc65SNeil Armstrong #define HHI_MIPI_CNTL1_BANDGAP BIT(16)
262a56dc65SNeil Armstrong #define HHI_MIPI_CNTL2_DIF_REF_CTL2 GENMASK(15, 0)
272a56dc65SNeil Armstrong
282a56dc65SNeil Armstrong #define HHI_MIPI_CNTL2 0x08
292a56dc65SNeil Armstrong #define HHI_MIPI_CNTL2_DIF_TX_CTL1 GENMASK(31, 16)
302a56dc65SNeil Armstrong #define HHI_MIPI_CNTL2_CH_EN GENMASK(15, 11)
312a56dc65SNeil Armstrong #define HHI_MIPI_CNTL2_DIF_TX_CTL0 GENMASK(10, 0)
322a56dc65SNeil Armstrong
332a56dc65SNeil Armstrong #define DSI_LANE_0 BIT(4)
342a56dc65SNeil Armstrong #define DSI_LANE_1 BIT(3)
352a56dc65SNeil Armstrong #define DSI_LANE_CLK BIT(2)
362a56dc65SNeil Armstrong #define DSI_LANE_2 BIT(1)
372a56dc65SNeil Armstrong #define DSI_LANE_3 BIT(0)
382a56dc65SNeil Armstrong
392a56dc65SNeil Armstrong struct phy_g12a_mipi_dphy_analog_priv {
402a56dc65SNeil Armstrong struct phy *phy;
412a56dc65SNeil Armstrong struct regmap *regmap;
422a56dc65SNeil Armstrong struct phy_configure_opts_mipi_dphy config;
432a56dc65SNeil Armstrong };
442a56dc65SNeil Armstrong
phy_g12a_mipi_dphy_analog_configure(struct phy * phy,union phy_configure_opts * opts)452a56dc65SNeil Armstrong static int phy_g12a_mipi_dphy_analog_configure(struct phy *phy,
462a56dc65SNeil Armstrong union phy_configure_opts *opts)
472a56dc65SNeil Armstrong {
482a56dc65SNeil Armstrong struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy);
492a56dc65SNeil Armstrong int ret;
502a56dc65SNeil Armstrong
512a56dc65SNeil Armstrong ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
522a56dc65SNeil Armstrong if (ret)
532a56dc65SNeil Armstrong return ret;
542a56dc65SNeil Armstrong
552a56dc65SNeil Armstrong memcpy(&priv->config, opts, sizeof(priv->config));
562a56dc65SNeil Armstrong
572a56dc65SNeil Armstrong return 0;
582a56dc65SNeil Armstrong }
592a56dc65SNeil Armstrong
phy_g12a_mipi_dphy_analog_power_on(struct phy * phy)602a56dc65SNeil Armstrong static int phy_g12a_mipi_dphy_analog_power_on(struct phy *phy)
612a56dc65SNeil Armstrong {
622a56dc65SNeil Armstrong struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy);
632a56dc65SNeil Armstrong unsigned int reg;
642a56dc65SNeil Armstrong
652a56dc65SNeil Armstrong regmap_write(priv->regmap, HHI_MIPI_CNTL0,
662a56dc65SNeil Armstrong FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL0, 0x8) |
672a56dc65SNeil Armstrong FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0xa487));
682a56dc65SNeil Armstrong
692a56dc65SNeil Armstrong regmap_write(priv->regmap, HHI_MIPI_CNTL1,
702a56dc65SNeil Armstrong FIELD_PREP(HHI_MIPI_CNTL2_DIF_REF_CTL2, 0x2e) |
712a56dc65SNeil Armstrong HHI_MIPI_CNTL1_BANDGAP);
722a56dc65SNeil Armstrong
732a56dc65SNeil Armstrong regmap_write(priv->regmap, HHI_MIPI_CNTL2,
74b9491930SNeil Armstrong FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL0, 0x45a) |
752a56dc65SNeil Armstrong FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL1, 0x2680));
762a56dc65SNeil Armstrong
772a56dc65SNeil Armstrong reg = DSI_LANE_CLK;
782a56dc65SNeil Armstrong switch (priv->config.lanes) {
792a56dc65SNeil Armstrong case 4:
802a56dc65SNeil Armstrong reg |= DSI_LANE_3;
812a56dc65SNeil Armstrong fallthrough;
822a56dc65SNeil Armstrong case 3:
832a56dc65SNeil Armstrong reg |= DSI_LANE_2;
842a56dc65SNeil Armstrong fallthrough;
852a56dc65SNeil Armstrong case 2:
862a56dc65SNeil Armstrong reg |= DSI_LANE_1;
872a56dc65SNeil Armstrong fallthrough;
882a56dc65SNeil Armstrong case 1:
892a56dc65SNeil Armstrong reg |= DSI_LANE_0;
902a56dc65SNeil Armstrong break;
912a56dc65SNeil Armstrong default:
922a56dc65SNeil Armstrong reg = 0;
932a56dc65SNeil Armstrong }
942a56dc65SNeil Armstrong
952a56dc65SNeil Armstrong regmap_update_bits(priv->regmap, HHI_MIPI_CNTL2,
962a56dc65SNeil Armstrong HHI_MIPI_CNTL2_CH_EN,
972a56dc65SNeil Armstrong FIELD_PREP(HHI_MIPI_CNTL2_CH_EN, reg));
982a56dc65SNeil Armstrong
992a56dc65SNeil Armstrong return 0;
1002a56dc65SNeil Armstrong }
1012a56dc65SNeil Armstrong
phy_g12a_mipi_dphy_analog_power_off(struct phy * phy)1022a56dc65SNeil Armstrong static int phy_g12a_mipi_dphy_analog_power_off(struct phy *phy)
1032a56dc65SNeil Armstrong {
1042a56dc65SNeil Armstrong struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy);
1052a56dc65SNeil Armstrong
1062a56dc65SNeil Armstrong regmap_write(priv->regmap, HHI_MIPI_CNTL0, 0);
1072a56dc65SNeil Armstrong regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0);
1082a56dc65SNeil Armstrong regmap_write(priv->regmap, HHI_MIPI_CNTL2, 0);
1092a56dc65SNeil Armstrong
1102a56dc65SNeil Armstrong return 0;
1112a56dc65SNeil Armstrong }
1122a56dc65SNeil Armstrong
1132a56dc65SNeil Armstrong static const struct phy_ops phy_g12a_mipi_dphy_analog_ops = {
1142a56dc65SNeil Armstrong .configure = phy_g12a_mipi_dphy_analog_configure,
1152a56dc65SNeil Armstrong .power_on = phy_g12a_mipi_dphy_analog_power_on,
1162a56dc65SNeil Armstrong .power_off = phy_g12a_mipi_dphy_analog_power_off,
1172a56dc65SNeil Armstrong .owner = THIS_MODULE,
1182a56dc65SNeil Armstrong };
1192a56dc65SNeil Armstrong
phy_g12a_mipi_dphy_analog_probe(struct platform_device * pdev)1202a56dc65SNeil Armstrong static int phy_g12a_mipi_dphy_analog_probe(struct platform_device *pdev)
1212a56dc65SNeil Armstrong {
1222a56dc65SNeil Armstrong struct phy_provider *phy;
1232a56dc65SNeil Armstrong struct device *dev = &pdev->dev;
1242a56dc65SNeil Armstrong struct phy_g12a_mipi_dphy_analog_priv *priv;
1252a56dc65SNeil Armstrong struct device_node *np = dev->of_node, *parent_np;
1262a56dc65SNeil Armstrong struct regmap *map;
1272a56dc65SNeil Armstrong
1282a56dc65SNeil Armstrong priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);
1292a56dc65SNeil Armstrong if (!priv)
1302a56dc65SNeil Armstrong return -ENOMEM;
1312a56dc65SNeil Armstrong
1322a56dc65SNeil Armstrong /* Get the hhi system controller node */
1332a56dc65SNeil Armstrong parent_np = of_get_parent(np);
1342a56dc65SNeil Armstrong map = syscon_node_to_regmap(parent_np);
1352a56dc65SNeil Armstrong of_node_put(parent_np);
1362a56dc65SNeil Armstrong if (IS_ERR(map))
1372a56dc65SNeil Armstrong return dev_err_probe(dev, PTR_ERR(map), "failed to get HHI regmap\n");
1382a56dc65SNeil Armstrong
1392a56dc65SNeil Armstrong priv->regmap = map;
1402a56dc65SNeil Armstrong
1412a56dc65SNeil Armstrong priv->phy = devm_phy_create(dev, np, &phy_g12a_mipi_dphy_analog_ops);
1422a56dc65SNeil Armstrong if (IS_ERR(priv->phy))
1432a56dc65SNeil Armstrong return dev_err_probe(dev, PTR_ERR(priv->phy), "failed to create PHY\n");
1442a56dc65SNeil Armstrong
1452a56dc65SNeil Armstrong phy_set_drvdata(priv->phy, priv);
1462a56dc65SNeil Armstrong dev_set_drvdata(dev, priv);
1472a56dc65SNeil Armstrong
1482a56dc65SNeil Armstrong phy = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
1492a56dc65SNeil Armstrong
1502a56dc65SNeil Armstrong return PTR_ERR_OR_ZERO(phy);
1512a56dc65SNeil Armstrong }
1522a56dc65SNeil Armstrong
1532a56dc65SNeil Armstrong static const struct of_device_id phy_g12a_mipi_dphy_analog_of_match[] = {
1542a56dc65SNeil Armstrong {
1552a56dc65SNeil Armstrong .compatible = "amlogic,g12a-mipi-dphy-analog",
1562a56dc65SNeil Armstrong },
1572a56dc65SNeil Armstrong { /* sentinel */ }
1582a56dc65SNeil Armstrong };
1592a56dc65SNeil Armstrong MODULE_DEVICE_TABLE(of, phy_g12a_mipi_dphy_analog_of_match);
1602a56dc65SNeil Armstrong
1612a56dc65SNeil Armstrong static struct platform_driver phy_g12a_mipi_dphy_analog_driver = {
1622a56dc65SNeil Armstrong .probe = phy_g12a_mipi_dphy_analog_probe,
1632a56dc65SNeil Armstrong .driver = {
1642a56dc65SNeil Armstrong .name = "phy-meson-g12a-mipi-dphy-analog",
1652a56dc65SNeil Armstrong .of_match_table = phy_g12a_mipi_dphy_analog_of_match,
1662a56dc65SNeil Armstrong },
1672a56dc65SNeil Armstrong };
1682a56dc65SNeil Armstrong module_platform_driver(phy_g12a_mipi_dphy_analog_driver);
1692a56dc65SNeil Armstrong
1702a56dc65SNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
1712a56dc65SNeil Armstrong MODULE_DESCRIPTION("Meson G12A MIPI Analog D-PHY driver");
1722a56dc65SNeil Armstrong MODULE_LICENSE("GPL v2");
173