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