Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
CTCaer
GitHub Repository: CTCaer/hekate
Path: blob/master/bdk/display/di.c
3694 views
1
/*
2
* Copyright (c) 2018 naehrwert
3
* Copyright (c) 2018-2025 CTCaer
4
*
5
* This program is free software; you can redistribute it and/or modify it
6
* under the terms and conditions of the GNU General Public License,
7
* version 2, as published by the Free Software Foundation.
8
*
9
* This program is distributed in the hope it will be useful, but WITHOUT
10
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12
* more details.
13
*
14
* You should have received a copy of the GNU General Public License
15
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
*/
17
18
#include <string.h>
19
20
#include "di.h"
21
#include <power/max77620.h>
22
#include <power/max7762x.h>
23
#include <soc/clock.h>
24
#include <soc/fuse.h>
25
#include <soc/gpio.h>
26
#include <soc/hw_init.h>
27
#include <soc/pinmux.h>
28
#include <soc/pmc.h>
29
#include <soc/timer.h>
30
#include <soc/t210.h>
31
#include <utils/util.h>
32
33
#include "di.inl"
34
35
static bool _nx_aula = false;
36
static u32 _panel_id = 0;
37
static u32 _panel_id_raw = 0;
38
39
static void _display_panel_and_hw_end(bool no_panel_deinit);
40
41
void display_enable_interrupt(u32 intr)
42
{
43
DISPLAY_A(DC_CMD_INT_ENABLE) |= intr;
44
}
45
46
void display_disable_interrupt(u32 intr)
47
{
48
DISPLAY_A(DC_CMD_INT_ENABLE) &= ~intr;
49
DISPLAY_A(DC_CMD_INT_STATUS) = intr;
50
}
51
52
void display_wait_interrupt(u32 intr)
53
{
54
DISPLAY_A(DC_CMD_INT_STATUS) = intr;
55
56
// Interrupts are masked. Poll status register for checking if fired.
57
while (!(DISPLAY_A(DC_CMD_INT_STATUS) & intr))
58
;
59
}
60
61
static void _display_dsi_wait(u32 timeout, u32 off, u32 mask)
62
{
63
u32 end = get_tmr_us() + timeout * 1000;
64
while (get_tmr_us() < end && DSI(off) & mask)
65
;
66
usleep(5);
67
}
68
69
static void _display_dsi_soft_reset()
70
{
71
// Disable DSI interface.
72
DSI(DSI_POWER_CONTROL) = 0;
73
74
// Settle time.
75
usleep(10);
76
}
77
78
static void _display_dsi_send_cmd(u8 cmd, u32 param, u32 wait)
79
{
80
DSI(DSI_WR_DATA) = (param << 8) | cmd;
81
DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
82
83
if (wait)
84
usleep(wait);
85
}
86
87
static void _display_dsi_wait_vblank(bool enable)
88
{
89
if (enable)
90
{
91
// Enable vblank interrupt.
92
display_enable_interrupt(DC_CMD_INT_FRAME_END_INT);
93
94
// Use the 4th line to transmit the host cmd packet.
95
DSI(DSI_VIDEO_MODE_CONTROL) = DSI_CMD_PKT_VID_ENABLE | DSI_DSI_LINE_TYPE(4);
96
97
// Wait for vblank before starting the transfer.
98
display_wait_interrupt(DC_CMD_INT_FRAME_END_INT);
99
}
100
else
101
{
102
// Wait for vblank before resetting sync points.
103
display_wait_interrupt(DC_CMD_INT_FRAME_END_INT);
104
usleep(14);
105
106
// Reset all states of syncpt block.
107
DSI(DSI_INCR_SYNCPT_CNTRL) = DSI_INCR_SYNCPT_SOFT_RESET;
108
usleep(300); // Stabilization delay.
109
110
// Clear syncpt block reset.
111
DSI(DSI_INCR_SYNCPT_CNTRL) = 0;
112
usleep(300); // Stabilization delay.
113
114
// Restore video mode and host control.
115
DSI(DSI_VIDEO_MODE_CONTROL) = 0;
116
117
// Disable and clear vblank interrupt.
118
display_disable_interrupt(DC_CMD_INT_FRAME_END_INT);
119
}
120
}
121
122
static void _display_dsi_read_rx_fifo(u32 *data)
123
{
124
u32 fifo_count = DSI(DSI_STATUS) & DSI_STATUS_RX_FIFO_SIZE;
125
if (fifo_count)
126
DSI(DSI_TRIGGER) = 0;
127
128
for (u32 i = 0; i < fifo_count; i++)
129
{
130
// Read or Drain RX FIFO.
131
if (data)
132
data[i] = DSI(DSI_RD_DATA);
133
else
134
(void)DSI(DSI_RD_DATA);
135
}
136
}
137
138
int display_dsi_read(u8 cmd, u32 len, void *data)
139
{
140
u32 fifo[DSI_STATUS_RX_FIFO_SIZE] = {0};
141
142
// Drain RX FIFO.
143
_display_dsi_read_rx_fifo(NULL);
144
145
// Set reply size.
146
_display_dsi_send_cmd(MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0);
147
_display_dsi_wait(250, DSI_TRIGGER, DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO);
148
149
// Request register read.
150
_display_dsi_send_cmd(MIPI_DSI_DCS_READ, cmd, 0);
151
_display_dsi_wait(250, DSI_TRIGGER, DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO);
152
153
// Transfer bus control to device for transmitting the reply.
154
DSI(DSI_HOST_CONTROL) |= DSI_HOST_CONTROL_IMM_BTA;
155
156
// Wait for reply to complete. DSI_HOST_CONTROL_IMM_BTA bit acts as a DSI host read busy.
157
_display_dsi_wait(150, DSI_HOST_CONTROL, DSI_HOST_CONTROL_IMM_BTA);
158
159
// Wait a bit for the reply.
160
usleep(5000);
161
162
// Read RX FIFO.
163
_display_dsi_read_rx_fifo(fifo);
164
165
// Parse packet and copy over the data.
166
if ((fifo[0] & 0xFF) == DSI_ESCAPE_CMD)
167
{
168
// Act based on reply type.
169
switch (fifo[1] & 0xFF)
170
{
171
case GEN_LONG_RD_RES:
172
case DCS_LONG_RD_RES:
173
memcpy(data, &fifo[2], MIN((fifo[1] >> 8) & 0xFFFF, len));
174
break;
175
176
case GEN_1_BYTE_SHORT_RD_RES:
177
case DCS_1_BYTE_SHORT_RD_RES:
178
memcpy(data, &fifo[2], 1);
179
break;
180
181
case GEN_2_BYTE_SHORT_RD_RES:
182
case DCS_2_BYTE_SHORT_RD_RES:
183
memcpy(data, &fifo[2], 2);
184
break;
185
186
case ACK_ERROR_RES:
187
default:
188
return 1;
189
}
190
}
191
else
192
return 1;
193
194
return 0;
195
}
196
197
int display_dsi_vblank_read(u8 cmd, u32 len, void *data)
198
{
199
int res = 0;
200
u32 host_control = 0;
201
u32 fifo[DSI_STATUS_RX_FIFO_SIZE] = {0};
202
203
// Drain RX FIFO.
204
_display_dsi_read_rx_fifo(NULL);
205
206
// Save host control and enable host cmd packets during video.
207
host_control = DSI(DSI_HOST_CONTROL);
208
209
_display_dsi_wait_vblank(true);
210
211
// Set reply size.
212
_display_dsi_send_cmd(MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0);
213
_display_dsi_wait(0, DSI_TRIGGER, DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO);
214
215
// Request register read.
216
_display_dsi_send_cmd(MIPI_DSI_DCS_READ, cmd, 0);
217
_display_dsi_wait(0, DSI_TRIGGER, DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO);
218
219
_display_dsi_wait_vblank(false);
220
221
// Transfer bus control to device for transmitting the reply.
222
DSI(DSI_HOST_CONTROL) |= DSI_HOST_CONTROL_IMM_BTA;
223
224
// Wait for reply to complete. DSI_HOST_CONTROL_IMM_BTA bit acts as a DSI host read busy.
225
_display_dsi_wait(150, DSI_HOST_CONTROL, DSI_HOST_CONTROL_IMM_BTA);
226
227
// Wait a bit for the reply.
228
usleep(5000);
229
230
// Read RX FIFO.
231
_display_dsi_read_rx_fifo(fifo);
232
233
// Parse packet and copy over the data.
234
if ((fifo[0] & 0xFF) == DSI_ESCAPE_CMD)
235
{
236
// Act based on reply type.
237
switch (fifo[1] & 0xFF)
238
{
239
case GEN_LONG_RD_RES:
240
case DCS_LONG_RD_RES:
241
memcpy(data, &fifo[2], MIN((fifo[1] >> 8) & 0xFFFF, len));
242
break;
243
244
case GEN_1_BYTE_SHORT_RD_RES:
245
case DCS_1_BYTE_SHORT_RD_RES:
246
memcpy(data, &fifo[2], 1);
247
break;
248
249
case GEN_2_BYTE_SHORT_RD_RES:
250
case DCS_2_BYTE_SHORT_RD_RES:
251
memcpy(data, &fifo[2], 2);
252
break;
253
254
case ACK_ERROR_RES:
255
default:
256
res = 1;
257
break;
258
}
259
}
260
else
261
res = 1;
262
263
// Restore host control.
264
DSI(DSI_HOST_CONTROL) = host_control;
265
266
return res;
267
}
268
269
void display_dsi_write(u8 cmd, u32 len, void *data)
270
{
271
u32 host_control;
272
u32 fifo32[DSI_STATUS_TX_FIFO_SIZE] = {0};
273
u8 *fifo8 = (u8 *)fifo32;
274
275
// Prepare data for long write.
276
if (len >= 2)
277
{
278
memcpy(&fifo8[5], data, len);
279
memset(&fifo8[5] + len, 0, len % sizeof(u32));
280
len++; // Increase length by CMD.
281
}
282
283
// Save host control.
284
host_control = DSI(DSI_HOST_CONTROL);
285
286
// Enable host transfer trigger.
287
DSI(DSI_HOST_CONTROL) = (host_control & ~(DSI_HOST_CONTROL_TX_TRIG_MASK)) | DSI_HOST_CONTROL_TX_TRIG_HOST;
288
289
switch (len)
290
{
291
case 0:
292
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, cmd, 0);
293
break;
294
295
case 1:
296
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd | (*(u8 *)data << 8), 0);
297
break;
298
299
default:
300
fifo32[0] = (len << 8) | MIPI_DSI_DCS_LONG_WRITE;
301
fifo8[4] = cmd;
302
len += sizeof(u32); // Increase length by length word and DCS CMD.
303
for (u32 i = 0; i < (ALIGN(len, sizeof(u32)) / sizeof(u32)); i++)
304
DSI(DSI_WR_DATA) = fifo32[i];
305
DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
306
break;
307
}
308
309
// Wait for the write to happen.
310
_display_dsi_wait(250, DSI_TRIGGER, DSI_TRIGGER_HOST);
311
312
// Restore host control.
313
DSI(DSI_HOST_CONTROL) = host_control;
314
}
315
316
void display_dsi_vblank_write(u8 cmd, u32 len, void *data)
317
{
318
u32 fifo32[DSI_STATUS_TX_FIFO_SIZE] = {0};
319
u8 *fifo8 = (u8 *)fifo32;
320
321
// Prepare data for long write.
322
if (len >= 2)
323
{
324
memcpy(&fifo8[5], data, len);
325
memset(&fifo8[5] + len, 0, len % sizeof(u32));
326
len++; // Increase length by CMD.
327
}
328
329
_display_dsi_wait_vblank(true);
330
331
switch (len)
332
{
333
case 0:
334
DSI(DSI_WR_DATA) = (cmd << 8) | MIPI_DSI_DCS_SHORT_WRITE;
335
break;
336
337
case 1:
338
DSI(DSI_WR_DATA) = ((cmd | (*(u8 *)data << 8)) << 8) | MIPI_DSI_DCS_SHORT_WRITE_PARAM;
339
break;
340
341
default:
342
fifo32[0] = (len << 8) | MIPI_DSI_DCS_LONG_WRITE;
343
fifo8[4] = cmd;
344
len += sizeof(u32); // Increase length by length word and DCS CMD.
345
for (u32 i = 0; i < (ALIGN(len, sizeof(u32)) / sizeof(u32)); i++)
346
DSI(DSI_WR_DATA) = fifo32[i];
347
break;
348
}
349
350
_display_dsi_wait_vblank(false);
351
}
352
353
void display_init()
354
{
355
// Get Hardware type, as it's used in various DI functions.
356
_nx_aula = fuse_read_hw_type() == FUSE_NX_HW_TYPE_AULA;
357
358
// Check if display is already initialized.
359
if (CLOCK(CLK_RST_CONTROLLER_CLK_OUT_ENB_L) & BIT(CLK_L_DISP1))
360
_display_panel_and_hw_end(true);
361
362
// Get Chip ID.
363
bool tegra_t210 = hw_get_chip_id() == GP_HIDREV_MAJOR_T210;
364
365
// Enable DSI AVDD.
366
max7762x_regulator_set_voltage(REGULATOR_LDO0, 1200000);
367
max7762x_regulator_enable(REGULATOR_LDO0, true);
368
369
// Enable Display Interface specific clocks.
370
CLOCK(CLK_RST_CONTROLLER_RST_DEV_H_CLR) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
371
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_SET) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
372
CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_CLR) = BIT(CLK_L_DISP1);
373
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_SET) = BIT(CLK_L_DISP1);
374
375
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_X_SET) = BIT(CLK_X_UART_FST_MIPI_CAL);
376
CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL) = CLK_SRC_DIV(6); // Set PLLP_OUT3 and div 6 (17MHz).
377
378
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_W_SET) = BIT(CLK_W_DSIA_LP);
379
CLOCK(CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP) = CLK_SRC_DIV(6); // Set PLLP_OUT and div 6 (68MHz).
380
381
// Bring every IO rail out of deep power down. (Though no rail bit is set.)
382
PMC(APBDEV_PMC_IO_DPD_REQ) = PMC_IO_DPD_REQ_DPD_OFF;
383
PMC(APBDEV_PMC_IO_DPD2_REQ) = PMC_IO_DPD_REQ_DPD_OFF;
384
385
// Configure LCD/WLED driver pins.
386
if (!_nx_aula)
387
{
388
// Configure LCD driver pins.
389
PINMUX_AUX(PINMUX_AUX_NFC_EN) = PINMUX_PULL_DOWN;
390
PINMUX_AUX(PINMUX_AUX_NFC_INT) = PINMUX_PULL_DOWN;
391
392
// Configure WLED driver pins.
393
PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = PINMUX_PULL_DOWN;
394
PINMUX_AUX(PINMUX_AUX_LCD_BL_EN) = PINMUX_PULL_DOWN;
395
396
// Enable LCD driver AVDD channels (+5.4V CH2 EN, -5.4V CH1 EN).
397
gpio_direction_output(GPIO_PORT_I, GPIO_PIN_0 | GPIO_PIN_1, GPIO_HIGH);
398
usleep(10000); // Wait minimum 4.2ms to stabilize.
399
400
// Configure WLED driver PWM/EN pins.
401
gpio_direction_output(GPIO_PORT_V, GPIO_PIN_0 | GPIO_PIN_1, GPIO_LOW);
402
403
// Enable WLED driver.
404
gpio_write(GPIO_PORT_V, GPIO_PIN_1, GPIO_HIGH);
405
}
406
// else
407
// {
408
// // Configure OLED status pin.
409
// PINMUX_AUX(PINMUX_AUX_WIFI_EN) = PINMUX_INPUT_ENABLE | PINMUX_TRISTATE;
410
// gpio_direction_input(GPIO_PORT_H, GPIO_PIN_0);
411
// }
412
413
// Configure Panel Reset pin.
414
PINMUX_AUX(PINMUX_AUX_LCD_RST) = PINMUX_PULL_DOWN;
415
gpio_direction_output(GPIO_PORT_V, GPIO_PIN_2, GPIO_LOW);
416
417
// Power up supply regulator for display interface.
418
MIPI_CAL(MIPI_CAL_MIPI_BIAS_PAD_CFG2) = 0;
419
MIPI_CAL(MIPI_CAL_MIPI_BIAS_PAD_CFG0) = 0;
420
421
if (!tegra_t210)
422
APB_MISC(APB_MISC_GP_DSI_PAD_CONTROL) = 0;
423
424
// Set DISP1 clock source, parent clock and DSI/PCLK to command mode.
425
// T210: DIVM: 1, DIVN: 20, DIVP: 3. PLLD_OUT: 100.0 MHz, PLLD_OUT0 (DSI-BCLK): 50.0 MHz. (PCLK: 16.66 MHz)
426
// T210B01: DIVM: 1, DIVN: 20, DIVP: 3. PLLD_OUT: 97.8 MHz, PLLD_OUT0 (DSI-BCLK): 48.9 MHz. (PCLK: 16.30 MHz)
427
clock_enable_plld(3, 20, true, tegra_t210);
428
429
// Setup Display Interface initial configuration.
430
reg_write_array((vu32 *)DISPLAY_A_BASE, _di_dc_init_config, ARRAY_SIZE(_di_dc_init_config));
431
432
// Setup DSI init sequence packets.
433
reg_write_array((vu32 *)DSI_BASE, _di_dsi_seq_pkt_reset_config0, ARRAY_SIZE(_di_dsi_seq_pkt_reset_config0));
434
DSI(tegra_t210 ? DSI_INIT_SEQ_DATA_15 : DSI_INIT_SEQ_DATA_15_B01) = 0;
435
reg_write_array((vu32 *)DSI_BASE, _di_dsi_seq_pkt_reset_config1, ARRAY_SIZE(_di_dsi_seq_pkt_reset_config1));
436
437
// Reset pad trimmers for T210B01.
438
if (!tegra_t210)
439
reg_write_array((vu32 *)DSI_BASE, _di_dsi_init_pads_t210b01, ARRAY_SIZE(_di_dsi_init_pads_t210b01));
440
441
// Setup init seq packet lengths, timings and power on DSI.
442
reg_write_array((vu32 *)DSI_BASE, _di_dsi_init_config, ARRAY_SIZE(_di_dsi_init_config));
443
usleep(10);
444
445
// DSI soft reset.
446
_display_dsi_soft_reset();
447
448
// Set DSI LP timings.
449
reg_write_array((vu32 *)DSI_BASE, _di_dsi_timing_lp_config, ARRAY_SIZE(_di_dsi_timing_lp_config));
450
usleep(10000);
451
452
// Enable Panel Reset.
453
gpio_write(GPIO_PORT_V, GPIO_PIN_2, GPIO_HIGH);
454
usleep(60000);
455
456
// Setup DSI device takeover timeout.
457
DSI(DSI_BTA_TIMING) = _nx_aula ? 0x40103 : 0x50204;
458
459
// Get Display ID.
460
_panel_id_raw = 0xCCCCCC;
461
for (u32 i = 0; i < 3; i++)
462
{
463
if (!display_dsi_read(MIPI_DCS_GET_DISPLAY_ID, 3, &_panel_id_raw))
464
break;
465
466
usleep(5000);
467
}
468
469
// Decode Display ID.
470
_panel_id = ((_panel_id_raw >> 8) & 0xFF00) | (_panel_id_raw & 0xFF);
471
472
if ((_panel_id & 0xFF) == PANEL_JDI_XXX062M)
473
_panel_id = PANEL_JDI_XXX062M;
474
475
// For Aula ensure that we have a compatible panel id.
476
if (_nx_aula)
477
_panel_id = PANEL_SAM_AMS699VC01;
478
479
// Initialize display panel.
480
switch (_panel_id)
481
{
482
case PANEL_SAM_AMS699VC01:
483
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 180000);
484
// Set color mode to basic (natural). Stock is Saturated (0x00). (POR/Exit value is 0x20/0x00).
485
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM,
486
MIPI_DCS_PRIV_SM_SET_COLOR_MODE | (DCS_SM_COLOR_MODE_BASIC << 8), 0);
487
// Enable backlight and smooth PWM.
488
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM,
489
MIPI_DCS_SET_CONTROL_DISPLAY | ((DCS_CONTROL_DISPLAY_BRIGHTNESS_CTRL | DCS_CONTROL_DISPLAY_DIMMING_CTRL) << 8), 0);
490
491
// Unlock Level 2 registers.
492
DSI(DSI_WR_DATA) = 0x539; // MIPI_DSI_DCS_LONG_WRITE: 5 bytes.
493
DSI(DSI_WR_DATA) = 0x5A5A5AE2; // MIPI_DCS_PRIV_SM_SET_REGS_LOCK: Unlock Level 2 registers.
494
DSI(DSI_WR_DATA) = 0x5A;
495
DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
496
497
// Set registers offset and set PWM transition to 6 frames (100ms).
498
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM, MIPI_DCS_PRIV_SM_SET_REG_OFFSET | (7 << 8), 0);
499
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM, MIPI_DCS_PRIV_SM_SET_ELVSS | (6 << 8), 0);
500
501
// Relock Level 2 registers.
502
DSI(DSI_WR_DATA) = 0x539; // MIPI_DSI_DCS_LONG_WRITE: 5 bytes.
503
DSI(DSI_WR_DATA) = 0xA55A5AE2; // MIPI_DCS_PRIV_SM_SET_REGS_LOCK: Lock Level 2 registers.
504
DSI(DSI_WR_DATA) = 0xA5;
505
DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
506
507
// Set backlight to 0%.
508
DSI(DSI_WR_DATA) = 0x339; // MIPI_DSI_DCS_LONG_WRITE: 3 bytes.
509
DSI(DSI_WR_DATA) = 0x000051; // MIPI_DCS_SET_BRIGHTNESS 0000: 0%. FF07: 100%.
510
DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
511
usleep(5000);
512
break;
513
514
case PANEL_JDI_XXX062M:
515
reg_write_array((vu32 *)DSI_BASE, _di_dsi_panel_init_config_jdi, ARRAY_SIZE(_di_dsi_panel_init_config_jdi));
516
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 180000);
517
break;
518
519
case PANEL_INL_P062CCA_AZ1:
520
case PANEL_AUO_A062TAN01:
521
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 180000);
522
523
// Unlock extension cmds.
524
DSI(DSI_WR_DATA) = 0x439; // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
525
DSI(DSI_WR_DATA) = 0x9483FFB9; // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
526
DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
527
usleep(5000);
528
529
// Set Power control.
530
DSI(DSI_WR_DATA) = 0x739; // MIPI_DSI_DCS_LONG_WRITE: 7 bytes.
531
if (_panel_id == PANEL_INL_P062CCA_AZ1)
532
DSI(DSI_WR_DATA) = 0x751548B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT5 / XDK, VRH gamma volt adj 53 / x40).
533
else // PANEL_AUO_A062TAN01.
534
DSI(DSI_WR_DATA) = 0x711148B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT1 / XDK, VRH gamma volt adj 49 / x40).
535
DSI(DSI_WR_DATA) = 0x143209; // (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/32).
536
DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
537
usleep(5000);
538
break;
539
540
case PANEL_INL_2J055IA_27A:
541
case PANEL_AUO_A055TAN01:
542
case PANEL_SHP_LQ055T1SW10:
543
default: // Allow spare part displays to work.
544
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_EXIT_SLEEP_MODE, 120000);
545
break;
546
}
547
548
// Unblank display.
549
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_SET_DISPLAY_ON, 20000);
550
551
// Switch to DSI HS mode.
552
// DIVM: 1, DIVN: 24, DIVP: 1. PLLD_OUT: 468.0 MHz, PLLD_OUT0 (DSI-BCLK): 234.0 MHz. (PCLK: 78 MHz)
553
clock_enable_plld(1, 24, false, tegra_t210);
554
555
// Set HS PHY timing and finalize DSI packet sequence configuration.
556
reg_write_array((vu32 *)DSI_BASE, _di_dsi_seq_pkt_video_non_burst_no_eot_config, ARRAY_SIZE(_di_dsi_seq_pkt_video_non_burst_no_eot_config));
557
558
// Set 1-by-1 pixel/clock and pixel clock to 234 / 3 = 78 MHz. For 60 Hz refresh rate.
559
DISPLAY_A(DC_DISP_DISP_CLOCK_CONTROL) = PIXEL_CLK_DIVIDER_PCD1 | SHIFT_CLK_DIVIDER(4); // div3. Default: div4.
560
561
// Set DSI mode to HOST.
562
reg_write_array((vu32 *)DSI_BASE, _di_dsi_host_mode_config, ARRAY_SIZE(_di_dsi_host_mode_config));
563
usleep(10000);
564
565
/*
566
* Calibrate display communication pads.
567
* When switching to the 16ff pad brick, the clock lane termination control
568
* is separated from data lane termination. This change of the mipi cal
569
* brings in a bug that the DSI pad clock termination code can't be loaded
570
* in one time calibration on T210B01. Trigger calibration twice.
571
*/
572
reg_write_array((vu32 *)MIPI_CAL_BASE, _di_mipi_pad_cal_config, ARRAY_SIZE(_di_mipi_pad_cal_config));
573
for (u32 i = 0; i < 2; i++)
574
{
575
// Set MIPI bias pad config.
576
MIPI_CAL(MIPI_CAL_MIPI_BIAS_PAD_CFG2) = 0x10010;
577
MIPI_CAL(MIPI_CAL_MIPI_BIAS_PAD_CFG1) = tegra_t210 ? 0x300 : 0;
578
579
// Set pad trimmers and set MIPI DSI cal offsets.
580
if (tegra_t210)
581
{
582
reg_write_array((vu32 *)DSI_BASE, _di_dsi_pad_cal_config_t210, ARRAY_SIZE(_di_dsi_pad_cal_config_t210));
583
reg_write_array((vu32 *)MIPI_CAL_BASE, _di_mipi_dsi_cal_prod_config_t210, ARRAY_SIZE(_di_mipi_dsi_cal_prod_config_t210));
584
}
585
else
586
{
587
reg_write_array((vu32 *)DSI_BASE, _di_dsi_pad_cal_config_t210b01, ARRAY_SIZE(_di_dsi_pad_cal_config_t210b01));
588
reg_write_array((vu32 *)MIPI_CAL_BASE, _di_mipi_dsi_cal_prod_config_t210b01, ARRAY_SIZE(_di_mipi_dsi_cal_prod_config_t210b01));
589
}
590
591
// Reset all unused MIPI cal offsets.
592
reg_write_array((vu32 *)MIPI_CAL_BASE, _di_mipi_dsi_cal_unused_config, ARRAY_SIZE(_di_mipi_dsi_cal_unused_config));
593
594
// Set Prescale/filter and start calibration.
595
MIPI_CAL(MIPI_CAL_MIPI_CAL_CTRL) = 0x2A000001;
596
}
597
usleep(10000);
598
599
// Setup video mode.
600
reg_write_array((vu32 *)DISPLAY_A_BASE, _di_dc_video_mode_config, ARRAY_SIZE(_di_dc_video_mode_config));
601
}
602
603
void display_backlight_pwm_init()
604
{
605
if (_panel_id == PANEL_SAM_AMS699VC01)
606
return;
607
608
// Enable PWM clock.
609
clock_enable_pwm();
610
611
// Enable PWM and set it to 25KHz PFM. 29.5KHz is stock.
612
PWM(PWM_CONTROLLER_PWM_CSR_0) = PWM_CSR_EN;
613
614
PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = (PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) & ~PINMUX_FUNC_MASK) | 1; // Set PWM0 mode.
615
usleep(2);
616
617
gpio_config(GPIO_PORT_V, GPIO_PIN_0, GPIO_MODE_SPIO); // Backlight power mode.
618
}
619
620
void display_backlight(bool enable)
621
{
622
if (_panel_id == PANEL_SAM_AMS699VC01)
623
return;
624
625
// Backlight PWM GPIO.
626
gpio_write(GPIO_PORT_V, GPIO_PIN_0, enable ? GPIO_HIGH : GPIO_LOW);
627
}
628
629
static void _display_dsi_backlight_brightness(u32 duty)
630
{
631
if (DISPLAY_A(DC_DCS_BACKLIGHT_LEVEL) == duty)
632
return;
633
634
// Convert duty to candela.
635
u32 candela = duty * PANEL_SM_BL_CANDELA_MAX / 255;
636
637
u16 bl_ctrl = byte_swap_16((u16)candela);
638
display_dsi_vblank_write(MIPI_DCS_SET_BRIGHTNESS, 2, &bl_ctrl);
639
640
// Wait for backlight to completely turn off. 6 frames.
641
if (!duty)
642
usleep(100000);
643
644
DISPLAY_A(DC_DCS_BACKLIGHT_LEVEL) = duty;
645
}
646
647
static void _display_pwm_backlight_brightness(u32 duty, u32 step_delay)
648
{
649
u32 old_value = (PWM(PWM_CONTROLLER_PWM_CSR_0) >> 16) & 0xFF;
650
if (duty == old_value)
651
return;
652
653
if (old_value < duty)
654
{
655
for (u32 i = old_value; i <= duty; i++)
656
{
657
PWM(PWM_CONTROLLER_PWM_CSR_0) = PWM_CSR_EN | (i << 16);
658
usleep(step_delay);
659
}
660
}
661
else
662
{
663
for (int i = old_value; i >= (int)duty; i--)
664
{
665
PWM(PWM_CONTROLLER_PWM_CSR_0) = PWM_CSR_EN | (i << 16);
666
usleep(step_delay);
667
}
668
}
669
}
670
671
void display_backlight_brightness(u32 brightness, u32 step_delay)
672
{
673
if (brightness > 255)
674
brightness = 255;
675
676
if (_panel_id != PANEL_SAM_AMS699VC01)
677
_display_pwm_backlight_brightness(brightness, step_delay);
678
else
679
_display_dsi_backlight_brightness(brightness);
680
}
681
682
static void _display_panel_and_hw_end(bool no_panel_deinit)
683
{
684
if (no_panel_deinit)
685
goto skip_panel_deinit;
686
687
display_backlight_brightness(0, 1000);
688
689
// Enable host cmd packets during video.
690
DSI(DSI_VIDEO_MODE_CONTROL) = DSI_CMD_PKT_VID_ENABLE;
691
692
// Blank display.
693
DSI(DSI_WR_DATA) = (MIPI_DCS_SET_DISPLAY_OFF << 8) | MIPI_DSI_DCS_SHORT_WRITE;
694
695
// Wait for 5 frames (HOST1X_CH0_SYNC_SYNCPT_9).
696
// Not here. Wait for 1 frame + transmission manually.
697
usleep((_panel_id == PANEL_SAM_AMS699VC01) ? (15933 + 195) : (16666 + 230));
698
699
// Propagate changes to all register buffers and disable host cmd packets during video.
700
DISPLAY_A(DC_CMD_STATE_ACCESS) = READ_MUX_ACTIVE | WRITE_MUX_ACTIVE;
701
DSI(DSI_VIDEO_MODE_CONTROL) = 0;
702
703
// De-initialize video controller.
704
reg_write_array((vu32 *)DISPLAY_A_BASE, _di_dc_video_disable_config, ARRAY_SIZE(_di_dc_video_disable_config));
705
706
// Set DISP1 clock source, parent clock and DSI/PCLK to command mode.
707
// T210: DIVM: 1, DIVN: 20, DIVP: 3. PLLD_OUT: 100.0 MHz, PLLD_OUT0 (DSI-BCLK): 50.0 MHz. (PCLK: 16.66 MHz)
708
// T210B01: DIVM: 1, DIVN: 20, DIVP: 3. PLLD_OUT: 97.8 MHz, PLLD_OUT0 (DSI-BCLK): 48.9 MHz. (PCLK: 16.30 MHz)
709
clock_enable_plld(3, 20, true, hw_get_chip_id() == GP_HIDREV_MAJOR_T210);
710
711
// DSI soft reset.
712
_display_dsi_soft_reset();
713
714
// Set DSI LP timings.
715
reg_write_array((vu32 *)DSI_BASE, _di_dsi_timing_lp_config, ARRAY_SIZE(_di_dsi_timing_lp_config));
716
717
if (_panel_id != PANEL_SAM_AMS699VC01)
718
usleep(10000);
719
720
// De-initialize display panel.
721
switch (_panel_id)
722
{
723
case PANEL_JDI_XXX062M:
724
reg_write_array((vu32 *)DSI_BASE, _di_dsi_panel_deinit_config_jdi, ARRAY_SIZE(_di_dsi_panel_deinit_config_jdi));
725
break;
726
727
case PANEL_AUO_A062TAN01:
728
reg_write_array((vu32 *)DSI_BASE, _di_dsi_panel_deinit_config_auo, ARRAY_SIZE(_di_dsi_panel_deinit_config_auo));
729
usleep(5000);
730
break;
731
732
case PANEL_INL_2J055IA_27A:
733
case PANEL_AUO_A055TAN01:
734
case PANEL_SHP_LQ055T1SW10:
735
// Unlock extension cmds.
736
DSI(DSI_WR_DATA) = 0x439; // MIPI_DSI_DCS_LONG_WRITE: 4 bytes.
737
DSI(DSI_WR_DATA) = 0x9483FFB9; // MIPI_DCS_PRIV_SET_EXTC. (Pass: FF 83 94).
738
DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
739
usleep(5000);
740
741
// Set Power control.
742
DSI(DSI_WR_DATA) = 0xB39; // MIPI_DSI_DCS_LONG_WRITE: 11 bytes.
743
if (_panel_id == PANEL_INL_2J055IA_27A)
744
DSI(DSI_WR_DATA) = 0x751548B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT5 / XDK, VRH gamma volt adj 53 / x40).
745
else if (_panel_id == PANEL_AUO_A055TAN01)
746
DSI(DSI_WR_DATA) = 0x711148B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT1 / XDK, VRH gamma volt adj 49 / x40).
747
else // PANEL_SHP_LQ055T1SW10.
748
DSI(DSI_WR_DATA) = 0x731348B1; // MIPI_DCS_PRIV_SET_POWER_CONTROL. (Not deep standby, BT3 / XDK, VRH gamma volt adj 51 / x40).
749
if (_panel_id == PANEL_INL_2J055IA_27A || _panel_id == PANEL_AUO_A055TAN01)
750
{
751
// (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/32, Enter standby / PON / VCOMG).
752
DSI(DSI_WR_DATA) = 0x71143209;
753
DSI(DSI_WR_DATA) = 0x114D31; // (Unknown).
754
}
755
else // PANEL_SHP_LQ055T1SW10.
756
{
757
// (NVRH gamma volt adj 9, Amplifier current small / x30, FS0 freq Fosc/80 / FS1 freq Fosc/48, Enter standby / PON / VCOMG).
758
DSI(DSI_WR_DATA) = 0x71243209;
759
DSI(DSI_WR_DATA) = 0x004C31; // (Unknown).
760
}
761
DSI(DSI_TRIGGER) = DSI_TRIGGER_HOST;
762
usleep(5000);
763
break;
764
765
case PANEL_INL_P062CCA_AZ1:
766
case PANEL_SAM_AMS699VC01:
767
default:
768
break;
769
}
770
771
// Blank - powerdown.
772
_display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, MIPI_DCS_ENTER_SLEEP_MODE,
773
(_panel_id == PANEL_SAM_AMS699VC01) ? 120000 : 50000);
774
775
skip_panel_deinit:
776
// Disable Panel Reset.
777
gpio_write(GPIO_PORT_V, GPIO_PIN_2, GPIO_LOW);
778
usleep(10000);
779
780
if (!_nx_aula) // HOS uses panel id.
781
{
782
// Disable LCD driver AVDD channels.
783
gpio_write(GPIO_PORT_I, GPIO_PIN_0 | GPIO_PIN_1, GPIO_LOW);
784
785
// Make sure LCD driver PWM pin is in PWM0 mode.
786
gpio_config(GPIO_PORT_V, GPIO_PIN_0, GPIO_MODE_SPIO); // Backlight PWM.
787
PINMUX_AUX(PINMUX_AUX_LCD_BL_PWM) = PINMUX_TRISTATE | PINMUX_PULL_DOWN | 1; // Set PWM0 mode.
788
}
789
usleep(10000);
790
791
// Disable Display Interface specific clocks.
792
CLOCK(CLK_RST_CONTROLLER_RST_DEV_H_SET) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
793
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_H_CLR) = BIT(CLK_H_MIPI_CAL) | BIT(CLK_H_DSI);
794
CLOCK(CLK_RST_CONTROLLER_RST_DEV_L_SET) = BIT(CLK_L_DISP1);
795
CLOCK(CLK_RST_CONTROLLER_CLK_ENB_L_CLR) = BIT(CLK_L_DISP1);
796
797
// Power down.
798
DSI(DSI_PAD_CONTROL_0) = DSI_PAD_CONTROL_VS1_PULLDN_CLK | DSI_PAD_CONTROL_VS1_PULLDN(0xF) |
799
DSI_PAD_CONTROL_VS1_PDIO_CLK | DSI_PAD_CONTROL_VS1_PDIO(0xF);
800
_display_dsi_soft_reset();
801
802
// Disable DSI AVDD.
803
max7762x_regulator_enable(REGULATOR_LDO0, false);
804
}
805
806
void display_end() { _display_panel_and_hw_end(false); };
807
808
u32 display_get_verbose_panel_id()
809
{
810
return _panel_id_raw;
811
}
812
813
u16 display_get_decoded_panel_id()
814
{
815
return _panel_id;
816
}
817
818
void display_set_decoded_panel_id(u32 id)
819
{
820
// Get Hardware type, as it's used in various DI functions.
821
_nx_aula = fuse_read_hw_type() == FUSE_NX_HW_TYPE_AULA;
822
823
// Decode Display ID.
824
_panel_id = ((id >> 8) & 0xFF00) | (id & 0xFF);
825
826
if ((_panel_id & 0xFF) == PANEL_JDI_XXX062M)
827
_panel_id = PANEL_JDI_XXX062M;
828
829
// For Aula ensure that we have a compatible panel id.
830
if (_nx_aula && _panel_id == 0xCCCC)
831
_panel_id = PANEL_SAM_AMS699VC01;
832
}
833
834
void display_color_screen(u32 color)
835
{
836
// Disable all windows.
837
reg_write_array((vu32 *)DISPLAY_A_BASE, _di_win_one_color, ARRAY_SIZE(_di_win_one_color));
838
839
// Configure display to show single color.
840
DISPLAY_A(DC_DISP_BLEND_BACKGROUND_COLOR) = color;
841
842
// Arm and activate changes.
843
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_A_UPDATE | WIN_B_UPDATE | WIN_C_UPDATE | WIN_D_UPDATE;
844
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_A_ACT_REQ | WIN_B_ACT_REQ | WIN_C_ACT_REQ | WIN_D_ACT_REQ;
845
usleep(35000); // Wait 2 frames. No need on Aula.
846
847
if (_panel_id != PANEL_SAM_AMS699VC01)
848
display_backlight(true);
849
else
850
display_backlight_brightness(150, 0);
851
}
852
853
u32 *display_init_window_a_pitch()
854
{
855
// Sanitize framebuffer area.
856
memset((u32 *)IPL_FB_ADDRESS, 0, IPL_FB_SZ);
857
858
// This configures the framebuffer @ IPL_FB_ADDRESS with a resolution of 720x1280 (line stride 720).
859
reg_write_array((vu32 *)DISPLAY_A_BASE, _di_winA_pitch, ARRAY_SIZE(_di_winA_pitch));
860
//usleep(35000); // Wait 2 frames. No need on Aula.
861
862
return (u32 *)DISPLAY_A(DC_WINBUF_START_ADDR);
863
}
864
865
u32 *display_init_window_a_pitch_vic()
866
{
867
// This configures the framebuffer @ NYX_FB_ADDRESS with a resolution of 720x1280 (line stride 720).
868
if (_panel_id != PANEL_SAM_AMS699VC01)
869
usleep(8000); // Wait half frame for PWM to apply.
870
reg_write_array((vu32 *)DISPLAY_A_BASE, _di_winA_pitch_vic, ARRAY_SIZE(_di_winA_pitch_vic));
871
if (_panel_id != PANEL_SAM_AMS699VC01)
872
usleep(35000); // Wait 2 frames.
873
874
return (u32 *)DISPLAY_A(DC_WINBUF_START_ADDR);
875
}
876
877
u32 *display_init_window_a_pitch_inv()
878
{
879
// This configures the framebuffer @ NYX_FB_ADDRESS with a resolution of 720x1280 (line stride 720).
880
reg_write_array((vu32 *)DISPLAY_A_BASE, _di_winA_pitch_inv, ARRAY_SIZE(_di_winA_pitch_inv));
881
usleep(35000); // Wait 2 frames. No need on Aula.
882
883
return (u32 *)DISPLAY_A(DC_WINBUF_START_ADDR);
884
}
885
886
u32 *display_init_window_a_block()
887
{
888
// This configures the framebuffer @ NYX_FB_ADDRESS with a resolution of 720x1280.
889
reg_write_array((vu32 *)DISPLAY_A_BASE, _di_winA_block, ARRAY_SIZE(_di_winA_block));
890
usleep(35000); // Wait 2 frames. No need on Aula.
891
892
return (u32 *)DISPLAY_A(DC_WINBUF_START_ADDR);
893
}
894
895
u32 *display_init_window_d_console()
896
{
897
// This configures the framebuffer @ LOG_FB_ADDRESS with a resolution of 1280x720 (line stride 720).
898
reg_write_array((vu32 *)DISPLAY_A_BASE, _di_winD_log, ARRAY_SIZE(_di_winD_log));
899
900
return (u32 *)DISPLAY_A(DC_WINBUF_START_ADDR);
901
}
902
903
void display_window_disable(u32 window)
904
{
905
// Select window C.
906
DISPLAY_A(DC_CMD_DISPLAY_WINDOW_HEADER) = BIT(WINDOW_SELECT + window);
907
908
// Disable window C.
909
DISPLAY_A(DC_WIN_WIN_OPTIONS) = 0;
910
911
// Arm and activate changes.
912
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | BIT(WIN_UPDATE + window);
913
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | BIT(WIN_ACT_REQ + window);
914
}
915
916
void display_set_framebuffer(u32 window, void *fb)
917
{
918
// Select window.
919
DISPLAY_A(DC_CMD_DISPLAY_WINDOW_HEADER) = BIT(WINDOW_SELECT + window);
920
921
// Set new fb address.
922
DISPLAY_A(DC_WINBUF_START_ADDR) = (u32)fb;
923
924
// Arm and activate changes.
925
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | BIT(WIN_UPDATE + window);
926
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | BIT(WIN_ACT_REQ + window);
927
}
928
929
void display_move_framebuffer(u32 window, void *fb)
930
{
931
// Select window.
932
DISPLAY_A(DC_CMD_DISPLAY_WINDOW_HEADER) = BIT(WINDOW_SELECT + window);
933
934
// Get current framebuffer address.
935
const void *fb_curr = (void *)DISPLAY_A(DC_WINBUF_START_ADDR);
936
u32 win_size = DISPLAY_A(DC_WIN_PRESCALED_SIZE);
937
win_size = (win_size & 0x7FFF) * ((win_size >> 16) & 0x1FFF);
938
939
// Copy fb over.
940
memcpy(fb, fb_curr, win_size);
941
942
// Set new fb address.
943
DISPLAY_A(DC_WINBUF_START_ADDR) = (u32)fb;
944
945
// Arm and activate changes.
946
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | BIT(WIN_UPDATE + window);
947
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | BIT(WIN_ACT_REQ + window);
948
}
949
950
void display_window_d_console_enable()
951
{
952
// Only update active registers on vsync.
953
DISPLAY_A(DC_CMD_REG_ACT_CONTROL) = DISPLAY_A(DC_CMD_REG_ACT_CONTROL) & ~WIN_D_ACT_HCNTR_SEL;
954
955
// Select window D.
956
DISPLAY_A(DC_CMD_DISPLAY_WINDOW_HEADER) = WINDOW_D_SELECT;
957
958
// Enable and setup window D.
959
DISPLAY_A(DC_WIN_WIN_OPTIONS) = WIN_ENABLE;
960
DISPLAY_A(DC_WIN_POSITION) = 0xFF80; // X: -128.
961
962
// Arm and activate changes.
963
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_D_UPDATE;
964
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
965
966
// Pull-down effect.
967
for (u32 i = 0xFF80; i < 0x10000; i++)
968
{
969
// Set window position.
970
DISPLAY_A(DC_WIN_POSITION) = i & 0xFFFF;
971
972
// Arm and activate changes.
973
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_D_UPDATE;
974
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
975
usleep(1000);
976
}
977
978
DISPLAY_A(DC_WIN_POSITION) = 0;
979
980
// Arm and activate changes.
981
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_D_UPDATE;
982
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
983
}
984
985
void display_window_d_console_disable()
986
{
987
// Select window D.
988
DISPLAY_A(DC_CMD_DISPLAY_WINDOW_HEADER) = WINDOW_D_SELECT;
989
990
// Pull-up effect.
991
for (u32 i = 0xFFFF; i > 0xFF7F; i--)
992
{
993
// Set window position.
994
DISPLAY_A(DC_WIN_POSITION) = i & 0xFFFF;
995
996
// Arm and activate changes.
997
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_D_UPDATE;
998
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
999
usleep(500);
1000
}
1001
1002
// Disable window D.
1003
DISPLAY_A(DC_WIN_POSITION) = 0;
1004
DISPLAY_A(DC_WIN_WIN_OPTIONS) = 0;
1005
1006
// Arm and activate changes.
1007
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | WIN_D_UPDATE;
1008
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | WIN_D_ACT_REQ;
1009
}
1010
1011
void display_cursor_init(void *crs_fb, u32 size)
1012
{
1013
// Setup cursor.
1014
DISPLAY_A(DC_DISP_CURSOR_START_ADDR) = CURSOR_CLIPPING(CURSOR_CLIP_WIN_A) | size | ((u32)crs_fb >> 10);
1015
DISPLAY_A(DC_DISP_BLEND_CURSOR_CONTROL) = CURSOR_BLEND_R8G8B8A8 |
1016
CURSOR_BLEND_DST_FACTOR(CURSOR_BLEND_K1) |
1017
CURSOR_BLEND_SRC_FACTOR(CURSOR_BLEND_K1) | 0xFF;
1018
1019
// Enable cursor window.
1020
DISPLAY_A(DC_DISP_DISP_WIN_OPTIONS) |= CURSOR_ENABLE;
1021
1022
// Arm and activate changes.
1023
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | CURSOR_UPDATE;
1024
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | CURSOR_ACT_REQ;
1025
}
1026
1027
void display_cursor_set_pos(u32 x, u32 y)
1028
{
1029
// Set cursor position.
1030
DISPLAY_A(DC_DISP_CURSOR_POSITION) = x | (y << 16);
1031
1032
// Arm and activate changes.
1033
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | CURSOR_UPDATE;
1034
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | CURSOR_ACT_REQ;
1035
}
1036
1037
void display_cursor_deinit()
1038
{
1039
DISPLAY_A(DC_DISP_BLEND_CURSOR_CONTROL) = 0;
1040
DISPLAY_A(DC_DISP_DISP_WIN_OPTIONS) &= ~CURSOR_ENABLE;
1041
1042
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_UPDATE | CURSOR_UPDATE;
1043
DISPLAY_A(DC_CMD_STATE_CONTROL) = GENERAL_ACT_REQ | CURSOR_ACT_REQ;
1044
}
1045
1046