cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

phy-meson8-hdmi-tx.c (4322B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Meson8, Meson8b and Meson8m2 HDMI TX PHY.
      4 *
      5 * Copyright (C) 2021 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
      6 */
      7
      8#include <linux/bitfield.h>
      9#include <linux/bits.h>
     10#include <linux/clk.h>
     11#include <linux/mfd/syscon.h>
     12#include <linux/module.h>
     13#include <linux/of_device.h>
     14#include <linux/phy/phy.h>
     15#include <linux/platform_device.h>
     16#include <linux/property.h>
     17#include <linux/regmap.h>
     18
     19/*
     20 * Unfortunately there is no detailed documentation available for the
     21 * HHI_HDMI_PHY_CNTL0 register. CTL0 and CTL1 is all we know about.
     22 * Magic register values in the driver below are taken from the vendor
     23 * BSP / kernel.
     24 */
     25#define HHI_HDMI_PHY_CNTL0				0x3a0
     26	#define HHI_HDMI_PHY_CNTL0_HDMI_CTL1		GENMASK(31, 16)
     27	#define HHI_HDMI_PHY_CNTL0_HDMI_CTL0		GENMASK(15, 0)
     28
     29#define HHI_HDMI_PHY_CNTL1				0x3a4
     30	#define HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE		BIT(1)
     31	#define HHI_HDMI_PHY_CNTL1_SOFT_RESET		BIT(0)
     32
     33#define HHI_HDMI_PHY_CNTL2				0x3a8
     34
     35struct phy_meson8_hdmi_tx_priv {
     36	struct regmap		*hhi;
     37	struct clk		*tmds_clk;
     38};
     39
     40static int phy_meson8_hdmi_tx_init(struct phy *phy)
     41{
     42	struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy);
     43
     44	return clk_prepare_enable(priv->tmds_clk);
     45}
     46
     47static int phy_meson8_hdmi_tx_exit(struct phy *phy)
     48{
     49	struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy);
     50
     51	clk_disable_unprepare(priv->tmds_clk);
     52
     53	return 0;
     54}
     55
     56static int phy_meson8_hdmi_tx_power_on(struct phy *phy)
     57{
     58	struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy);
     59	unsigned int i;
     60	u16 hdmi_ctl0;
     61
     62	if (clk_get_rate(priv->tmds_clk) >= 2970UL * 1000 * 1000)
     63		hdmi_ctl0 = 0x1e8b;
     64	else
     65		hdmi_ctl0 = 0x4d0b;
     66
     67	regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0,
     68		     FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL1, 0x08c3) |
     69		     FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL0, hdmi_ctl0));
     70
     71	regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, 0x0);
     72
     73	/* Reset three times, just like the vendor driver does */
     74	for (i = 0; i < 3; i++) {
     75		regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1,
     76			     HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE |
     77			     HHI_HDMI_PHY_CNTL1_SOFT_RESET);
     78		usleep_range(1000, 2000);
     79
     80		regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1,
     81			     HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE);
     82		usleep_range(1000, 2000);
     83	}
     84
     85	return 0;
     86}
     87
     88static int phy_meson8_hdmi_tx_power_off(struct phy *phy)
     89{
     90	struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy);
     91
     92	regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0,
     93		     FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL1, 0x0841) |
     94		     FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL0, 0x8d00));
     95
     96	return 0;
     97}
     98
     99static const struct phy_ops phy_meson8_hdmi_tx_ops = {
    100	.init		= phy_meson8_hdmi_tx_init,
    101	.exit		= phy_meson8_hdmi_tx_exit,
    102	.power_on	= phy_meson8_hdmi_tx_power_on,
    103	.power_off	= phy_meson8_hdmi_tx_power_off,
    104	.owner		= THIS_MODULE,
    105};
    106
    107static int phy_meson8_hdmi_tx_probe(struct platform_device *pdev)
    108{
    109	struct device_node *np = pdev->dev.of_node;
    110	struct phy_meson8_hdmi_tx_priv *priv;
    111	struct phy_provider *phy_provider;
    112	struct resource *res;
    113	struct phy *phy;
    114
    115	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    116	if (!res)
    117		return -EINVAL;
    118
    119	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
    120	if (!priv)
    121		return -ENOMEM;
    122
    123	priv->hhi = syscon_node_to_regmap(np->parent);
    124	if (IS_ERR(priv->hhi))
    125		return PTR_ERR(priv->hhi);
    126
    127	priv->tmds_clk = devm_clk_get(&pdev->dev, NULL);
    128	if (IS_ERR(priv->tmds_clk))
    129		return PTR_ERR(priv->tmds_clk);
    130
    131	phy = devm_phy_create(&pdev->dev, np, &phy_meson8_hdmi_tx_ops);
    132	if (IS_ERR(phy))
    133		return PTR_ERR(phy);
    134
    135	phy_set_drvdata(phy, priv);
    136
    137	phy_provider = devm_of_phy_provider_register(&pdev->dev,
    138						     of_phy_simple_xlate);
    139
    140	return PTR_ERR_OR_ZERO(phy_provider);
    141}
    142
    143static const struct of_device_id phy_meson8_hdmi_tx_of_match[] = {
    144	{ .compatible = "amlogic,meson8-hdmi-tx-phy" },
    145	{ /* sentinel */ }
    146};
    147MODULE_DEVICE_TABLE(of, phy_meson8_hdmi_tx_of_match);
    148
    149static struct platform_driver phy_meson8_hdmi_tx_driver = {
    150	.probe	= phy_meson8_hdmi_tx_probe,
    151	.driver	= {
    152		.name		= "phy-meson8-hdmi-tx",
    153		.of_match_table	= phy_meson8_hdmi_tx_of_match,
    154	},
    155};
    156module_platform_driver(phy_meson8_hdmi_tx_driver);
    157
    158MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
    159MODULE_DESCRIPTION("Meson8, Meson8b and Meson8m2 HDMI TX PHY driver");
    160MODULE_LICENSE("GPL v2");