The merge block is one of the components of the video pipeline on some MediaTek SoCs, such as the MT8188. With two inputs it combines them side by side into one output; with a single input it only buffers that input through its SRAMs.
A merge declaring "mediatek,merge-fifo-en" additionally uses both SRAMs as a FIFO, which needs the DRAM latency thresholds to be programmed so that the block does not underrun. Signed-off-by: Julien Stephan <[email protected]> --- drivers/video/mediatek/mtk_disp_merge.c | 162 ++++++++++++++++++++++++++++++++ drivers/video/mediatek/mtk_disp_merge.h | 25 +++++ 2 files changed, 187 insertions(+) diff --git a/drivers/video/mediatek/mtk_disp_merge.c b/drivers/video/mediatek/mtk_disp_merge.c new file mode 100644 index 00000000000..b40e247d8f3 --- /dev/null +++ b/drivers/video/mediatek/mtk_disp_merge.c @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Mediatek Video Disp Merge Support + * + * Copyright (c) 2026 BayLibre, SAS. + * Author: Julien Stephan <[email protected]> + */ + +#include <dm.h> +#include <linux/bitfield.h> + +#include "mtk_disp_comp.h" +#include "mtk_disp_merge.h" + +#define DISP_REG_MERGE_CTRL 0x000 +#define MERGE_EN 1 +#define DISP_REG_MERGE_CFG_0 0x010 +#define DISP_REG_MERGE_CFG_1 0x014 +#define DISP_REG_MERGE_CFG_4 0x020 +#define DISP_REG_MERGE_CFG_10 0x038 +#define SWAP_MODE 0 +#define FLD_SWAP_MODE GENMASK(4, 0) +#define DISP_REG_MERGE_CFG_12 0x040 +#define CFG_10_10_1PI_2PO_BUF_MODE 6 +#define CFG_10_10_2PI_2PO_BUF_MODE 8 +#define CFG_11_10_1PI_2PO_MERGE 18 +#define FLD_CFG_MERGE_MODE GENMASK(4, 0) +#define DISP_REG_MERGE_CFG_24 0x070 +#define DISP_REG_MERGE_CFG_25 0x074 +#define DISP_REG_MERGE_CFG_26 0x078 +#define DISP_REG_MERGE_CFG_27 0x07c +#define DISP_REG_MERGE_CFG_36 0x0a0 +#define ULTRA_EN BIT(0) +#define PREULTRA_EN BIT(4) +#define DISP_REG_MERGE_CFG_37 0x0a4 +/* BUFFER_MODE: 0 - no fifo, 1 - SRAM0, 2 - SRAM1, 3 - SRAM0 and SRAM1 */ +#define BUFFER_MODE 3 +#define FLD_BUFFER_MODE GENMASK(1, 0) +/* + * ultra threshold in units of pixels, the recommended values are + * 6 us and 8 us worth of pixels at 594 Mpixel/s. + */ +#define ULTRA_TH_LOW (6 * 594) +#define ULTRA_TH_HIGH (8 * 594) +#define DISP_REG_MERGE_CFG_40 0x0b0 +#define FLD_ULTRA_TH_LOW GENMASK(15, 0) +#define FLD_ULTRA_TH_HIGH GENMASK(31, 16) +#define PREULTRA_TH_LOW (8 * 594) +#define PREULTRA_TH_HIGH (9 * 594) +#define DISP_REG_MERGE_CFG_41 0x0b4 +#define FLD_PREULTRA_TH_LOW GENMASK(15, 0) +#define FLD_PREULTRA_TH_HIGH GENMASK(31, 16) +#define DISP_REG_MERGE_MUTE_0 0xf00 + +/* CFG_30 (0x088, "12 bit lsb off") is not present in the upstream driver */ +#define DISP_REG_MERGE_CFG_30 0x088 + +/* + * The DRAM latency thresholds that keep the merge from underrunning while it + * buffers a line through its SRAMs. + */ +static void mtk_disp_merge_fifo_setting(struct udevice *dev) +{ + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_36, + ULTRA_EN | PREULTRA_EN); + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_37, + FIELD_PREP(FLD_BUFFER_MODE, BUFFER_MODE)); + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_40, + FIELD_PREP(FLD_ULTRA_TH_LOW, ULTRA_TH_LOW) | + FIELD_PREP(FLD_ULTRA_TH_HIGH, ULTRA_TH_HIGH)); + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_41, + FIELD_PREP(FLD_PREULTRA_TH_LOW, PREULTRA_TH_LOW) | + FIELD_PREP(FLD_PREULTRA_TH_HIGH, + PREULTRA_TH_HIGH)); +} + +/* + * Configure the merge for a @l_w x @h left input and an optional @r_w x @h + * right input. With a right input the two are merged side by side, otherwise + * the single input is only buffered through the merge SRAMs. + * + * A single input with no fifo just goes straight through, one pixel in and + * two out. The HDMI pipeline never takes that path — merge3 has a right input + * and merge5 declares mediatek,merge-fifo-en — so it is untested and only + * here for a future single-pixel user. + */ +void mtk_disp_merge_config(struct udevice *dev, u16 l_w, u16 r_w, u16 h) +{ + struct mtk_disp_merge_priv *priv = dev_get_priv(dev); + u32 mode = CFG_10_10_1PI_2PO_BUF_MODE; + + if (priv->fifo_en) { + mtk_disp_merge_fifo_setting(dev); + mode = CFG_10_10_2PI_2PO_BUF_MODE; + } + + if (r_w) + mode = CFG_11_10_1PI_2PO_MERGE; + + /* input 1 and input 2 sizes */ + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_0, h << 16 | l_w); + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_1, h << 16 | r_w); + + /* output size */ + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_4, h << 16 | (l_w + r_w)); + + /* no pixel/channel swap */ + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_10, + FIELD_PREP(FLD_SWAP_MODE, SWAP_MODE)); + + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_12, + FIELD_PREP(FLD_CFG_MERGE_MODE, mode)); + + /* + * CFG_24 is the merge SRAM0 size and CFG_25 the SRAM1 size. In merge + * mode input 1 goes to SRAM0 and input 2 to SRAM1; in buffer mode the + * single input goes through SRAM0 and then SRAM1, so both hold it. + */ + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_24, h << 16 | l_w); + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_25, + h << 16 | (r_w ? r_w : l_w)); + + /* merged sizes of inputs 1 and 2, only used in merge mode */ + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_26, h << 16 | l_w); + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_27, h << 16 | r_w); + + /* 12 bit lsb off */ + mtk_disp_comp_write(dev, DISP_REG_MERGE_CFG_30, 0x0); + + /* unmute */ + mtk_disp_comp_write(dev, DISP_REG_MERGE_MUTE_0, 0x0); + + /* enable */ + mtk_disp_comp_write(dev, DISP_REG_MERGE_CTRL, MERGE_EN); +} + +static int mtk_disp_merge_probe(struct udevice *dev) +{ + struct mtk_disp_merge_priv *priv = dev_get_priv(dev); + int ret; + + ret = mtk_disp_comp_probe(dev); + if (ret) + return ret; + + priv->fifo_en = dev_read_bool(dev, "mediatek,merge-fifo-en"); + + return 0; +} + +static const struct udevice_id mtk_disp_merge_ids[] = { + { .compatible = "mediatek,mt8195-disp-merge" }, + { } +}; + +U_BOOT_DRIVER(mtk_disp_merge) = { + .name = "mtk_disp_merge", + .id = UCLASS_MISC, + .of_match = mtk_disp_merge_ids, + .probe = mtk_disp_merge_probe, + .priv_auto = sizeof(struct mtk_disp_merge_priv), +}; diff --git a/drivers/video/mediatek/mtk_disp_merge.h b/drivers/video/mediatek/mtk_disp_merge.h new file mode 100644 index 00000000000..54248633ac1 --- /dev/null +++ b/drivers/video/mediatek/mtk_disp_merge.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Mediatek Video Disp Merge Support + * + * Copyright (c) 2026 BayLibre, SAS. + * Author: Julien Stephan <[email protected]> + */ + +#ifndef _MTK_DISP_MERGE_H +#define _MTK_DISP_MERGE_H + +#include "mtk_disp_comp.h" + +/* + * @comp must be the first member: the shared component helpers cast the + * private data of every pipeline component to struct mtk_disp_comp_priv. + */ +struct mtk_disp_merge_priv { + struct mtk_disp_comp_priv comp; + bool fifo_en; +}; + +void mtk_disp_merge_config(struct udevice *dev, u16 l_w, u16 r_w, u16 h); + +#endif /* _MTK_DISP_MERGE_H */ -- 2.55.0
