blob: e5fefe028f031a8ccee437c6badb7b22a4a3c677 [file] [log] [blame]
Svyatoslav Ryhel446fa892024-10-04 11:54:46 +03001// 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
24struct 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
46static 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
69static 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
86static const struct mipi_dsi_host_ops tegra_cpu_bridge_host_ops = {
87 .transfer = tegra_cpu_bridge_transfer,
88};
89
90static 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
113static 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
184static 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
191static 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
201static 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
213static 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
244static 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
306static 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
312static const struct udevice_id tegra_cpu_bridge_ids[] = {
313 { .compatible = "nvidia,tegra-8bit-cpu" },
314 { }
315};
316
317U_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};