173 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Meson G12A MIPI DSI Analog PHY4 *5 * Copyright (C) 2018 Amlogic, Inc. All rights reserved6 * Copyright (C) 2022 BayLibre, SAS7 * Author: Neil Armstrong <narmstrong@baylibre.com>8 */9#include <linux/bitfield.h>10#include <linux/bitops.h>11#include <linux/module.h>12#include <linux/phy/phy.h>13#include <linux/regmap.h>14#include <linux/delay.h>15#include <linux/mfd/syscon.h>16#include <linux/of.h>17#include <linux/platform_device.h>18#include <dt-bindings/phy/phy.h>19 20#define HHI_MIPI_CNTL0 0x0021#define HHI_MIPI_CNTL0_DIF_REF_CTL1 GENMASK(31, 16)22#define HHI_MIPI_CNTL0_DIF_REF_CTL0 GENMASK(15, 0)23 24#define HHI_MIPI_CNTL1 0x0425#define HHI_MIPI_CNTL1_BANDGAP BIT(16)26#define HHI_MIPI_CNTL2_DIF_REF_CTL2 GENMASK(15, 0)27 28#define HHI_MIPI_CNTL2 0x0829#define HHI_MIPI_CNTL2_DIF_TX_CTL1 GENMASK(31, 16)30#define HHI_MIPI_CNTL2_CH_EN GENMASK(15, 11)31#define HHI_MIPI_CNTL2_DIF_TX_CTL0 GENMASK(10, 0)32 33#define DSI_LANE_0 BIT(4)34#define DSI_LANE_1 BIT(3)35#define DSI_LANE_CLK BIT(2)36#define DSI_LANE_2 BIT(1)37#define DSI_LANE_3 BIT(0)38 39struct phy_g12a_mipi_dphy_analog_priv {40 struct phy *phy;41 struct regmap *regmap;42 struct phy_configure_opts_mipi_dphy config;43};44 45static int phy_g12a_mipi_dphy_analog_configure(struct phy *phy,46 union phy_configure_opts *opts)47{48 struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy);49 int ret;50 51 ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);52 if (ret)53 return ret;54 55 memcpy(&priv->config, opts, sizeof(priv->config));56 57 return 0;58}59 60static int phy_g12a_mipi_dphy_analog_power_on(struct phy *phy)61{62 struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy);63 unsigned int reg;64 65 regmap_write(priv->regmap, HHI_MIPI_CNTL0,66 FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL0, 0x8) |67 FIELD_PREP(HHI_MIPI_CNTL0_DIF_REF_CTL1, 0xa487));68 69 regmap_write(priv->regmap, HHI_MIPI_CNTL1,70 FIELD_PREP(HHI_MIPI_CNTL2_DIF_REF_CTL2, 0x2e) |71 HHI_MIPI_CNTL1_BANDGAP);72 73 regmap_write(priv->regmap, HHI_MIPI_CNTL2,74 FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL0, 0x45a) |75 FIELD_PREP(HHI_MIPI_CNTL2_DIF_TX_CTL1, 0x2680));76 77 reg = DSI_LANE_CLK;78 switch (priv->config.lanes) {79 case 4:80 reg |= DSI_LANE_3;81 fallthrough;82 case 3:83 reg |= DSI_LANE_2;84 fallthrough;85 case 2:86 reg |= DSI_LANE_1;87 fallthrough;88 case 1:89 reg |= DSI_LANE_0;90 break;91 default:92 reg = 0;93 }94 95 regmap_update_bits(priv->regmap, HHI_MIPI_CNTL2,96 HHI_MIPI_CNTL2_CH_EN,97 FIELD_PREP(HHI_MIPI_CNTL2_CH_EN, reg));98 99 return 0;100}101 102static int phy_g12a_mipi_dphy_analog_power_off(struct phy *phy)103{104 struct phy_g12a_mipi_dphy_analog_priv *priv = phy_get_drvdata(phy);105 106 regmap_write(priv->regmap, HHI_MIPI_CNTL0, 0);107 regmap_write(priv->regmap, HHI_MIPI_CNTL1, 0);108 regmap_write(priv->regmap, HHI_MIPI_CNTL2, 0);109 110 return 0;111}112 113static const struct phy_ops phy_g12a_mipi_dphy_analog_ops = {114 .configure = phy_g12a_mipi_dphy_analog_configure,115 .power_on = phy_g12a_mipi_dphy_analog_power_on,116 .power_off = phy_g12a_mipi_dphy_analog_power_off,117 .owner = THIS_MODULE,118};119 120static int phy_g12a_mipi_dphy_analog_probe(struct platform_device *pdev)121{122 struct phy_provider *phy;123 struct device *dev = &pdev->dev;124 struct phy_g12a_mipi_dphy_analog_priv *priv;125 struct device_node *np = dev->of_node, *parent_np;126 struct regmap *map;127 128 priv = devm_kmalloc(dev, sizeof(*priv), GFP_KERNEL);129 if (!priv)130 return -ENOMEM;131 132 /* Get the hhi system controller node */133 parent_np = of_get_parent(np);134 map = syscon_node_to_regmap(parent_np);135 of_node_put(parent_np);136 if (IS_ERR(map))137 return dev_err_probe(dev, PTR_ERR(map), "failed to get HHI regmap\n");138 139 priv->regmap = map;140 141 priv->phy = devm_phy_create(dev, np, &phy_g12a_mipi_dphy_analog_ops);142 if (IS_ERR(priv->phy))143 return dev_err_probe(dev, PTR_ERR(priv->phy), "failed to create PHY\n");144 145 phy_set_drvdata(priv->phy, priv);146 dev_set_drvdata(dev, priv);147 148 phy = devm_of_phy_provider_register(dev, of_phy_simple_xlate);149 150 return PTR_ERR_OR_ZERO(phy);151}152 153static const struct of_device_id phy_g12a_mipi_dphy_analog_of_match[] = {154 {155 .compatible = "amlogic,g12a-mipi-dphy-analog",156 },157 { /* sentinel */ }158};159MODULE_DEVICE_TABLE(of, phy_g12a_mipi_dphy_analog_of_match);160 161static struct platform_driver phy_g12a_mipi_dphy_analog_driver = {162 .probe = phy_g12a_mipi_dphy_analog_probe,163 .driver = {164 .name = "phy-meson-g12a-mipi-dphy-analog",165 .of_match_table = phy_g12a_mipi_dphy_analog_of_match,166 },167};168module_platform_driver(phy_g12a_mipi_dphy_analog_driver);169 170MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");171MODULE_DESCRIPTION("Meson G12A MIPI Analog D-PHY driver");172MODULE_LICENSE("GPL v2");173