From ee930fffee469d076998274a2ca55e13dc1efb67 Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Fri, 10 May 2024 08:50:54 +0000 Subject: [PATCH] enable tun/tap/iptables --- u-boot/drivers/video/drm/rockchip-inno-hdmi-phy.c | 426 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 421 insertions(+), 5 deletions(-) diff --git a/u-boot/drivers/video/drm/rockchip-inno-hdmi-phy.c b/u-boot/drivers/video/drm/rockchip-inno-hdmi-phy.c index 71c710f..1f60e1f 100644 --- a/u-boot/drivers/video/drm/rockchip-inno-hdmi-phy.c +++ b/u-boot/drivers/video/drm/rockchip-inno-hdmi-phy.c @@ -3,6 +3,7 @@ * (C) Copyright 2008-2016 Fuzhou Rockchip Electronics Co., Ltd */ +#include <clk-uclass.h> #include <config.h> #include <common.h> #include <errno.h> @@ -11,7 +12,9 @@ #include <fdtdec.h> #include <fdt_support.h> #include <asm/unaligned.h> +#include <asm/arch/clock.h> #include <dm/device.h> +#include <dm/lists.h> #include <dm/read.h> #include <asm/io.h> #include <linux/list.h> @@ -20,6 +23,7 @@ #include "rockchip_display.h" #include "rockchip_crtc.h" +#include "rockchip_connector.h" #include "rockchip_phy.h" #define INNO_HDMI_PHY_TIMEOUT_LOOP_COUNT 1000 @@ -145,7 +149,8 @@ enum inno_hdmi_phy_type { INNO_HDMI_PHY_RK3228, - INNO_HDMI_PHY_RK3328 + INNO_HDMI_PHY_RK3328, + INNO_HDMI_PHY_RK3528 }; struct inno_hdmi_phy_drv_data; @@ -214,6 +219,14 @@ const void *data; }; +struct clk_inno_hdmi { + struct udevice *dev; + ulong rate; +}; + +/* global variables are used to pass reource from phy drivers to clk driver */ +static struct inno_hdmi_phy *g_inno; + 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}, @@ -249,9 +262,13 @@ {33750000, 1, 10, 2, 4}, {74250000, 1, 40, 8, 1}, {74250000, 18, 80, 8, 2}, + {74250000, 1, 20, 4, 8}, {148500000, 2, 40, 4, 3}, + {148500000, 1, 10, 2, 8}, {297000000, 4, 40, 2, 3}, + {297000000, 2, 20, 2, 8}, {594000000, 8, 40, 1, 3}, + {594000000, 4, 20, 1, 8}, { ~0UL, 0, 0, 0, 0} }; @@ -292,6 +309,30 @@ 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 const struct phy_config rk3528_phy_cfg[] = { + /* tmdsclk bias-clk bias-data voltage-clk voltage-data pre-emphasis-data */ + { 165000000, { + 0x03, 0x04, 0x0c, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, { + 340000000, { + 0x03, 0x04, 0x0c, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, + }, + }, { + 594000000, { + 0x02, 0x08, 0x0d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, }, }, { ~0UL, { @@ -398,7 +439,11 @@ static int inno_hdmi_phy_power_on(struct rockchip_phy *phy) { +#ifdef CONFIG_SPL_BUILD + struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data; +#else struct inno_hdmi_phy *inno = dev_get_priv(phy->dev); +#endif const struct post_pll_config *cfg = post_pll_cfg_table; const struct phy_config *phy_cfg = inno->plat_data->phy_cfg_table; u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, inno->pixclock); @@ -420,6 +465,8 @@ else if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3228 && tmdsclock <= 33750000) chipversion = 4; + else if (inno->plat_data->dev_type == INNO_HDMI_PHY_RK3528) + chipversion = 8; printf("tmdsclock = %d; chipversion = %d\n", tmdsclock, chipversion); @@ -444,7 +491,11 @@ static int inno_hdmi_phy_power_off(struct rockchip_phy *phy) { +#ifdef CONFIG_SPL_BUILD + struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data; +#else struct inno_hdmi_phy *inno = dev_get_priv(phy->dev); +#endif if (inno->plat_data->ops->power_off) inno->plat_data->ops->power_off(inno); @@ -858,6 +909,229 @@ return rate; } +static int +inno_hdmi_phy_rk3528_power_on(struct inno_hdmi_phy *inno, + const struct post_pll_config *cfg, + const struct phy_config *phy_cfg) +{ + u32 val; + u64 temp; + u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, inno->pixclock); + + /* Power off post PLL */ + inno_update_bits(inno, 0xaa, 1, 0); + + val = cfg->prediv; + inno_write(inno, 0xab, val); + + if (cfg->postdiv == 1) { + inno_write(inno, 0xad, 0x8); + inno_write(inno, 0xaa, 2); + } else { + val = (cfg->postdiv / 2) - 1; + inno_write(inno, 0xad, val); + inno_write(inno, 0xaa, 0x0e); + } + + val = cfg->fbdiv & 0xff; + inno_write(inno, 0xac, val); + val = (cfg->fbdiv >> 8) & BIT(0); + inno_update_bits(inno, 0xad, BIT(4), val); + + /* current bias clk/data 2 */ + val = phy_cfg->regs[0] << 4 | phy_cfg->regs[1]; + inno_write(inno, 0xbf, val); + + /* current bias data 1/0 */ + val = phy_cfg->regs[1] << 4 | phy_cfg->regs[1]; + inno_write(inno, 0xc0, val); + + /* output voltage */ + inno_write(inno, 0xb5, phy_cfg->regs[2]); + inno_write(inno, 0xb6, phy_cfg->regs[3]); + inno_write(inno, 0xb7, phy_cfg->regs[3]); + inno_write(inno, 0xb8, phy_cfg->regs[3]); + + /* pre-emphasis */ + inno_write(inno, 0xbb, phy_cfg->regs[4]); + inno_write(inno, 0xbc, phy_cfg->regs[4]); + inno_write(inno, 0xbd, phy_cfg->regs[4]); + + /* enable LDO */ + inno_write(inno, 0xb4, 0x7); + + /* enable serializer */ + inno_write(inno, 0xbe, 0x70); + + inno_write(inno, 0xb2, 0x0f); + + for (val = 0; val < 5; val++) { + if (inno_read(inno, 0xaf) & 1) + break; + udelay(1000); + } + if (!(inno_read(inno, 0xaf) & 1)) { + dev_err(inno->dev, "HDMI PHY Post PLL unlock\n"); + return -ETIMEDOUT; + } + + /* set termination resistance */ + if (phy_cfg->tmdsclock > 340000000) { + inno_write(inno, 0xc7, 0x76); + inno_write(inno, 0xc5, 0x83); + inno_write(inno, 0xc8, 0x00); + inno_write(inno, 0xc9, 0x2f); + inno_write(inno, 0xca, 0x2f); + inno_write(inno, 0xcb, 0x2f); + } else { + inno_write(inno, 0xc7, 0x76); + inno_write(inno, 0xc5, 0x83); + inno_write(inno, 0xc8, 0x00); + inno_write(inno, 0xc9, 0x0f); + inno_write(inno, 0xca, 0x0f); + inno_write(inno, 0xcb, 0x0f); + } + + + /* set TMDS sync detection counter length */ + temp = 47520000000UL / tmdsclock; + inno_write(inno, 0xd8, (temp >> 8) & 0xff); + inno_write(inno, 0xd9, temp & 0xff); + + if (phy_cfg->tmdsclock > 340000000) + mdelay(100); + /* set pdata_en to 0/1 */ + inno_update_bits(inno, 0x02, 1, 0); + inno_update_bits(inno, 0x02, 1, 1); + + /* Enable PHY IRQ */ + inno_write(inno, 0x05, 0x22); + inno_write(inno, 0x07, 0x22); + inno_write(inno, 0xcc, 0x0f); + + return 0; +} + +static void inno_hdmi_phy_rk3528_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); + + /* Disable PHY IRQ */ + inno_write(inno, 0x05, 0); + inno_write(inno, 0x07, 0); +} + +static void inno_hdmi_phy_rk3528_init(struct inno_hdmi_phy *inno) +{ + /* + * Use phy internal register control + * rxsense/poweron/pllpd/pdataen signal. + */ + inno_write(inno, 0x02, 0x81); +} + +static int +inno_hdmi_phy_rk3528_pre_pll_update(struct inno_hdmi_phy *inno, + const struct pre_pll_config *cfg) +{ + u32 val; + + inno_update_bits(inno, 0xb0, 4, 4); + inno_write(inno, 0xcc, 0x0f); + + /* Power on PLL */ + inno_update_bits(inno, 0xa0, 1, 0); + /* 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); + } + + /* Wait for PLL lock */ + for (val = 0; val < 5; val++) { + if (inno_read(inno, 0xa9) & 1) + break; + udelay(1000); + } + if (val == 5) { + dev_err(inno->dev, "Pre-PLL unlock\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static unsigned long +inno_hdmi_rk3528_phy_pll_recalc_rate(struct inno_hdmi_phy *inno, + unsigned long parent_rate) +{ + unsigned long frac; + u8 nd, no_a, no_b, no_d; + u16 nf; + u64 vco = parent_rate; + + nd = inno_read(inno, 0xa1) & 0x3f; + nf = ((inno_read(inno, 0xa2) & 0x0f) << 8) | inno_read(inno, 0xa3); + vco *= 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) { + do_div(vco, nd * 5); + } else { + no_a = inno_read(inno, 0xa5) & 0x1f; + no_b = ((inno_read(inno, 0xa5) >> 5) & 7) + 2; + no_d = inno_read(inno, 0xa6) & 0x1f; + if (no_a == 1) + do_div(vco, nd * no_b * no_d * 2); + else + do_div(vco, nd * no_a * no_d * 2); + } + + frac = vco; + inno->pixclock = DIV_ROUND_CLOSEST(frac, 1000) * 1000; + + dev_dbg(inno->dev, "%s rate %lu\n", __func__, inno->pixclock); + + return frac; +} + +#ifndef CONFIG_SPL_BUILD #define PHY_TAB_LEN 60 static @@ -889,6 +1163,7 @@ return 0; } +#endif static const struct inno_hdmi_phy_ops rk3228_hdmi_phy_ops = { .init = inno_hdmi_phy_rk3228_init, @@ -905,6 +1180,14 @@ .recalc_rate = inno_hdmi_3328_phy_pll_recalc_rate, }; +static const struct inno_hdmi_phy_ops rk3528_hdmi_phy_ops = { + .init = inno_hdmi_phy_rk3528_init, + .power_on = inno_hdmi_phy_rk3528_power_on, + .power_off = inno_hdmi_phy_rk3528_power_off, + .pre_pll_update = inno_hdmi_phy_rk3528_pre_pll_update, + .recalc_rate = inno_hdmi_rk3528_phy_pll_recalc_rate, +}; + static const struct inno_hdmi_phy_drv_data rk3228_hdmi_phy_drv_data = { .dev_type = INNO_HDMI_PHY_RK3228, .ops = &rk3228_hdmi_phy_ops, @@ -917,6 +1200,12 @@ .phy_cfg_table = rk3328_phy_cfg, }; +static const struct inno_hdmi_phy_drv_data rk3528_hdmi_phy_drv_data = { + .dev_type = INNO_HDMI_PHY_RK3528, + .ops = &rk3528_hdmi_phy_ops, + .phy_cfg_table = rk3528_phy_cfg, +}; + static const struct rockchip_inno_data inno_hdmi_phy_of_match[] = { { .compatible = "rockchip,rk3228-hdmi-phy", .data = &rk3228_hdmi_phy_drv_data @@ -924,26 +1213,41 @@ { .compatible = "rockchip,rk3328-hdmi-phy", .data = &rk3328_hdmi_phy_drv_data }, + { .compatible = "rockchip,rk3528-hdmi-phy", + .data = &rk3528_hdmi_phy_drv_data + }, {} }; static int inno_hdmi_phy_init(struct rockchip_phy *phy) { +#ifdef CONFIG_SPL_BUILD + struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data; +#else struct udevice *dev = phy->dev; struct inno_hdmi_phy *inno = dev_get_priv(phy->dev); - int i, val, phy_table_size, ret; - const char *name; + int val, phy_table_size, ret; u32 *phy_config; +#endif + int i; + const char *name; - inno->node = dev->node; - +#ifdef CONFIG_SPL_BUILD + inno->regs = (void *)RK3528_HDMIPHY_BASE; +#else inno->regs = dev_read_addr_ptr(dev); + inno->node = dev->node; +#endif if (!inno->regs) { printf("%s: failed to get phy address\n", __func__); return -ENOMEM; } +#ifdef CONFIG_SPL_BUILD + name = "rockchip,rk3528-hdmi-phy"; +#else name = dev_read_string(dev, "compatible"); +#endif for (i = 0; i < ARRAY_SIZE(inno_hdmi_phy_of_match); i++) { if (!strcmp(name, inno_hdmi_phy_of_match[i].compatible)) { inno->plat_data = inno_hdmi_phy_of_match[i].data; @@ -951,6 +1255,7 @@ } } +#ifndef CONFIG_SPL_BUILD dev_read_prop(dev, "rockchip,phy-table", &val); if (val >= 0) { @@ -986,6 +1291,7 @@ } else { printf("use default hdmi phy table\n"); } +#endif if (i >= ARRAY_SIZE(inno_hdmi_phy_of_match)) return 0; @@ -1002,8 +1308,16 @@ static unsigned long inno_hdmi_phy_set_pll(struct rockchip_phy *phy, unsigned long rate) { +#ifdef CONFIG_SPL_BUILD + struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data; +#else struct inno_hdmi_phy *inno = dev_get_priv(phy->dev); +#endif +#ifdef CONFIG_SPL_BUILD + if (!inno) + inno = g_inno; +#endif inno_hdmi_phy_clk_prepare(inno); inno_hdmi_phy_clk_is_prepared(inno); inno_hdmi_phy_clk_set_rate(inno, rate); @@ -1013,7 +1327,11 @@ static int inno_hdmi_phy_set_bus_width(struct rockchip_phy *phy, u32 bus_width) { +#ifdef CONFIG_SPL_BUILD + struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data; +#else struct inno_hdmi_phy *inno = dev_get_priv(phy->dev); +#endif inno->bus_width = bus_width; @@ -1023,7 +1341,11 @@ static long inno_hdmi_phy_clk_round_rate(struct rockchip_phy *phy, unsigned long rate) { +#ifdef CONFIG_SPL_BUILD + struct inno_hdmi_phy *inno = (struct inno_hdmi_phy *)phy->data; +#else struct inno_hdmi_phy *inno = dev_get_priv(phy->dev); +#endif int i; const struct pre_pll_config *cfg = pre_pll_cfg_table; u32 tmdsclock = inno_hdmi_phy_get_tmdsclk(inno, rate); @@ -1082,9 +1404,26 @@ .compatible = "rockchip,rk3228-hdmi-phy", .data = (ulong)&inno_hdmi_phy_driver_data, }, + { + .compatible = "rockchip,rk3528-hdmi-phy", + .data = (ulong)&inno_hdmi_phy_driver_data, + }, {} }; +#ifdef CONFIG_SPL_BUILD +int inno_spl_hdmi_phy_probe(struct display_state *state) +{ + struct inno_hdmi_phy *inno = malloc(sizeof(struct inno_hdmi_phy)); + + memset(inno, 0, sizeof(*inno)); + g_inno = inno; + + state->conn_state.connector->phy = &inno_hdmi_phy_driver_data; + state->conn_state.connector->phy->data = (void *)inno; + return 0; +} +#else static int inno_hdmi_phy_probe(struct udevice *dev) { struct inno_hdmi_phy *inno = dev_get_priv(dev); @@ -1094,6 +1433,32 @@ inno->dev = dev; phy->dev = dev; + g_inno = inno; + dev->driver_data = (ulong)&inno_hdmi_phy_driver_data; + phy = &inno_hdmi_phy_driver_data; + + return 0; +} +#endif + +static int rockchip_inno_phy_hdmi_bind(struct udevice *parent) +{ + struct udevice *child; + ofnode subnode; + int ret; + + subnode = ofnode_find_subnode(parent->node, "clk-port"); + if (!ofnode_valid(subnode)) { + printf("%s: no subnode for %s\n", __func__, parent->name); + return -ENXIO; + } + + ret = device_bind_driver_to_node(parent, "clk_inno_hdmi", "inno_hdmi_pll_clk", subnode, &child); + if (ret) { + printf("%s: clk-port cannot bind its driver\n", __func__); + return ret; + } + return 0; } @@ -1101,6 +1466,57 @@ .name = "inno_hdmi_phy", .id = UCLASS_PHY, .of_match = inno_hdmi_phy_ids, +#ifndef CONFIG_SPL_BUILD .probe = inno_hdmi_phy_probe, +#endif + .bind = rockchip_inno_phy_hdmi_bind, .priv_auto_alloc_size = sizeof(struct inno_hdmi_phy), }; + + +static ulong inno_hdmi_clk_get_rate(struct clk *clk) +{ + struct clk_inno_hdmi *priv = dev_get_priv(clk->dev); + + return priv->rate; +} + +static ulong inno_hdmi_clk_set_rate(struct clk *clk, ulong rate) +{ + struct clk_inno_hdmi *priv = dev_get_priv(clk->dev); + int ret; + + inno_hdmi_phy_clk_prepare(g_inno); + inno_hdmi_phy_clk_is_prepared(g_inno); + ret = inno_hdmi_phy_clk_set_rate(g_inno, rate); + if (ret < 0) { + printf("inno hdmi set rate failed ret:%d\n", ret); + return ret; + } + + priv->rate = g_inno->pixclock; + + return priv->rate; +} + +static const struct clk_ops inno_hdmi_clk_ops = { + .get_rate = inno_hdmi_clk_get_rate, + .set_rate = inno_hdmi_clk_set_rate, +}; + +static int inno_hdmi_clk_probe(struct udevice *dev) +{ + return 0; +} + +/* + * In order for other display interfaces to use hdmiphy as source + * for dclk, hdmiphy must register a virtual clock driver + */ +U_BOOT_DRIVER(clk_inno_hdmi) = { + .name = "clk_inno_hdmi", + .id = UCLASS_CLK, + .priv_auto_alloc_size = sizeof(struct clk_inno_hdmi), + .ops = &inno_hdmi_clk_ops, + .probe = inno_hdmi_clk_probe, +}; -- Gitblit v1.6.2