From aaaf470dcf6696849f57fccdc2a4ff6c3fdf3b70 Mon Sep 17 00:00:00 2001 From: CTCaer Date: Wed, 2 Dec 2020 02:09:49 +0200 Subject: [PATCH] display: Provide dsi command reading/writing to user These work while video stream is either disabled or enabled. --- bdk/gfx/di.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++- bdk/gfx/di.h | 163 +++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 318 insertions(+), 10 deletions(-) diff --git a/bdk/gfx/di.c b/bdk/gfx/di.c index 4fab323..9d95349 100644 --- a/bdk/gfx/di.c +++ b/bdk/gfx/di.c @@ -54,6 +54,164 @@ static void _display_dsi_send_cmd(u8 cmd, u32 param, u32 wait) usleep(wait); } +static void _display_dsi_read_rx_fifo(u32 *data) +{ + u32 fifo_count = DSI(_DSIREG(DSI_STATUS)) & DSI_STATUS_RX_FIFO_SIZE; + for (u32 i = 0; i < fifo_count; i++) + { + // Read or Drain RX FIFO. + if (data) + data[i] = DSI(_DSIREG(DSI_RD_DATA)); + else + (void)DSI(_DSIREG(DSI_RD_DATA)); + } +} + +int display_dsi_read(u8 cmd, u32 len, void *data, bool video_enabled) +{ + int res = 0; + u32 host_control = 0; + u32 cmd_timeout = video_enabled ? 0 : 250000; + u32 fifo[DSI_STATUS_RX_FIFO_SIZE] = {0}; + + // Drain RX FIFO. + _display_dsi_read_rx_fifo(NULL); + + // Save host control and enable host cmd packets during video. + if (video_enabled) + { + host_control = DSI(_DSIREG(DSI_HOST_CONTROL)); + + // Enable vblank interrupt. + DISPLAY_A(_DIREG(DC_CMD_INT_ENABLE)) = DC_CMD_INT_FRAME_END_INT; + + // Use the 4th line to transmit the host cmd packet. + DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = DSI_CMD_PKT_VID_ENABLE | DSI_DSI_LINE_TYPE(4); + + // Wait for vblank before starting the transfer. + DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) = DC_CMD_INT_FRAME_END_INT; // Clear interrupt. + while (DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) & DC_CMD_INT_FRAME_END_INT) + ; + } + + // Set reply size. + _display_dsi_send_cmd(MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, len, 0); + _display_dsi_wait(cmd_timeout, _DSIREG(DSI_TRIGGER), DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO); + + // Request register read. + _display_dsi_send_cmd(MIPI_DSI_DCS_READ, cmd, 0); + _display_dsi_wait(cmd_timeout, _DSIREG(DSI_TRIGGER), DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO); + + // Transfer bus control to device for transmitting the reply. + u32 high_speed = video_enabled ? DSI_HOST_CONTROL_HS : 0; + DSI(_DSIREG(DSI_HOST_CONTROL)) = DSI_HOST_CONTROL_TX_TRIG_HOST | DSI_HOST_CONTROL_IMM_BTA | DSI_HOST_CONTROL_CS | DSI_HOST_CONTROL_ECC | high_speed; + _display_dsi_wait(150000, _DSIREG(DSI_HOST_CONTROL), DSI_HOST_CONTROL_IMM_BTA); + + // Wait a bit for the reply. + usleep(5000); + + // Read RX FIFO. + _display_dsi_read_rx_fifo(fifo); + + // Parse packet and copy over the data. + if ((fifo[0] & 0xFF) == DSI_ESCAPE_CMD) + { + // Act based on reply type. + switch (fifo[1] & 0xFF) + { + case GEN_LONG_RD_RES: + case DCS_LONG_RD_RES: + memcpy(data, &fifo[2], MIN((fifo[1] >> 8) & 0xFFFF, len)); + break; + + case GEN_1_BYTE_SHORT_RD_RES: + case DCS_1_BYTE_SHORT_RD_RES: + memcpy(data, &fifo[2], 1); + break; + + case GEN_2_BYTE_SHORT_RD_RES: + case DCS_2_BYTE_SHORT_RD_RES: + memcpy(data, &fifo[2], 2); + break; + case ACK_ERROR_RES: + default: + res = 1; + break; + } + } + + // Disable host cmd packets during video and restore host control. + if (video_enabled) + { + // Wait for vblank before reseting sync points. + DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) = DC_CMD_INT_FRAME_END_INT; // Clear interrupt. + while (DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) & DC_CMD_INT_FRAME_END_INT) + ; + + // Reset all states of syncpt block. + DSI(_DSIREG(DSI_INCR_SYNCPT_CNTRL)) = DSI_INCR_SYNCPT_SOFT_RESET; + usleep(300); // Stabilization delay. + + // Clear syncpt block reset. + DSI(_DSIREG(DSI_INCR_SYNCPT_CNTRL)) = 0; + usleep(300); // Stabilization delay. + + // Restore video mode and host control. + DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = 0; + DSI(_DSIREG(DSI_HOST_CONTROL)) = host_control; + + // Disable and clear vblank interrupt. + DISPLAY_A(_DIREG(DC_CMD_INT_ENABLE)) = 0; + DISPLAY_A(_DIREG(DC_CMD_INT_STATUS)) = DC_CMD_INT_FRAME_END_INT; + } + + return res; +} + +void display_dsi_write(u8 cmd, u32 len, void *data, bool video_enabled) +{ + u32 host_control; + u32 fifo32[DSI_STATUS_RX_FIFO_SIZE] = {0}; + u8 *fifo8 = (u8 *)fifo32; + + // Enable host cmd packets during video and save host control. + if (video_enabled) + DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = DSI_CMD_PKT_VID_ENABLE; + host_control = DSI(_DSIREG(DSI_HOST_CONTROL)); + + // Enable host transfer trigger. + DSI(_DSIREG(DSI_HOST_CONTROL)) |= DSI_HOST_CONTROL_TX_TRIG_HOST; + + switch (len) + { + case 0: + _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE, cmd, 0); + break; + + case 1: + _display_dsi_send_cmd(MIPI_DSI_DCS_SHORT_WRITE_PARAM, cmd | (*(u8 *)data << 8), 0); + break; + + default: + fifo32[0] = (len << 8) | MIPI_DSI_DCS_LONG_WRITE; + fifo8[4] = cmd; + memcpy(&fifo8[5], data, len); + len += 4 + 1; // Increase length by CMD/length word and DCS CMD. + for (u32 i = 0; i < (ALIGN(len, 4) / 4); i++) + DSI(_DSIREG(DSI_WR_DATA)) = fifo32[i]; + DSI(_DSIREG(DSI_TRIGGER)) = DSI_TRIGGER_HOST; + break; + } + + // Wait for the write to happen. + _display_dsi_wait(250000, _DSIREG(DSI_TRIGGER), DSI_TRIGGER_HOST); + + // Disable host cmd packets during video and restore host control. + if (video_enabled) + DSI(_DSIREG(DSI_VIDEO_MODE_CONTROL)) = 0; + DSI(_DSIREG(DSI_HOST_CONTROL)) = host_control; +} + void display_init() { // Check if display is already initialized. @@ -175,6 +333,11 @@ void display_init() // Setup DSI device takeover timeout. DSI(_DSIREG(DSI_BTA_TIMING)) = 0x50204; +#if 0 + // Get Display ID. + _display_id = 0xCCCCCC; + display_dsi_read(MIPI_DCS_GET_DISPLAY_ID, 3, &_display_id, DSI_VIDEO_DISABLED); +#else // Set reply size. _display_dsi_send_cmd(MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, 3, 0); _display_dsi_wait(250000, _DSIREG(DSI_TRIGGER), DSI_TRIGGER_HOST | DSI_TRIGGER_VIDEO); @@ -193,7 +356,7 @@ void display_init() // MIPI_DCS_GET_DISPLAY_ID reply is a long read, size 3 x u32. for (u32 i = 0; i < 3; i++) _display_id = DSI(_DSIREG(DSI_RD_DATA)) & 0xFFFFFF; // Skip ack and msg type info and get the payload (display id). - +#endif // Save raw Display ID to Nyx storage. nyx_str->info.disp_id = _display_id; diff --git a/bdk/gfx/di.h b/bdk/gfx/di.h index dd3b550..e304fb6 100644 --- a/bdk/gfx/di.h +++ b/bdk/gfx/di.h @@ -21,6 +21,9 @@ #include #include +#define DSI_VIDEO_DISABLED 0 +#define DSI_VIDEO_ENABLED 1 + /*! Display registers. */ #define _DIREG(reg) ((reg) * 4) @@ -68,6 +71,7 @@ #define DC_CMD_INT_STATUS 0x37 #define DC_CMD_INT_MASK 0x38 #define DC_CMD_INT_ENABLE 0x39 +#define DC_CMD_INT_FRAME_END_INT BIT(1) #define DC_CMD_STATE_ACCESS 0x40 #define READ_MUX BIT(0) @@ -360,6 +364,10 @@ /*! Display serial interface registers. */ #define _DSIREG(reg) ((reg) * 4) +#define DSI_INCR_SYNCPT_CNTRL 0x1 +#define DSI_INCR_SYNCPT_SOFT_RESET BIT(0) +#define DSI_INCR_SYNCPT_NO_STALL BIT(8) + #define DSI_RD_DATA 0x9 #define DSI_WR_DATA 0xA @@ -403,7 +411,10 @@ #define DSI_TRIGGER_HOST BIT(1) #define DSI_TX_CRC 0x14 + #define DSI_STATUS 0x15 +#define DSI_STATUS_RX_FIFO_SIZE 0x1F + #define DSI_INIT_SEQ_CONTROL 0x1A #define DSI_INIT_SEQ_DATA_0 0x1B #define DSI_INIT_SEQ_DATA_1 0x1C @@ -450,6 +461,7 @@ #define DSI_PAD_CONTROL_CD 0x4C #define DSI_VIDEO_MODE_CONTROL 0x4E #define DSI_CMD_PKT_VID_ENABLE 1 +#define DSI_DSI_LINE_TYPE(x) ((x) << 1) #define DSI_PAD_CONTROL_1 0x4F #define DSI_PAD_CONTROL_2 0x50 @@ -467,6 +479,18 @@ #define DSI_INIT_SEQ_DATA_15 0x5F #define DSI_INIT_SEQ_DATA_15_B01 0x62 +/*! DSI packet defines */ +#define DSI_ESCAPE_CMD 0x87 +#define DSI_ACK_NO_ERR 0x84 + +#define ACK_ERROR_RES 0x02 +#define GEN_LONG_RD_RES 0x1A +#define DCS_LONG_RD_RES 0x1C +#define GEN_1_BYTE_SHORT_RD_RES 0x11 +#define DCS_1_BYTE_SHORT_RD_RES 0x21 +#define GEN_2_BYTE_SHORT_RD_RES 0x12 +#define DCS_2_BYTE_SHORT_RD_RES 0x22 + /*! MIPI registers. */ #define MIPI_CAL_MIPI_CAL_CTRL (0x00 / 0x4) #define MIPI_CAL_CIL_MIPI_CAL_STATUS (0x08 / 0x4) @@ -489,18 +513,136 @@ #define MIPI_CAL_DSID_MIPI_CAL_CONFIG_2 (0x74 / 0x4) /*! MIPI CMDs. */ -#define MIPI_DSI_DCS_SHORT_WRITE 0x05 -#define MIPI_DSI_DCS_READ 0x06 -#define MIPI_DSI_DCS_SHORT_WRITE_PARAM 0x15 +#define MIPI_DSI_V_SYNC_START 0x01 +#define MIPI_DSI_COLOR_MODE_OFF 0x02 +#define MIPI_DSI_END_OF_TRANSMISSION 0x08 +#define MIPI_DSI_NULL_PACKET 0x09 +#define MIPI_DSI_V_SYNC_END 0x11 +#define MIPI_DSI_COLOR_MODE_ON 0x12 +#define MIPI_DSI_BLANKING_PACKET 0x19 +#define MIPI_DSI_H_SYNC_START 0x21 +#define MIPI_DSI_SHUTDOWN_PERIPHERAL 0x22 +#define MIPI_DSI_H_SYNC_END 0x31 +#define MIPI_DSI_TURN_ON_PERIPHERAL 0x32 #define MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE 0x37 -#define MIPI_DSI_DCS_LONG_WRITE 0x39 + +#define MIPI_DSI_DCS_SHORT_WRITE 0x05 +#define MIPI_DSI_DCS_READ 0x06 +#define MIPI_DSI_DCS_SHORT_WRITE_PARAM 0x15 +#define MIPI_DSI_DCS_LONG_WRITE 0x39 + +#define MIPI_DSI_GENERIC_LONG_WRITE 0x29 +#define MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM 0x03 +#define MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM 0x13 +#define MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM 0x23 +#define MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM 0x04 +#define MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM 0x14 +#define MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM 0x24 /*! MIPI DCS CMDs. */ -#define MIPI_DCS_GET_DISPLAY_ID 0x04 -#define MIPI_DCS_ENTER_SLEEP_MODE 0x10 -#define MIPI_DCS_EXIT_SLEEP_MODE 0x11 -#define MIPI_DCS_SET_DISPLAY_OFF 0x28 -#define MIPI_DCS_SET_DISPLAY_ON 0x29 +#define MIPI_DCS_NOP 0x00 +#define MIPI_DCS_SOFT_RESET 0x01 +#define MIPI_DCS_GET_COMPRESSION_MODE 0x03 +#define MIPI_DCS_GET_DISPLAY_ID 0x04 +#define MIPI_DCS_GET_DISPLAY_ID1 0xDA // GET_DISPLAY_ID Byte0, Module Manufacturer ID. +#define MIPI_DCS_GET_DISPLAY_ID2 0xDB // GET_DISPLAY_ID Byte1, Module/Driver Version ID. +#define MIPI_DCS_GET_DISPLAY_ID3 0xDC // GET_DISPLAY_ID Byte2, Module/Driver ID. +#define MIPI_DCS_GET_NUM_ERRORS 0x05 +#define MIPI_DCS_GET_RED_CHANNEL 0x06 +#define MIPI_DCS_GET_GREEN_CHANNEL 0x07 +#define MIPI_DCS_GET_BLUE_CHANNEL 0x08 +#define MIPI_DCS_GET_DISPLAY_STATUS 0x09 +#define MIPI_DCS_GET_POWER_MODE 0x0A +#define MIPI_DCS_GET_ADDRESS_MODE 0x0B +#define MIPI_DCS_GET_PIXEL_FORMAT 0x0C +#define MIPI_DCS_GET_DISPLAY_MODE 0x0D +#define MIPI_DCS_GET_SIGNAL_MODE 0x0E +#define MIPI_DCS_GET_DIAGNOSTIC_RESULT 0x0F +#define MIPI_DCS_ENTER_SLEEP_MODE 0x10 +#define MIPI_DCS_EXIT_SLEEP_MODE 0x11 +#define MIPI_DCS_ENTER_PARTIAL_MODE 0x12 +#define MIPI_DCS_ENTER_NORMAL_MODE 0x13 +#define MIPI_DCS_EXIT_INVERT_MODE 0x20 +#define MIPI_DCS_ENTER_INVERT_MODE 0x21 +#define MIPI_DCS_ALL_PIXELS_OFF 0x22 +#define MIPI_DCS_ALL_PIXELS_ON 0x23 +#define MIPI_DCS_SET_CONTRAST 0x25 // VCON in 40mV steps. 7-bit integer. +#define MIPI_DCS_SET_GAMMA_CURVE 0x26 +#define MIPI_DCS_SET_DISPLAY_OFF 0x28 +#define MIPI_DCS_SET_DISPLAY_ON 0x29 +#define MIPI_DCS_SET_COLUMN_ADDRESS 0x2A +#define MIPI_DCS_SET_PAGE_ADDRESS 0x2B +#define MIPI_DCS_WRITE_MEMORY_START 0x2C +#define MIPI_DCS_WRITE_LUT 0x2D // 24-bit: 192 bytes. +#define MIPI_DCS_READ_MEMORY_START 0x2E +#define MIPI_DCS_SET_PARTIAL_ROWS 0x30 +#define MIPI_DCS_SET_PARTIAL_COLUMNS 0x31 +#define MIPI_DCS_SET_SCROLL_AREA 0x33 +#define MIPI_DCS_SET_TEAR_OFF 0x34 +#define MIPI_DCS_SET_TEAR_ON 0x35 +#define MIPI_DCS_SET_ADDRESS_MODE 0x36 +#define MIPI_DCS_SET_SCROLL_START 0x37 +#define MIPI_DCS_EXIT_IDLE_MODE 0x38 +#define MIPI_DCS_ENTER_IDLE_MODE 0x39 +#define MIPI_DCS_SET_PIXEL_FORMAT 0x3A +#define MIPI_DCS_WRITE_MEMORY_CONTINUE 0x3C +#define MIPI_DCS_READ_MEMORY_CONTINUE 0x3E +#define MIPI_DCS_GET_3D_CONTROL 0x3F +#define MIPI_DCS_SET_VSYNC_TIMING 0x40 +#define MIPI_DCS_SET_TEAR_SCANLINE 0x44 +#define MIPI_DCS_GET_SCANLINE 0x45 +#define MIPI_DCS_SET_TEAR_SCANLINE_WIDTH 0x46 +#define MIPI_DCS_GET_SCANLINE_WIDTH 0x47 +#define MIPI_DCS_SET_BRIGHTNESS 0x51 // DCS_CONTROL_DISPLAY_BRIGHTNESS_CTRL. +#define MIPI_DCS_GET_BRIGHTNESS 0x52 +#define MIPI_DCS_SET_CONTROL_DISPLAY 0x53 +#define MIPI_DCS_GET_CONTROL_DISPLAY 0x54 +#define MIPI_DCS_SET_CABC_VALUE 0x55 +#define MIPI_DCS_GET_CABC_VALUE 0x56 +#define MIPI_DCS_SET_CABC_MIN_BRI 0x5E +#define MIPI_DCS_GET_CABC_MIN_BRI 0x5F +#define MIPI_DCS_READ_DDB_START 0xA1 +#define MIPI_DCS_READ_DDB_CONTINUE 0xA8 + +/*! MIPI DCS Panel Private CMDs. */ +#define MIPI_DCS_PRIV_UNK_A0 0xA0 +#define MIPI_DCS_PRIV_SET_POWER_CONTROL 0xB1 +#define MIPI_DCS_PRIV_SET_EXTC 0xB9 +#define MIPI_DCS_PRIV_UNK_BD 0xBD +#define MIPI_DCS_PRIV_UNK_D5 0xD5 +#define MIPI_DCS_PRIV_UNK_D6 0xD6 +#define MIPI_DCS_PRIV_UNK_D8 0xD8 +#define MIPI_DCS_PRIV_UNK_D9 0xD9 + +/*! MIPI DCS CMD Defines. */ +#define DCS_POWER_MODE_DISPLAY_ON BIT(2) +#define DCS_POWER_MODE_NORMAL_MODE BIT(3) +#define DCS_POWER_MODE_SLEEP_MODE BIT(4) +#define DCS_POWER_MODE_PARTIAL_MODE BIT(5) +#define DCS_POWER_MODE_IDLE_MODE BIT(6) + +#define DCS_ADDRESS_MODE_V_FLIP BIT(0) +#define DCS_ADDRESS_MODE_H_FLIP BIT(1) +#define DCS_ADDRESS_MODE_LATCH_RL BIT(2) // Latch Data Order. +#define DCS_ADDRESS_MODE_BGR_COLOR BIT(3) +#define DCS_ADDRESS_MODE_LINE_ORDER BIT(4) // Line Refresh Order. +#define DCS_ADDRESS_MODE_SWAP_XY BIT(5) // Page/Column Addressing Reverse Order. +#define DCS_ADDRESS_MODE_MIRROR_X BIT(6) // Column Address Order. +#define DCS_ADDRESS_MODE_MIRROR_Y BIT(7) // Page Address Order. +#define DCS_ADDRESS_MODE_ROTATION_MASK (0xF << 4) +#define DCS_ADDRESS_MODE_ROTATION_90 (DCS_ADDRESS_MODE_SWAP_XY | DCS_ADDRESS_MODE_LINE_ORDER) +#define DCS_ADDRESS_MODE_ROTATION_180 (DCS_ADDRESS_MODE_MIRROR_X | DCS_ADDRESS_MODE_LINE_ORDER) +#define DCS_ADDRESS_MODE_ROTATION_270 (DCS_ADDRESS_MODE_SWAP_XY) + +#define DCS_GAMMA_CURVE_NONE 0 +#define DCS_GAMMA_CURVE_GC0_1_8 BIT(0) +#define DCS_GAMMA_CURVE_GC1_2_5 BIT(1) +#define DCS_GAMMA_CURVE_GC2_1_0 BIT(2) +#define DCS_GAMMA_CURVE_GC3_1_0 BIT(3) // Are there more? + +#define DCS_CONTROL_DISPLAY_BACKLIGHT_CTRL BIT(2) +#define DCS_CONTROL_DISPLAY_DIMMING_CTRL BIT(3) +#define DCS_CONTROL_DISPLAY_BRIGHTNESS_CTRL BIT(5) /* Switch Panels: * @@ -572,4 +714,7 @@ void display_init_cursor(void *crs_fb, u32 size); void display_set_pos_cursor(u32 x, u32 y); void display_deinit_cursor(); +void display_dsi_write(u8 cmd, u32 len, void *data, bool video_enabled); +int display_dsi_read(u8 cmd, u32 len, void *data, bool video_enabled); + #endif