armbian-build/patch/u-boot/v2024.01/driver-rockchip-inno-hdmi-phy.patch
Paolo Sabatino fb7484f3f9 rockchip: bump rk322x u-boot to v2024.01
* update the defconfig and accomodate upstream changes
 * introduce hdmi/vop/phy drivers and enable hdmi
 * fix clock driver
2024-05-01 08:29:03 +01:00

1076 lines
31 KiB
Diff

From 42a891d30cd2c4c00ee674738128c03277d2d5f8 Mon Sep 17 00:00:00 2001
From: Jagan Teki <jagan@edgeble.ai>
Date: Thu, 16 Feb 2023 21:59:42 +0530
Subject: [PATCH] phy: rockchip: Add Rockchip INNO HDMI PHY driver
Signed-off-by: Jagan Teki <jagan@edgeble.ai>
Signed-off-by: Jagan Teki <jagan@amarulasolutions.com>
---
drivers/phy/rockchip/Kconfig | 7 +
drivers/phy/rockchip/Makefile | 1 +
drivers/phy/rockchip/phy-rockchip-inno-hdmi.c | 734 ++++++++++++++++++
3 files changed, 742 insertions(+)
create mode 100644 drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
diff --git a/drivers/phy/rockchip/Kconfig b/drivers/phy/rockchip/Kconfig
index 13057639403..4809c436c17 100644
--- a/drivers/phy/rockchip/Kconfig
+++ b/drivers/phy/rockchip/Kconfig
@@ -4,6 +4,13 @@
menu "Rockchip PHY driver"
+config PHY_ROCKCHIP_INNO_HDMI
+ bool "Rockchip INNO HDMI PHY Driver"
+ depends on ARCH_ROCKCHIP
+ select PHY
+ help
+ Enable this to support the Rockchip Innosilicon HDMI PHY.
+
config PHY_ROCKCHIP_INNO_USB2
bool "Rockchip INNO USB2PHY Driver"
depends on ARCH_ROCKCHIP
diff --git a/drivers/phy/rockchip/Makefile b/drivers/phy/rockchip/Makefile
index a236877234b..800d5c61ef2 100644
--- a/drivers/phy/rockchip/Makefile
+++ b/drivers/phy/rockchip/Makefile
@@ -3,6 +3,7 @@
# Copyright (C) 2020 Amarula Solutions(India)
#
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_HDMI) += phy-rockchip-inno-hdmi.o
obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2) += phy-rockchip-inno-usb2.o
obj-$(CONFIG_PHY_ROCKCHIP_NANENG_COMBOPHY) += phy-rockchip-naneng-combphy.o
obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
diff --git a/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
new file mode 100644
index 0000000000..207bb0acf6
--- /dev/null
+++ b/drivers/phy/rockchip/phy-rockchip-inno-hdmi.c
@@ -0,0 +1,989 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Rockchip Innosilicon HDMI PHY
+ *
+ * Copyright (c) 2023 Edgeble AI Technologies Pvt. Ltd.
+ * Copyright (c) 2017 Rockchip Electronics Co. Ltd.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <generic-phy.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <inno/phy-inno-hdmi.h>
+
+#define INNO_HDMI_PHY_TIMEOUT_LOOP_COUNT 1000
+#define UPDATE(x, h, l) (((x) << (l)) & GENMASK((h), (l)))
+
+/* REG: 0x00 */
+#define PRE_PLL_REFCLK_SEL_MASK BIT(0)
+#define PRE_PLL_REFCLK_SEL_PCLK BIT(0)
+#define PRE_PLL_REFCLK_SEL_OSCCLK 0
+/* REG: 0x01 */
+#define BYPASS_RXSENSE_EN BIT(2)
+#define BYPASS_PWRON_EN BIT(1)
+#define BYPASS_PLLPD_EN BIT(0)
+/* REG: 0x02 */
+#define INT_POL_HIGH BIT(7)
+#define BYPASS_PDATA_EN BIT(4)
+#define RK3328_PDATA_EN BIT(0)
+#define RK3228_PDATA_EN_DISABLE BIT(0)
+/* REG: 0x03 */
+#define BYPASS_AUTO_TERM_RES_CAL BIT(7)
+#define AUDO_TERM_RES_CAL_SPEED_14_8(x) UPDATE(x, 6, 0)
+/* REG: 0x04 */
+#define AUDO_TERM_RES_CAL_SPEED_7_0(x) UPDATE(x, 7, 0)
+/* REG: 0xaa */
+#define POST_PLL_CTRL_MANUAL BIT(0)
+/* REG: 0xe0 */
+#define RK3228_POST_PLL_POWER_DOWN BIT(5)
+#define RK3228_POST_PLL_POWER_UP 0
+#define RK3228_PRE_PLL_POWER_DOWN BIT(4)
+#define RK3228_RXSENSE_CLK_CH_MASK BIT(3)
+#define RK3228_RXSENSE_CLK_CH_ENABLE BIT(3)
+#define RK3228_RXSENSE_DATA_CH2_MASK BIT(2)
+#define RK3228_RXSENSE_DATA_CH2_ENABLE BIT(2)
+#define RK3228_RXSENSE_DATA_CH1_MASK BIT(1)
+#define RK3228_RXSENSE_DATA_CH1_ENABLE BIT(1)
+#define RK3228_RXSENSE_DATA_CH0_MASK BIT(0)
+#define RK3228_RXSENSE_DATA_CH0_ENABLE BIT(0)
+/* REG: 0xe1 */
+#define RK3228_BANDGAP_ENABLE BIT(4)
+#define RK3228_TMDS_DRIVER_ENABLE GENMASK(3, 0)
+/* REG: 0xe2 */
+#define RK3228_PRE_PLL_FB_DIV_8_MASK BIT(7)
+#define RK3228_PRE_PLL_FB_DIV_8(x) UPDATE((x) >> 8, 7, 7)
+#define RK3228_PCLK_VCO_DIV_5_MASK BIT(5)
+#define RK3228_PCLK_VCO_DIV_5(x) UPDATE(x, 5, 5)
+#define RK3228_PRE_PLL_PRE_DIV_MASK GENMASK(4, 0)
+#define RK3228_PRE_PLL_PRE_DIV(x) UPDATE(x, 4, 0)
+/* REG: 0xe3 */
+#define RK3228_PRE_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0)
+/* REG: 0xe4 */
+#define RK3228_PRE_PLL_PCLK_DIV_B_MASK GENMASK(6, 5)
+#define RK3228_PRE_PLL_PCLK_DIV_B_SHIFT 5
+#define RK3228_PRE_PLL_PCLK_DIV_B(x) UPDATE(x, 6, 5)
+#define RK3228_PRE_PLL_PCLK_DIV_A_MASK GENMASK(4, 0)
+#define RK3228_PRE_PLL_PCLK_DIV_A(x) UPDATE(x, 4, 0)
+/* REG: 0xe5 */
+#define RK3228_PRE_PLL_PCLK_DIV_C_MASK GENMASK(6, 5)
+#define RK3228_PRE_PLL_PCLK_DIV_C(x) UPDATE(x, 6, 5)
+#define RK3228_PRE_PLL_PCLK_DIV_D_MASK GENMASK(4, 0)
+#define RK3228_PRE_PLL_PCLK_DIV_D(x) UPDATE(x, 4, 0)
+/* REG: 0xe6 */
+#define RK3228_PRE_PLL_TMDSCLK_DIV_C_MASK GENMASK(5, 4)
+#define RK3228_PRE_PLL_TMDSCLK_DIV_C(x) UPDATE(x, 5, 4)
+#define RK3228_PRE_PLL_TMDSCLK_DIV_A_MASK GENMASK(3, 2)
+#define RK3228_PRE_PLL_TMDSCLK_DIV_A(x) UPDATE(x, 3, 2)
+#define RK3228_PRE_PLL_TMDSCLK_DIV_B_MASK GENMASK(1, 0)
+#define RK3228_PRE_PLL_TMDSCLK_DIV_B(x) UPDATE(x, 1, 0)
+/* REG: 0xe8 */
+#define RK3228_PRE_PLL_LOCK_STATUS BIT(0)
+/* REG: 0xe9 */
+#define RK3228_POST_PLL_POST_DIV_ENABLE UPDATE(3, 7, 6)
+#define RK3228_POST_PLL_PRE_DIV_MASK GENMASK(4, 0)
+#define RK3228_POST_PLL_PRE_DIV(x) UPDATE(x, 4, 0)
+/* REG: 0xea */
+#define RK3228_POST_PLL_FB_DIV_7_0(x) UPDATE(x, 7, 0)
+/* REG: 0xeb */
+#define RK3228_POST_PLL_FB_DIV_8_MASK BIT(7)
+#define RK3228_POST_PLL_FB_DIV_8(x) UPDATE((x) >> 8, 7, 7)
+#define RK3228_POST_PLL_POST_DIV_MASK GENMASK(5, 4)
+#define RK3228_POST_PLL_POST_DIV(x) UPDATE(x, 5, 4)
+#define RK3228_POST_PLL_LOCK_STATUS BIT(0)
+/* REG: 0xee */
+#define RK3228_TMDS_CH_TA_ENABLE GENMASK(7, 4)
+/* REG: 0xef */
+#define RK3228_TMDS_CLK_CH_TA(x) UPDATE(x, 7, 6)
+#define RK3228_TMDS_DATA_CH2_TA(x) UPDATE(x, 5, 4)
+#define RK3228_TMDS_DATA_CH1_TA(x) UPDATE(x, 3, 2)
+#define RK3228_TMDS_DATA_CH0_TA(x) UPDATE(x, 1, 0)
+/* REG: 0xf0 */
+#define RK3228_TMDS_DATA_CH2_PRE_EMPHASIS_MASK GENMASK(5, 4)
+#define RK3228_TMDS_DATA_CH2_PRE_EMPHASIS(x) UPDATE(x, 5, 4)
+#define RK3228_TMDS_DATA_CH1_PRE_EMPHASIS_MASK GENMASK(3, 2)
+#define RK3228_TMDS_DATA_CH1_PRE_EMPHASIS(x) UPDATE(x, 3, 2)
+#define RK3228_TMDS_DATA_CH0_PRE_EMPHASIS_MASK GENMASK(1, 0)
+#define RK3228_TMDS_DATA_CH0_PRE_EMPHASIS(x) UPDATE(x, 1, 0)
+/* REG: 0xf1 */
+#define RK3228_TMDS_CLK_CH_OUTPUT_SWING(x) UPDATE(x, 7, 4)
+#define RK3228_TMDS_DATA_CH2_OUTPUT_SWING(x) UPDATE(x, 3, 0)
+/* REG: 0xf2 */
+#define RK3228_TMDS_DATA_CH1_OUTPUT_SWING(x) UPDATE(x, 7, 4)
+#define RK3228_TMDS_DATA_CH0_OUTPUT_SWING(x) UPDATE(x, 3, 0)
+
+struct inno_hdmi_phy;
+
+struct phy_config {
+ unsigned long tmdsclock;
+ u8 regs[14];
+};
+
+struct pre_pll_config {
+ unsigned long pixclock;
+ unsigned long tmdsclock;
+ u8 prediv;
+ u16 fbdiv;
+ u8 tmds_div_a;
+ u8 tmds_div_b;
+ u8 tmds_div_c;
+ u8 pclk_div_a;
+ u8 pclk_div_b;
+ u8 pclk_div_c;
+ u8 pclk_div_d;
+ u8 vco_div_5_en;
+ u32 fracdiv;
+};
+
+struct post_pll_config {
+ unsigned long tmdsclock;
+ u8 prediv;
+ u16 fbdiv;
+ u8 postdiv;
+ u8 version;
+};
+
+struct inno_hdmi_phy_plat_ops {
+ void (*init)(struct inno_hdmi_phy *inno);
+ int (*power_on)(struct inno_hdmi_phy *inno,
+ const struct post_pll_config *cfg,
+ const struct phy_config *phy_cfg);
+ void (*power_off)(struct inno_hdmi_phy *inno);
+ int (*pre_pll_update)(struct inno_hdmi_phy *inno,
+ const struct pre_pll_config *cfg);
+ unsigned long (*recalc_rate)(struct inno_hdmi_phy *inno,
+ unsigned long parent_rate);
+ int (*clk_is_prepared)(struct inno_hdmi_phy *inno);
+ int (*clk_prepare)(struct inno_hdmi_phy *inno);
+
+};
+
+enum inno_hdmi_phy_type {
+ INNO_HDMI_PHY_RK3228,
+ INNO_HDMI_PHY_RK3328,
+};
+
+struct inno_hdmi_phy_data {
+ enum inno_hdmi_phy_type phy_type;
+ const struct inno_hdmi_phy_plat_ops *plat_ops;
+ const struct phy_config *phy_cfg_table;
+};
+
+struct rockchip_inno_data {
+ char compatible[30];
+ const void *data;
+};
+
+struct inno_hdmi_phy {
+ struct udevice *dev;
+ ofnode node;
+ void *regs;
+
+ unsigned long pixclock;
+ u32 bus_width;
+ struct phy_config *phy_cfg;
+ const struct inno_hdmi_phy_data *data;
+};
+
+static const struct pre_pll_config pre_pll_cfg_table[] = {
+ { 27000000, 27000000, 1, 90, 3, 2, 2, 10, 3, 3, 4, 0, 0},
+ { 27000000, 33750000, 1, 90, 1, 3, 3, 10, 3, 3, 4, 0, 0},
+ { 40000000, 40000000, 1, 80, 2, 2, 2, 12, 2, 2, 2, 0, 0},
+ { 59341000, 59341000, 1, 98, 3, 1, 2, 1, 3, 3, 4, 0, 0xE6AE6B},
+ { 59400000, 59400000, 1, 99, 3, 1, 1, 1, 3, 3, 4, 0, 0},
+ { 59341000, 74176250, 1, 98, 0, 3, 3, 1, 3, 3, 4, 0, 0xE6AE6B},
+ { 59400000, 74250000, 1, 99, 1, 2, 2, 1, 3, 3, 4, 0, 0},
+ { 74176000, 74176000, 1, 98, 1, 2, 2, 1, 2, 3, 4, 0, 0xE6AE6B},
+ { 74250000, 74250000, 1, 99, 1, 2, 2, 1, 2, 3, 4, 0, 0},
+ { 74176000, 92720000, 4, 494, 1, 2, 2, 1, 3, 3, 4, 0, 0x816817},
+ { 74250000, 92812500, 4, 495, 1, 2, 2, 1, 3, 3, 4, 0, 0},
+ {148352000, 148352000, 1, 98, 1, 1, 1, 1, 2, 2, 2, 0, 0xE6AE6B},
+ {148500000, 148500000, 1, 99, 1, 1, 1, 1, 2, 2, 2, 0, 0},
+ {148352000, 185440000, 4, 494, 0, 2, 2, 1, 3, 2, 2, 0, 0x816817},
+ {148500000, 185625000, 4, 495, 0, 2, 2, 1, 3, 2, 2, 0, 0},
+ {296703000, 296703000, 1, 98, 0, 1, 1, 1, 0, 2, 2, 0, 0xE6AE6B},
+ {297000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 2, 0, 0},
+ {296703000, 370878750, 4, 494, 1, 2, 0, 1, 3, 1, 1, 0, 0x816817},
+ {297000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 0, 0},
+ {593407000, 296703500, 1, 98, 0, 1, 1, 1, 0, 2, 1, 0, 0xE6AE6B},
+ {594000000, 297000000, 1, 99, 0, 1, 1, 1, 0, 2, 1, 0, 0},
+ {593407000, 370879375, 4, 494, 1, 2, 0, 1, 3, 1, 1, 1, 0x816817},
+ {594000000, 371250000, 4, 495, 1, 2, 0, 1, 3, 1, 1, 1, 0},
+ {593407000, 593407000, 1, 98, 0, 2, 0, 1, 0, 1, 1, 0, 0xE6AE6B},
+ {594000000, 594000000, 1, 99, 0, 2, 0, 1, 0, 1, 1, 0, 0},
+ { ~0UL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+};
+
+static const struct post_pll_config post_pll_cfg_table[] = {
+ {33750000, 1, 40, 8, 1},
+ {33750000, 1, 80, 8, 2},
+ {33750000, 1, 10, 2, 4},
+ {74250000, 1, 40, 8, 1},
+ {74250000, 18, 80, 8, 2},
+ {148500000, 2, 40, 4, 3},
+ {297000000, 4, 40, 2, 3},
+ {594000000, 8, 40, 1, 3},
+ { ~0UL, 0, 0, 0, 0}
+};
+
+/* phy tuning values for an undocumented set of registers */
+static const struct phy_config rk3228_phy_cfg[] = {
+ { 165000000, {
+ 0xaa, 0x00, 0x44, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ }, {
+ 340000000, {
+ 0xaa, 0x15, 0x6a, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ }, {
+ 594000000, {
+ 0xaa, 0x15, 0x7a, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ }, { /* sentinel */ },
+};
+
+/* phy tuning values for an undocumented set of registers */
+static const struct phy_config rk3328_phy_cfg[] = {
+ { 165000000, {
+ 0x07, 0x08, 0x08, 0x08, 0x00, 0x00, 0x08, 0x08, 0x08,
+ 0x00, 0xac, 0xcc, 0xcc, 0xcc,
+ },
+ }, {
+ 340000000, {
+ 0x0b, 0x0d, 0x0d, 0x0d, 0x07, 0x15, 0x08, 0x08, 0x08,
+ 0x3f, 0xac, 0xcc, 0xcd, 0xdd,
+ },
+ }, {
+ 594000000, {
+ 0x10, 0x1a, 0x1a, 0x1a, 0x07, 0x15, 0x08, 0x08, 0x08,
+ 0x00, 0xac, 0xcc, 0xcc, 0xcc,
+ },
+ }, {
+ ~0UL, {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ }
+};
+
+static inline void inno_write(struct inno_hdmi_phy *inno, u32 reg, u8 val)
+{
+ writel(val, inno->regs + (reg * 4));
+}
+
+static inline u8 inno_read(struct inno_hdmi_phy *inno, u32 reg)
+{
+ u32 val;
+
+ val = readl(inno->regs + (reg * 4));
+
+ return val;
+}
+
+static inline void inno_update_bits(struct inno_hdmi_phy *inno, u8 reg,
+ u8 mask, u8 val)
+{
+ u32 tmp, orig;
+
+ orig = inno_read(inno, reg);
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+ inno_write(inno, reg, tmp);
+}
+
+static u32 inno_hdmi_phy_get_tmdsclk(struct inno_hdmi_phy *inno,
+ unsigned long rate)
+{
+ u32 tmdsclk;
+
+ switch (inno->bus_width) {
+ case 4:
+ tmdsclk = (u32)rate / 2;
+ break;
+ case 5:
+ tmdsclk = (u32)rate * 5 / 8;
+ break;
+ case 6:
+ tmdsclk = (u32)rate * 3 / 4;
+ break;
+ case 10:
+ tmdsclk = (u32)rate * 5 / 4;
+ break;
+ case 12:
+ tmdsclk = (u32)rate * 3 / 2;
+ break;
+ case 16:
+ tmdsclk = (u32)rate * 2;
+ break;
+ default:
+ tmdsclk = rate;
+ }
+
+ return tmdsclk;
+}
+
+static int inno_hdmi_phy_power_on(struct phy *phy)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+ const struct post_pll_config *cfg = post_pll_cfg_table;
+ const struct phy_config *phy_cfg = inno->data->phy_cfg_table;
+ u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, inno->pixclock);
+ u32 chipversion = 1;
+
+ debug("[PHY] %s: In?\n", __func__);
+
+ if (inno->phy_cfg)
+ phy_cfg = inno->phy_cfg;
+
+ if (!tmdsclock) {
+ printf("TMDS clock is zero!\n");
+ return -EINVAL;
+ }
+
+ printf("tmdsclock = %d; chipversion = %d\n", tmdsclock, chipversion);
+
+ for (; cfg->tmdsclock != ~0UL; cfg++)
+ if (tmdsclock <= cfg->tmdsclock &&
+ cfg->version & chipversion)
+ break;
+
+ for (; phy_cfg->tmdsclock != ~0UL; phy_cfg++)
+ if (tmdsclock <= phy_cfg->tmdsclock)
+ break;
+
+ if (cfg->tmdsclock == ~0UL || phy_cfg->tmdsclock == ~0UL)
+ return -EINVAL;
+
+ debug("Inno HDMI PHY Power On\n");
+ if (inno->data->plat_ops->power_on)
+ return inno->data->plat_ops->power_on(inno, cfg, phy_cfg);
+ else
+ return -EINVAL;
+}
+
+static int inno_hdmi_phy_power_off(struct phy *phy)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+
+ if (inno->data->plat_ops->power_off)
+ inno->data->plat_ops->power_off(inno);
+ debug("Inno HDMI PHY Power Off\n");
+
+ return 0;
+}
+
+static int inno_hdmi_phy_rk3328_clk_is_prepared(struct inno_hdmi_phy *inno)
+{
+ u8 status = inno_read(inno, 0xa0) & 1;
+
+ return status ? 0 : 1;
+}
+
+static int inno_hdmi_phy_rk3328_clk_prepare(struct inno_hdmi_phy *inno)
+{
+ inno_update_bits(inno, 0xa0, 1, 0);
+
+ return 0;
+}
+
+static int inno_hdmi_phy_rk3228_clk_is_prepared(struct inno_hdmi_phy *inno)
+{
+ u8 status = inno_read(inno, 0xe0) & RK3228_PRE_PLL_POWER_DOWN;
+
+ status = status ? 0 : 1;
+
+ debug("%s: status=%d\n", __func__, status);
+
+ return status;
+}
+
+static int inno_hdmi_phy_rk3228_clk_prepare(struct inno_hdmi_phy *inno)
+{
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN, 0);
+
+ debug("%s\n", __func__);
+
+ return 0;
+}
+
+static int inno_hdmi_phy_clk_set_rate(struct inno_hdmi_phy *inno,
+ unsigned long rate)
+{
+ const struct pre_pll_config *cfg = pre_pll_cfg_table;
+ u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
+
+ for (; cfg->pixclock != ~0UL; cfg++)
+ if (cfg->pixclock == rate && cfg->tmdsclock == tmdsclock)
+ break;
+
+ if (cfg->pixclock == ~0UL) {
+ printf("unsupported rate %lu\n", rate);
+ return -EINVAL;
+ }
+
+ if (inno->data->plat_ops->pre_pll_update)
+ inno->data->plat_ops->pre_pll_update(inno, cfg);
+
+ inno->pixclock = rate;
+
+ return 0;
+}
+
+static int
+inno_hdmi_phy_rk3328_power_on(struct inno_hdmi_phy *inno,
+ const struct post_pll_config *cfg,
+ const struct phy_config *phy_cfg)
+{
+ u32 val;
+
+ /* set pdata_en to 0 */
+ inno_update_bits(inno, 0x02, 1, 0);
+ /* Power off post PLL */
+ inno_update_bits(inno, 0xaa, 1, 1);
+
+ val = cfg->fbdiv & 0xff;
+ inno_write(inno, 0xac, val);
+ if (cfg->postdiv == 1) {
+ inno_write(inno, 0xaa, 2);
+ val = (cfg->fbdiv >> 8) | cfg->prediv;
+ inno_write(inno, 0xab, val);
+ } else {
+ val = (cfg->postdiv / 2) - 1;
+ inno_write(inno, 0xad, val);
+ val = (cfg->fbdiv >> 8) | cfg->prediv;
+ inno_write(inno, 0xab, val);
+ inno_write(inno, 0xaa, 0x0e);
+ }
+
+ for (val = 0; val < 14; val++)
+ inno_write(inno, 0xb5 + val, phy_cfg->regs[val]);
+
+ /* bit[7:6] of reg c8/c9/ca/c8 is ESD detect threshold:
+ * 00 - 340mV
+ * 01 - 280mV
+ * 10 - 260mV
+ * 11 - 240mV
+ * default is 240mV, now we set it to 340mV
+ */
+ inno_write(inno, 0xc8, 0);
+ inno_write(inno, 0xc9, 0);
+ inno_write(inno, 0xca, 0);
+ inno_write(inno, 0xcb, 0);
+
+ if (phy_cfg->tmdsclock > 340000000) {
+ /* Set termination resistor to 100ohm */
+ val = 75000000 / 100000;
+ inno_write(inno, 0xc5, ((val >> 8) & 0xff) | 0x80);
+ inno_write(inno, 0xc6, val & 0xff);
+ inno_write(inno, 0xc7, 3 << 1);
+ inno_write(inno, 0xc5, ((val >> 8) & 0xff));
+ } else if (phy_cfg->tmdsclock > 165000000) {
+ inno_write(inno, 0xc5, 0x81);
+ /* clk termination resistor is 50ohm
+ * data termination resistor is 150ohm
+ */
+ inno_write(inno, 0xc8, 0x30);
+ inno_write(inno, 0xc9, 0x10);
+ inno_write(inno, 0xca, 0x10);
+ inno_write(inno, 0xcb, 0x10);
+ } else {
+ inno_write(inno, 0xc5, 0x81);
+ }
+
+ /* set TMDS sync detection counter length */
+ val = 47520000000UL / phy_cfg->tmdsclock;
+ inno_write(inno, 0xd8, (val >> 8) & 0xff);
+ inno_write(inno, 0xd9, val & 0xff);
+
+ /* Power up post PLL */
+ inno_update_bits(inno, 0xaa, 1, 0);
+ /* Power up tmds driver */
+ inno_update_bits(inno, 0xb0, 4, 4);
+ inno_write(inno, 0xb2, 0x0f);
+
+ /* Wait for post PLL lock */
+ for (val = 0; val < 5; val++) {
+ if (inno_read(inno, 0xaf) & 1)
+ break;
+ udelay(1000);
+ }
+ if (!(inno_read(inno, 0xaf) & 1)) {
+ printf("HDMI PHY Post PLL unlock\n");
+ return -ETIMEDOUT;
+ }
+ if (phy_cfg->tmdsclock > 340000000)
+ mdelay(100);
+ /* set pdata_en to 1 */
+ inno_update_bits(inno, 0x02, 1, 1);
+
+ return 0;
+}
+
+static void inno_hdmi_phy_rk3328_power_off(struct inno_hdmi_phy *inno)
+{
+ /* Power off driver */
+ inno_write(inno, 0xb2, 0);
+ /* Power off band gap */
+ inno_update_bits(inno, 0xb0, 4, 0);
+ /* Power off post pll */
+ inno_update_bits(inno, 0xaa, 1, 1);
+}
+
+static int
+inno_hdmi_phy_rk3328_pre_pll_update(struct inno_hdmi_phy *inno,
+ const struct pre_pll_config *cfg)
+{
+ u32 val;
+
+ /* Power off PLL */
+ inno_update_bits(inno, 0xa0, 1, 1);
+ /* Configure pre-pll */
+ inno_update_bits(inno, 0xa0, 2, (cfg->vco_div_5_en & 1) << 1);
+ inno_write(inno, 0xa1, cfg->prediv);
+ if (cfg->fracdiv)
+ val = ((cfg->fbdiv >> 8) & 0x0f) | 0xc0;
+ else
+ val = ((cfg->fbdiv >> 8) & 0x0f) | 0xf0;
+ inno_write(inno, 0xa2, val);
+ inno_write(inno, 0xa3, cfg->fbdiv & 0xff);
+ val = (cfg->pclk_div_a & 0x1f) |
+ ((cfg->pclk_div_b & 3) << 5);
+ inno_write(inno, 0xa5, val);
+ val = (cfg->pclk_div_d & 0x1f) |
+ ((cfg->pclk_div_c & 3) << 5);
+ inno_write(inno, 0xa6, val);
+ val = ((cfg->tmds_div_a & 3) << 4) |
+ ((cfg->tmds_div_b & 3) << 2) |
+ (cfg->tmds_div_c & 3);
+ inno_write(inno, 0xa4, val);
+
+ if (cfg->fracdiv) {
+ val = cfg->fracdiv & 0xff;
+ inno_write(inno, 0xd3, val);
+ val = (cfg->fracdiv >> 8) & 0xff;
+ inno_write(inno, 0xd2, val);
+ val = (cfg->fracdiv >> 16) & 0xff;
+ inno_write(inno, 0xd1, val);
+ } else {
+ inno_write(inno, 0xd3, 0);
+ inno_write(inno, 0xd2, 0);
+ inno_write(inno, 0xd1, 0);
+ }
+
+ /* Power up PLL */
+ inno_update_bits(inno, 0xa0, 1, 0);
+
+ /* Wait for PLL lock */
+ for (val = 0; val < 5; val++) {
+ if (inno_read(inno, 0xa9) & 1)
+ break;
+ udelay(1000);
+ }
+ if (val == 5) {
+ printf("Pre-PLL unlock\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static unsigned long
+inno_hdmi_phy_rk3328_pll_recalc_rate(struct inno_hdmi_phy *inno,
+ unsigned long parent_rate)
+{
+ unsigned long rate, vco, frac;
+ u8 nd, no_a, no_b, no_d;
+ __maybe_unused u8 no_c;
+ u16 nf;
+
+ nd = inno_read(inno, 0xa1) & 0x3f;
+ nf = ((inno_read(inno, 0xa2) & 0x0f) << 8) | inno_read(inno, 0xa3);
+ vco = parent_rate * nf;
+ if ((inno_read(inno, 0xa2) & 0x30) == 0) {
+ frac = inno_read(inno, 0xd3) |
+ (inno_read(inno, 0xd2) << 8) |
+ (inno_read(inno, 0xd1) << 16);
+ vco += DIV_ROUND_CLOSEST(parent_rate * frac, (1 << 24));
+ }
+ if (inno_read(inno, 0xa0) & 2) {
+ rate = vco / (nd * 5);
+ } else {
+ no_a = inno_read(inno, 0xa5) & 0x1f;
+ no_b = ((inno_read(inno, 0xa5) >> 5) & 7) + 2;
+ no_c = (1 << ((inno_read(inno, 0xa6) >> 5) & 7));
+ no_d = inno_read(inno, 0xa6) & 0x1f;
+ if (no_a == 1)
+ rate = vco / (nd * no_b * no_d * 2);
+ else
+ rate = vco / (nd * no_a * no_d * 2);
+ }
+ inno->pixclock = rate;
+
+ return rate;
+}
+
+static void inno_hdmi_phy_rk3328_init(struct inno_hdmi_phy *inno)
+{
+ debug("[PHY] %s: In?\n", __func__);
+ /*
+ * Use phy internal register control
+ * rxsense/poweron/pllpd/pdataen signal.
+ */
+ inno_write(inno, 0x01, BYPASS_RXSENSE_EN | BYPASS_PWRON_EN | BYPASS_PLLPD_EN);
+ inno_write(inno, 0x02, INT_POL_HIGH | BYPASS_PDATA_EN | RK3328_PDATA_EN);
+ debug("[PHY] %s: done!\n", __func__);
+}
+
+static const struct inno_hdmi_phy_plat_ops rk3328_hdmi_phy_plat_ops = {
+ .init = inno_hdmi_phy_rk3328_init,
+ .power_on = inno_hdmi_phy_rk3328_power_on,
+ .power_off = inno_hdmi_phy_rk3328_power_off,
+ .pre_pll_update = inno_hdmi_phy_rk3328_pre_pll_update,
+ .recalc_rate = inno_hdmi_phy_rk3328_pll_recalc_rate,
+ .clk_is_prepared = inno_hdmi_phy_rk3328_clk_is_prepared,
+ .clk_prepare = inno_hdmi_phy_rk3328_clk_prepare
+};
+
+static void inno_hdmi_phy_rk3228_init(struct inno_hdmi_phy *inno)
+{
+ debug("[PHY] %s: In?\n", __func__);
+ /*
+ * Use phy internal register control
+ * rxsense/poweron/pllpd/pdataen signal.
+ */
+ inno_write(inno, 0x01, BYPASS_RXSENSE_EN | BYPASS_PWRON_EN | BYPASS_PLLPD_EN);
+ inno_update_bits(inno, 0x02, BYPASS_PDATA_EN, BYPASS_PDATA_EN);
+
+ /* manual power down post-PLL */
+ inno_update_bits(inno, 0xaa, POST_PLL_CTRL_MANUAL, POST_PLL_CTRL_MANUAL);
+
+ debug("[PHY] %s: done!\n", __func__);
+}
+
+static int
+inno_hdmi_phy_rk3228_power_on(struct inno_hdmi_phy *inno,
+ const struct post_pll_config *cfg,
+ const struct phy_config *phy_cfg)
+{
+ u32 val;
+
+ /* set pdata_en to 0 */
+ inno_update_bits(inno, 0x02, RK3228_PDATA_EN_DISABLE, RK3228_PDATA_EN_DISABLE);
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN | RK3228_POST_PLL_POWER_DOWN,
+ RK3228_PRE_PLL_POWER_DOWN | RK3228_POST_PLL_POWER_DOWN);
+
+ /* Post-PLL update */
+ inno_update_bits(inno, 0xe9, RK3228_POST_PLL_PRE_DIV_MASK,
+ RK3228_POST_PLL_PRE_DIV(cfg->prediv));
+ inno_update_bits(inno, 0xeb, RK3228_POST_PLL_FB_DIV_8_MASK,
+ RK3228_POST_PLL_FB_DIV_8(cfg->fbdiv));
+ inno_write(inno, 0xea, RK3228_POST_PLL_FB_DIV_7_0(cfg->fbdiv));
+
+ if (cfg->postdiv == 1) {
+ inno_update_bits(inno, 0xe9, RK3228_POST_PLL_POST_DIV_ENABLE, 0);
+ } else {
+ int div = cfg->postdiv / 2 - 1;
+
+ inno_update_bits(inno, 0xe9, RK3228_POST_PLL_POST_DIV_ENABLE,
+ RK3228_POST_PLL_POST_DIV_ENABLE);
+ inno_update_bits(inno, 0xeb, RK3228_POST_PLL_POST_DIV_MASK,
+ RK3228_POST_PLL_POST_DIV(div));
+ }
+
+ for (val = 0; val < 4; val++)
+ inno_write(inno, 0xef + val, phy_cfg->regs[val]);
+
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN |
+ RK3228_POST_PLL_POWER_DOWN, 0);
+ inno_update_bits(inno, 0xe1, RK3228_BANDGAP_ENABLE,
+ RK3228_BANDGAP_ENABLE);
+ inno_update_bits(inno, 0xe1, RK3228_TMDS_DRIVER_ENABLE,
+ RK3228_TMDS_DRIVER_ENABLE);
+
+ /* Wait for post PLL lock, up to 100ms */
+ for (val = 0; val < 100; val++) {
+ if (inno_read(inno, 0xeb) & RK3228_POST_PLL_LOCK_STATUS)
+ break;
+ udelay(1000);
+ }
+
+ if (!(inno_read(inno, 0xeb) & RK3228_POST_PLL_LOCK_STATUS)) {
+ printf("%s: HDMI PHY Post PLL unlock\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ if (phy_cfg->tmdsclock > 340000000)
+ mdelay(100);
+
+ /* set pdata_en_disable to 0 */
+ inno_update_bits(inno, 0x02, RK3228_PDATA_EN_DISABLE, 0);
+
+ return 0;
+}
+
+static void inno_hdmi_phy_rk3228_power_off(struct inno_hdmi_phy *inno)
+{
+
+ debug("%s\n", __func__);
+
+ inno_update_bits(inno, 0xe1, RK3228_TMDS_DRIVER_ENABLE, 0);
+ inno_update_bits(inno, 0xe1, RK3228_BANDGAP_ENABLE, 0);
+ inno_update_bits(inno, 0xe0, RK3228_POST_PLL_POWER_DOWN,
+ RK3228_POST_PLL_POWER_DOWN);
+}
+
+static unsigned long
+inno_hdmi_phy_rk3228_pll_recalc_rate(struct inno_hdmi_phy *inno,
+ unsigned long parent_rate)
+{
+ u64 rate, vco;
+ u8 nd, no_a, no_b, no_d;
+ __maybe_unused u8 no_c;
+ u16 nf;
+
+ nd = inno_read(inno, 0xe2) & RK3228_PRE_PLL_PRE_DIV_MASK;
+ nf = (inno_read(inno, 0xe2) & RK3228_PRE_PLL_FB_DIV_8_MASK) << 1;
+ nf |= inno_read(inno, 0xe3);
+ vco = parent_rate * nf;
+
+ if (inno_read(inno, 0xe2) & RK3228_PCLK_VCO_DIV_5_MASK) {
+ rate = vco / (nd * 5);
+ } else {
+ no_a = inno_read(inno, 0xe4) & RK3228_PRE_PLL_PCLK_DIV_A_MASK;
+ if (!no_a)
+ no_a = 1;
+ no_b = inno_read(inno, 0xe4) & RK3228_PRE_PLL_PCLK_DIV_B_MASK;
+ no_b >>= RK3228_PRE_PLL_PCLK_DIV_B_SHIFT;
+ no_b += 2;
+ no_d = inno_read(inno, 0xe5) & RK3228_PRE_PLL_PCLK_DIV_D_MASK;
+
+ if (no_a == 1)
+ rate = vco / (nd * no_b * no_d * 2);
+ else
+ rate = vco / (nd * no_a * no_d * 2);
+
+ }
+
+ inno->pixclock = rate;
+
+ debug("%s, pixclock=%llu\n", __func__, rate);
+
+ return rate;
+}
+
+static int
+inno_hdmi_phy_rk3228_pre_pll_update(struct inno_hdmi_phy *inno,
+ const struct pre_pll_config *cfg)
+{
+ u32 val;
+
+ debug("%s\n", __func__);
+
+ /* Power down PRE-PLL */
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN,
+ RK3228_PRE_PLL_POWER_DOWN);
+
+ inno_update_bits(inno, 0xe2, RK3228_PRE_PLL_FB_DIV_8_MASK |
+ RK3228_PCLK_VCO_DIV_5_MASK |
+ RK3228_PRE_PLL_PRE_DIV_MASK,
+ RK3228_PRE_PLL_FB_DIV_8(cfg->fbdiv) |
+ RK3228_PCLK_VCO_DIV_5(cfg->vco_div_5_en) |
+ RK3228_PRE_PLL_PRE_DIV(cfg->prediv));
+ inno_write(inno, 0xe3, RK3228_PRE_PLL_FB_DIV_7_0(cfg->fbdiv));
+ inno_update_bits(inno, 0xe4, RK3228_PRE_PLL_PCLK_DIV_B_MASK |
+ RK3228_PRE_PLL_PCLK_DIV_A_MASK,
+ RK3228_PRE_PLL_PCLK_DIV_B(cfg->pclk_div_b) |
+ RK3228_PRE_PLL_PCLK_DIV_A(cfg->pclk_div_a));
+ inno_update_bits(inno, 0xe5, RK3228_PRE_PLL_PCLK_DIV_C_MASK |
+ RK3228_PRE_PLL_PCLK_DIV_D_MASK,
+ RK3228_PRE_PLL_PCLK_DIV_C(cfg->pclk_div_c) |
+ RK3228_PRE_PLL_PCLK_DIV_D(cfg->pclk_div_d));
+ inno_update_bits(inno, 0xe6, RK3228_PRE_PLL_TMDSCLK_DIV_C_MASK |
+ RK3228_PRE_PLL_TMDSCLK_DIV_A_MASK |
+ RK3228_PRE_PLL_TMDSCLK_DIV_B_MASK,
+ RK3228_PRE_PLL_TMDSCLK_DIV_C(cfg->tmds_div_c) |
+ RK3228_PRE_PLL_TMDSCLK_DIV_A(cfg->tmds_div_a) |
+ RK3228_PRE_PLL_TMDSCLK_DIV_B(cfg->tmds_div_b));
+
+ /* Power up PRE-PLL */
+ inno_update_bits(inno, 0xe0, RK3228_PRE_PLL_POWER_DOWN, 0);
+
+ /* Wait for PLL lock, up to 100ms*/
+ for (val = 0; val < 100; val++) {
+ if (inno_read(inno, 0xe8) & RK3228_PRE_PLL_LOCK_STATUS)
+ break;
+ udelay(1000);
+ }
+ if (!(inno_read(inno, 0xe8) & RK3228_PRE_PLL_LOCK_STATUS)) {
+ printf("%s, failed to lock Pre-PLL, left unlocked\n", __func__);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static const struct inno_hdmi_phy_plat_ops rk3228_hdmi_phy_plat_ops = {
+ .init = inno_hdmi_phy_rk3228_init,
+ .power_on = inno_hdmi_phy_rk3228_power_on,
+ .power_off = inno_hdmi_phy_rk3228_power_off,
+ .pre_pll_update = inno_hdmi_phy_rk3228_pre_pll_update,
+ .recalc_rate = inno_hdmi_phy_rk3228_pll_recalc_rate,
+ .clk_is_prepared = inno_hdmi_phy_rk3228_clk_is_prepared,
+ .clk_prepare = inno_hdmi_phy_rk3228_clk_prepare
+};
+
+static unsigned long inno_hdmi_phy_set_pll(struct phy *phy,
+ unsigned long rate)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+
+ debug("[PHY] %s: In?\n", __func__);
+
+ if (inno->data->plat_ops->clk_prepare)
+ inno->data->plat_ops->clk_prepare(inno);
+
+ if (inno->data->plat_ops->clk_is_prepared)
+ inno->data->plat_ops->clk_is_prepared(inno);
+
+ inno_hdmi_phy_clk_set_rate(inno, rate);
+
+ debug("[PHY] %s: done!\n", __func__);
+
+ return 0;
+}
+
+static int
+inno_hdmi_phy_set_bus_width(struct phy *phy, u32 bus_width)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+
+ debug("[PHY] %s: In?\n", __func__);
+ inno->bus_width = bus_width;
+ debug("[PHY] %s: done!\n", __func__);
+
+ return 0;
+}
+
+static long
+inno_hdmi_phy_clk_round_rate(struct phy *phy, unsigned long rate)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(phy->dev);
+ const struct pre_pll_config *cfg = pre_pll_cfg_table;
+ u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate);
+ int i;
+
+ debug("[PHY] %s: [rate %ld] In?\n", __func__, rate);
+ for (; cfg->pixclock != ~0UL; cfg++)
+ if (cfg->pixclock == rate)
+ break;
+
+ /*
+ * If there is no dts phy cfg table, use default phy cfg table.
+ * The tmds clock maximum is 594MHz. So there is no need to check
+ * whether tmds clock is out of range.
+ */
+ if (!inno->phy_cfg)
+ return cfg->pixclock;
+
+ /* Check if tmds clock is out of dts phy config's range. */
+ for (i = 0; inno->phy_cfg[i].tmdsclock != ~0UL; i++) {
+ if (inno->phy_cfg[i].tmdsclock >= tmdsclock)
+ break;
+ }
+
+ if (inno->phy_cfg[i].tmdsclock == ~0UL)
+ return -EINVAL;
+
+ debug("[PHY] %s: [pixclock %ld] done!\n", __func__, cfg->pixclock);
+ return cfg->pixclock;
+}
+
+static int
+inno_hdmi_phy_configure(struct phy *phy, void *params)
+{
+ struct phy_configure_opts_inno_hdmi *config = params;
+ int ret;
+ unsigned long rate;
+
+ rate = inno_hdmi_phy_clk_round_rate(phy, config->pixel_clock);
+ if (rate < 0) {
+ printf("failed phy round rate (pixel_clok=%d, rate=%ld)\n",
+ config->pixel_clock, rate);
+ return ret;
+ }
+
+ inno_hdmi_phy_set_bus_width(phy, config->bus_width);
+
+ ret = inno_hdmi_phy_set_pll(phy, rate);
+ if (ret) {
+ printf("failed set phy pll (ret=%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+
+}
+
+static struct phy_ops inno_hdmi_phy_ops = {
+ .power_on = inno_hdmi_phy_power_on,
+ .power_off = inno_hdmi_phy_power_off,
+ .configure = inno_hdmi_phy_configure,
+};
+
+static int inno_hdmi_phy_probe(struct udevice *dev)
+{
+ struct inno_hdmi_phy *inno = dev_get_priv(dev);
+
+ debug("[PHY] %s: In?\n", __func__);
+ inno->regs = dev_read_addr_ptr(dev);
+ if (!inno->regs)
+ return -ENOMEM;
+
+ inno->data = (const struct inno_hdmi_phy_data *)dev_get_driver_data(dev);
+ if (!inno->data)
+ return -EINVAL;
+
+ if (inno->data->plat_ops->init)
+ inno->data->plat_ops->init(inno);
+
+ debug("[PHY] %s: done!\n", __func__);
+ return 0;
+}
+
+static const struct inno_hdmi_phy_data rk3328_inno_hdmi_phy_drv_data = {
+ .phy_type = INNO_HDMI_PHY_RK3328,
+ .plat_ops = &rk3328_hdmi_phy_plat_ops,
+ .phy_cfg_table = rk3328_phy_cfg,
+};
+
+static const struct inno_hdmi_phy_data rk3228_inno_hdmi_phy_drv_data = {
+ .phy_type = INNO_HDMI_PHY_RK3228,
+ .plat_ops = &rk3228_hdmi_phy_plat_ops,
+ .phy_cfg_table = rk3228_phy_cfg,
+};
+
+static const struct udevice_id inno_hdmi_phy_ids[] = {
+ {
+ .compatible = "rockchip,rk3228-hdmi-phy",
+ .data = (ulong)&rk3228_inno_hdmi_phy_drv_data,
+ },
+ {
+ .compatible = "rockchip,rk3328-hdmi-phy",
+ .data = (ulong)&rk3328_inno_hdmi_phy_drv_data,
+ },
+ { /* sentile */ }
+};
+
+U_BOOT_DRIVER(inno_hdmi_phy) = {
+ .name = "inno_hdmi_phy",
+ .id = UCLASS_PHY,
+ .of_match = inno_hdmi_phy_ids,
+ .ops = &inno_hdmi_phy_ops,
+ .probe = inno_hdmi_phy_probe,
+ .priv_auto = sizeof(struct inno_hdmi_phy),
+};
diff --git a/include/inno/phy-inno-hdmi.h b/include/inno/phy-inno-hdmi.h
new file mode 100644
index 0000000000..a73712c921
--- /dev/null
+++ b/include/inno/phy-inno-hdmi.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __PHY_INNO_HDMI_H_
+#define __PHY_INNO_HDMI_H_
+
+/**
+ * struct phy_configure_opts_inno_hdmi - INNO HDMI configuration set
+ *
+ * This structure is used to represent the configuration state of a
+ * INNO HDMI phy.
+ */
+struct phy_configure_opts_inno_hdmi {
+ /**
+ * @pixel_clock:
+ *
+ * pixel clock rate in Hertz
+ *
+ */
+ unsigned int pixel_clock;
+
+ /**
+ * @bus_width:
+ *
+ * bus width to assign
+ */
+ unsigned int bus_width;
+
+};
+
+#endif /* __PHY_INNO_HDMI_H_ */