1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2024 Svyatoslav Ryhel <clamor95@gmail.com>
4  *
5  * This driver uses 8-bit CPU interface found in Tegra 2
6  * and Tegra 3 to drive MIPI DSI panel.
7  */
8 
9 #include <dm.h>
10 #include <dm/ofnode_graph.h>
11 #include <log.h>
12 #include <mipi_display.h>
13 #include <mipi_dsi.h>
14 #include <backlight.h>
15 #include <panel.h>
16 #include <video_bridge.h>
17 #include <linux/delay.h>
18 #include <linux/err.h>
19 #include <asm/gpio.h>
20 #include <asm/io.h>
21 
22 #include "dc.h"
23 
24 struct tegra_cpu_bridge_priv {
25 	struct dc_ctlr *dc;
26 
27 	struct mipi_dsi_host host;
28 	struct mipi_dsi_device device;
29 
30 	struct udevice *panel;
31 	struct display_timing timing;
32 
33 	struct gpio_desc dc_gpio;
34 	struct gpio_desc rw_gpio;
35 	struct gpio_desc cs_gpio;
36 
37 	struct gpio_desc data_gpios[8];
38 
39 	u32 pixel_format;
40 	u32 spi_init_seq[4];
41 };
42 
43 #define TEGRA_CPU_BRIDGE_COMM 0
44 #define TEGRA_CPU_BRIDGE_DATA 1
45 
tegra_cpu_bridge_write(struct tegra_cpu_bridge_priv * priv,u8 type,u8 value)46 static void tegra_cpu_bridge_write(struct tegra_cpu_bridge_priv *priv,
47 				   u8 type, u8 value)
48 {
49 	int i;
50 
51 	dm_gpio_set_value(&priv->dc_gpio, type);
52 
53 	dm_gpio_set_value(&priv->cs_gpio, 0);
54 	dm_gpio_set_value(&priv->rw_gpio, 0);
55 
56 	for (i = 0; i < 8; i++)
57 		dm_gpio_set_value(&priv->data_gpios[i],
58 				  (value >> i) & 0x1);
59 
60 	dm_gpio_set_value(&priv->cs_gpio, 1);
61 	dm_gpio_set_value(&priv->rw_gpio, 1);
62 
63 	udelay(10);
64 
65 	log_debug("%s: type 0x%x, val 0x%x\n",
66 		  __func__, type, value);
67 }
68 
tegra_cpu_bridge_transfer(struct mipi_dsi_host * host,const struct mipi_dsi_msg * msg)69 static ssize_t tegra_cpu_bridge_transfer(struct mipi_dsi_host *host,
70 					 const struct mipi_dsi_msg *msg)
71 {
72 	struct udevice *dev = (struct udevice *)host->dev;
73 	struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
74 	u8 command = *(u8 *)msg->tx_buf;
75 	const u8 *data = msg->tx_buf;
76 	int i;
77 
78 	tegra_cpu_bridge_write(priv, TEGRA_CPU_BRIDGE_COMM, command);
79 
80 	for (i = 1; i < msg->tx_len; i++)
81 		tegra_cpu_bridge_write(priv, TEGRA_CPU_BRIDGE_DATA, data[i]);
82 
83 	return 0;
84 }
85 
86 static const struct mipi_dsi_host_ops tegra_cpu_bridge_host_ops = {
87 	.transfer	= tegra_cpu_bridge_transfer,
88 };
89 
tegra_cpu_bridge_get_format(enum mipi_dsi_pixel_format format,u32 * fmt)90 static int tegra_cpu_bridge_get_format(enum mipi_dsi_pixel_format format, u32 *fmt)
91 {
92 	switch (format) {
93 	case MIPI_DSI_FMT_RGB888:
94 	case MIPI_DSI_FMT_RGB666_PACKED:
95 		*fmt = BASE_COLOR_SIZE_888;
96 		break;
97 
98 	case MIPI_DSI_FMT_RGB666:
99 		*fmt = BASE_COLOR_SIZE_666;
100 		break;
101 
102 	case MIPI_DSI_FMT_RGB565:
103 		*fmt = BASE_COLOR_SIZE_565;
104 		break;
105 
106 	default:
107 		return -EINVAL;
108 	}
109 
110 	return 0;
111 }
112 
tegra_cpu_bridge_attach(struct udevice * dev)113 static int tegra_cpu_bridge_attach(struct udevice *dev)
114 {
115 	struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
116 	struct dc_disp_reg *disp = &priv->dc->disp;
117 	struct dc_cmd_reg *cmd = &priv->dc->cmd;
118 	struct dc_com_reg *com = &priv->dc->com;
119 	u32 value;
120 	int ret;
121 
122 	writel(CTRL_MODE_STOP << CTRL_MODE_SHIFT, &cmd->disp_cmd);
123 	writel(0, &disp->disp_win_opt);
124 	writel(GENERAL_UPDATE, &cmd->state_ctrl);
125 	writel(GENERAL_ACT_REQ, &cmd->state_ctrl);
126 
127 	/* TODO: parametrize if needed */
128 	writel(V_PULSE1_ENABLE, &disp->disp_signal_opt0);
129 	writel(PULSE_POLARITY_LOW, &disp->v_pulse1.v_pulse_ctrl);
130 
131 	writel(PULSE_END(1), &disp->v_pulse1.v_pulse_pos[V_PULSE0_POSITION_A]);
132 	writel(0, &disp->v_pulse1.v_pulse_pos[V_PULSE0_POSITION_B]);
133 	writel(0, &disp->v_pulse1.v_pulse_pos[V_PULSE0_POSITION_C]);
134 
135 	ret = dev_read_u32_array(dev, "nvidia,init-sequence", priv->spi_init_seq, 4);
136 	if (!ret) {
137 		value = 1 << FRAME_INIT_SEQ_CYCLES_SHIFT |
138 			DC_SIGNAL_VPULSE1 << INIT_SEQ_DC_SIGNAL_SHIFT |
139 			INIT_SEQUENCE_MODE_PLCD | SEND_INIT_SEQUENCE;
140 		writel(value, &disp->seq_ctrl);
141 
142 		writel(priv->spi_init_seq[0], &disp->spi_init_seq_data_a);
143 		writel(priv->spi_init_seq[1], &disp->spi_init_seq_data_b);
144 		writel(priv->spi_init_seq[2], &disp->spi_init_seq_data_c);
145 		writel(priv->spi_init_seq[3], &disp->spi_init_seq_data_d);
146 	}
147 
148 	value = readl(&cmd->disp_cmd);
149 	value &= ~CTRL_MODE_MASK;
150 	value |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT;
151 	writel(value, &cmd->disp_cmd);
152 
153 	/* set LDC pin to V Pulse 1 */
154 	value = readl(&com->pin_output_sel[6]) | LDC_OUTPUT_SELECT_V_PULSE1;
155 	writel(value, &com->pin_output_sel[6]);
156 
157 	value = readl(&disp->disp_interface_ctrl);
158 	value |= DATA_ALIGNMENT_LSB << DATA_ALIGNMENT_SHIFT;
159 	writel(value, &disp->disp_interface_ctrl);
160 
161 	value = SC_H_QUALIFIER_NONE << SC1_H_QUALIFIER_SHIFT |
162 		SC_V_QUALIFIER_VACTIVE << SC0_V_QUALIFIER_SHIFT |
163 		SC_H_QUALIFIER_HACTIVE << SC0_H_QUALIFIER_SHIFT;
164 	writel(value, &disp->shift_clk_opt);
165 
166 	value = readl(&disp->disp_color_ctrl);
167 	value |= priv->pixel_format;
168 	writel(value, &disp->disp_color_ctrl);
169 
170 	/* Perform panel setup */
171 	panel_enable_backlight(priv->panel);
172 
173 	dm_gpio_set_value(&priv->cs_gpio, 0);
174 
175 	dm_gpio_free(dev, &priv->dc_gpio);
176 	dm_gpio_free(dev, &priv->rw_gpio);
177 	dm_gpio_free(dev, &priv->cs_gpio);
178 
179 	gpio_free_list(dev, priv->data_gpios, 8);
180 
181 	return 0;
182 }
183 
tegra_cpu_bridge_set_panel(struct udevice * dev,int percent)184 static int tegra_cpu_bridge_set_panel(struct udevice *dev, int percent)
185 {
186 	struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
187 
188 	return panel_set_backlight(priv->panel, percent);
189 }
190 
tegra_cpu_bridge_panel_timings(struct udevice * dev,struct display_timing * timing)191 static int tegra_cpu_bridge_panel_timings(struct udevice *dev,
192 					  struct display_timing *timing)
193 {
194 	struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
195 
196 	memcpy(timing, &priv->timing, sizeof(*timing));
197 
198 	return 0;
199 }
200 
tegra_cpu_bridge_hw_init(struct udevice * dev)201 static int tegra_cpu_bridge_hw_init(struct udevice *dev)
202 {
203 	struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
204 
205 	dm_gpio_set_value(&priv->cs_gpio, 1);
206 
207 	dm_gpio_set_value(&priv->rw_gpio, 1);
208 	dm_gpio_set_value(&priv->dc_gpio, 0);
209 
210 	return 0;
211 }
212 
tegra_cpu_bridge_get_links(struct udevice * dev)213 static int tegra_cpu_bridge_get_links(struct udevice *dev)
214 {
215 	struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
216 	int i, ret;
217 
218 	u32 num = ofnode_graph_get_port_count(dev_ofnode(dev));
219 
220 	for (i = 0; i < num; i++) {
221 		ofnode remote = ofnode_graph_get_remote_node(dev_ofnode(dev), i, -1);
222 
223 		/* Look for DC source */
224 		if (ofnode_name_eq(remote, "rgb")) {
225 			ofnode dc = ofnode_get_parent(remote);
226 
227 			priv->dc = (struct dc_ctlr *)ofnode_get_addr(dc);
228 			if (!priv->dc) {
229 				log_err("%s: failed to get DC controller\n", __func__);
230 				return -EINVAL;
231 			}
232 		}
233 
234 		/* Look for driven panel */
235 		ret = uclass_get_device_by_ofnode(UCLASS_PANEL, remote, &priv->panel);
236 		if (!ret)
237 			return 0;
238 	}
239 
240 	/* If this point is reached, no panels were found */
241 	return -ENODEV;
242 }
243 
tegra_cpu_bridge_probe(struct udevice * dev)244 static int tegra_cpu_bridge_probe(struct udevice *dev)
245 {
246 	struct tegra_cpu_bridge_priv *priv = dev_get_priv(dev);
247 	struct mipi_dsi_device *device = &priv->device;
248 	struct mipi_dsi_panel_plat *mipi_plat;
249 	int ret;
250 
251 	ret = tegra_cpu_bridge_get_links(dev);
252 	if (ret) {
253 		log_debug("%s: links not found, ret %d\n", __func__, ret);
254 		return ret;
255 	}
256 
257 	panel_get_display_timing(priv->panel, &priv->timing);
258 
259 	mipi_plat = dev_get_plat(priv->panel);
260 	mipi_plat->device = device;
261 
262 	priv->host.dev = (struct device *)dev;
263 	priv->host.ops = &tegra_cpu_bridge_host_ops;
264 
265 	device->host = &priv->host;
266 	device->lanes = mipi_plat->lanes;
267 	device->format = mipi_plat->format;
268 	device->mode_flags = mipi_plat->mode_flags;
269 
270 	tegra_cpu_bridge_get_format(device->format, &priv->pixel_format);
271 
272 	/* get control gpios */
273 	ret = gpio_request_by_name(dev, "dc-gpios", 0,
274 				   &priv->dc_gpio, GPIOD_IS_OUT);
275 	if (ret) {
276 		log_debug("%s: could not decode dc-gpios (%d)\n", __func__, ret);
277 		return ret;
278 	}
279 
280 	ret = gpio_request_by_name(dev, "rw-gpios", 0,
281 				   &priv->rw_gpio, GPIOD_IS_OUT);
282 	if (ret) {
283 		log_debug("%s: could not decode rw-gpios (%d)\n", __func__, ret);
284 		return ret;
285 	}
286 
287 	ret = gpio_request_by_name(dev, "cs-gpios", 0,
288 				   &priv->cs_gpio, GPIOD_IS_OUT);
289 	if (ret) {
290 		log_debug("%s: could not decode cs-gpios (%d)\n", __func__, ret);
291 		return ret;
292 	}
293 
294 	/* get data gpios */
295 	ret = gpio_request_list_by_name(dev, "data-gpios",
296 					priv->data_gpios, 8,
297 					GPIOD_IS_OUT);
298 	if (ret < 0) {
299 		log_debug("%s: could not decode data-gpios (%d)\n", __func__, ret);
300 		return ret;
301 	}
302 
303 	return tegra_cpu_bridge_hw_init(dev);
304 }
305 
306 static const struct video_bridge_ops tegra_cpu_bridge_ops = {
307 	.attach			= tegra_cpu_bridge_attach,
308 	.set_backlight		= tegra_cpu_bridge_set_panel,
309 	.get_display_timing	= tegra_cpu_bridge_panel_timings,
310 };
311 
312 static const struct udevice_id tegra_cpu_bridge_ids[] = {
313 	{ .compatible = "nvidia,tegra-8bit-cpu" },
314 	{ }
315 };
316 
317 U_BOOT_DRIVER(tegra_8bit_cpu) = {
318 	.name		= "tegra_8bit_cpu",
319 	.id		= UCLASS_VIDEO_BRIDGE,
320 	.of_match	= tegra_cpu_bridge_ids,
321 	.ops		= &tegra_cpu_bridge_ops,
322 	.bind		= dm_scan_fdt_dev,
323 	.probe		= tegra_cpu_bridge_probe,
324 	.priv_auto	= sizeof(struct tegra_cpu_bridge_priv),
325 };
326