blob: 3f1a42d184489d421ec451bbe696bae57046bd68 [file] [log] [blame]
Chris Morgan8c4e3042023-04-21 10:59:19 -05001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2023 Chris Morgan <macromorgan@hotmail.com>
4 */
5
6#include <abuf.h>
7#include <adc.h>
8#include <asm/io.h>
Chris Morgan3db220e2023-05-15 11:00:30 -05009#include <display.h>
Chris Morgan8c4e3042023-04-21 10:59:19 -050010#include <dm.h>
Chris Morgana824aa62023-05-15 11:00:29 -050011#include <dm/lists.h>
12#include <env.h>
13#include <fdt_support.h>
Chris Morgan8c4e3042023-04-21 10:59:19 -050014#include <linux/delay.h>
Chris Morgan3db220e2023-05-15 11:00:30 -050015#include <mipi_dsi.h>
Chris Morgana824aa62023-05-15 11:00:29 -050016#include <mmc.h>
Chris Morgan3db220e2023-05-15 11:00:30 -050017#include <panel.h>
Chris Morgan8c4e3042023-04-21 10:59:19 -050018#include <pwm.h>
19#include <rng.h>
20#include <stdlib.h>
Chris Morgan3db220e2023-05-15 11:00:30 -050021#include <video_bridge.h>
Chris Morgan8c4e3042023-04-21 10:59:19 -050022
23#define GPIO0_BASE 0xfdd60000
Chris Morgan3db220e2023-05-15 11:00:30 -050024#define GPIO4_BASE 0xfe770000
Chris Morgana824aa62023-05-15 11:00:29 -050025#define GPIO_SWPORT_DR_L 0x0000
Chris Morgan8c4e3042023-04-21 10:59:19 -050026#define GPIO_SWPORT_DR_H 0x0004
Chris Morgana824aa62023-05-15 11:00:29 -050027#define GPIO_SWPORT_DDR_L 0x0008
Chris Morgan8c4e3042023-04-21 10:59:19 -050028#define GPIO_SWPORT_DDR_H 0x000c
Chris Morgan3db220e2023-05-15 11:00:30 -050029#define GPIO_A0 BIT(0)
Chris Morgana824aa62023-05-15 11:00:29 -050030#define GPIO_C5 BIT(5)
31#define GPIO_C6 BIT(6)
32#define GPIO_C7 BIT(7)
Chris Morgan8c4e3042023-04-21 10:59:19 -050033
34#define GPIO_WRITEMASK(bits) ((bits) << 16)
35
36#define DTB_DIR "rockchip/"
37
38struct rg3xx_model {
Chris Morgana824aa62023-05-15 11:00:29 -050039 const u16 adc_value;
Chris Morgan8c4e3042023-04-21 10:59:19 -050040 const char *board;
41 const char *board_name;
42 const char *fdtfile;
43};
44
45enum rgxx3_device_id {
46 RG353M,
47 RG353P,
48 RG353V,
Chris Morgan8c4e3042023-04-21 10:59:19 -050049 RG503,
Chris Morgana824aa62023-05-15 11:00:29 -050050 /* Devices with duplicate ADC value */
51 RG353PS,
52 RG353VS,
Chris Morgan8c4e3042023-04-21 10:59:19 -050053};
54
55static const struct rg3xx_model rg3xx_model_details[] = {
56 [RG353M] = {
Chris Morgana824aa62023-05-15 11:00:29 -050057 517, /* Observed average from device */
Chris Morgan8c4e3042023-04-21 10:59:19 -050058 "rk3566-anbernic-rg353m",
59 "RG353M",
Chris Morgana824aa62023-05-15 11:00:29 -050060 DTB_DIR "rk3566-anbernic-rg353p.dtb", /* Identical devices */
Chris Morgan8c4e3042023-04-21 10:59:19 -050061 },
62 [RG353P] = {
Chris Morgana824aa62023-05-15 11:00:29 -050063 860, /* Documented value of 860 */
Chris Morgan8c4e3042023-04-21 10:59:19 -050064 "rk3566-anbernic-rg353p",
65 "RG353P",
66 DTB_DIR "rk3566-anbernic-rg353p.dtb",
67 },
68 [RG353V] = {
Chris Morgana824aa62023-05-15 11:00:29 -050069 695, /* Observed average from device */
Chris Morgan8c4e3042023-04-21 10:59:19 -050070 "rk3566-anbernic-rg353v",
71 "RG353V",
72 DTB_DIR "rk3566-anbernic-rg353v.dtb",
73 },
Chris Morgan8c4e3042023-04-21 10:59:19 -050074 [RG503] = {
Chris Morgana824aa62023-05-15 11:00:29 -050075 1023, /* Observed average from device */
Chris Morgan8c4e3042023-04-21 10:59:19 -050076 "rk3566-anbernic-rg503",
77 "RG503",
78 DTB_DIR "rk3566-anbernic-rg503.dtb",
79 },
Chris Morgana824aa62023-05-15 11:00:29 -050080 /* Devices with duplicate ADC value */
81 [RG353PS] = {
82 860, /* Observed average from device */
83 "rk3566-anbernic-rg353ps",
84 "RG353PS",
85 DTB_DIR "rk3566-anbernic-rg353ps.dtb",
86 },
87 [RG353VS] = {
88 695, /* Gathered from second hand information */
89 "rk3566-anbernic-rg353vs",
90 "RG353VS",
91 DTB_DIR "rk3566-anbernic-rg353vs.dtb",
92 },
Chris Morgan8c4e3042023-04-21 10:59:19 -050093};
94
Chris Morgan3db220e2023-05-15 11:00:30 -050095struct rg353_panel {
96 const u16 id;
97 const char *panel_compat;
98};
99
100static const struct rg353_panel rg353_panel_details[] = {
101 { .id = 0x3052, .panel_compat = "newvision,nv3051d"},
102 { .id = 0x3821, .panel_compat = "anbernic,rg353v-panel-v2"},
103};
104
Chris Morgan8c4e3042023-04-21 10:59:19 -0500105/*
106 * Start LED very early so user knows device is on. Set color
Chris Morgana824aa62023-05-15 11:00:29 -0500107 * to red.
Chris Morgan8c4e3042023-04-21 10:59:19 -0500108 */
109void spl_board_init(void)
110{
Chris Morgana824aa62023-05-15 11:00:29 -0500111 /* Set GPIO0_C5, GPIO0_C6, and GPIO0_C7 to output. */
112 writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) | \
113 (GPIO_C7 | GPIO_C6 | GPIO_C5),
Chris Morgan8c4e3042023-04-21 10:59:19 -0500114 (GPIO0_BASE + GPIO_SWPORT_DDR_H));
Chris Morgana824aa62023-05-15 11:00:29 -0500115 /* Set GPIO0_C5 and GPIO_C6 to 0 and GPIO0_C7 to 1. */
116 writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) | GPIO_C7,
Chris Morgan8c4e3042023-04-21 10:59:19 -0500117 (GPIO0_BASE + GPIO_SWPORT_DR_H));
118}
119
120/* Use hardware rng to seed Linux random. */
121int board_rng_seed(struct abuf *buf)
122{
123 struct udevice *dev;
124 size_t len = 0x8;
125 u64 *data;
126
127 data = malloc(len);
128 if (!data) {
129 printf("Out of memory\n");
130 return -ENOMEM;
131 }
132
133 if (uclass_get_device(UCLASS_RNG, 0, &dev) || !dev) {
134 printf("No RNG device\n");
135 return -ENODEV;
136 }
137
138 if (dm_rng_read(dev, data, len)) {
139 printf("Reading RNG failed\n");
140 return -EIO;
141 }
142
143 abuf_init_set(buf, data, len);
144
145 return 0;
146}
147
148/*
149 * Buzz the buzzer so the user knows something is going on. Make it
150 * optional in case PWM is disabled.
151 */
152void __maybe_unused startup_buzz(void)
153{
154 struct udevice *dev;
155 int err;
156
157 err = uclass_get_device(UCLASS_PWM, 0, &dev);
158 if (err)
159 printf("pwm not found\n");
160
161 pwm_set_enable(dev, 0, 1);
162 mdelay(200);
163 pwm_set_enable(dev, 0, 0);
164}
165
Chris Morgan3db220e2023-05-15 11:00:30 -0500166/*
167 * Provide the bare minimum to identify the panel for the RG353
168 * series. Since we don't have a working framebuffer device, no
169 * need to init the panel; just identify it and provide the
170 * clocks so we know what to set the different clock values to.
171 */
172
173static const struct display_timing rg353_default_timing = {
174 .pixelclock.typ = 24150000,
175 .hactive.typ = 640,
176 .hfront_porch.typ = 40,
177 .hback_porch.typ = 80,
178 .hsync_len.typ = 2,
179 .vactive.typ = 480,
180 .vfront_porch.typ = 18,
181 .vback_porch.typ = 28,
182 .vsync_len.typ = 2,
183 .flags = DISPLAY_FLAGS_HSYNC_HIGH |
184 DISPLAY_FLAGS_VSYNC_HIGH,
185};
186
187static int anbernic_rg353_panel_get_timing(struct udevice *dev,
188 struct display_timing *timings)
189{
190 memcpy(timings, &rg353_default_timing, sizeof(*timings));
191
192 return 0;
193}
194
195static int anbernic_rg353_panel_probe(struct udevice *dev)
196{
197 struct mipi_dsi_panel_plat *plat = dev_get_plat(dev);
198
199 plat->lanes = 4;
200 plat->format = MIPI_DSI_FMT_RGB888;
201 plat->mode_flags = MIPI_DSI_MODE_VIDEO |
202 MIPI_DSI_MODE_VIDEO_BURST |
203 MIPI_DSI_MODE_EOT_PACKET |
204 MIPI_DSI_MODE_LPM;
205
206 return 0;
207}
208
209static const struct panel_ops anbernic_rg353_panel_ops = {
210 .get_display_timing = anbernic_rg353_panel_get_timing,
211};
212
213U_BOOT_DRIVER(anbernic_rg353_panel) = {
214 .name = "anbernic_rg353_panel",
215 .id = UCLASS_PANEL,
216 .ops = &anbernic_rg353_panel_ops,
217 .probe = anbernic_rg353_panel_probe,
218 .plat_auto = sizeof(struct mipi_dsi_panel_plat),
219};
220
221int rgxx3_detect_display(void)
222{
223 struct udevice *dev;
224 struct mipi_dsi_device *dsi;
225 struct mipi_dsi_panel_plat *mplat;
226 const struct rg353_panel *panel;
227 int ret = 0;
228 int i;
229 u8 panel_id[2];
230
231 /*
232 * Take panel out of reset status.
233 * Set GPIO4_A0 to output.
234 */
235 writel(GPIO_WRITEMASK(GPIO_A0) | GPIO_A0,
236 (GPIO4_BASE + GPIO_SWPORT_DDR_L));
237 /* Set GPIO4_A0 to 1. */
238 writel(GPIO_WRITEMASK(GPIO_A0) | GPIO_A0,
239 (GPIO4_BASE + GPIO_SWPORT_DR_L));
240
241 /* Probe the DSI controller. */
242 ret = uclass_get_device_by_name(UCLASS_VIDEO_BRIDGE,
243 "dsi@fe060000", &dev);
244 if (ret) {
245 printf("DSI host not probed: %d\n", ret);
246 return ret;
247 }
248
249 /* Probe the DSI panel. */
250 ret = device_bind_driver_to_node(dev, "anbernic_rg353_panel",
251 "anbernic_rg353_panel",
252 dev_ofnode(dev), NULL);
253 if (ret) {
254 printf("Failed to probe RG353 panel: %d\n", ret);
255 return ret;
256 }
257
258 /*
259 * Attach the DSI controller which will also probe and attach
260 * the DSIDPHY.
261 */
262 ret = video_bridge_attach(dev);
263 if (ret) {
264 printf("Failed to attach DSI controller: %d\n", ret);
265 return ret;
266 }
267
268 /*
269 * Get the panel which should have already been probed by the
270 * video_bridge_attach() function.
271 */
272 ret = uclass_first_device_err(UCLASS_PANEL, &dev);
273 if (ret) {
274 printf("Panel device error: %d\n", ret);
275 return ret;
276 }
277
278 /* Now call the panel via DSI commands to get the panel ID. */
279 mplat = dev_get_plat(dev);
280 dsi = mplat->device;
281 mipi_dsi_set_maximum_return_packet_size(dsi, sizeof(panel_id));
282 ret = mipi_dsi_dcs_read(dsi, MIPI_DCS_GET_DISPLAY_ID, &panel_id,
283 sizeof(panel_id));
284 if (ret < 0) {
285 printf("Unable to read panel ID: %d\n", ret);
286 return ret;
287 }
288
289 /* Get the correct panel compatible from the table. */
290 for (i = 0; i < ARRAY_SIZE(rg353_panel_details); i++) {
291 if (rg353_panel_details[i].id == ((panel_id[0] << 8) |
292 panel_id[1])) {
293 panel = &rg353_panel_details[i];
294 break;
295 }
296 }
297
298 if (!panel) {
299 printf("Unable to identify panel_id %x\n",
300 (panel_id[0] << 8) | panel_id[1]);
301 env_set("panel", "unknown");
302 return -EINVAL;
303 }
304
305 env_set("panel", panel->panel_compat);
306
307 return 0;
308}
309
Chris Morgan8c4e3042023-04-21 10:59:19 -0500310/* Detect which Anbernic RGXX3 device we are using so as to load the
311 * correct devicetree for Linux. Set an environment variable once
312 * found. The detection depends on the value of ADC channel 1, the
Chris Morgana824aa62023-05-15 11:00:29 -0500313 * presence of an eMMC on mmc0, and querying the DSI panel.
Chris Morgan8c4e3042023-04-21 10:59:19 -0500314 */
315int rgxx3_detect_device(void)
316{
317 u32 adc_info;
Chris Morgana824aa62023-05-15 11:00:29 -0500318 int ret, i;
Chris Morgan8c4e3042023-04-21 10:59:19 -0500319 int board_id = -ENXIO;
320 struct mmc *mmc;
321
322 ret = adc_channel_single_shot("saradc@fe720000", 1, &adc_info);
323 if (ret) {
324 printf("Read SARADC failed with error %d\n", ret);
325 return ret;
326 }
327
Chris Morgana824aa62023-05-15 11:00:29 -0500328 /*
329 * Get the correct device from the table. The ADC value is
330 * determined by a resistor on ADC channel 0. The hardware
331 * design calls for no more than a 1% variance on the
332 * resistor, so assume a +- value of 15 should be enough.
333 */
334 for (i = 0; i < ARRAY_SIZE(rg3xx_model_details); i++) {
335 u32 adc_min = rg3xx_model_details[i].adc_value - 15;
336 u32 adc_max = rg3xx_model_details[i].adc_value + 15;
337
338 if (adc_min < adc_info && adc_max > adc_info) {
339 board_id = i;
340 break;
341 }
342 }
Chris Morgan8c4e3042023-04-21 10:59:19 -0500343
344 /*
Chris Morgana824aa62023-05-15 11:00:29 -0500345 * Try to access the eMMC on an RG353V or RG353P. If it's
346 * missing, it's an RG353VS or RG353PS. Note we could also
347 * check for a touchscreen at 0x1a on i2c2.
Chris Morgan8c4e3042023-04-21 10:59:19 -0500348 */
Chris Morgana824aa62023-05-15 11:00:29 -0500349 if (board_id == RG353V || board_id == RG353P) {
Chris Morgan8c4e3042023-04-21 10:59:19 -0500350 mmc = find_mmc_device(0);
351 if (mmc) {
352 ret = mmc_init(mmc);
Chris Morgana824aa62023-05-15 11:00:29 -0500353 if (ret) {
354 if (board_id == RG353V)
355 board_id = RG353VS;
356 else
357 board_id = RG353PS;
358 }
Chris Morgan8c4e3042023-04-21 10:59:19 -0500359 }
360 }
361
362 if (board_id < 0)
363 return board_id;
364
365 env_set("board", rg3xx_model_details[board_id].board);
366 env_set("board_name",
367 rg3xx_model_details[board_id].board_name);
368 env_set("fdtfile", rg3xx_model_details[board_id].fdtfile);
369
Chris Morgan3db220e2023-05-15 11:00:30 -0500370 /* Detect the panel type for any device that isn't a 503. */
371 if (board_id == RG503)
372 return 0;
373
374 ret = rgxx3_detect_display();
375 if (ret)
376 return ret;
377
Chris Morgan8c4e3042023-04-21 10:59:19 -0500378 return 0;
379}
380
381int rk_board_late_init(void)
382{
383 int ret;
384
Chris Morgan8c4e3042023-04-21 10:59:19 -0500385 ret = rgxx3_detect_device();
386 if (ret) {
387 printf("Unable to detect device type: %d\n", ret);
388 return ret;
389 }
390
Chris Morgana824aa62023-05-15 11:00:29 -0500391 /* Turn off red LED and turn on orange LED. */
392 writel(GPIO_WRITEMASK(GPIO_C7 | GPIO_C6 | GPIO_C5) | GPIO_C6,
393 (GPIO0_BASE + GPIO_SWPORT_DR_H));
394
Chris Morgan8c4e3042023-04-21 10:59:19 -0500395 if (IS_ENABLED(CONFIG_DM_PWM))
396 startup_buzz();
397
398 return 0;
399}
Chris Morgana824aa62023-05-15 11:00:29 -0500400
401int ft_board_setup(void *blob, struct bd_info *bd)
402{
Chris Morgan3db220e2023-05-15 11:00:30 -0500403 int node, ret;
Chris Morgana824aa62023-05-15 11:00:29 -0500404 char *env;
405
406 /* No fixups necessary for the RG503 */
407 env = env_get("board_name");
408 if (env && (!strcmp(env, rg3xx_model_details[RG503].board_name)))
409 return 0;
410
411 /* Change the model name of the RG353M */
412 if (env && (!strcmp(env, rg3xx_model_details[RG353M].board_name)))
413 fdt_setprop(blob, 0, "model",
414 rg3xx_model_details[RG353M].board_name,
415 sizeof(rg3xx_model_details[RG353M].board_name));
416
Chris Morgan3db220e2023-05-15 11:00:30 -0500417 /*
418 * Check if the environment variable doesn't equal the panel.
419 * If it doesn't, update the devicetree to the correct panel.
420 */
421 node = fdt_path_offset(blob, "/dsi@fe060000/panel@0");
422 if (!(node > 0)) {
423 printf("Can't find the DSI node\n");
424 return -ENODEV;
425 }
426
427 env = env_get("panel");
428 if (!env) {
429 printf("Can't get panel env\n");
430 return -ENODEV;
431 }
432
433 ret = fdt_node_check_compatible(blob, node, env);
434 if (ret < 0)
435 return -ENODEV;
436
437 /* Panels match, return 0. */
438 if (!ret)
439 return 0;
440
441 do_fixup_by_path_string(blob, "/dsi@fe060000/panel@0",
442 "compatible", env);
443
Chris Morgana824aa62023-05-15 11:00:29 -0500444 return 0;
445}