blob: 7bda33fb16e10e57a91d485d3c03051028726ead [file] [log] [blame]
Stefan Bosch5ed5ad42020-07-10 19:07:36 +02001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2016 Nexell Co., Ltd.
4 *
5 * Author: junghyun, kim <jhkim@nexell.co.kr>
6 *
7 * Copyright (C) 2020 Stefan Bosch <stefan_b@posteo.net>
8 */
9
10#include <config.h>
Stefan Bosch5ed5ad42020-07-10 19:07:36 +020011#include <command.h>
12#include <dm.h>
13#include <mapmem.h>
14#include <malloc.h>
15#include <linux/compat.h>
16#include <linux/err.h>
Dario Binacchi2ec85772021-01-23 19:43:52 +010017#include <video.h> /* For struct video_uc_plat */
Stefan Bosch5ed5ad42020-07-10 19:07:36 +020018#include <asm/global_data.h>
19#include <asm/io.h>
20#include <asm/arch/display.h>
21#include <asm/arch/display_dev.h>
22#include "videomodes.h"
23
24DECLARE_GLOBAL_DATA_PTR;
25
26#if !defined(CONFIG_DM) && !defined(CONFIG_OF_CONTROL)
27static struct nx_display_dev *dp_dev;
28#endif
29
30static char *const dp_dev_str[] = {
31 [DP_DEVICE_RESCONV] = "RESCONV",
32 [DP_DEVICE_RGBLCD] = "LCD",
33 [DP_DEVICE_HDMI] = "HDMI",
34 [DP_DEVICE_MIPI] = "MiPi",
35 [DP_DEVICE_LVDS] = "LVDS",
36 [DP_DEVICE_CVBS] = "TVOUT",
37 [DP_DEVICE_DP0] = "DP0",
38 [DP_DEVICE_DP1] = "DP1",
39};
40
41#if CONFIG_IS_ENABLED(OF_CONTROL)
42static void nx_display_parse_dp_sync(ofnode node, struct dp_sync_info *sync)
43{
44 sync->h_active_len = ofnode_read_s32_default(node, "h_active_len", 0);
45 sync->h_sync_width = ofnode_read_s32_default(node, "h_sync_width", 0);
46 sync->h_back_porch = ofnode_read_s32_default(node, "h_back_porch", 0);
47 sync->h_front_porch = ofnode_read_s32_default(node, "h_front_porch", 0);
48 sync->h_sync_invert = ofnode_read_s32_default(node, "h_sync_invert", 0);
49 sync->v_active_len = ofnode_read_s32_default(node, "v_active_len", 0);
50 sync->v_sync_width = ofnode_read_s32_default(node, "v_sync_width", 0);
51 sync->v_back_porch = ofnode_read_s32_default(node, "v_back_porch", 0);
52 sync->v_front_porch = ofnode_read_s32_default(node, "v_front_porch", 0);
53 sync->v_sync_invert = ofnode_read_s32_default(node, "v_sync_invert", 0);
54 sync->pixel_clock_hz = ofnode_read_s32_default(node, "pixel_clock_hz", 0);
55
56 debug("DP: sync ->\n");
57 debug("ha:%d, hs:%d, hb:%d, hf:%d, hi:%d\n",
58 sync->h_active_len, sync->h_sync_width,
59 sync->h_back_porch, sync->h_front_porch, sync->h_sync_invert);
60 debug("va:%d, vs:%d, vb:%d, vf:%d, vi:%d\n",
61 sync->v_active_len, sync->v_sync_width,
62 sync->v_back_porch, sync->v_front_porch, sync->v_sync_invert);
63}
64
65static void nx_display_parse_dp_ctrl(ofnode node, struct dp_ctrl_info *ctrl)
66{
67 /* clock gen */
68 ctrl->clk_src_lv0 = ofnode_read_s32_default(node, "clk_src_lv0", 0);
69 ctrl->clk_div_lv0 = ofnode_read_s32_default(node, "clk_div_lv0", 0);
70 ctrl->clk_src_lv1 = ofnode_read_s32_default(node, "clk_src_lv1", 0);
71 ctrl->clk_div_lv1 = ofnode_read_s32_default(node, "clk_div_lv1", 0);
72
73 /* scan format */
74 ctrl->interlace = ofnode_read_s32_default(node, "interlace", 0);
75
76 /* syncgen format */
77 ctrl->out_format = ofnode_read_s32_default(node, "out_format", 0);
78 ctrl->invert_field = ofnode_read_s32_default(node, "invert_field", 0);
79 ctrl->swap_RB = ofnode_read_s32_default(node, "swap_RB", 0);
80 ctrl->yc_order = ofnode_read_s32_default(node, "yc_order", 0);
81
82 /* extern sync delay */
83 ctrl->delay_mask = ofnode_read_s32_default(node, "delay_mask", 0);
84 ctrl->d_rgb_pvd = ofnode_read_s32_default(node, "d_rgb_pvd", 0);
85 ctrl->d_hsync_cp1 = ofnode_read_s32_default(node, "d_hsync_cp1", 0);
86 ctrl->d_vsync_fram = ofnode_read_s32_default(node, "d_vsync_fram", 0);
87 ctrl->d_de_cp2 = ofnode_read_s32_default(node, "d_de_cp2", 0);
88
89 /* extern sync delay */
90 ctrl->vs_start_offset =
91 ofnode_read_s32_default(node, "vs_start_offset", 0);
92 ctrl->vs_end_offset = ofnode_read_s32_default(node, "vs_end_offset", 0);
93 ctrl->ev_start_offset =
94 ofnode_read_s32_default(node, "ev_start_offset", 0);
95 ctrl->ev_end_offset = ofnode_read_s32_default(node, "ev_end_offset", 0);
96
97 /* pad clock seletor */
98 ctrl->vck_select = ofnode_read_s32_default(node, "vck_select", 0);
99 ctrl->clk_inv_lv0 = ofnode_read_s32_default(node, "clk_inv_lv0", 0);
100 ctrl->clk_delay_lv0 = ofnode_read_s32_default(node, "clk_delay_lv0", 0);
101 ctrl->clk_inv_lv1 = ofnode_read_s32_default(node, "clk_inv_lv1", 0);
102 ctrl->clk_delay_lv1 = ofnode_read_s32_default(node, "clk_delay_lv1", 0);
103 ctrl->clk_sel_div1 = ofnode_read_s32_default(node, "clk_sel_div1", 0);
104
105 debug("DP: ctrl [%s] ->\n",
106 ctrl->interlace ? "Interlace" : " Progressive");
107 debug("cs0:%d, cd0:%d, cs1:%d, cd1:%d\n",
108 ctrl->clk_src_lv0, ctrl->clk_div_lv0,
109 ctrl->clk_src_lv1, ctrl->clk_div_lv1);
110 debug("fmt:0x%x, inv:%d, swap:%d, yb:0x%x\n",
111 ctrl->out_format, ctrl->invert_field,
112 ctrl->swap_RB, ctrl->yc_order);
113 debug("dm:0x%x, drp:%d, dhs:%d, dvs:%d, dde:0x%x\n",
114 ctrl->delay_mask, ctrl->d_rgb_pvd,
115 ctrl->d_hsync_cp1, ctrl->d_vsync_fram, ctrl->d_de_cp2);
116 debug("vss:%d, vse:%d, evs:%d, eve:%d\n",
117 ctrl->vs_start_offset, ctrl->vs_end_offset,
118 ctrl->ev_start_offset, ctrl->ev_end_offset);
119 debug("sel:%d, i0:%d, d0:%d, i1:%d, d1:%d, s1:%d\n",
120 ctrl->vck_select, ctrl->clk_inv_lv0, ctrl->clk_delay_lv0,
121 ctrl->clk_inv_lv1, ctrl->clk_delay_lv1, ctrl->clk_sel_div1);
122}
123
124static void nx_display_parse_dp_top_layer(ofnode node, struct dp_plane_top *top)
125{
126 top->screen_width = ofnode_read_s32_default(node, "screen_width", 0);
127 top->screen_height = ofnode_read_s32_default(node, "screen_height", 0);
128 top->video_prior = ofnode_read_s32_default(node, "video_prior", 0);
129 top->interlace = ofnode_read_s32_default(node, "interlace", 0);
130 top->back_color = ofnode_read_s32_default(node, "back_color", 0);
131 top->plane_num = DP_PLANS_NUM;
132
133 debug("DP: top [%s] ->\n",
134 top->interlace ? "Interlace" : " Progressive");
135 debug("w:%d, h:%d, prior:%d, bg:0x%x\n",
136 top->screen_width, top->screen_height,
137 top->video_prior, top->back_color);
138}
139
140static void nx_display_parse_dp_layer(ofnode node, struct dp_plane_info *plane)
141{
142 plane->left = ofnode_read_s32_default(node, "left", 0);
143 plane->width = ofnode_read_s32_default(node, "width", 0);
144 plane->top = ofnode_read_s32_default(node, "top", 0);
145 plane->height = ofnode_read_s32_default(node, "height", 0);
146 plane->pixel_byte = ofnode_read_s32_default(node, "pixel_byte", 0);
147 plane->format = ofnode_read_s32_default(node, "format", 0);
148 plane->alpha_on = ofnode_read_s32_default(node, "alpha_on", 0);
149 plane->alpha_depth = ofnode_read_s32_default(node, "alpha", 0);
150 plane->tp_on = ofnode_read_s32_default(node, "tp_on", 0);
151 plane->tp_color = ofnode_read_s32_default(node, "tp_color", 0);
152
153 /* enable layer */
154 if (plane->fb_base)
155 plane->enable = 1;
156 else
157 plane->enable = 0;
158
159 if (plane->fb_base == 0) {
160 printf("fail : dp plane.%d invalid fb base [0x%x] ->\n",
161 plane->layer, plane->fb_base);
162 return;
163 }
164
165 debug("DP: plane.%d [0x%x] ->\n", plane->layer, plane->fb_base);
166 debug("f:0x%x, l:%d, t:%d, %d * %d, bpp:%d, a:%d(%d), t:%d(0x%x)\n",
167 plane->format, plane->left, plane->top, plane->width,
168 plane->height, plane->pixel_byte, plane->alpha_on,
169 plane->alpha_depth, plane->tp_on, plane->tp_color);
170}
171
172static void nx_display_parse_dp_planes(ofnode node,
173 struct nx_display_dev *dp,
Simon Glassb75b15b2020-12-03 16:55:23 -0700174 struct video_uc_plat *plat)
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200175{
176 const char *name;
177 ofnode subnode;
178
179 ofnode_for_each_subnode(subnode, node) {
180 name = ofnode_get_name(subnode);
181
182 if (strcmp(name, "layer_top") == 0)
183 nx_display_parse_dp_top_layer(subnode, &dp->top);
184
185 /*
186 * TODO: Is it sure that only one layer is used? Otherwise
187 * fb_base must be different?
188 */
189 if (strcmp(name, "layer_0") == 0) {
190 dp->planes[0].fb_base =
191 (uint)map_sysmem(plat->base, plat->size);
192 debug("%s(): dp->planes[0].fb_base == 0x%x\n", __func__,
193 (uint)dp->planes[0].fb_base);
194 nx_display_parse_dp_layer(subnode, &dp->planes[0]);
195 }
196
197 if (strcmp(name, "layer_1") == 0) {
198 dp->planes[1].fb_base =
199 (uint)map_sysmem(plat->base, plat->size);
200 debug("%s(): dp->planes[1].fb_base == 0x%x\n", __func__,
201 (uint)dp->planes[1].fb_base);
202 nx_display_parse_dp_layer(subnode, &dp->planes[1]);
203 }
204
205 if (strcmp(name, "layer_2") == 0) {
206 dp->planes[2].fb_base =
207 (uint)map_sysmem(plat->base, plat->size);
208 debug("%s(): dp->planes[2].fb_base == 0x%x\n", __func__,
209 (uint)dp->planes[2].fb_base);
210 nx_display_parse_dp_layer(subnode, &dp->planes[2]);
211 }
212 }
213}
214
215static int nx_display_parse_dp_lvds(ofnode node, struct nx_display_dev *dp)
216{
217 struct dp_lvds_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
218
219 if (!dev) {
220 printf("failed to allocate display LVDS object.\n");
221 return -ENOMEM;
222 }
223
224 dp->device = dev;
225
226 dev->lvds_format = ofnode_read_s32_default(node, "format", 0);
227 dev->pol_inv_hs = ofnode_read_s32_default(node, "pol_inv_hs", 0);
228 dev->pol_inv_vs = ofnode_read_s32_default(node, "pol_inv_vs", 0);
229 dev->pol_inv_de = ofnode_read_s32_default(node, "pol_inv_de", 0);
230 dev->pol_inv_ck = ofnode_read_s32_default(node, "pol_inv_ck", 0);
231 dev->voltage_level = ofnode_read_s32_default(node, "voltage_level", 0);
232
233 if (!dev->voltage_level)
234 dev->voltage_level = DEF_VOLTAGE_LEVEL;
235
236 debug("DP: LVDS -> %s, voltage LV:0x%x\n",
237 dev->lvds_format == DP_LVDS_FORMAT_VESA ? "VESA" :
238 dev->lvds_format == DP_LVDS_FORMAT_JEIDA ? "JEIDA" : "LOC",
239 dev->voltage_level);
240 debug("pol inv hs:%d, vs:%d, de:%d, ck:%d\n",
241 dev->pol_inv_hs, dev->pol_inv_vs,
242 dev->pol_inv_de, dev->pol_inv_ck);
243
244 return 0;
245}
246
247static int nx_display_parse_dp_rgb(ofnode node, struct nx_display_dev *dp)
248{
249 struct dp_rgb_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
250
251 if (!dev) {
252 printf("failed to allocate display RGB LCD object.\n");
253 return -ENOMEM;
254 }
255 dp->device = dev;
256
257 dev->lcd_mpu_type = ofnode_read_s32_default(node, "lcd_mpu_type", 0);
258
259 debug("DP: RGB -> MPU[%s]\n", dev->lcd_mpu_type ? "O" : "X");
260 return 0;
261}
262
263static int nx_display_parse_dp_mipi(ofnode node, struct nx_display_dev *dp)
264{
265 struct dp_mipi_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
266
267 if (!dev) {
268 printf("failed to allocate display MiPi object.\n");
269 return -ENOMEM;
270 }
271 dp->device = dev;
272
273 dev->lp_bitrate = ofnode_read_s32_default(node, "lp_bitrate", 0);
274 dev->hs_bitrate = ofnode_read_s32_default(node, "hs_bitrate", 0);
275 dev->lpm_trans = 1;
276 dev->command_mode = 0;
277
278 debug("DP: MIPI ->\n");
279 debug("lp:%dmhz, hs:%dmhz\n", dev->lp_bitrate, dev->hs_bitrate);
280
281 return 0;
282}
283
284static int nx_display_parse_dp_hdmi(ofnode node, struct nx_display_dev *dp)
285{
286 struct dp_hdmi_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL);
287
288 if (!dev) {
289 printf("failed to allocate display HDMI object.\n");
290 return -ENOMEM;
291 }
292 dp->device = dev;
293
294 dev->preset = ofnode_read_s32_default(node, "preset", 0);
295
296 debug("DP: HDMI -> %d\n", dev->preset);
297
298 return 0;
299}
300
301static int nx_display_parse_dp_lcds(ofnode node, const char *type,
302 struct nx_display_dev *dp)
303{
304 if (strcmp(type, "lvds") == 0) {
305 dp->dev_type = DP_DEVICE_LVDS;
306 return nx_display_parse_dp_lvds(node, dp);
307 } else if (strcmp(type, "rgb") == 0) {
308 dp->dev_type = DP_DEVICE_RGBLCD;
309 return nx_display_parse_dp_rgb(node, dp);
310 } else if (strcmp(type, "mipi") == 0) {
311 dp->dev_type = DP_DEVICE_MIPI;
312 return nx_display_parse_dp_mipi(node, dp);
313 } else if (strcmp(type, "hdmi") == 0) {
314 dp->dev_type = DP_DEVICE_HDMI;
315 return nx_display_parse_dp_hdmi(node, dp);
316 }
317
318 printf("%s: node %s unknown display type\n", __func__,
319 ofnode_get_name(node));
320 return -EINVAL;
321
322 return 0;
323}
324
325#define DT_SYNC (1 << 0)
326#define DT_CTRL (1 << 1)
327#define DT_PLANES (1 << 2)
328#define DT_DEVICE (1 << 3)
329
330static int nx_display_parse_dt(struct udevice *dev,
331 struct nx_display_dev *dp,
Simon Glassb75b15b2020-12-03 16:55:23 -0700332 struct video_uc_plat *plat)
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200333{
334 const char *name, *dtype;
335 int ret = 0;
336 unsigned int dt_status = 0;
337 ofnode subnode;
338
339 if (!dev)
340 return -ENODEV;
341
342 dp->module = dev_read_s32_default(dev, "module", -1);
343 if (dp->module == -1)
344 dp->module = dev_read_s32_default(dev, "index", 0);
345
346 dtype = dev_read_string(dev, "lcd-type");
347
348 ofnode_for_each_subnode(subnode, dev_ofnode(dev)) {
349 name = ofnode_get_name(subnode);
350
351 if (strcmp("dp-sync", name) == 0) {
352 dt_status |= DT_SYNC;
353 nx_display_parse_dp_sync(subnode, &dp->sync);
354 }
355
356 if (strcmp("dp-ctrl", name) == 0) {
357 dt_status |= DT_CTRL;
358 nx_display_parse_dp_ctrl(subnode, &dp->ctrl);
359 }
360
361 if (strcmp("dp-planes", name) == 0) {
362 dt_status |= DT_PLANES;
363 nx_display_parse_dp_planes(subnode, dp, plat);
364 }
365
366 if (strcmp("dp-device", name) == 0) {
367 dt_status |= DT_DEVICE;
368 ret = nx_display_parse_dp_lcds(subnode, dtype, dp);
369 }
370 }
371
372 if (dt_status != (DT_SYNC | DT_CTRL | DT_PLANES | DT_DEVICE)) {
373 printf("Not enough DT config for display [0x%x]\n", dt_status);
374 return -ENODEV;
375 }
376
377 return ret;
378}
379#endif
380
381__weak int nx_display_fixup_dp(struct nx_display_dev *dp)
382{
383 return 0;
384}
385
386static struct nx_display_dev *nx_display_setup(void)
387{
388 struct nx_display_dev *dp;
389 int i, ret;
390 int node = 0;
Simon Glassb75b15b2020-12-03 16:55:23 -0700391 struct video_uc_plat *plat = NULL;
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200392
393 struct udevice *dev;
394
395 /* call driver probe */
396 debug("DT: uclass device call...\n");
397
398 ret = uclass_get_device(UCLASS_VIDEO, 0, &dev);
399 if (ret) {
400 debug("%s(): uclass_get_device(UCLASS_VIDEO, 0, &dev) != 0 --> return NULL\n",
401 __func__);
402 return NULL;
403 }
Simon Glass71fa5b42020-12-03 16:55:18 -0700404 plat = dev_get_uclass_plat(dev);
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200405 if (!dev) {
Simon Glass71fa5b42020-12-03 16:55:18 -0700406 debug("%s(): dev_get_uclass_plat(dev) == NULL --> return NULL\n",
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200407 __func__);
408 return NULL;
409 }
410 dp = dev_get_priv(dev);
411 if (!dp) {
412 debug("%s(): dev_get_priv(dev) == NULL --> return NULL\n",
413 __func__);
414 return NULL;
415 }
Simon Glassa7ece582020-12-19 10:40:14 -0700416 node = dev_ofnode(dev).of_offset;
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200417
418 if (CONFIG_IS_ENABLED(OF_CONTROL)) {
419 ret = nx_display_parse_dt(dev, dp, plat);
420 if (ret)
421 goto err_setup;
422 }
423
424 nx_display_fixup_dp(dp);
425
426 for (i = 0; dp->top.plane_num > i; i++) {
427 dp->planes[i].layer = i;
428 if (dp->planes[i].enable && !dp->fb_plane) {
429 dp->fb_plane = &dp->planes[i];
430 dp->fb_addr = dp->fb_plane->fb_base;
431 dp->depth = dp->fb_plane->pixel_byte;
432 }
433 }
434
435 switch (dp->dev_type) {
436#ifdef CONFIG_VIDEO_NX_RGB
437 case DP_DEVICE_RGBLCD:
438 nx_rgb_display(dp->module,
439 &dp->sync, &dp->ctrl, &dp->top,
440 dp->planes, (struct dp_rgb_dev *)dp->device);
441 break;
442#endif
443#ifdef CONFIG_VIDEO_NX_LVDS
444 case DP_DEVICE_LVDS:
445 nx_lvds_display(dp->module,
446 &dp->sync, &dp->ctrl, &dp->top,
447 dp->planes, (struct dp_lvds_dev *)dp->device);
448 break;
449#endif
450#ifdef CONFIG_VIDEO_NX_MIPI
451 case DP_DEVICE_MIPI:
452 nx_mipi_display(dp->module,
453 &dp->sync, &dp->ctrl, &dp->top,
454 dp->planes, (struct dp_mipi_dev *)dp->device);
455 break;
456#endif
457#ifdef CONFIG_VIDEO_NX_HDMI
458 case DP_DEVICE_HDMI:
459 nx_hdmi_display(dp->module,
460 &dp->sync, &dp->ctrl, &dp->top,
461 dp->planes, (struct dp_hdmi_dev *)dp->device);
462 break;
463#endif
464 default:
465 printf("fail : not support lcd type %d !!!\n", dp->dev_type);
466 goto err_setup;
467 };
468
469 printf("LCD: [%s] dp.%d.%d %dx%d %dbpp FB:0x%08x\n",
470 dp_dev_str[dp->dev_type], dp->module, dp->fb_plane->layer,
471 dp->fb_plane->width, dp->fb_plane->height, dp->depth * 8,
472 dp->fb_addr);
473
474 return dp;
475
476err_setup:
477 kfree(dp);
478
479 return NULL;
480}
481
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200482static int nx_display_probe(struct udevice *dev)
483{
Simon Glassb75b15b2020-12-03 16:55:23 -0700484 struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200485 struct video_priv *uc_priv = dev_get_uclass_priv(dev);
Simon Glassb75b15b2020-12-03 16:55:23 -0700486 struct nx_display_plat *plat = dev_get_plat(dev);
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200487 char addr[64];
488
489 debug("%s()\n", __func__);
490
491 if (!dev)
492 return -EINVAL;
493
494 if (!uc_plat) {
Simon Glassb75b15b2020-12-03 16:55:23 -0700495 debug("%s(): video_uc_plat *plat == NULL --> return -EINVAL\n",
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200496 __func__);
497 return -EINVAL;
498 }
499
500 if (!uc_priv) {
501 debug("%s(): video_priv *uc_priv == NULL --> return -EINVAL\n",
502 __func__);
503 return -EINVAL;
504 }
505
506 if (!plat) {
Simon Glassb75b15b2020-12-03 16:55:23 -0700507 debug("%s(): nx_display_plat *plat == NULL --> return -EINVAL\n",
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200508 __func__);
509 return -EINVAL;
510 }
511
512 struct nx_display_dev *dp;
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200513
514 dp = nx_display_setup();
515 if (!dp) {
516 debug("%s(): nx_display_setup() == 0 --> return -EINVAL\n",
517 __func__);
518 return -EINVAL;
519 }
520
521 switch (dp->depth) {
522 case 2:
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200523 uc_priv->bpix = VIDEO_BPP16;
524 break;
525 case 3:
526 /* There is no VIDEO_BPP24 because these values are of
527 * type video_log2_bpp
528 */
529 case 4:
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200530 uc_priv->bpix = VIDEO_BPP32;
531 break;
532 default:
533 printf("fail : not support LCD bit per pixel %d\n",
534 dp->depth * 8);
535 return -EINVAL;
536 }
537
538 uc_priv->xsize = dp->fb_plane->width;
539 uc_priv->ysize = dp->fb_plane->height;
540 uc_priv->rot = 0;
541
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200542 /*
543 * set environment variable "fb_addr" (frame buffer address), required
Simon Glass52cb5042022-10-18 07:46:31 -0600544 * for splash image, which is not set if CONFIG_VIDEO is enabled).
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200545 */
546 sprintf(addr, "0x%x", dp->fb_addr);
547 debug("%s(): env_set(\"fb_addr\", %s) ...\n", __func__, addr);
548 env_set("fb_addr", addr);
549
550 return 0;
551}
552
553static int nx_display_bind(struct udevice *dev)
554{
Simon Glassb75b15b2020-12-03 16:55:23 -0700555 struct video_uc_plat *plat = dev_get_uclass_plat(dev);
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200556
557 debug("%s()\n", __func__);
558
559 /* Datasheet S5p4418:
560 * Resolution up to 2048 x 1280, up to 12 Bit per color (HDMI)
561 * Actual (max.) size is 0x1000000 because in U-Boot nanopi2-2016.01
Tom Rini05d5dcf2022-12-04 10:03:51 -0500562 * "#define CFG_FB_ADDR 0x77000000" and next address is
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200563 * "#define BMP_LOAD_ADDR 0x78000000"
564 */
565 plat->size = 0x1000000;
566
567 return 0;
568}
569
570static const struct udevice_id nx_display_ids[] = {
571 {.compatible = "nexell,nexell-display", },
572 {}
573};
574
575U_BOOT_DRIVER(nexell_display) = {
576 .name = "nexell-display",
577 .id = UCLASS_VIDEO,
578 .of_match = nx_display_ids,
Simon Glassb75b15b2020-12-03 16:55:23 -0700579 .plat_auto = sizeof(struct nx_display_plat),
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200580 .bind = nx_display_bind,
581 .probe = nx_display_probe,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700582 .priv_auto = sizeof(struct nx_display_dev),
Stefan Bosch5ed5ad42020-07-10 19:07:36 +0200583};