brintos

brintos / linux-shallow public Read only

0
0
Text · 4.2 KiB · 2617f7f Raw
161 lines · c
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.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 the21 * 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 vendor23 * BSP / kernel.24 */25#define HHI_HDMI_PHY_CNTL0				0x3a026	#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				0x3a430	#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				0x3a834 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	else65		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");161