1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Derived work from:
4  *   Philippe Cornu <philippe.cornu@st.com>
5  *   Yannick Fertre <yannick.fertre@st.com>
6  * Adapted by Miquel Raynal <miquel.raynal@bootlin.com>
7  */
8 
9 #define LOG_CATEGORY UCLASS_VIDEO_BRIDGE
10 
11 #include <clk.h>
12 #include <dm.h>
13 #include <log.h>
14 #include <panel.h>
15 #include <video_bridge.h>
16 #include <asm/io.h>
17 #include <linux/delay.h>
18 
19 #define LDB_CTRL_CH0_ENABLE BIT(0)
20 #define LDB_CTRL_CH1_ENABLE BIT(2)
21 #define LDB_CTRL_CH0_DATA_WIDTH BIT(5)
22 #define LDB_CTRL_CH0_BIT_MAPPING BIT(6)
23 #define LDB_CTRL_CH1_DATA_WIDTH BIT(7)
24 #define LDB_CTRL_CH1_BIT_MAPPING BIT(8)
25 #define LDB_CTRL_DI0_VSYNC_POLARITY BIT(9)
26 #define LDB_CTRL_DI1_VSYNC_POLARITY BIT(10)
27 
28 #define LVDS_CTRL_CH0_EN BIT(0)
29 #define LVDS_CTRL_CH1_EN BIT(1)
30 #define LVDS_CTRL_VBG_EN BIT(2)
31 #define LVDS_CTRL_PRE_EMPH_EN BIT(4)
32 #define LVDS_CTRL_PRE_EMPH_ADJ(n) (((n) & 0x7) << 5)
33 #define LVDS_CTRL_CC_ADJ(n) (((n) & 0x7) << 11)
34 
35 struct imx_ldb_priv {
36 	struct clk ldb_clk;
37 	void __iomem *ldb_ctrl;
38 	void __iomem *lvds_ctrl;
39 	struct udevice *lvds1;
40 	struct udevice *lvds2;
41 };
42 
imx_ldb_set_backlight(struct udevice * dev,int percent)43 static int imx_ldb_set_backlight(struct udevice *dev, int percent)
44 {
45 	struct imx_ldb_priv *priv = dev_get_priv(dev);
46 	int ret;
47 
48 	if (priv->lvds1) {
49 		ret = panel_enable_backlight(priv->lvds1);
50 		if (ret) {
51 			debug("ldb: Cannot enable lvds1 backlight\n");
52 			return ret;
53 		}
54 
55 		ret = panel_set_backlight(priv->lvds1, percent);
56 		if (ret)
57 			return ret;
58 	}
59 
60 	if (priv->lvds2) {
61 		ret = panel_enable_backlight(priv->lvds2);
62 		if (ret) {
63 			debug("ldb: Cannot enable lvds2 backlight\n");
64 			return ret;
65 		}
66 
67 		ret = panel_set_backlight(priv->lvds2, percent);
68 		if (ret)
69 			return ret;
70 	}
71 
72 	return 0;
73 }
74 
imx_ldb_of_to_plat(struct udevice * dev)75 static int imx_ldb_of_to_plat(struct udevice *dev)
76 {
77 	struct imx_ldb_priv *priv = dev_get_priv(dev);
78 	int ret;
79 
80 	uclass_get_device_by_endpoint(UCLASS_PANEL, dev, 1, -1, &priv->lvds1);
81 	uclass_get_device_by_endpoint(UCLASS_PANEL, dev, 2, -1, &priv->lvds2);
82 	if (!priv->lvds1 && !priv->lvds2) {
83 		debug("ldb: No remote panel for '%s' (ret=%d)\n",
84 		      dev_read_name(dev), ret);
85 		return ret;
86 	}
87 
88 	return 0;
89 }
90 
91 /* The block has a mysterious x7 internal divisor (x3.5 in dual configuration) */
92 #define IMX_LDB_INTERNAL_DIVISOR(x) (((x) * 70) / 10)
93 #define IMX_LDB_INTERNAL_DIVISOR_DUAL(x) (((x) * 35) / 10)
94 
imx_ldb_input_rate(struct imx_ldb_priv * priv,struct display_timing * timings)95 static ulong imx_ldb_input_rate(struct imx_ldb_priv *priv,
96 				struct display_timing *timings)
97 {
98 	ulong target_rate = timings->pixelclock.typ;
99 
100 	if (priv->lvds1 && priv->lvds2)
101 		return IMX_LDB_INTERNAL_DIVISOR_DUAL(target_rate);
102 
103 	return IMX_LDB_INTERNAL_DIVISOR(target_rate);
104 }
105 
imx_ldb_attach(struct udevice * dev)106 static int imx_ldb_attach(struct udevice *dev)
107 {
108 	struct imx_ldb_priv *priv = dev_get_priv(dev);
109 	struct display_timing timings;
110 	bool format_jeida = false;
111 	bool format_24bpp = true;
112 	u32 ldb_ctrl = 0, lvds_ctrl;
113 	ulong ldb_rate;
114 	int ret;
115 
116 	/* TODO: update the 24bpp/jeida booleans with proper checks when they
117 	 * will be supported.
118 	 */
119 	if (priv->lvds1) {
120 		ret = panel_get_display_timing(priv->lvds1, &timings);
121 		if (ret) {
122 			ret = ofnode_decode_display_timing(dev_ofnode(priv->lvds1),
123 							   0, &timings);
124 			if (ret) {
125 				printf("Cannot decode lvds1 timings (%d)\n", ret);
126 				return ret;
127 			}
128 		}
129 
130 		ldb_ctrl |= LDB_CTRL_CH0_ENABLE;
131 		if (format_24bpp)
132 			ldb_ctrl |= LDB_CTRL_CH0_DATA_WIDTH;
133 		if (format_jeida)
134 			ldb_ctrl |= LDB_CTRL_CH0_BIT_MAPPING;
135 		if (timings.flags & DISPLAY_FLAGS_VSYNC_HIGH)
136 			ldb_ctrl |= LDB_CTRL_DI0_VSYNC_POLARITY;
137 	}
138 
139 	if (priv->lvds2) {
140 		ret = panel_get_display_timing(priv->lvds2, &timings);
141 		if (ret) {
142 			ret = ofnode_decode_display_timing(dev_ofnode(priv->lvds2),
143 							   0, &timings);
144 			if (ret) {
145 				printf("Cannot decode lvds2 timings (%d)\n", ret);
146 				return ret;
147 			}
148 		}
149 
150 		ldb_ctrl |= LDB_CTRL_CH1_ENABLE;
151 		if (format_24bpp)
152 			ldb_ctrl |= LDB_CTRL_CH1_DATA_WIDTH;
153 		if (format_jeida)
154 			ldb_ctrl |= LDB_CTRL_CH1_BIT_MAPPING;
155 		if (timings.flags & DISPLAY_FLAGS_VSYNC_HIGH)
156 			ldb_ctrl |= LDB_CTRL_DI1_VSYNC_POLARITY;
157 	}
158 
159 	/*
160 	 * Not all pixel clocks will work, as the final rate (after internal
161 	 * integer division) should be identical to the LCDIF clock, otherwise
162 	 * the rendering will appear resized/shimmering.
163 	 */
164 	ldb_rate = imx_ldb_input_rate(priv, &timings);
165 	clk_set_rate(&priv->ldb_clk, ldb_rate);
166 
167 	writel(ldb_ctrl, priv->ldb_ctrl);
168 
169 	lvds_ctrl = LVDS_CTRL_CC_ADJ(2) | LVDS_CTRL_PRE_EMPH_EN |
170 		    LVDS_CTRL_PRE_EMPH_ADJ(3) | LVDS_CTRL_VBG_EN;
171 	writel(lvds_ctrl, priv->lvds_ctrl);
172 
173 	/* Wait for VBG to stabilize. */
174 	udelay(15);
175 
176 	if (priv->lvds1)
177 		lvds_ctrl |= LVDS_CTRL_CH0_EN;
178 	if (priv->lvds2)
179 		lvds_ctrl |= LVDS_CTRL_CH1_EN;
180 
181 	writel(lvds_ctrl, priv->lvds_ctrl);
182 
183 	return 0;
184 }
185 
imx_ldb_probe(struct udevice * dev)186 static int imx_ldb_probe(struct udevice *dev)
187 {
188 	struct imx_ldb_priv *priv = dev_get_priv(dev);
189 	struct udevice *parent = dev_get_parent(dev);
190 	fdt_addr_t parent_addr, child_addr;
191 	int ret;
192 
193 	ret = clk_get_by_name(dev, "ldb", &priv->ldb_clk);
194 	if (ret < 0)
195 		return ret;
196 
197 	parent_addr = dev_read_addr(parent);
198 	if (parent_addr == FDT_ADDR_T_NONE)
199 		return -EINVAL;
200 
201 	child_addr = dev_read_addr_name(dev, "ldb");
202 	if (child_addr == FDT_ADDR_T_NONE)
203 		return -EINVAL;
204 
205 	priv->ldb_ctrl = map_physmem(parent_addr + child_addr, 0, MAP_NOCACHE);
206 	if (!priv->ldb_ctrl)
207 		return -EINVAL;
208 
209 	child_addr = dev_read_addr_name(dev, "lvds");
210 	if (child_addr == FDT_ADDR_T_NONE)
211 		return -EINVAL;
212 
213 	priv->lvds_ctrl = map_physmem(parent_addr + child_addr, 0, MAP_NOCACHE);
214 	if (!priv->lvds_ctrl)
215 		return -EINVAL;
216 
217 	ret = clk_enable(&priv->ldb_clk);
218 	if (ret)
219 		return ret;
220 
221 	ret = video_bridge_set_active(dev, true);
222 	if (ret)
223 		goto dis_clk;
224 
225 	return 0;
226 
227 dis_clk:
228 	clk_disable(&priv->ldb_clk);
229 
230 	return ret;
231 }
232 
233 struct video_bridge_ops imx_ldb_ops = {
234 	.attach = imx_ldb_attach,
235 	.set_backlight	= imx_ldb_set_backlight,
236 };
237 
238 static const struct udevice_id imx_ldb_ids[] = {
239 	{ .compatible = "fsl,imx8mp-ldb"},
240 	{ }
241 };
242 
243 U_BOOT_DRIVER(imx_ldb) = {
244 	.name = "imx-lvds-display-bridge",
245 	.id = UCLASS_VIDEO_BRIDGE,
246 	.of_match = imx_ldb_ids,
247 	.probe = imx_ldb_probe,
248 	.of_to_plat = imx_ldb_of_to_plat,
249 	.ops = &imx_ldb_ops,
250 	.priv_auto = sizeof(struct imx_ldb_priv),
251 };
252