| /* SPDX-License-Identifier: GPL-2.0-only */ |
| |
| #include <mipi/dsi.h> |
| #include <mipi/panel.h> |
| #include <device/mmio.h> |
| #include <console/console.h> |
| #include <assert.h> |
| #include <edid.h> |
| #include <delay.h> |
| #include <symbols.h> |
| #include <types.h> |
| #include <string.h> |
| #include <soc/display/mipi_dsi.h> |
| #include <soc/display/mdssreg.h> |
| #include <soc/display/dsi_phy.h> |
| |
| #define DSI_DMA_STREAM1 0x0 |
| #define DSI_EMBED_MODE1 0x1 |
| #define DSI_POWER_MODE2 0x1 |
| #define DSI_PACK_TYPE1 0x0 |
| #define DSI_VC1 0x0 |
| #define DSI_DT1 0x0 |
| #define DSI_WC1 0x0 |
| #define DSI_EOF_BLLP_PWR 0x9 |
| #define DSI_DMA_TRIGGER_SEL 0x4 |
| #define TRAFFIC_MODE 0x1 |
| |
| #define DSI_EN 0x1 |
| #define DSI_CLKLN_EN 0x1 |
| #define DSI_VIDEO_EN 0x1 |
| |
| #define HS_TX_TO 0xEA60 |
| #define TIMER_RESOLUTION 0x4 |
| #define DSI_PAYLOAD_BYTE_BOUND 256 |
| #define DSI_PAYLOAD_SIZE_ALIGN 4 |
| #define DSI_CMD_DMA_TPG_EN BIT(1) |
| #define DSI_TPG_DMA_FIFO_MODE BIT(2) |
| #define DSI_CMD_DMA_PATTERN_SEL (BIT(16) | BIT(17)) |
| |
| static void mdss_dsi_host_init(int num_of_lanes) |
| { |
| uint8_t dlnx_en; |
| uint32_t ctrl_mode = BIT(8) | BIT(0); /* Enable DSI and CLKlane. */ |
| |
| switch (num_of_lanes) { |
| default: |
| case 1: |
| dlnx_en = 1; |
| break; |
| |
| case 2: |
| dlnx_en = 3; |
| break; |
| |
| case 3: |
| dlnx_en = 7; |
| break; |
| |
| case 4: |
| dlnx_en = 0x0F; |
| break; |
| } |
| |
| /* |
| * Need to send pixel data before sending the ON commands |
| * so need to configure controller to VIDEO MODE. |
| */ |
| ctrl_mode |= BIT(1); |
| |
| mdss_dsi_clock_config(); |
| write32(&dsi0->trig_ctrl, DSI_DMA_STREAM1 << 8 | DSI_DMA_TRIGGER_SEL); |
| write32(&dsi0->ctrl, dlnx_en << 4 | ctrl_mode); |
| write32(&dsi0->cmd_mode_dma_ctrl, |
| DSI_EMBED_MODE1 << 28 | DSI_POWER_MODE2 << 26 | |
| DSI_PACK_TYPE1 << 24 | DSI_VC1 << 22 | DSI_DT1 << 16 | DSI_WC1); |
| write32(&dsi0->eot_packet_ctrl, 0x1); |
| } |
| |
| static void mdss_dsi_reset(void) |
| { |
| /* |
| * Disable DSI Controller, DSI lane states, |
| * DSI command-mode and DSI video-mode engines |
| */ |
| write32(&dsi0->ctrl, 0x0); |
| |
| /* DSI soft reset */ |
| write32(&dsi0->soft_reset, 0x1); |
| write32(&dsi0->soft_reset, 0x0); |
| |
| /* set hs timer count speed */ |
| write32(&dsi0->hs_timer_ctrl, HS_TX_TO | TIMER_RESOLUTION << 16); |
| |
| /* dma fifo reset */ |
| write32(&dsi0->tpg_dma_fifo_reset, 0x1); |
| write32(&dsi0->tpg_dma_fifo_reset, 0x0); |
| } |
| |
| void mdss_dsi_video_mode_config(struct edid *edid, uint32_t bpp) |
| { |
| uint16_t dst_format; |
| uint8_t lane_en = 15; /* Enable 4 lanes by default */ |
| |
| switch (bpp) { |
| case 16: |
| dst_format = DSI_VIDEO_DST_FORMAT_RGB565; |
| break; |
| case 18: |
| dst_format = DSI_VIDEO_DST_FORMAT_RGB666; |
| break; |
| case 24: |
| default: |
| dst_format = DSI_VIDEO_DST_FORMAT_RGB888; |
| break; |
| } |
| |
| write32(&dsi0->video_mode_active_h, |
| ((edid->mode.ha + edid->mode.hbl - edid->mode.hso) << 16) | |
| (edid->mode.hbl - edid->mode.hso)); |
| |
| write32(&dsi0->video_mode_active_v, |
| ((edid->mode.va + edid->mode.vbl - edid->mode.vso) << 16) | |
| (edid->mode.vbl - edid->mode.vso)); |
| |
| write32(&dsi0->video_mode_active_total, |
| ((edid->mode.va + edid->mode.vbl - 1) << 16) | |
| (edid->mode.ha + edid->mode.hbl - 1)); |
| |
| write32(&dsi0->video_mode_active_hsync, (edid->mode.hspw << 16)); |
| write32(&dsi0->video_mode_active_vsync, 0x0); |
| write32(&dsi0->video_mode_active_vsync_vpos, edid->mode.vspw << 16); |
| |
| write32(&dsi0->video_mode_ctrl, |
| DSI_EOF_BLLP_PWR << 12 | dst_format << 4 | TRAFFIC_MODE << 8); |
| |
| write32(&dsi0->hs_timer_ctrl, HS_TX_TO | TIMER_RESOLUTION << 16); |
| |
| write32(&dsi0->ctrl, lane_en << 4 | DSI_VIDEO_EN << 1 | DSI_EN | DSI_CLKLN_EN << 8); |
| } |
| |
| enum cb_err mdss_dsi_config(struct edid *edid, uint32_t num_of_lanes, uint32_t bpp) |
| { |
| mdss_dsi_reset(); |
| if ((mdss_dsi_phy_10nm_init(edid, num_of_lanes, bpp)) != 0) { |
| printk(BIOS_ERR, "dsi phy setup returned error\n"); |
| return CB_ERR; |
| } |
| |
| mdss_dsi_host_init(num_of_lanes); |
| |
| return CB_SUCCESS; |
| } |
| |
| void mdss_dsi_clock_config(void) |
| { |
| /* Clock for AHI Bus Master, for DMA out from memory */ |
| write32(&dsi0->clk_ctrl, 0); |
| setbits32(&dsi0->clk_ctrl, DSI_AHBM_SCLK_ON | DSI_FORCE_ON_DYN_AHBM_HCLK); |
| |
| /* Clock for MDP/DSI, for DMA out from MDP */ |
| setbits32(&dsi0->clk_ctrl, DSI_PCLK_ON); |
| |
| /* Clock for rest of DSI */ |
| setbits32(&dsi0->clk_ctrl, DSI_AHBS_HCLK_ON | DSI_DSICLK_ON | |
| DSI_BYTECLK_ON | DSI_ESCCLK_ON); |
| } |
| |
| static void mdss_dsi_set_intr(void) |
| { |
| write32(&dsi0->int_ctrl, 0x0); |
| |
| /* Enable all HW interrupts. */ |
| setbits32(&dsi0->int_ctrl, DSI_CMD_MODE_DMA_DONE_MASK | DSI_CMD_MODE_MDP_DONE_MASK | |
| DSI_VIDEO_MODE_DONE_MASK | DSI_ERROR_MASK | DSI_BTA_DONE_MASK); |
| } |
| |
| static int mdss_dsi_cmd_dma_trigger_for_panel(void) |
| { |
| uint32_t read_value; |
| uint32_t count = 0; |
| int status = 0; |
| |
| mdss_dsi_set_intr(); |
| write32(&dsi0->cmd_mode_dma_sw_trigger, 0x1); |
| dsb(); |
| |
| read_value = read32(&dsi0->int_ctrl) & 0x1; |
| |
| while (read_value != 0x1) { |
| read_value = read32(&dsi0->int_ctrl) & 0x1; |
| count++; |
| if (count > 0xffff) { |
| status = -1; |
| printk(BIOS_ERR, |
| "Panel CMD: count :%d command mode dma test failed\n", count); |
| printk(BIOS_ERR, |
| "Panel CMD: read value = %x, addr=%p\n", |
| read_value, (&dsi0->int_ctrl)); |
| return status; |
| } |
| } |
| |
| write32(&dsi0->int_ctrl, (read32(&dsi0->int_ctrl) | 0x01000001)); |
| return status; |
| } |
| |
| static enum cb_err mdss_dsi_send_init_cmd(enum mipi_dsi_transaction type, const u8 *body, |
| u8 len) |
| { |
| uint8_t *pload = _dma_coherent; |
| uint32_t size; |
| enum cb_err ret = CB_SUCCESS; |
| int data = 0; |
| uint32_t *bp = NULL; |
| |
| if (len > 2) { |
| pload[0] = len; |
| pload[1] = 0; |
| pload[2] = type; |
| pload[3] = BIT(7) | BIT(6); |
| |
| /* The payload size has to be a multiple of 4 */ |
| memcpy(pload + 4, body, len); |
| size = ALIGN_UP(len + 4, DSI_PAYLOAD_SIZE_ALIGN); |
| memset(pload + 4 + len, 0, size - 4 - len); |
| assert(size < DSI_PAYLOAD_BYTE_BOUND); |
| } else { |
| pload[0] = body[0]; |
| pload[1] = len > 1 ? body[1] : 0; |
| pload[2] = type; |
| pload[3] = BIT(7); |
| size = 4; |
| } |
| |
| bp = (uint32_t *)pload; |
| |
| /* Enable custom pattern stored in TPG DMA FIFO */ |
| data = DSI_CMD_DMA_PATTERN_SEL; |
| |
| /* select CMD_DMA_FIFO_MODE to 1 */ |
| data |= DSI_TPG_DMA_FIFO_MODE; |
| data |= DSI_CMD_DMA_TPG_EN; |
| |
| write32(&dsi0->test_pattern_gen_ctrl, data); |
| for (int j = 0; j < size; j += 4) { |
| write32(&dsi0->test_pattern_gen_cmd_dma_init_val, *bp); |
| bp++; |
| } |
| |
| if ((size % 8) != 0) |
| write32(&dsi0->test_pattern_gen_cmd_dma_init_val, 0x0); |
| |
| write32(&dsi0->dma_cmd_length, size); |
| write32(&dsi0->cmd_mode_dma_sw_trigger, 0x1); |
| if (mdss_dsi_cmd_dma_trigger_for_panel()) |
| ret = CB_ERR; |
| |
| /* Reset the DMA TPG FIFO */ |
| write32(&dsi0->tpg_dma_fifo_reset, 0x1); |
| write32(&dsi0->tpg_dma_fifo_reset, 0x0); |
| |
| /* Disable CMD_DMA_TPG */ |
| write32(&dsi0->test_pattern_gen_ctrl, 0x0); |
| |
| udelay(80); |
| |
| return ret; |
| } |
| |
| static void mdss_dsi_clear_intr(void) |
| { |
| write32(&dsi0->int_ctrl, 0x0); |
| |
| /* Clear all the hardware interrupts */ |
| setbits32(&dsi0->int_ctrl, DSI_CMD_MODE_DMA_DONE_AK | DSI_CMD_MODE_MDP_DONE_AK | |
| DSI_VIDEO_MODE_DONE_AK | DSI_BTA_DONE_AK | DSI_ERROR_AK); |
| write32(&dsi0->err_int_mask0, 0x13FF3BFF); |
| } |
| |
| enum cb_err mdss_dsi_panel_initialize(const u8 *init_cmds) |
| { |
| uint32_t ctrl_mode = 0; |
| |
| assert(init_cmds != NULL); |
| ctrl_mode = read32(&dsi0->ctrl); |
| |
| /* Enable command mode before sending the commands */ |
| write32(&dsi0->ctrl, ctrl_mode | 0x04); |
| |
| enum cb_err ret = mipi_panel_parse_init_commands(init_cmds, mdss_dsi_send_init_cmd); |
| write32(&dsi0->ctrl, ctrl_mode); |
| mdss_dsi_clear_intr(); |
| |
| return ret; |
| } |