xref: /openbmc/linux/drivers/phy/amlogic/phy-meson-g12a-mipi-dphy-analog.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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