1c2539483SSimon Glass /*
2c2539483SSimon Glass  * Copyright (c) 2015 Google, Inc
3c2539483SSimon Glass  * Copyright 2014 Rockchip Inc.
4c2539483SSimon Glass  *
5c2539483SSimon Glass  * SPDX-License-Identifier:	GPL-2.0+
6c2539483SSimon Glass  */
7c2539483SSimon Glass 
8c2539483SSimon Glass #include <common.h>
9c2539483SSimon Glass #include <clk.h>
10c2539483SSimon Glass #include <display.h>
11c2539483SSimon Glass #include <dm.h>
12cc232a9dSJernej Skrabec #include <dw_hdmi.h>
13c2539483SSimon Glass #include <edid.h>
14c2539483SSimon Glass #include <regmap.h>
15c2539483SSimon Glass #include <syscon.h>
16c2539483SSimon Glass #include <asm/gpio.h>
17c2539483SSimon Glass #include <asm/io.h>
18c2539483SSimon Glass #include <asm/arch/clock.h>
19c2539483SSimon Glass #include <asm/arch/grf_rk3288.h>
20c2539483SSimon Glass #include <power/regulator.h>
21c2539483SSimon Glass 
22c2539483SSimon Glass struct rk_hdmi_priv {
23cc232a9dSJernej Skrabec 	struct dw_hdmi hdmi;
24c2539483SSimon Glass 	struct rk3288_grf *grf;
25c2539483SSimon Glass };
26c2539483SSimon Glass 
27c2539483SSimon Glass static const struct hdmi_phy_config rockchip_phy_config[] = {
28c2539483SSimon Glass 	{
290fc41e55SNickey Yang Nickey Yang 		.mpixelclock = 74250000,
30c2539483SSimon Glass 		.sym_ctr = 0x8009, .term = 0x0004, .vlev_ctr = 0x0272,
31c2539483SSimon Glass 	}, {
320fc41e55SNickey Yang Nickey Yang 		.mpixelclock = 148500000,
33c2539483SSimon Glass 		.sym_ctr = 0x802b, .term = 0x0004, .vlev_ctr = 0x028d,
34c2539483SSimon Glass 	}, {
350fc41e55SNickey Yang Nickey Yang 		.mpixelclock = 297000000,
36c2539483SSimon Glass 		.sym_ctr = 0x8039, .term = 0x0005, .vlev_ctr = 0x028d,
37c2539483SSimon Glass 	}, {
38c2539483SSimon Glass 		.mpixelclock = ~0ul,
39c2539483SSimon Glass 		.sym_ctr = 0x0000, .term = 0x0000, .vlev_ctr = 0x0000,
40c2539483SSimon Glass 	}
41c2539483SSimon Glass };
42c2539483SSimon Glass 
43c2539483SSimon Glass static const struct hdmi_mpll_config rockchip_mpll_cfg[] = {
44c2539483SSimon Glass 	{
450fc41e55SNickey Yang Nickey Yang 		.mpixelclock = 40000000,
46c2539483SSimon Glass 		.cpce = 0x00b3, .gmp = 0x0000, .curr = 0x0018,
47c2539483SSimon Glass 	}, {
480fc41e55SNickey Yang Nickey Yang 		.mpixelclock = 65000000,
49c2539483SSimon Glass 		.cpce = 0x0072, .gmp = 0x0001, .curr = 0x0028,
50c2539483SSimon Glass 	}, {
510fc41e55SNickey Yang Nickey Yang 		.mpixelclock = 66000000,
52c2539483SSimon Glass 		.cpce = 0x013e, .gmp = 0x0003, .curr = 0x0038,
53c2539483SSimon Glass 	}, {
5494412745SNickey Yang Nickey Yang 		.mpixelclock = 83500000,
55c2539483SSimon Glass 		.cpce = 0x0072, .gmp = 0x0001, .curr = 0x0028,
56c2539483SSimon Glass 	}, {
570fc41e55SNickey Yang Nickey Yang 		.mpixelclock = 146250000,
58c2539483SSimon Glass 		.cpce = 0x0051, .gmp = 0x0002, .curr = 0x0038,
59c2539483SSimon Glass 	}, {
600fc41e55SNickey Yang Nickey Yang 		.mpixelclock = 148500000,
61c2539483SSimon Glass 		.cpce = 0x0051, .gmp = 0x0003, .curr = 0x0000,
62c2539483SSimon Glass 	}, {
63c2539483SSimon Glass 		.mpixelclock = ~0ul,
64c2539483SSimon Glass 		.cpce = 0x0051, .gmp = 0x0003, .curr = 0x0000,
65c2539483SSimon Glass 	}
66c2539483SSimon Glass };
67c2539483SSimon Glass 
68c2539483SSimon Glass static int rk_hdmi_read_edid(struct udevice *dev, u8 *buf, int buf_size)
69c2539483SSimon Glass {
70c2539483SSimon Glass 	struct rk_hdmi_priv *priv = dev_get_priv(dev);
71c2539483SSimon Glass 
72cc232a9dSJernej Skrabec 	return dw_hdmi_read_edid(&priv->hdmi, buf, buf_size);
73c2539483SSimon Glass }
74c2539483SSimon Glass 
75c2539483SSimon Glass static int rk_hdmi_enable(struct udevice *dev, int panel_bpp,
76c2539483SSimon Glass 			  const struct display_timing *edid)
77c2539483SSimon Glass {
78c2539483SSimon Glass 	struct rk_hdmi_priv *priv = dev_get_priv(dev);
79c2539483SSimon Glass 
80cc232a9dSJernej Skrabec 	return dw_hdmi_enable(&priv->hdmi, edid);
81c2539483SSimon Glass }
82c2539483SSimon Glass 
83c2539483SSimon Glass static int rk_hdmi_ofdata_to_platdata(struct udevice *dev)
84c2539483SSimon Glass {
85c2539483SSimon Glass 	struct rk_hdmi_priv *priv = dev_get_priv(dev);
86cc232a9dSJernej Skrabec 	struct dw_hdmi *hdmi = &priv->hdmi;
87c2539483SSimon Glass 
88*a821c4afSSimon Glass 	hdmi->ioaddr = (ulong)devfdt_get_addr(dev);
89cc232a9dSJernej Skrabec 	hdmi->mpll_cfg = rockchip_mpll_cfg;
90cc232a9dSJernej Skrabec 	hdmi->phy_cfg = rockchip_phy_config;
91cc232a9dSJernej Skrabec 	hdmi->i2c_clk_high = 0x7a;
92cc232a9dSJernej Skrabec 	hdmi->i2c_clk_low = 0x8d;
93cc232a9dSJernej Skrabec 
94cc232a9dSJernej Skrabec 	/*
95cc232a9dSJernej Skrabec 	 * TODO(sjg@chromium.org): The above values don't work - these ones
96cc232a9dSJernej Skrabec 	 * work better, but generate lots of errors in the data.
97cc232a9dSJernej Skrabec 	 */
98cc232a9dSJernej Skrabec 	hdmi->i2c_clk_high = 0x0d;
99cc232a9dSJernej Skrabec 	hdmi->i2c_clk_low = 0x0d;
100cc232a9dSJernej Skrabec 	hdmi->reg_io_width = 4;
101cc232a9dSJernej Skrabec 	hdmi->phy_set = dw_hdmi_phy_cfg;
102cc232a9dSJernej Skrabec 
103c2539483SSimon Glass 	priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
104c2539483SSimon Glass 
105c2539483SSimon Glass 	return 0;
106c2539483SSimon Glass }
107c2539483SSimon Glass 
108c2539483SSimon Glass static int rk_hdmi_probe(struct udevice *dev)
109c2539483SSimon Glass {
110c2539483SSimon Glass 	struct display_plat *uc_plat = dev_get_uclass_platdata(dev);
111c2539483SSimon Glass 	struct rk_hdmi_priv *priv = dev_get_priv(dev);
112cc232a9dSJernej Skrabec 	struct dw_hdmi *hdmi = &priv->hdmi;
113135aa950SStephen Warren 	struct udevice *reg;
114135aa950SStephen Warren 	struct clk clk;
115c2539483SSimon Glass 	int ret;
116c2539483SSimon Glass 	int vop_id = uc_plat->source_id;
117c2539483SSimon Glass 
118c2539483SSimon Glass 	ret = clk_get_by_index(dev, 0, &clk);
119c2539483SSimon Glass 	if (ret >= 0) {
120135aa950SStephen Warren 		ret = clk_set_rate(&clk, 0);
121135aa950SStephen Warren 		clk_free(&clk);
122c2539483SSimon Glass 	}
123c2539483SSimon Glass 	if (ret) {
1240fc41e55SNickey Yang Nickey Yang 		debug("%s: Failed to set hdmi clock: ret=%d\n", __func__, ret);
125c2539483SSimon Glass 		return ret;
126c2539483SSimon Glass 	}
127c2539483SSimon Glass 
128c2539483SSimon Glass 	/*
129c2539483SSimon Glass 	 * Configure the maximum clock to permit whatever resolution the
130c2539483SSimon Glass 	 * monitor wants
131c2539483SSimon Glass 	 */
132c2539483SSimon Glass 	ret = clk_get_by_index(uc_plat->src_dev, 0, &clk);
133c2539483SSimon Glass 	if (ret >= 0) {
134135aa950SStephen Warren 		ret = clk_set_rate(&clk, 384000000);
135135aa950SStephen Warren 		clk_free(&clk);
136c2539483SSimon Glass 	}
137c2539483SSimon Glass 	if (ret < 0) {
138c2539483SSimon Glass 		debug("%s: Failed to set clock in source device '%s': ret=%d\n",
139c2539483SSimon Glass 		      __func__, uc_plat->src_dev->name, ret);
140c2539483SSimon Glass 		return ret;
141c2539483SSimon Glass 	}
142c2539483SSimon Glass 
143c2539483SSimon Glass 	ret = regulator_get_by_platname("vcc50_hdmi", &reg);
144c2539483SSimon Glass 	if (!ret)
145c2539483SSimon Glass 		ret = regulator_set_enable(reg, true);
146c2539483SSimon Glass 	if (ret)
147c2539483SSimon Glass 		debug("%s: Cannot set regulator vcc50_hdmi\n", __func__);
148c2539483SSimon Glass 
149c2539483SSimon Glass 	/* hdmi source select hdmi controller */
150c2539483SSimon Glass 	rk_setreg(&priv->grf->soc_con6, 1 << 15);
151c2539483SSimon Glass 
152c2539483SSimon Glass 	/* hdmi data from vop id */
153e4ab3d71SSimon Glass 	rk_clrsetreg(&priv->grf->soc_con6, 1 << 4,
154e4ab3d71SSimon Glass 		     (vop_id == 1) ? (1 << 4) : 0);
155c2539483SSimon Glass 
156cc232a9dSJernej Skrabec 	ret = dw_hdmi_phy_wait_for_hpd(hdmi);
157c2539483SSimon Glass 	if (ret < 0) {
158c2539483SSimon Glass 		debug("hdmi can not get hpd signal\n");
159c2539483SSimon Glass 		return -1;
160c2539483SSimon Glass 	}
161c2539483SSimon Glass 
162cc232a9dSJernej Skrabec 	dw_hdmi_init(hdmi);
163cc232a9dSJernej Skrabec 	dw_hdmi_phy_init(hdmi);
164c2539483SSimon Glass 
165c2539483SSimon Glass 	return 0;
166c2539483SSimon Glass }
167c2539483SSimon Glass 
168c2539483SSimon Glass static const struct dm_display_ops rk_hdmi_ops = {
169c2539483SSimon Glass 	.read_edid = rk_hdmi_read_edid,
170c2539483SSimon Glass 	.enable = rk_hdmi_enable,
171c2539483SSimon Glass };
172c2539483SSimon Glass 
173c2539483SSimon Glass static const struct udevice_id rk_hdmi_ids[] = {
174c2539483SSimon Glass 	{ .compatible = "rockchip,rk3288-dw-hdmi" },
175c2539483SSimon Glass 	{ }
176c2539483SSimon Glass };
177c2539483SSimon Glass 
178c2539483SSimon Glass U_BOOT_DRIVER(hdmi_rockchip) = {
179c2539483SSimon Glass 	.name	= "hdmi_rockchip",
180c2539483SSimon Glass 	.id	= UCLASS_DISPLAY,
181c2539483SSimon Glass 	.of_match = rk_hdmi_ids,
182c2539483SSimon Glass 	.ops	= &rk_hdmi_ops,
183c2539483SSimon Glass 	.ofdata_to_platdata	= rk_hdmi_ofdata_to_platdata,
184c2539483SSimon Glass 	.probe	= rk_hdmi_probe,
185c2539483SSimon Glass 	.priv_auto_alloc_size	 = sizeof(struct rk_hdmi_priv),
186c2539483SSimon Glass };
187