From 060d18f0704ed31f74f413f921cdbe3ffcf078e2 Mon Sep 17 00:00:00 2001 From: Payne Lin Date: Mon, 17 Nov 2025 21:02:57 +0800 Subject: [PATCH] soc/mediatek/mt8196: Add DSI dual channel Add support for DSI dual channel and dual Display Stream Compression (DSC) features. - Add DSI dual channel and dual DSC feature. - Add dsi1, mipi1, dsc0, dsc1 engine drivers. - Add configuration for dual channel feature selection. - Add 'dsi.c' for mt8196 mipi data rate calculation. - Add 'mtk_mipi_dphy.c' for mt8196 timing calculation. BUG=b:424782827 TEST=Build pass, boot ok. Verify display output on the following platforms: - 8196 Navi: eDP path. - 8189 Skywalker: eDP path. - 8189 Padme: single MIPI path (without DSC). - 8196 Sapphire: dual MIPI path (with DSC). Change-Id: I2bea829da72d23165a74b399eabfcdd55a7f28a1 Signed-off-by: Payne Lin Reviewed-on: https://review.coreboot.org/c/coreboot/+/90504 Tested-by: build bot (Jenkins) Reviewed-by: Yu-Ping Wu Reviewed-by: Chen-Tsung Hsieh Reviewed-by: Yidi Lin --- src/soc/mediatek/common/Kconfig | 6 + src/soc/mediatek/common/dsi_common.c | 332 ++++++++++++------ .../mediatek/common/include/soc/dsi_common.h | 1 + src/soc/mediatek/mt8196/Kconfig | 1 + src/soc/mediatek/mt8196/Makefile.mk | 2 + src/soc/mediatek/mt8196/ddp.c | 280 ++++++++++++++- src/soc/mediatek/mt8196/dsi.c | 88 +++++ src/soc/mediatek/mt8196/include/soc/ddp.h | 52 ++- src/soc/mediatek/mt8196/mtk_mipi_dphy.c | 29 ++ 9 files changed, 655 insertions(+), 136 deletions(-) create mode 100644 src/soc/mediatek/mt8196/dsi.c create mode 100644 src/soc/mediatek/mt8196/mtk_mipi_dphy.c diff --git a/src/soc/mediatek/common/Kconfig b/src/soc/mediatek/common/Kconfig index 30cab53df6..c52f8ed407 100644 --- a/src/soc/mediatek/common/Kconfig +++ b/src/soc/mediatek/common/Kconfig @@ -69,6 +69,12 @@ config MEDIATEK_WDT_RESET_BY_SW If the kernel disables WDT external reset (mediatek,disable-extrst), then this option must be disabled to allow WDT hardware external reset. +config MEDIATEK_DSI_DUAL_CHANNEL + bool + default n + help + The configuration to support DSI dual channel. + config MEMORY_TEST bool default y diff --git a/src/soc/mediatek/common/dsi_common.c b/src/soc/mediatek/common/dsi_common.c index 2298aa0ace..008817ade9 100644 --- a/src/soc/mediatek/common/dsi_common.c +++ b/src/soc/mediatek/common/dsi_common.c @@ -16,6 +16,25 @@ #define CPHY_SYMBOL_RATE 7 #define CPHY_SYMBOL_RATE_DIVISOR 16 +#define COMPRESSION_RATIO 3 +#define UNCOMPRESSED_RATIO 1 + +static const struct { + struct dsi_regs *const dsi_reg; + struct mipi_tx_regs *const mipi_reg; +} dsi_mipi_regs[] = { + { + .dsi_reg = dsi0, + .mipi_reg = mipi_tx0, + }, +#if CONFIG(MEDIATEK_DSI_DUAL_CHANNEL) + { + .dsi_reg = dsi1, + .mipi_reg = mipi_tx1, + }, +#endif +}; + static unsigned int mtk_dsi_get_bits_per_pixel(u32 format) { switch (format) { @@ -33,7 +52,7 @@ static unsigned int mtk_dsi_get_bits_per_pixel(u32 format) } static u32 mtk_dsi_get_data_rate(u32 bits_per_pixel, u32 lanes, - const struct edid *edid, bool is_cphy) + const struct edid *edid, u32 mode_flags) { /* data_rate = pixel_clock * bits_per_pixel * mipi_ratio / lanes * Note pixel_clock comes in kHz and returned data_rate is in bps. @@ -41,9 +60,18 @@ static u32 mtk_dsi_get_data_rate(u32 bits_per_pixel, u32 lanes, * for older platforms which do not have complete implementation in HFP. * Newer platforms should just set that to 1.0 (100 / 100). */ + u64 pixel_clock = edid->mode.pixel_clock; u32 data_rate; - u64 dividend = (u64)edid->mode.pixel_clock * bits_per_pixel * 1000 * - MTK_DSI_MIPI_RATIO_NUMERATOR; + bool is_cphy = !!(mode_flags & MIPI_DSI_MODE_CPHY); + bool is_dsi_dual_channel = !!(mode_flags & MIPI_DSI_DUAL_CHANNEL); + bool is_dsc_enabled = !!(mode_flags & MIPI_DSI_DSC_MODE); + + if (is_dsc_enabled) + pixel_clock = pixel_clock * + (DIV_ROUND_UP(edid->mode.ha, 3) + edid->mode.hbl) / + (edid->mode.ha + edid->mode.hbl); + + u64 dividend = pixel_clock * bits_per_pixel * 1000 * MTK_DSI_MIPI_RATIO_NUMERATOR; u64 divisor = (u64)lanes * MTK_DSI_MIPI_RATIO_DENOMINATOR; if (is_cphy) { @@ -51,15 +79,20 @@ static u32 mtk_dsi_get_data_rate(u32 bits_per_pixel, u32 lanes, divisor *= CPHY_SYMBOL_RATE_DIVISOR; } data_rate = DIV_ROUND_UP(dividend, divisor); - printk(BIOS_INFO, "DSI data_rate: %u bps\n", data_rate); + printk(BIOS_INFO, "pixel_clock: %lld\n", pixel_clock); + printk(BIOS_INFO, "bits_per_pixel: %d\n", bits_per_pixel); + if (is_dsi_dual_channel) + data_rate = data_rate / 2; + + printk(BIOS_INFO, "DSI final data_rate: %u bps\n", data_rate); if (data_rate < MTK_DSI_DATA_RATE_MIN_MHZ * MHz) { printk(BIOS_ERR, "data rate (%ubps) must be >= %ubps. " - "Please check the pixel clock (%u), " + "Please check the pixel clock (%llu), " "bits per pixel (%u), " "mipi_ratio (%d%%) and number of lanes (%d)\n", data_rate, MTK_DSI_DATA_RATE_MIN_MHZ * MHz, - edid->mode.pixel_clock, bits_per_pixel, + pixel_clock, bits_per_pixel, (100 * MTK_DSI_MIPI_RATIO_NUMERATOR / MTK_DSI_MIPI_RATIO_DENOMINATOR), lanes); return 0; @@ -72,7 +105,8 @@ __weak void mtk_dsi_override_phy_timing(struct mtk_phy_timing *timing) /* Do nothing. */ } -static void mtk_dsi_dphy_timing(u32 data_rate, struct mtk_phy_timing *timing) +static void mtk_dsi_dphy_timing(struct dsi_regs *dsi_reg, u32 data_rate, + struct mtk_phy_timing *timing) { u32 timcon0, timcon1, timcon2, timcon3; u32 data_rate_mhz = DIV_ROUND_UP(data_rate, MHz); @@ -91,23 +125,23 @@ static void mtk_dsi_dphy_timing(u32 data_rate, struct mtk_phy_timing *timing) timcon3 = timing->clk_hs_prepare | timing->clk_hs_post << 8 | timing->clk_hs_exit << 16; - write32(&dsi0->dsi_phy_timecon0, timcon0); - write32(&dsi0->dsi_phy_timecon1, timcon1); - write32(&dsi0->dsi_phy_timecon2, timcon2); - write32(&dsi0->dsi_phy_timecon3, timcon3); + write32(&dsi_reg->dsi_phy_timecon0, timcon0); + write32(&dsi_reg->dsi_phy_timecon1, timcon1); + write32(&dsi_reg->dsi_phy_timecon2, timcon2); + write32(&dsi_reg->dsi_phy_timecon3, timcon3); } -static void mtk_dsi_clk_hs_mode_enable(void) +static void mtk_dsi_clk_hs_mode_enable(struct dsi_regs *dsi_reg) { - setbits32(&dsi0->dsi_phy_lccon, LC_HS_TX_EN); + setbits32(&dsi_reg->dsi_phy_lccon, LC_HS_TX_EN); } -static void mtk_dsi_clk_hs_mode_disable(void) +static void mtk_dsi_clk_hs_mode_disable(struct dsi_regs *dsi_reg) { - clrbits32(&dsi0->dsi_phy_lccon, LC_HS_TX_EN); + clrbits32(&dsi_reg->dsi_phy_lccon, LC_HS_TX_EN); } -static void mtk_dsi_set_mode(u32 mode_flags) +static void mtk_dsi_set_mode(struct dsi_regs *dsi_reg, u32 mode_flags) { u32 tmp_reg1 = 0; @@ -121,10 +155,10 @@ static void mtk_dsi_set_mode(u32 mode_flags) tmp_reg1 = SYNC_PULSE_MODE; } - write32(&dsi0->dsi_mode_ctrl, tmp_reg1); + write32(&dsi_reg->dsi_mode_ctrl, tmp_reg1); } -static void mtk_dsi_rxtx_control(u32 mode_flags, u32 lanes) +static void mtk_dsi_rxtx_control(struct dsi_regs *dsi_reg, u32 mode_flags, u32 lanes) { u32 tmp_reg = 0; @@ -150,7 +184,7 @@ static void mtk_dsi_rxtx_control(u32 mode_flags, u32 lanes) if (!(mode_flags & MIPI_DSI_MODE_EOT_PACKET)) tmp_reg |= EOTP_DISABLE; - write32(&dsi0->dsi_txrx_ctrl, tmp_reg); + write32(&dsi_reg->dsi_txrx_ctrl, tmp_reg); } static void mtk_dsi_dphy_vdo_timing(const u32 mode_flags, @@ -168,6 +202,9 @@ static void mtk_dsi_dphy_vdo_timing(const u32 mode_flags, phy_timing->da_hs_zero + phy_timing->da_hs_exit + 3; u32 delta = 10; + int channels = (mode_flags & MIPI_DSI_DUAL_CHANNEL) ? 2 : 1; + int dsc_ratio = (mode_flags & MIPI_DSI_DSC_MODE) ? COMPRESSION_RATIO : + UNCOMPRESSED_RATIO; if (mode_flags & MIPI_DSI_MODE_EOT_PACKET) delta += 2; @@ -188,17 +225,44 @@ static void mtk_dsi_dphy_vdo_timing(const u32 mode_flags, hbp * bytes_per_pixel, d_phy); } - *hsync_active_byte = edid->mode.hspw * bytes_per_pixel - 10; + *hsync_active_byte = edid->mode.hspw / channels * bytes_per_pixel - 10; + if (mode_flags & MIPI_DSI_MODE_LINE_END) { *hsync_active_byte = DIV_ROUND_UP(*hsync_active_byte, lanes) * lanes - 2; *hbp_byte = DIV_ROUND_UP(*hbp_byte, lanes) * lanes - 2; *hfp_byte = DIV_ROUND_UP(*hfp_byte, lanes) * lanes - 2; - *hbp_byte -= (edid->mode.ha * bytes_per_pixel + 2) % lanes; + *hbp_byte -= (edid->mode.ha / dsc_ratio / channels * bytes_per_pixel + 2) % + lanes; } } -static void mtk_dsi_config_vdo_timing(u32 mode_flags, u32 format, u32 lanes, - const struct edid *edid, +static u32 mtk_dsi_get_packet_fmt(u32 format) +{ + u32 packet_fmt; + + switch (format) { + case MIPI_DSI_FMT_RGB888: + packet_fmt = PACKED_PS_24BIT_RGB888; + break; + case MIPI_DSI_FMT_RGB666: + packet_fmt = LOOSELY_PS_18BIT_RGB666; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + packet_fmt = PACKED_PS_18BIT_RGB666; + break; + case MIPI_DSI_FMT_RGB565: + packet_fmt = PACKED_PS_16BIT_RGB565; + break; + default: + packet_fmt = PACKED_PS_24BIT_RGB888; + break; + } + + return packet_fmt; +} + +static void mtk_dsi_config_vdo_timing(struct dsi_regs *const dsi_reg, u32 mode_flags, + u32 format, u32 lanes, const struct edid *edid, const struct mtk_phy_timing *phy_timing) { u32 hsync_active_byte; @@ -211,26 +275,30 @@ static void mtk_dsi_config_vdo_timing(u32 mode_flags, u32 format, u32 lanes, u32 bytes_per_pixel; u32 packet_fmt; u32 hactive; + u32 hbp_offset; bool is_cphy = !!(mode_flags & MIPI_DSI_MODE_CPHY); + bool is_dsc_enabled = !!(mode_flags & MIPI_DSI_DSC_MODE); + int channels = !!(mode_flags & MIPI_DSI_DUAL_CHANNEL) ? 2 : 1; + int dsc_ratio = is_dsc_enabled ? COMPRESSION_RATIO : UNCOMPRESSED_RATIO; bytes_per_pixel = DIV_ROUND_UP(mtk_dsi_get_bits_per_pixel(format), 8); vbp_byte = edid->mode.vbl - edid->mode.vso - edid->mode.vspw - edid->mode.vborder; vfp_byte = edid->mode.vso - edid->mode.vborder; - write32(&dsi0->dsi_vsa_nl, edid->mode.vspw); - write32(&dsi0->dsi_vbp_nl, vbp_byte); - write32(&dsi0->dsi_vfp_nl, vfp_byte); - write32(&dsi0->dsi_vact_nl, edid->mode.va); + write32(&dsi_reg->dsi_vsa_nl, edid->mode.vspw); + write32(&dsi_reg->dsi_vbp_nl, vbp_byte); + write32(&dsi_reg->dsi_vfp_nl, vfp_byte); + write32(&dsi_reg->dsi_vact_nl, edid->mode.va); - hbp = edid->mode.hbl - edid->mode.hso - edid->mode.hspw - - edid->mode.hborder; - hfp = edid->mode.hso - edid->mode.hborder; + hbp = (edid->mode.hbl - edid->mode.hso - edid->mode.hspw - + edid->mode.hborder) / channels; + hfp = (edid->mode.hso - edid->mode.hborder) / channels; + + hbp_offset = (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) ? + 0 : (edid->mode.hspw / channels); + hbp_byte = (hbp + hbp_offset) * bytes_per_pixel - 10; - if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) - hbp_byte = hbp * bytes_per_pixel - 10; - else - hbp_byte = (hbp + edid->mode.hspw) * bytes_per_pixel - 10; hfp_byte = hfp * bytes_per_pixel; if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy) @@ -255,49 +323,40 @@ static void mtk_dsi_config_vdo_timing(u32 mode_flags, u32 format, u32 lanes, hbp_byte = MIN_HBP_BYTE; } - write32(&dsi0->dsi_hsa_wc, hsync_active_byte); - write32(&dsi0->dsi_hbp_wc, hbp_byte); - write32(&dsi0->dsi_hfp_wc, hfp_byte); + write32(&dsi_reg->dsi_hsa_wc, hsync_active_byte); + write32(&dsi_reg->dsi_hbp_wc, hbp_byte); + write32(&dsi_reg->dsi_hfp_wc, hfp_byte); - switch (format) { - case MIPI_DSI_FMT_RGB888: - packet_fmt = PACKED_PS_24BIT_RGB888; - break; - case MIPI_DSI_FMT_RGB666: - packet_fmt = LOOSELY_PS_18BIT_RGB666; - break; - case MIPI_DSI_FMT_RGB666_PACKED: - packet_fmt = PACKED_PS_18BIT_RGB666; - break; - case MIPI_DSI_FMT_RGB565: - packet_fmt = PACKED_PS_16BIT_RGB565; - break; - default: - packet_fmt = PACKED_PS_24BIT_RGB888; - break; - } + if (is_dsc_enabled) + packet_fmt = COMPRESSED_PIXEL_STREAM_V2; + else + packet_fmt = mtk_dsi_get_packet_fmt(format); - hactive = edid->mode.ha; + hactive = edid->mode.ha / dsc_ratio / channels; packet_fmt |= (hactive * bytes_per_pixel) & DSI_PS_WC; - write32(&dsi0->dsi_psctrl, + write32(&dsi_reg->dsi_psctrl, PIXEL_STREAM_CUSTOM_HEADER << DSI_PSCON_CUSTOM_HEADER_SHIFT | packet_fmt); /* Older systems like MT8173 do not support size_con. */ if (MTK_DSI_HAVE_SIZE_CON) - write32(&dsi0->dsi_size_con, + write32(&dsi_reg->dsi_size_con, edid->mode.va << DSI_SIZE_CON_HEIGHT_SHIFT | hactive << DSI_SIZE_CON_WIDTH_SHIFT); if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy) - mtk_dsi_cphy_enable_cmdq_6byte(dsi0); + mtk_dsi_cphy_enable_cmdq_6byte(dsi_reg); } -static void mtk_dsi_start(void) +static void mtk_dsi_dual_enable(struct dsi_regs *dsi_reg, bool enable) { - write32(&dsi0->dsi_start, 0); - /* Only start master DSI */ - write32(&dsi0->dsi_start, 1); + clrsetbits32(&dsi_reg->dsi_con_ctrl, DSI_DUAL, (enable ? DSI_DUAL : 0)); +} + +static void mtk_dsi_start(struct dsi_regs *dsi_reg) +{ + write32(&dsi_reg->dsi_start, 0); + write32(&dsi_reg->dsi_start, 1); } static bool mtk_dsi_is_read_command(enum mipi_dsi_transaction type) @@ -313,60 +372,77 @@ static bool mtk_dsi_is_read_command(enum mipi_dsi_transaction type) } } +static void mtk_dsi_enable_and_start(bool is_dsi_dual_channel) +{ + /* + * For dual channel synchronization, + * the secondary dsi1 must be dual-enabled before starting the primary dsi0. + */ + if (is_dsi_dual_channel) + mtk_dsi_dual_enable(dsi_mipi_regs[ARRAY_SIZE(dsi_mipi_regs) - 1].dsi_reg, true); + /* + * Only start primary dsi0 for single or dual channel mode. + * The secondary dsi1 is dual-enabled in mtk_dsi_dual_enable() + * and does not require a separate start. + * Starting only the primary dsi0 ensures correct synchronization + * with secondary dsi1 if there is. + */ + mtk_dsi_start(dsi_mipi_regs[0].dsi_reg); +} + static enum cb_err mtk_dsi_cmdq(enum mipi_dsi_transaction type, const u8 *data, u8 len, void *user_data) { const u8 *tx_buf = data; u32 config; - int i, j; + uint32_t *mode_flags = (uint32_t *)user_data; + bool is_dsi_dual_channel = (*mode_flags & MIPI_DSI_DUAL_CHANNEL); - if (!wait_ms(20, !(read32(&dsi0->dsi_intsta) & DSI_BUSY))) { - printk(BIOS_ERR, "%s: cannot get DSI ready for sending commands" - " after 20ms and the panel may not work properly.\n", - __func__); - return CB_ERR; - } - write32(&dsi0->dsi_intsta, 0); - - if (mtk_dsi_is_read_command(type)) - config = BTA; - else - config = (len > 2) ? LONG_PACKET : SHORT_PACKET; - - if (len <= 2) { - uint32_t val = (type << 8) | config; - for (i = 0; i < len; i++) - val |= tx_buf[i] << (i + 2) * 8; - write32(&dsi0->dsi_cmdq[0], val); - write32(&dsi0->dsi_cmdq_size, 1); - } else { - /* TODO(hungte) Replace by buffer_to_fifo32_prefix */ - write32(&dsi0->dsi_cmdq[0], (len << 16) | (type << 8) | config); - for (i = 0; i < len; i += 4) { - uint32_t val = 0; - for (j = 0; j < MIN(len - i, 4); j++) - val |= tx_buf[i + j] << j * 8; - write32(&dsi0->dsi_cmdq[i / 4 + 1], val); + for (unsigned int k = 0; k < ARRAY_SIZE(dsi_mipi_regs); k++) { + struct dsi_regs *dsi = dsi_mipi_regs[k].dsi_reg; + if (!wait_ms(20, !(read32(&dsi->dsi_intsta) & DSI_BUSY))) { + printk(BIOS_ERR, "%s: cannot get DSI-%d ready for sending commands" + " after 20ms and the panel may not work properly.\n", + __func__, k); + return CB_ERR; } - write32(&dsi0->dsi_cmdq_size, 1 + DIV_ROUND_UP(len, 4)); + write32(&dsi->dsi_intsta, 0); + + if (mtk_dsi_is_read_command(type)) + config = BTA; + else + config = (len > 2) ? LONG_PACKET : SHORT_PACKET; + + u32 prefix = config | type << 8; + int prefsz = 2; + if (len > 2) { + prefix |= len << 16; + prefsz += 2; + } + buffer_to_fifo32_prefix(tx_buf, prefix, prefsz, prefsz + len, &dsi->dsi_cmdq[0], + 4, 4); + write32(&dsi->dsi_cmdq_size, DIV_ROUND_UP(prefsz + len, 4)); + setbits32(&dsi->dsi_cmdq_size, CMDQ_SIZE_SEL); } - setbits32(&dsi0->dsi_cmdq_size, CMDQ_SIZE_SEL); - mtk_dsi_start(); + mtk_dsi_enable_and_start(is_dsi_dual_channel); - if (!wait_us(400, read32(&dsi0->dsi_intsta) & CMD_DONE_INT_FLAG)) { - printk(BIOS_ERR, "%s: failed sending DSI command, " - "panel may not work.\n", __func__); - return CB_ERR; + for (unsigned int k = 0; k < ARRAY_SIZE(dsi_mipi_regs); k++) { + struct dsi_regs *dsi = dsi_mipi_regs[k].dsi_reg; + if (!wait_us(400, read32(&dsi->dsi_intsta) & CMD_DONE_INT_FLAG)) { + printk(BIOS_ERR, "%s: failed sending DSI-%d command, " + "panel may not work.\n", __func__, k); + return CB_ERR; + } } return CB_SUCCESS; } -static void mtk_dsi_reset_phy(void) +static void mtk_dsi_reset_phy(struct dsi_regs *const dsi_reg) { - setbits32(&dsi0->dsi_con_ctrl, DPHY_RESET); - clrbits32(&dsi0->dsi_con_ctrl, DPHY_RESET); + setbits32(&dsi_reg->dsi_con_ctrl, DPHY_RESET); + clrbits32(&dsi_reg->dsi_con_ctrl, DPHY_RESET); } int mtk_dsi_init(u32 mode_flags, u32 format, u32 lanes, const struct edid *edid, @@ -375,6 +451,8 @@ int mtk_dsi_init(u32 mode_flags, u32 format, u32 lanes, const struct edid *edid, u32 data_rate; u32 bits_per_pixel = mtk_dsi_get_bits_per_pixel(format); bool is_cphy = !!(mode_flags & MIPI_DSI_MODE_CPHY); + bool is_dsi_dual_channel = !!(mode_flags & MIPI_DSI_DUAL_CHANNEL); + unsigned int num_dsi = is_dsi_dual_channel ? 2 : 1; if (!CONFIG(MEDIATEK_DSI_CPHY) && is_cphy) { printk(BIOS_ERR, "%s: Board is built without C-PHY interface support. " @@ -382,27 +460,47 @@ int mtk_dsi_init(u32 mode_flags, u32 format, u32 lanes, const struct edid *edid, return -1; } - data_rate = mtk_dsi_get_data_rate(bits_per_pixel, lanes, edid, is_cphy); + if (num_dsi > ARRAY_SIZE(dsi_mipi_regs)) { + printk(BIOS_ERR, "%s: num_dsi %d > %lu\n", __func__, + num_dsi, ARRAY_SIZE(dsi_mipi_regs)); + return -1; + } + + data_rate = mtk_dsi_get_data_rate(bits_per_pixel, lanes, edid, mode_flags); if (!data_rate) return -1; - mtk_dsi_configure_mipi_tx(mipi_tx0, data_rate, lanes, is_cphy); - mtk_dsi_reset(dsi0); - struct mtk_phy_timing phy_timing = {}; - if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy) - mtk_dsi_cphy_timing(data_rate, &phy_timing); - else - mtk_dsi_dphy_timing(data_rate, &phy_timing); - mtk_dsi_rxtx_control(mode_flags, lanes); - mdelay(1); - mtk_dsi_reset_phy(); - mtk_dsi_clk_hs_mode_disable(); - mtk_dsi_config_vdo_timing(mode_flags, format, lanes, edid, &phy_timing); - mtk_dsi_clk_hs_mode_enable(); + for (unsigned int i = 0; i < num_dsi; i++) { + struct dsi_regs *dsi = dsi_mipi_regs[i].dsi_reg; + struct mipi_tx_regs *mipi = dsi_mipi_regs[i].mipi_reg; + if (!dsi || !mipi) { + printk(BIOS_ERR, "%s: Null dsi/mipi reg for DSI-%d\n", __func__, i); + return -1; + } + mtk_dsi_configure_mipi_tx(mipi, data_rate, lanes, is_cphy); + mtk_dsi_reset(dsi); + struct mtk_phy_timing phy_timing = {}; + if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy) + /* Dual channel is not implemented for CPHY. */ + mtk_dsi_cphy_timing(data_rate, &phy_timing); + else + mtk_dsi_dphy_timing(dsi, data_rate, &phy_timing); + + mtk_dsi_rxtx_control(dsi, mode_flags, lanes); + mdelay(1); + mtk_dsi_reset_phy(dsi); + mtk_dsi_clk_hs_mode_disable(dsi); + mtk_dsi_config_vdo_timing(dsi, mode_flags, format, lanes, edid, &phy_timing); + mtk_dsi_clk_hs_mode_enable(dsi); + } + if (init_commands) - mipi_panel_parse_init_commands(init_commands, mtk_dsi_cmdq, NULL); - mtk_dsi_set_mode(mode_flags); - mtk_dsi_start(); + mipi_panel_parse_init_commands(init_commands, mtk_dsi_cmdq, &mode_flags); + + for (unsigned int i = 0; i < num_dsi; i++) + mtk_dsi_set_mode(dsi_mipi_regs[i].dsi_reg, mode_flags); + + mtk_dsi_enable_and_start(is_dsi_dual_channel); return 0; } diff --git a/src/soc/mediatek/common/include/soc/dsi_common.h b/src/soc/mediatek/common/include/soc/dsi_common.h index e06701f490..57961d10bb 100644 --- a/src/soc/mediatek/common/include/soc/dsi_common.h +++ b/src/soc/mediatek/common/include/soc/dsi_common.h @@ -59,6 +59,7 @@ enum { LOOSELY_PS_18BIT_RGB666 = (1 << 16), PACKED_PS_18BIT_RGB666 = (2 << 16), PACKED_PS_24BIT_RGB888 = (3 << 16), + COMPRESSED_PIXEL_STREAM_V2 = (5 << 16), DSI_PSCON_CUSTOM_HEADER_SHIFT = 26, }; diff --git a/src/soc/mediatek/mt8196/Kconfig b/src/soc/mediatek/mt8196/Kconfig index 90c85007ef..c6eedf9341 100644 --- a/src/soc/mediatek/mt8196/Kconfig +++ b/src/soc/mediatek/mt8196/Kconfig @@ -18,6 +18,7 @@ config SOC_MEDIATEK_MT8196 select ARM64_USE_ARCH_TIMER select PCI select EARLY_MMU_INIT + select MEDIATEK_DSI_DUAL_CHANNEL if SOC_MEDIATEK_MT8196 diff --git a/src/soc/mediatek/mt8196/Makefile.mk b/src/soc/mediatek/mt8196/Makefile.mk index cc7ec0ad11..47f9592bc8 100644 --- a/src/soc/mediatek/mt8196/Makefile.mk +++ b/src/soc/mediatek/mt8196/Makefile.mk @@ -64,12 +64,14 @@ ramstage-y += ../common/dp/dp_intf_v2.c ramstage-y += ../common/dp/dptx_common.c ../common/dp/dptx_v2.c dptx.c ramstage-y += ../common/dp/dptx_hal_common.c ../common/dp/dptx_hal_v2.c dptx_hal.c ramstage-y += ../common/dramc_info.c +ramstage-y += ../common/dsi_common.c dsi.c ramstage-y += ../common/early_init.c ramstage-y += ../common/emi.c ramstage-y += gpueb.c ramstage-y += l2c_ops.c ramstage-y += ../common/mcu.c mcupm.c ramstage-y += ../common/mmu_operations.c +ramstage-y += ../common/mtk_mipi_dphy.c mtk_mipi_dphy.c ramstage-$(CONFIG_PCI) += ../common/pcie.c pcie.c ramstage-$(CONFIG_COMMONLIB_STORAGE_MMC) += msdc.c ramstage-y += ../common/mt6363.c mt6363.c diff --git a/src/soc/mediatek/mt8196/ddp.c b/src/soc/mediatek/mt8196/ddp.c index 380d52f171..ba317d4e79 100644 --- a/src/soc/mediatek/mt8196/ddp.c +++ b/src/soc/mediatek/mt8196/ddp.c @@ -11,6 +11,30 @@ #define SIZE(w, h) ((u32)(h) << 16 | (w)) #define DUAL_PIPE(path) ((path) == DISP_PATH_DUAL_MIPI) +#define DSC_EN BIT(0) +#define DSC_DUAL_INOUT BIT(2) +#define DSC_IN_SRC_SEL BIT(3) +#define DSC_BYPASS BIT(4) +#define DSC_RELAY BIT(5) +#define DSC_PT_MEM_EN BIT(7) +#define DSC_EMPTY_FLAG_SEL GENMASK(15, 14) +#define DSC_EMPTY_FLAG_ALWAYS_LOW BIT(15) +#define DSC_UFOE_SEL BIT(16) +#define DSC_ZERO_FIFO_STALL_DISABLE BIT(20) + +#define DSC_INTEN_SEL GENMASK(6, 0) +#define DSC_ZERO_FIFO BIT(2) + +#define DSC_INTACK_SEL GENMASK(6, 0) +#define DSC_INTACK_BUF_UNDERFLOW BIT(6) + +#define DSC_PIC_PREPAD_HEIGHT_SEL GENMASK(15, 0) +#define DSC_PIC_PREPAD_WIDTH_SEL GENMASK(31, 16) + +#define ALIGN_PADDING(V, N) (((N) - ((V) % (N))) % (N)) +/* Provide default value for x == 0. */ +#define DEF(x, default) ((x) == 0 ? (default) : (x)) + struct disp_pipe_regs { struct disp_mdp_rsz_regs *const mdp_rsz; struct disp_tdshp_regs *const tdshp; @@ -44,6 +68,245 @@ static const struct disp_pipe_regs disp_pipe1_regs = { .dsc = disp_dsc3_reg, }; +static void dsc_configure_registers(struct disp_dsc_regs *reg, u16 w, u16 h, + const struct dsc_config *dsc_cfg) +{ + u32 init_delay_limit, init_delay_height; + u32 pic_group_width, pic_height_ext_num; + u32 slice_group_width; + u32 pad_num; + u32 slice_mode; + u32 dsc_cfg_mode = 0x22; + u32 mask; + u32 val; + u32 rgb_swap = 0; + + if (dsc_cfg->bits_per_component == 0xA) + dsc_cfg_mode = 0x828; + + assert(dsc_cfg->pic_width > 0); + assert(dsc_cfg->pic_width >= dsc_cfg->slice_width); + assert(dsc_cfg->slice_width > 0); + slice_mode = dsc_cfg->pic_width / dsc_cfg->slice_width - 1; + pic_group_width = DIV_ROUND_UP(dsc_cfg->pic_width, 3); + pic_height_ext_num = DIV_ROUND_UP(h, dsc_cfg->slice_height); + slice_group_width = DIV_ROUND_UP(dsc_cfg->slice_width, 3); + pad_num = ALIGN_PADDING(dsc_cfg->slice_chunk_size * (slice_mode + 1), 3); + init_delay_limit = DIV_ROUND_UP(dsc_cfg->initial_xmit_delay, 3); + init_delay_limit = DIV_ROUND_UP((128 + init_delay_limit) * 3, dsc_cfg->slice_width); + init_delay_height = MIN(15, init_delay_limit); + + mask = DSC_EN | DSC_DUAL_INOUT | DSC_IN_SRC_SEL | DSC_BYPASS | DSC_RELAY | + DSC_PT_MEM_EN | DSC_EMPTY_FLAG_SEL | DSC_UFOE_SEL | + DSC_ZERO_FIFO_STALL_DISABLE; + val = DSC_PT_MEM_EN | DSC_EMPTY_FLAG_ALWAYS_LOW | DSC_UFOE_SEL | + DSC_ZERO_FIFO_STALL_DISABLE; + + clrsetbits32(®->dsc_con, mask, val); + clrsetbits32(®->dsc_inten, DSC_INTEN_SEL, 0x7F); + clrsetbits32(®->dsc_intack, DSC_INTACK_SEL, DSC_INTACK_BUF_UNDERFLOW); + + write32(®->dsc_spr, 0x0); + + val = w | (pic_group_width - 1) << 16; + write32(®->pic_w, val); + + val = (h - 1) | (pic_height_ext_num * dsc_cfg->slice_height - 1) << 16; + write32(®->pic_h, val); + + val = dsc_cfg->slice_width | (slice_group_width - 1) << 16; + write32(®->dsc_slice_w, val); + + val = (dsc_cfg->slice_height - 1) | (pic_height_ext_num - 1) << 16 | + (dsc_cfg->slice_width % 3) << 30; + write32(®->dsc_slice_h, val); + + val = dsc_cfg->slice_chunk_size | + (((dsc_cfg->slice_chunk_size << slice_mode) + 2) / 3) << 16; + write32(®->chunk_size, val); + + mask = GENMASK(23, 0); + val = dsc_cfg->slice_chunk_size * dsc_cfg->slice_height; + clrsetbits32(®->dsc_buf_size, mask, val); + + mask = BIT(0) | BIT(2) | GENMASK(11, 8); + val = slice_mode | rgb_swap << 2 | init_delay_height << 8; + clrsetbits32(®->dsc_mode, mask, val); + + write32(®->dsc_cfg, dsc_cfg_mode); + + mask = GENMASK(2, 0); + val = pad_num; + clrsetbits32(®->dsc_pad, mask, val); + + val = dsc_cfg->slice_width | dsc_cfg->pic_width << 16; + write32(®->dsc_enc_width, val); + + mask = DSC_PIC_PREPAD_HEIGHT_SEL | DSC_PIC_PREPAD_WIDTH_SEL; + val = h | w << 16; + clrsetbits32(®->dsc_pic_pre_pad_size, mask, val); + + setbits32(®->dsc_dbg_con, BIT(9)); + + write32(®->dsc_obuf, 0x410); + + DEFINE_BITFIELD(LINE_BUF_DEPTH, 3, 0) + DEFINE_BITFIELD(BITS_PER_COMPONENT, 7, 4) + DEFINE_BITFIELD(BITS_PER_PIXEL, 17, 8) + DEFINE_BIT(CONVERT_RGB, 18) + DEFINE_BIT(BLOCK_PRED_ENABLE, 19) + SET32_BITFIELDS(®->dsc_pps[0], + LINE_BUF_DEPTH, DEF(dsc_cfg->line_buf_depth, 0x9), + BITS_PER_COMPONENT, DEF(dsc_cfg->bits_per_component, 0x8), + BITS_PER_PIXEL, DEF(dsc_cfg->bits_per_pixel, 0x80), + CONVERT_RGB, DEF((u8)dsc_cfg->convert_rgb, 1), + BLOCK_PRED_ENABLE, DEF((u8)dsc_cfg->block_pred_enable, 0)); + + DEFINE_BITFIELD(INITIAL_DEC_DELAY, 31, 16) + DEFINE_BITFIELD(INITIAL_XMIT_DELAY, 15, 0) + WRITE32_BITFIELDS(®->dsc_pps[1], + INITIAL_DEC_DELAY, DEF(dsc_cfg->initial_dec_delay, 0x268), + INITIAL_XMIT_DELAY, DEF(dsc_cfg->initial_xmit_delay, 0x200)); + + DEFINE_BITFIELD(INITIAL_SCALE_VALUE, 15, 0) + DEFINE_BITFIELD(SCALE_INCREMENT_INTERVAL, 31, 16) + WRITE32_BITFIELDS(®->dsc_pps[2], + INITIAL_SCALE_VALUE, DEF(dsc_cfg->initial_scale_value, 0x20), + SCALE_INCREMENT_INTERVAL, + DEF(dsc_cfg->scale_increment_interval, 0x387)); + + DEFINE_BITFIELD(FIRST_LINE_BPG_OFFSET, 31, 16) + DEFINE_BITFIELD(SCALE_DECREMENT_INTERVAL, 15, 0) + WRITE32_BITFIELDS(®->dsc_pps[3], + FIRST_LINE_BPG_OFFSET, DEF(dsc_cfg->first_line_bpg_offset, 0xc), + SCALE_DECREMENT_INTERVAL, + DEF(dsc_cfg->scale_decrement_interval, 0xa)); + + DEFINE_BITFIELD(NFL_BPG_OFFSET, 15, 0) + DEFINE_BITFIELD(SLICE_BPG_OFFSET, 31, 16) + WRITE32_BITFIELDS(®->dsc_pps[4], + NFL_BPG_OFFSET, DEF(dsc_cfg->nfl_bpg_offset, 0x319), + SLICE_BPG_OFFSET, DEF(dsc_cfg->slice_bpg_offset, 0x263)); + + DEFINE_BITFIELD(INITIAL_OFFSET, 15, 0) + DEFINE_BITFIELD(FINAL_OFFSET, 31, 16) + WRITE32_BITFIELDS(®->dsc_pps[5], + INITIAL_OFFSET, DEF(dsc_cfg->initial_offset, 0x1800), + FINAL_OFFSET, DEF(dsc_cfg->final_offset, 0x10f0)); + + DEFINE_BITFIELD(FLATNESS_MIN_QP, 4, 0) + DEFINE_BITFIELD(FLATNESS_MAX_QP, 12, 8) + DEFINE_BITFIELD(RC_MODEL_SIZE, 31, 16) + SET32_BITFIELDS(®->dsc_pps[6], + FLATNESS_MIN_QP, DEF(dsc_cfg->flatness_min_qp, 0x3), + FLATNESS_MAX_QP, DEF(dsc_cfg->flatness_max_qp, 0xc), + RC_MODEL_SIZE, DEF(dsc_cfg->rc_model_size, 0x2000)); + + DEFINE_BITFIELD(RC_TGT_OFFSET_LOW, 31, 28) + DEFINE_BITFIELD(RC_TGT_OFFSET_HIGH, 27, 24) + DEFINE_BITFIELD(RC_QUANT_INCR_LIMIT1, 20, 16) + DEFINE_BITFIELD(RC_QUANT_INCR_LIMIT0, 12, 8) + DEFINE_BITFIELD(RC_EDGE_FACTOR, 7, 0) + WRITE32_BITFIELDS(®->dsc_pps[7], + RC_TGT_OFFSET_LOW, dsc_cfg->rc_tgt_offset_low, + RC_TGT_OFFSET_HIGH, dsc_cfg->rc_tgt_offset_high, + RC_QUANT_INCR_LIMIT1, dsc_cfg->rc_quant_incr_limit1, + RC_QUANT_INCR_LIMIT0, dsc_cfg->rc_quant_incr_limit0, + RC_EDGE_FACTOR, dsc_cfg->rc_edge_factor); + + DEFINE_BITFIELD(RC_BUF_THRESH_3, 31, 24) + DEFINE_BITFIELD(RC_BUF_THRESH_2, 23, 16) + DEFINE_BITFIELD(RC_BUF_THRESH_1, 15, 8) + DEFINE_BITFIELD(RC_BUF_THRESH_0, 7, 0) + WRITE32_BITFIELDS(®->dsc_pps[8], + RC_BUF_THRESH_3, dsc_cfg->rc_buf_thresh[3], + RC_BUF_THRESH_2, dsc_cfg->rc_buf_thresh[2], + RC_BUF_THRESH_1, dsc_cfg->rc_buf_thresh[1], + RC_BUF_THRESH_0, dsc_cfg->rc_buf_thresh[0]); + + DEFINE_BITFIELD(RC_BUF_THRESH_7, 31, 24) + DEFINE_BITFIELD(RC_BUF_THRESH_6, 23, 16) + DEFINE_BITFIELD(RC_BUF_THRESH_5, 15, 8) + DEFINE_BITFIELD(RC_BUF_THRESH_4, 7, 0) + WRITE32_BITFIELDS(®->dsc_pps[9], + RC_BUF_THRESH_7, dsc_cfg->rc_buf_thresh[7], + RC_BUF_THRESH_6, dsc_cfg->rc_buf_thresh[6], + RC_BUF_THRESH_5, dsc_cfg->rc_buf_thresh[5], + RC_BUF_THRESH_4, dsc_cfg->rc_buf_thresh[4]); + + DEFINE_BITFIELD(RC_BUF_THRESH_11, 31, 24) + DEFINE_BITFIELD(RC_BUF_THRESH_10, 23, 16) + DEFINE_BITFIELD(RC_BUF_THRESH_9, 15, 8) + DEFINE_BITFIELD(RC_BUF_THRESH_8, 7, 0) + WRITE32_BITFIELDS(®->dsc_pps[10], + RC_BUF_THRESH_11, dsc_cfg->rc_buf_thresh[11], + RC_BUF_THRESH_10, dsc_cfg->rc_buf_thresh[10], + RC_BUF_THRESH_9, dsc_cfg->rc_buf_thresh[9], + RC_BUF_THRESH_8, dsc_cfg->rc_buf_thresh[8]); + + DEFINE_BITFIELD(RC_BUF_THRESH_13, 15, 8) + DEFINE_BITFIELD(RC_BUF_THRESH_12, 7, 0) + WRITE32_BITFIELDS(®->dsc_pps[11], + RC_BUF_THRESH_13, dsc_cfg->rc_buf_thresh[13], + RC_BUF_THRESH_12, dsc_cfg->rc_buf_thresh[12]); + + DEFINE_BITFIELD(RC_RANGE_BPG_OFFSET_ODD, 31, 26) + DEFINE_BITFIELD(RC_RANGE_MAX_QP_ODD, 25, 21) + DEFINE_BITFIELD(RC_RANGE_MIN_QP_ODD, 20, 16) + DEFINE_BITFIELD(RC_RANGE_BPG_OFFSET_EVEN, 15, 10) + DEFINE_BITFIELD(RC_RANGE_MAX_QP_EVEN, 9, 5) + DEFINE_BITFIELD(RC_RANGE_MIN_QP_EVEN, 4, 0) + for (int i = 0; i < 7; i++) { + WRITE32_BITFIELDS(®->dsc_pps_rc_range_params[i], + RC_RANGE_BPG_OFFSET_ODD, + dsc_cfg->rc_range_params[2 * i + 1].range_bpg_offset, + RC_RANGE_MAX_QP_ODD, + dsc_cfg->rc_range_params[2 * i + 1].range_max_qp, + RC_RANGE_MIN_QP_ODD, + dsc_cfg->rc_range_params[2 * i + 1].range_min_qp, + RC_RANGE_BPG_OFFSET_EVEN, + dsc_cfg->rc_range_params[2 * i].range_bpg_offset, + RC_RANGE_MAX_QP_EVEN, + dsc_cfg->rc_range_params[2 * i].range_max_qp, + RC_RANGE_MIN_QP_EVEN, + dsc_cfg->rc_range_params[2 * i].range_min_qp); + } + /* Special case for the last register */ + WRITE32_BITFIELDS(®->dsc_pps_rc_range_params[7], + RC_RANGE_BPG_OFFSET_EVEN, + dsc_cfg->rc_range_params[14].range_bpg_offset, + RC_RANGE_MAX_QP_EVEN, dsc_cfg->rc_range_params[14].range_max_qp, + RC_RANGE_MIN_QP_EVEN, dsc_cfg->rc_range_params[14].range_min_qp); + + if (dsc_cfg->dsc_version_minor == 1) + write32(®->dsc_shadow, 0x20); + else if (dsc_cfg->dsc_version_minor == 2) + write32(®->dsc_shadow, 0x40); + else + printk(BIOS_WARNING, "%s : wrong version minor:%d\n", __func__, + dsc_cfg->dsc_version_minor); +} + +static void dsc_config(struct disp_dsc_regs *reg, u16 w, u16 h, + const struct dsc_config *dsc_cfg) +{ + bool dsc_enable; + + dsc_enable = (dsc_cfg && dsc_cfg->dsc_version_major); + printk(BIOS_INFO, "%s: w:%d, h:%d, dsc enable:%d\n", __func__, w, h, dsc_enable); + + if (!dsc_enable) { + setbits32(®->dsc_con, DSC_RELAY); + clrsetbits32(®->chunk_size, GENMASK(31, 16), w << 16); + clrsetbits32(®->pic_w, GENMASK(15, 0), w); + clrsetbits32(®->pic_h, GENMASK(15, 0), h); + printk(BIOS_INFO, "%s: DSC_relay mode\n", __func__); + return; + } + + dsc_configure_registers(reg, w, h, dsc_cfg); +} + static void blender_config(struct blender *reg, u16 width, u16 height, enum mtk_disp_blender_layer type) { @@ -157,17 +420,9 @@ static void dither_start(struct disp_dither_regs *reg) setbits32(®->en, BIT(0)); } -static void dsc_config(struct disp_dsc_regs *reg, u16 width, u16 height) -{ - write32(®->dsc_con, BIT(5)); - write32(®->pic_w, width); - write32(®->pic_h, height); - write32(®->chunk_size, width << 16); -} - static void dsc_start(struct disp_dsc_regs *reg) { - setbits32(®->dsc_con, BIT(0)); + setbits32(®->dsc_con, DSC_EN); } static void ovlsys_path_connect(struct ovlsys_cfg *reg) @@ -325,7 +580,8 @@ static void disp_config_blender(struct blender *const blenders[], size_t size, u } } -static void main_disp_path_setup(u16 width, u16 height, u32 vrefresh, enum disp_path_sel path) +static void main_disp_path_setup(u16 width, u16 height, u32 vrefresh, enum disp_path_sel path, + const struct dsc_config *dsc_cfg) { u16 w = width; size_t num_pipe = DUAL_PIPE(path) ? 2 : 1; @@ -361,7 +617,7 @@ static void main_disp_path_setup(u16 width, u16 height, u32 vrefresh, enum disp_ postmask_start(pipes[i].postmask); dither_config(pipes[i].dither, w, height); dither_start(pipes[i].dither); - dsc_config(pipes[i].dsc, w, height); + dsc_config(pipes[i].dsc, w, height, dsc_cfg); dsc_start(pipes[i].dsc); } @@ -435,7 +691,7 @@ void mtk_ddp_soc_mode_set(u32 fmt, u32 bpp, u32 width, u32 height, u32 vrefresh, if (width > 0x1FFF || height > 0x1FFF) printk(BIOS_WARNING, "%s: w/h: %d/%d exceed hw limit %u\n", __func__, width, height, 0x1FFF); - main_disp_path_setup(width, height, vrefresh, path); + main_disp_path_setup(width, height, vrefresh, path, dsc_config); ovlsys_layer_config(fmt, bpp, width, height, path); } diff --git a/src/soc/mediatek/mt8196/dsi.c b/src/soc/mediatek/mt8196/dsi.c new file mode 100644 index 0000000000..6d6705986b --- /dev/null +++ b/src/soc/mediatek/mt8196/dsi.c @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ + +#include +#include +#include +#include +#include + +#define RG_DSI_PRD_REF_SEL GENMASK(5, 0) +#define RG_DSI_PRD_REF_MINI 0 +#define RG_DSI_PRD_REF_DEF 4 +#define RG_DSI_PRD_REF_MAX 7 + +void mtk_dsi_configure_mipi_tx(struct mipi_tx_regs *mipi_tx_reg, u32 data_rate, + u32 lanes, bool is_cphy) +{ + unsigned int txdiv0; + u64 pcw; + + /* Select different voltage when different data rate */ + if (data_rate < (u32)2500 * MHz) { + clrsetbits32(&mipi_tx_reg->pll_con1, RG_DSI_PRD_REF_SEL, RG_DSI_PRD_REF_MINI); + write32(&mipi_tx_reg->cdphy_preserved, 0xFFFF00F0); + } else { + clrsetbits32(&mipi_tx_reg->pll_con1, RG_DSI_PRD_REF_SEL, RG_DSI_PRD_REF_DEF); + write32(&mipi_tx_reg->cdphy_preserved, 0xFFFF0030); + } + + if (data_rate >= 2000 * MHz) { + txdiv0 = 0; + } else if (data_rate >= 1000 * MHz) { + txdiv0 = 1; + } else if (data_rate >= 500 * MHz) { + txdiv0 = 2; + } else if (data_rate > 250 * MHz) { + /* (data_rate == 250MHz) is a special case that should go to the + else-block below (txdiv0 = 4) */ + txdiv0 = 3; + } else { + /* MIN = 125 */ + assert(data_rate >= MTK_DSI_DATA_RATE_MIN_MHZ * MHz); + txdiv0 = 4; + } + + if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy) + mtk_dsi_cphy_lane_sel_setting(mipi_tx_reg); + + clrbits32(&mipi_tx_reg->pll_con4, BIT(11) | BIT(10)); + setbits32(&mipi_tx_reg->pll_pwr, AD_DSI_PLL_SDM_PWR_ON); + udelay(30); + clrbits32(&mipi_tx_reg->pll_pwr, AD_DSI_PLL_SDM_ISO_EN); + + pcw = (u64)(data_rate >> 1) * (1 << txdiv0); + pcw <<= 24; + pcw /= CLK26M_HZ; + + write32(&mipi_tx_reg->pll_con0, pcw); + clrsetbits32(&mipi_tx_reg->pll_con1, RG_DSI_PLL_POSDIV, txdiv0 << 8); + udelay(30); + setbits32(&mipi_tx_reg->pll_con1, RG_DSI_PLL_EN); + + /* BG_LPF_EN / BG_CORE_EN */ + write32(&mipi_tx_reg->lane_con, 0x3FFF0180); + udelay(40); + write32(&mipi_tx_reg->lane_con, 0x3FFF00C0); + + if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy) + mtk_dsi_cphy_enable(mipi_tx_reg); + + /* Switch OFF each Lane */ + clrbits32(&mipi_tx_reg->d0_sw_ctl_en, DSI_SW_CTL_EN); + clrbits32(&mipi_tx_reg->d1_sw_ctl_en, DSI_SW_CTL_EN); + clrbits32(&mipi_tx_reg->d2_sw_ctl_en, DSI_SW_CTL_EN); + clrbits32(&mipi_tx_reg->d3_sw_ctl_en, DSI_SW_CTL_EN); + clrbits32(&mipi_tx_reg->ck_sw_ctl_en, DSI_SW_CTL_EN); + + if (CONFIG(MEDIATEK_DSI_CPHY) && is_cphy) + mtk_dsi_cphy_disable_ck_mode(mipi_tx_reg); + else + mtk_dsi_dphy_disable_ck_mode(mipi_tx_reg); +} + +void mtk_dsi_reset(struct dsi_regs *dsi_reg) +{ + write32(&dsi_reg->dsi_shadow_ctrl, DSI_FORCE_COMMIT_ALWAYS); + write32(&dsi_reg->dsi_con_ctrl, 1); + write32(&dsi_reg->dsi_con_ctrl, 0); +} diff --git a/src/soc/mediatek/mt8196/include/soc/ddp.h b/src/soc/mediatek/mt8196/include/soc/ddp.h index 2c91747345..5d0130842f 100644 --- a/src/soc/mediatek/mt8196/include/soc/ddp.h +++ b/src/soc/mediatek/mt8196/include/soc/ddp.h @@ -539,17 +539,55 @@ check_member(disp_postmask_regs, size, 0x30); struct disp_dsc_regs { u32 dsc_con; - u32 reserved0x4[5]; + u32 dsc_inten; + u32 dsc_intsta; + u32 dsc_intack; + u32 dsc_sta; + u32 dsc_spr; u32 pic_w; u32 pic_h; - u32 reserved0x20; - u32 reserved0x24; + u32 dsc_slice_w; + u32 dsc_slice_h; u32 chunk_size; + u32 dsc_buf_size; + u32 dsc_mode; + u32 dsc_cfg; + u32 dsc_pad; + u32 dsc_enc_width; + u32 dsc_pic_pre_pad_size; + u32 dsc_pic_pre_pad_value; + u32 reserved0[6]; + u32 dsc_dbg_con; + u32 dsc_cksm_mon0; + u32 dsc_cksm_mon1; + u32 dsc_resv; + u32 dsc_obuf; + u32 dsc_obuf_mon; + u32 dsc_mute_con; + u32 dsc_ddren; + u32 dsc_pps[12]; + u32 dsc_pps_rc_range_params[8]; + u32 reserved1[12]; + u32 dsc_dbg[21]; + u32 reserved2[3]; + u32 dsc_enc_dbg[5]; + u32 dsc_enc_cov; + u32 reserved3[34]; + u32 dsc_shadow; + u32 reserved4[7]; + u32 dsc_dummy; + u32 reserved5; + u32 shadow_ctrl; + u32 reserved6; + u32 dsc_up_inten; + u32 dsc_up_intsta; + u32 dsc_up_intack; }; -check_member(disp_dsc_regs, dsc_con, 0x0); -check_member(disp_dsc_regs, pic_w, 0x18); -check_member(disp_dsc_regs, pic_h, 0x1C); -check_member(disp_dsc_regs, chunk_size, 0x28); +check_member(disp_dsc_regs, dsc_slice_w, 0x20); +check_member(disp_dsc_regs, dsc_dbg_con, 0x60); +check_member(disp_dsc_regs, dsc_enc_cov, 0x174); +check_member(disp_dsc_regs, dsc_shadow, 0x200); +check_member(disp_dsc_regs, dsc_up_intack, 0x238); static struct disp_mdp_rsz_regs *const disp_mdp_rsz0_reg = (void *)DISP_MDP_RSZ0_BASE; static struct disp_mdp_rsz_regs *const disp_mdp_rsz1_reg = (void *)DISP_MDP_RSZ1_BASE; diff --git a/src/soc/mediatek/mt8196/mtk_mipi_dphy.c b/src/soc/mediatek/mt8196/mtk_mipi_dphy.c new file mode 100644 index 0000000000..62001ec254 --- /dev/null +++ b/src/soc/mediatek/mt8196/mtk_mipi_dphy.c @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ + +#include +#include + +#define PHY_TIMING_DIV(x) (((x) / 8000) + 1) + +void mtk_dsi_dphy_timing_calculation(u32 data_rate_mhz, struct mtk_phy_timing *timing) +{ + u32 temp; + timing->lpx = ALIGN_UP(PHY_TIMING_DIV(80 * data_rate_mhz), 2); + timing->da_hs_prepare = ALIGN_UP(PHY_TIMING_DIV(59 * data_rate_mhz + 4 * 1000), 2); + timing->da_hs_zero = PHY_TIMING_DIV(163 * data_rate_mhz + 11 * 1000) - + timing->da_hs_prepare; + + temp = data_rate_mhz < 740 ? 443 : 420; + timing->da_hs_trail = PHY_TIMING_DIV(66 * data_rate_mhz + temp * 100); + timing->ta_go = 4 * timing->lpx; + timing->ta_sure = 3 * timing->lpx / 2; + timing->ta_get = 5 * timing->lpx; + timing->da_hs_exit = ALIGN_UP(PHY_TIMING_DIV(118 * data_rate_mhz), 2); + timing->da_hs_sync = 1; + + timing->clk_hs_prepare = ALIGN_UP(PHY_TIMING_DIV(57 * data_rate_mhz), 2); + timing->clk_hs_post = PHY_TIMING_DIV(65 * data_rate_mhz + 53 * 1000); + timing->clk_hs_trail = PHY_TIMING_DIV(65 * data_rate_mhz + 52 * 1000); + timing->clk_hs_zero = PHY_TIMING_DIV(330 * data_rate_mhz) - timing->clk_hs_prepare; + timing->clk_hs_exit = PHY_TIMING_DIV(118 * data_rate_mhz); +}