[PATCH v3 07/15] phy: qualcomm: add MSM8x60 HDMI PHY support
Dmitry Baryshkov
dmitry.baryshkov at linaro.org
Thu Sep 28 04:16:21 PDT 2023
Add support for HDMI PHY on Qualcomm MSM8x60 / APQ8060 platforms.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov at linaro.org>
---
drivers/phy/qualcomm/Makefile | 1 +
drivers/phy/qualcomm/phy-qcom-hdmi-45nm.c | 184 ++++++++++++++++++++
drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c | 32 ++--
drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.h | 1 +
4 files changed, 204 insertions(+), 14 deletions(-)
create mode 100644 drivers/phy/qualcomm/phy-qcom-hdmi-45nm.c
diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile
index 082020af7fc1..79df2f1e9bc8 100644
--- a/drivers/phy/qualcomm/Makefile
+++ b/drivers/phy/qualcomm/Makefile
@@ -10,6 +10,7 @@ phy-qcom-hdmi-y := \
phy-qcom-hdmi-preqmp.o \
phy-qcom-hdmi-28hpm.o \
phy-qcom-hdmi-28lpm.o \
+ phy-qcom-hdmi-45nm.o \
obj-$(CONFIG_PHY_QCOM_M31_USB) += phy-qcom-m31.o
obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o
diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-45nm.c b/drivers/phy/qualcomm/phy-qcom-hdmi-45nm.c
new file mode 100644
index 000000000000..31e1e9f2d037
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-hdmi-45nm.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 Red Hat
+ * Author: Rob Clark <robdclark at gmail.com>
+ * Copyright (c) 2023, Linaro Ltd.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+
+#include "phy-qcom-hdmi-preqmp.h"
+
+#define REG_HDMI_8x60_PHY_REG0 0x00000000
+#define HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK 0x0000001c
+
+#define REG_HDMI_8x60_PHY_REG1 0x00000004
+#define HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__MASK 0x000000f0
+#define HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__MASK 0x0000000f
+
+#define REG_HDMI_8x60_PHY_REG2 0x00000008
+#define HDMI_8x60_PHY_REG2_PD_DESER 0x00000001
+#define HDMI_8x60_PHY_REG2_PD_DRIVE_1 0x00000002
+#define HDMI_8x60_PHY_REG2_PD_DRIVE_2 0x00000004
+#define HDMI_8x60_PHY_REG2_PD_DRIVE_3 0x00000008
+#define HDMI_8x60_PHY_REG2_PD_DRIVE_4 0x00000010
+#define HDMI_8x60_PHY_REG2_PD_PLL 0x00000020
+#define HDMI_8x60_PHY_REG2_PD_PWRGEN 0x00000040
+#define HDMI_8x60_PHY_REG2_RCV_SENSE_EN 0x00000080
+
+#define REG_HDMI_8x60_PHY_REG3 0x0000000c
+#define HDMI_8x60_PHY_REG3_PLL_ENABLE 0x00000001
+
+#define REG_HDMI_8x60_PHY_REG4 0x00000010
+
+#define REG_HDMI_8x60_PHY_REG5 0x00000014
+
+#define REG_HDMI_8x60_PHY_REG6 0x00000018
+
+#define REG_HDMI_8x60_PHY_REG7 0x0000001c
+
+#define REG_HDMI_8x60_PHY_REG8 0x00000020
+
+#define REG_HDMI_8x60_PHY_REG9 0x00000024
+
+#define REG_HDMI_8x60_PHY_REG10 0x00000028
+
+#define REG_HDMI_8x60_PHY_REG11 0x0000002c
+
+#define REG_HDMI_8x60_PHY_REG12 0x00000030
+#define HDMI_8x60_PHY_REG12_RETIMING_EN 0x00000001
+#define HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN 0x00000002
+#define HDMI_8x60_PHY_REG12_FORCE_LOCK 0x00000010
+
+static int qcom_hdmi_msm8x60_phy_power_on(struct qcom_hdmi_preqmp_phy *hdmi_phy)
+{
+ unsigned long pixclock = hdmi_phy->hdmi_opts.pixel_clk_rate;
+
+ /* De-serializer delay D/C for non-lbk mode: */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG0,
+ FIELD_PREP(HDMI_8x60_PHY_REG0_DESER_DEL_CTRL__MASK, 3));
+
+ if (pixclock == 27000) {
+ /* video_format == HDMI_VFRMT_720x480p60_16_9 */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG1,
+ FIELD_PREP(HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__MASK, 5) |
+ FIELD_PREP(HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__MASK, 3));
+ } else {
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG1,
+ FIELD_PREP(HDMI_8x60_PHY_REG1_DTEST_MUX_SEL__MASK, 5) |
+ FIELD_PREP(HDMI_8x60_PHY_REG1_OUTVOL_SWING_CTRL__MASK, 4));
+ }
+
+ /* No matter what, start from the power down mode: */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG2,
+ HDMI_8x60_PHY_REG2_PD_PWRGEN |
+ HDMI_8x60_PHY_REG2_PD_PLL |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
+ HDMI_8x60_PHY_REG2_PD_DESER);
+
+ /* Turn PowerGen on: */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG2,
+ HDMI_8x60_PHY_REG2_PD_PLL |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
+ HDMI_8x60_PHY_REG2_PD_DESER);
+
+ /* Turn PLL power on: */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG2,
+ HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
+ HDMI_8x60_PHY_REG2_PD_DESER);
+
+ /* Write to HIGH after PLL power down de-assert: */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG3,
+ HDMI_8x60_PHY_REG3_PLL_ENABLE);
+
+ /* ASIC power on; PHY REG9 = 0 */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG9, 0);
+
+ /* Enable PLL lock detect, PLL lock det will go high after lock
+ * Enable the re-time logic
+ */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG12,
+ HDMI_8x60_PHY_REG12_RETIMING_EN |
+ HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN);
+
+ /* Drivers are on: */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG2,
+ HDMI_8x60_PHY_REG2_PD_DESER);
+
+ /* If the RX detector is needed: */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG2,
+ HDMI_8x60_PHY_REG2_RCV_SENSE_EN |
+ HDMI_8x60_PHY_REG2_PD_DESER);
+
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG4, 0);
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG5, 0);
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG6, 0);
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG7, 0);
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG8, 0);
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG9, 0);
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG10, 0);
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG11, 0);
+
+ /* If we want to use lock enable based on counting: */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG12,
+ HDMI_8x60_PHY_REG12_RETIMING_EN |
+ HDMI_8x60_PHY_REG12_PLL_LOCK_DETECT_EN |
+ HDMI_8x60_PHY_REG12_FORCE_LOCK);
+
+ return 0;
+}
+
+static int qcom_hdmi_msm8x60_phy_power_off(struct qcom_hdmi_preqmp_phy *hdmi_phy)
+{
+ /* Turn off Driver */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG2,
+ HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
+ HDMI_8x60_PHY_REG2_PD_DESER);
+ udelay(10);
+ /* Disable PLL */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG3, 0);
+ /* Power down PHY, but keep RX-sense: */
+ hdmi_phy_write(hdmi_phy, REG_HDMI_8x60_PHY_REG2,
+ HDMI_8x60_PHY_REG2_RCV_SENSE_EN |
+ HDMI_8x60_PHY_REG2_PD_PWRGEN |
+ HDMI_8x60_PHY_REG2_PD_PLL |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_4 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_3 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_2 |
+ HDMI_8x60_PHY_REG2_PD_DRIVE_1 |
+ HDMI_8x60_PHY_REG2_PD_DESER);
+
+ return 0;
+}
+
+const struct qcom_hdmi_preqmp_cfg msm8x60_hdmi_phy_cfg = {
+ .clk_names = { "slave_iface" },
+ .num_clks = 1,
+
+ .reg_names = { "core-vdda" },
+ .num_regs = 1,
+
+ .power_on = qcom_hdmi_msm8x60_phy_power_on,
+ .power_off = qcom_hdmi_msm8x60_phy_power_off,
+
+ /* FIXME: no PLL support */
+};
diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c b/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c
index e181a45df2dc..66aa199424b8 100644
--- a/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c
+++ b/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.c
@@ -147,20 +147,23 @@ static int qcom_hdmi_preqmp_probe(struct platform_device *pdev)
if (ret)
return ret;
- init.name = "hdmipll";
- init.ops = cfg->pll_ops;
- init.flags = CLK_GET_RATE_NOCACHE;
- init.parent_data = cfg->pll_parent;
- init.num_parents = 1;
-
- hdmi_phy->pll_hw.init = &init;
- ret = devm_clk_hw_register(hdmi_phy->dev, &hdmi_phy->pll_hw);
- if (ret)
- goto err;
-
- ret = devm_of_clk_add_hw_provider(hdmi_phy->dev, of_clk_hw_simple_get, &hdmi_phy->pll_hw);
- if (ret)
- goto err;
+ /* FIXME: msm8x60 doesn't yet have PLL ops */
+ if (cfg->pll_ops) {
+ init.name = "hdmipll";
+ init.ops = cfg->pll_ops;
+ init.flags = CLK_GET_RATE_NOCACHE;
+ init.parent_data = cfg->pll_parent;
+ init.num_parents = 1;
+
+ hdmi_phy->pll_hw.init = &init;
+ ret = devm_clk_hw_register(hdmi_phy->dev, &hdmi_phy->pll_hw);
+ if (ret)
+ goto err;
+
+ ret = devm_of_clk_add_hw_provider(hdmi_phy->dev, of_clk_hw_simple_get, &hdmi_phy->pll_hw);
+ if (ret)
+ goto err;
+ }
hdmi_phy->phy = devm_phy_create(dev, pdev->dev.of_node, &qcom_hdmi_preqmp_phy_ops);
if (IS_ERR(hdmi_phy->phy)) {
@@ -180,6 +183,7 @@ static int qcom_hdmi_preqmp_probe(struct platform_device *pdev)
}
static const struct of_device_id qcom_hdmi_preqmp_of_match_table[] = {
+ { .compatible = "qcom,hdmi-phy-8x60", .data = &msm8x60_hdmi_phy_cfg, },
{ .compatible = "qcom,hdmi-phy-8960", .data = &msm8960_hdmi_phy_cfg, },
{ .compatible = "qcom,hdmi-phy-8974", .data = &msm8974_hdmi_phy_cfg, },
{ },
diff --git a/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.h b/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.h
index bc81d68463ec..66fb9235520a 100644
--- a/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.h
+++ b/drivers/phy/qualcomm/phy-qcom-hdmi-preqmp.h
@@ -74,6 +74,7 @@ static inline u32 hdmi_pll_read(struct qcom_hdmi_preqmp_phy *phy, int offset)
return readl(phy->pll_reg + offset);
}
+extern const struct qcom_hdmi_preqmp_cfg msm8x60_hdmi_phy_cfg;
extern const struct qcom_hdmi_preqmp_cfg msm8960_hdmi_phy_cfg;
extern const struct qcom_hdmi_preqmp_cfg msm8974_hdmi_phy_cfg;
--
2.39.2
More information about the linux-phy
mailing list