Re: [PATCH v6 39/42] drm/mediatek: Introduce HDMI/DDC v2 for MT8195/MT8188

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Tue, 2025-02-11 at 12:34 +0100, AngeloGioacchino Del Regno wrote:
> External email : Please do not click links or open attachments until you have verified the sender or the content.
> 
> 
> Add support for the newer HDMI-TX (Encoder) v2 and DDC v2 IPs
> found in MediaTek's MT8195, MT8188 SoC and their variants, and
> including support for display modes up to 4k60 and for HDMI
> Audio, as per the HDMI 2.0 spec.
> 
> HDCP and CEC functionalities are also supported by this hardware,
> but are not included in this commit and that also poses a slight
> difference between the V2 and V1 controllers in how they handle
> Hotplug Detection (HPD).
> 
> While the v1 controller was using the CEC controller to check
> HDMI cable connection and disconnection, in this driver the v2
> one does not.
> 
> This is due to the fact that on parts with v2 designs, like the
> MT8195 SoC, there is one CEC controller shared between the HDMI
> Transmitter (HDMI-TX) and Receiver (HDMI-RX): before eventually
> adding support to use the CEC HW to wake up the HDMI controllers
> it is necessary to have support for one TX, one RX *and* for both
> at the same time.
> 
> Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@xxxxxxxxxxxxx>
> ---
>  drivers/gpu/drm/mediatek/Kconfig            |    7 +
>  drivers/gpu/drm/mediatek/Makefile           |    2 +
>  drivers/gpu/drm/mediatek/mtk_hdmi_common.c  |   10 +
>  drivers/gpu/drm/mediatek/mtk_hdmi_common.h  |    9 +
>  drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c  |  397 ++++++
>  drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h |  263 ++++
>  drivers/gpu/drm/mediatek/mtk_hdmi_v2.c      | 1368 +++++++++++++++++++
>  7 files changed, 2056 insertions(+)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
> 
> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 994b48b82d44..c89ae4ed2c96 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -45,3 +45,10 @@ config DRM_MEDIATEK_HDMI
>         select DRM_MEDIATEK_HDMI_COMMON
>         help
>           DRM/KMS HDMI driver for Mediatek SoCs
> +
> +config DRM_MEDIATEK_HDMI_V2
> +       tristate "DRM HDMI v2 IP support for MediaTek SoCs"
> +       depends on DRM_MEDIATEK
> +       select DRM_MEDIATEK_HDMI_COMMON
> +       help
> +         DRM/KMS HDMI driver for MediaTek SoCs with HDMIv2 IP
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 78cf2d4fc85f..e0ac49b07d50 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -25,5 +25,7 @@ obj-$(CONFIG_DRM_MEDIATEK_HDMI_COMMON) += mtk_hdmi_common.o
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mtk_cec.o
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mtk_hdmi.o
>  obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mtk_hdmi_ddc.o
> +obj-$(CONFIG_DRM_MEDIATEK_HDMI_V2) += mtk_hdmi_v2.o
> +obj-$(CONFIG_DRM_MEDIATEK_HDMI_V2) += mtk_hdmi_ddc_v2.o
> 
>  obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
> index d58752b772e8..2f2e77b664a2 100644
> --- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.c
> @@ -294,10 +294,20 @@ static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi, struct platform_device
>         if (ret)
>                 return dev_err_probe(dev, ret, "Failed to get clocks\n");
> 
> +
> +       hdmi->irq = platform_get_irq(pdev, 0);
> +       if (!hdmi->irq)
> +               return hdmi->irq;
> +
>         hdmi->regs = device_node_to_regmap(dev->of_node);
>         if (IS_ERR(hdmi->regs))
>                 return PTR_ERR(hdmi->regs);
> 
> +       /* Populate HDMI sub-devices if present */
> +       ret = devm_of_platform_populate(&pdev->dev);
> +       if (ret)
> +               return ret;

Why need this? v1 driver works fine without this.

> +
>         remote = of_graph_get_remote_node(np, 1, 0);
>         if (!remote)
>                 return -EINVAL;
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_common.h b/drivers/gpu/drm/mediatek/mtk_hdmi_common.h
> index d3de8afff40f..e74fe1371324 100644
> --- a/drivers/gpu/drm/mediatek/mtk_hdmi_common.h
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_common.h
> @@ -126,6 +126,12 @@ struct hdmi_audio_param {
>         struct hdmi_codec_params codec_params;
>  };
> 
> +enum hdmi_hpd_state {
> +       HDMI_PLUG_OUT = 0,
> +       HDMI_PLUG_IN_AND_SINK_POWER_ON,
> +       HDMI_PLUG_IN_ONLY,
> +};
> +
>  struct mtk_hdmi_ver_conf {
>         const struct drm_bridge_funcs *bridge_funcs;
>         const struct hdmi_codec_ops *codec_ops;
> @@ -138,6 +144,7 @@ struct mtk_hdmi_conf {
>         bool tz_disabled;
>         bool cea_modes_only;
>         unsigned long max_mode_clock;
> +       u32 reg_hdmi_tx_cfg;
>  };
> 
>  struct mtk_hdmi {
> @@ -160,6 +167,8 @@ struct mtk_hdmi {
>         bool audio_enable;
>         bool powered;
>         bool enabled;
> +       unsigned int irq;
> +       enum hdmi_hpd_state hpd;
>         hdmi_codec_plugged_cb plugged_cb;
>         struct device *codec_dev;
>         struct mutex update_plugged_status_lock;
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
> new file mode 100644
> index 000000000000..d88fc8a2d859
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_v2.c
> @@ -0,0 +1,397 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * MediaTek HDMI v2 Display Data Channel Driver
> + *
> + * Copyright (c) 2021 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre, SAS
> + * Copyright (c) 2024 Collabora Ltd.
> + *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@xxxxxxxxxxxxx>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/types.h>
> +
> +#include <drm/drm_edid.h>
> +
> +#include "mtk_hdmi_common.h"
> +#include "mtk_hdmi_regs_v2.h"
> +
> +#define DDC2_DLY_CNT 572 /* BIM=208M/(v*4) = 90Khz */
> +#define DDC2_DLY_CNT_EDID 832 /* BIM=208M/(v*4) = 62.5Khz */
> +#define SI2C_ADDR_READ 0xf4
> +#define SCDC_I2C_SLAVE_ADDRESS 0x54
> +
> +struct mtk_hdmi_ddc {
> +       struct device *dev;
> +       void __iomem *regs;
> +       struct clk *clk;
> +       struct i2c_adapter adap;
> +       /* Serialize read/write operations */
> +       struct mutex mtx;
> +};
> +
> +static int mtk_ddc_check_and_rise_low_bus(struct mtk_hdmi_ddc *ddc)
> +{
> +       u32 val;
> +
> +       regmap_read(ddc->regs, HDCP2X_DDCM_STATUS, &val);
> +       if (val & DDC_I2C_BUS_LOW) {
> +               regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
> +                                  FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_CLOCK_SCL));
> +               usleep_range(250, 300);
> +       }
> +
> +       if (val & DDC_I2C_NO_ACK) {
> +               u32 ddc_ctrl, hpd_ddc_ctrl, hpd_ddc_status;
> +
> +               regmap_read(ddc->regs, DDC_CTRL, &ddc_ctrl);
> +               regmap_read(ddc->regs, HPD_DDC_CTRL, &hpd_ddc_ctrl);
> +               regmap_read(ddc->regs, HPD_DDC_STATUS, &hpd_ddc_status);
> +       }
> +
> +       if (val & DDC_I2C_NO_ACK)
> +               return -EIO;
> +
> +       return 0;
> +}
> +
> +static int mtk_ddc_wr_one(struct mtk_hdmi_ddc *ddc, u16 addr_id,
> +                         u16 offset_id, u8 wr_data)
> +{
> +       u32 val;
> +       int ret;
> +
> +       /* If down, rise bus for write operation */
> +       mtk_ddc_check_and_rise_low_bus(ddc);
> +
> +       regmap_update_bits(ddc->regs, HPD_DDC_CTRL, HPD_DDC_DELAY_CNT,
> +                          FIELD_PREP(HPD_DDC_DELAY_CNT, DDC2_DLY_CNT));
> +
> +       regmap_write(ddc->regs, SI2C_CTRL,
> +                    FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
> +                    FIELD_PREP(SI2C_WDATA, wr_data) |
> +                    SI2C_WR);
> +
> +       regmap_write(ddc->regs, DDC_CTRL,
> +                    FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_SEQ_WRITE) |
> +                    FIELD_PREP(DDC_CTRL_DIN_CNT, 1) |
> +                    FIELD_PREP(DDC_CTRL_OFFSET, offset_id) |
> +                    FIELD_PREP(DDC_CTRL_ADDR, addr_id));
> +       usleep_range(1000, 1250);
> +
> +       ret = regmap_read_poll_timeout(ddc->regs, HPD_DDC_STATUS, val,
> +                                      !(val & DDC_I2C_IN_PROG), 500, 1000);
> +       if (ret) {
> +               dev_err(ddc->dev, "DDC I2C write timeout\n");
> +               return ret;
> +       }
> +
> +       /* The I2C bus might be down after WR operation: rise it again */
> +       ret = mtk_ddc_check_and_rise_low_bus(ddc);
> +       if (ret) {
> +               dev_err(ddc->dev, "Error during write operation: No ACK\n");
> +               return ret;
> +       }
> +
> +       return 0;
> +}
> +
> +static int mtk_ddcm_read_hdmi(struct mtk_hdmi_ddc *ddc, u16 dly_cnt, u16 uc_dev,
> +                             u8 addr, u8 *puc_value, u16 data_cnt)
> +{
> +       u16 i, loop_counter, temp_length, uc_idx;
> +       u32 rem, uc_read_count, val;
> +       int ret;
> +
> +       if (!puc_value || !data_cnt || !dly_cnt) {

dly_cnt is DDC2_DLY_CNT which is 572. So it would not be zero.
Maybe you need not to pass dly_cnt in this function because it is a constant.
puc_value is &msg->buf[0]. It would not be zero.
How could data_cnt be zero?

> +               dev_err(ddc->dev, "Bad DDCM read request\n");
> +               return 0;
> +       }
> +
> +       mtk_ddc_check_and_rise_low_bus(ddc);
> +
> +       regmap_update_bits(ddc->regs, DDC_CTRL, DDC_CTRL_CMD,
> +                          FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_CLEAR_FIFO));
> +
> +       if (data_cnt >= 16) {
> +               temp_length = 16;
> +               loop_counter = data_cnt;
> +
> +               rem = do_div(loop_counter, temp_length);
> +               if (rem)
> +                       loop_counter++;
> +       } else {
> +               temp_length = data_cnt;
> +               loop_counter = 1;
> +       }
> +
> +       if (uc_dev >= DDC_ADDR && dly_cnt < DDC2_DLY_CNT_EDID)

dly_cnt is DDC2_DLY_CNT, so checking 'dly_cnt < DDC2_DLY_CNT_EDID' is redundant.

> +               dly_cnt = DDC2_DLY_CNT_EDID;
> +
> +       regmap_update_bits(ddc->regs, HPD_DDC_CTRL, HPD_DDC_DELAY_CNT,
> +                          FIELD_PREP(HPD_DDC_DELAY_CNT, dly_cnt));
> +
> +       for (i = 0; i < loop_counter; i++) {
> +               rem = data_cnt % 16;
> +
> +               if (i > 0 && i == (loop_counter - 1) && rem)
> +                       temp_length = rem;
> +
> +               /* 0x51 - 0x53: Flow control */
> +               if (uc_dev > DDC_ADDR && uc_dev <= 0x53) {
> +                       regmap_update_bits(ddc->regs, SCDC_CTRL, SCDC_DDC_SEGMENT,
> +                                          FIELD_PREP(SCDC_DDC_SEGMENT, uc_dev - DDC_ADDR));
> +
> +                       regmap_write(ddc->regs, DDC_CTRL,
> +                                    FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_ENH_READ_NOACK) |
> +                                    FIELD_PREP(DDC_CTRL_DIN_CNT, temp_length) |
> +                                    FIELD_PREP(DDC_CTRL_OFFSET, addr + i * temp_length) |
> +                                    FIELD_PREP(DDC_CTRL_ADDR, DDC_ADDR));
> +               } else {
> +                       u16 offset;
> +
> +                       if (addr != 0x43)
> +                               offset = i * 16;
> +                       else
> +                               offset = 0;
> +
> +                       regmap_write(ddc->regs, DDC_CTRL,
> +                                    FIELD_PREP(DDC_CTRL_CMD, DDC_CMD_SEQ_READ_NOACK) |
> +                                    FIELD_PREP(DDC_CTRL_DIN_CNT, temp_length) |
> +                                    FIELD_PREP(DDC_CTRL_OFFSET, addr + offset) |
> +                                    FIELD_PREP(DDC_CTRL_ADDR, uc_dev));
> +               }
> +               usleep_range(5000, 5500);
> +
> +               ret = regmap_read_poll_timeout(ddc->regs, HPD_DDC_STATUS, val,
> +                                              !(val & DDC_I2C_IN_PROG), 1000,
> +                                              500 * (temp_length + 5));
> +               if (ret) {
> +                       dev_err(ddc->dev, "Timeout waiting for DDC I2C\n");
> +                       return ret;
> +               }
> +
> +               ret = mtk_ddc_check_and_rise_low_bus(ddc);
> +               if (ret) {
> +                       dev_err(ddc->dev, "Error during read operation: No ACK\n");
> +                       return ret;
> +               }
> +
> +               for (uc_idx = 0; uc_idx < temp_length; uc_idx++) {
> +                       unsigned int read_idx = i * 16 + uc_idx;
> +
> +                       regmap_write(ddc->regs, SI2C_CTRL,
> +                                    FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
> +                                    SI2C_RD);
> +
> +                       regmap_read(ddc->regs, HPD_DDC_STATUS, &val);
> +                       puc_value[read_idx] = FIELD_GET(DDC_DATA_OUT, val);
> +
> +                       regmap_write(ddc->regs, SI2C_CTRL,
> +                                    FIELD_PREP(SI2C_ADDR, SI2C_ADDR_READ) |
> +                                    SI2C_CONFIRM_READ);
> +
> +                       /*
> +                        * If HDMI IP gets reset during EDID read, DDC read
> +                        * operation will fail and its delay counter will be
> +                        * reset to 400.
> +                        */
> +                       regmap_read(ddc->regs, HPD_DDC_CTRL, &val);
> +                       if (FIELD_GET(HPD_DDC_DELAY_CNT, val) < DDC2_DLY_CNT)
> +                               return 0;
> +
> +                       uc_read_count = read_idx + 1;
> +               }
> +       }
> +       if (uc_read_count > U8_MAX)
> +               dev_warn(ddc->dev, "Invalid read data count %u\n", uc_read_count);
> +
> +       return uc_read_count;
> +}
> +
> +static int mtk_hdmi_fg_ddc_data_read(struct mtk_hdmi_ddc *ddc, u16 b_dev,
> +                                    u8 data_addr, u16 data_cnt, u8 *pr_data)
> +{
> +       int read_data_cnt;
> +       u16 req_data_cnt;
> +
> +       if (!pr_data || !data_cnt)
> +               return -EINVAL;

pr_data is &msg->buf[0]. It would not be zero.

> +
> +       req_data_cnt = U8_MAX - data_addr + 1;
> +       if (req_data_cnt > data_cnt)
> +               req_data_cnt = data_cnt;

The req_data_cnt calculation looks weird.
Could you explain how this work?

> +
> +       mutex_lock(&ddc->mtx);

I don't know why use mutex to protect this?
The function is call by i2c_algorithm callback function and v1 does not use mutex.
So I think this mutex is redundant.

> +
> +       regmap_set_bits(ddc->regs, HDCP2X_POL_CTRL, HDCP2X_DIS_POLL_EN);
> +
> +       read_data_cnt = mtk_ddcm_read_hdmi(ddc, DDC2_DLY_CNT, b_dev, data_addr,
> +                                          pr_data, req_data_cnt);
> +
> +       mutex_unlock(&ddc->mtx);
> +
> +       if (read_data_cnt < 0)
> +               return read_data_cnt;
> +       else if (read_data_cnt != req_data_cnt)
> +               return -EINVAL;
> +
> +       return 0;
> +}
> +
> +static int mtk_hdmi_ddc_fg_data_write(struct mtk_hdmi_ddc *ddc, u16 b_dev,
> +                                     u8 data_addr, u16 data_cnt, u8 *pr_data)
> +{
> +       int i = 0, ret;
> +
> +       mutex_lock(&ddc->mtx);

I don't know why use mutex to protect this?
The function is call by i2c_algorithm callback function and v1 does not use mutex.
So I think this mutex is redundant.

> +
> +       regmap_set_bits(ddc->regs, HDCP2X_POL_CTRL, HDCP2X_DIS_POLL_EN);
> +       do {
> +               ret = mtk_ddc_wr_one(ddc, b_dev, data_addr + i, pr_data[i]);
> +               if (ret)
> +                       break;
> +       } while (++i < data_cnt);
> +
> +       mutex_unlock(&ddc->mtx);
> +
> +       return ret;
> +}
> +
> +static int mtk_hdmi_ddc_v2_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num)
> +{
> +       struct mtk_hdmi_ddc *ddc;
> +       u8 offset = 0;
> +       int i, ret;
> +
> +       if (!adapter || !adapter->algo_data || !msgs)

In v1, it does not check these. These checking may be redundant.

> +               return -EINVAL;
> +
> +       ddc = adapter->algo_data;
> +
> +       for (i = 0; i < num; i++) {
> +               struct i2c_msg *msg = &msgs[i];
> +
> +               if (!msg->buf) {

In v1, it does not check these. This checking may be redundant.

> +                       pr_err("XFER: No message buffer\n");
> +                       return -EINVAL;
> +               }
> +
> +               if (msg->flags & I2C_M_RD) {
> +                       /*
> +                        * The underlying DDC hardware always issues a write request
> +                        * that assigns the read offset as part of the read operation,
> +                        * therefore, use the `offset` value assigned in the previous
> +                        * write request from drm_edid
> +                        */
> +                       ret = mtk_hdmi_fg_ddc_data_read(ddc, msg->addr, offset,
> +                                                       msg->len, &msg->buf[0]);
> +                       if (ret)
> +                               return ret;
> +               } else {
> +                       ret = mtk_hdmi_ddc_fg_data_write(ddc, msg->addr, msg->buf[0],
> +                                                        msg->len - 1, &msg->buf[1]);

In v1, it does not use &msg->buf[1]. Add comment to explain why v2 use this.

> +                       if (ret)
> +                               return ret;
> +
> +                       /*
> +                        * Store the offset value requested by drm_edid or by
> +                        * scdc to use in subsequent read requests.
> +                        */
> +                       if ((msg->addr == DDC_ADDR || msg->addr == SCDC_I2C_SLAVE_ADDRESS) &&
> +                           msg->len == 1) {
> +                               offset = msg->buf[0];
> +                       }
> +               }
> +       }
> +
> +       return i;
> +}
> +
> +static u32 mtk_hdmi_ddc_v2_func(struct i2c_adapter *adapter)
> +{
> +       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm mtk_hdmi_ddc_v2_algorithm = {
> +       .master_xfer = mtk_hdmi_ddc_v2_xfer,
> +       .functionality = mtk_hdmi_ddc_v2_func,
> +};
> +
> +static int mtk_hdmi_ddc_v2_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct mtk_hdmi_ddc *ddc;
> +       int ret;
> +
> +       ddc = devm_kzalloc(dev, sizeof(*ddc), GFP_KERNEL);
> +       if (!ddc)
> +               return -ENOMEM;
> +
> +       ddc->dev = dev;
> +       ddc->regs = device_node_to_regmap(dev->parent->of_node);
> +       if (IS_ERR_OR_NULL(ddc->regs))
> +               return dev_err_probe(dev,
> +                                    IS_ERR(ddc->regs) ? PTR_ERR(ddc->regs) : -EINVAL,
> +                                    "Cannot get regmap\n");
> +
> +       ddc->clk = devm_clk_get_enabled(dev, NULL);
> +       if (IS_ERR(ddc->clk))
> +               return dev_err_probe(dev, PTR_ERR(ddc->clk), "Cannot get DDC clock\n");
> +
> +       mutex_init(&ddc->mtx);
> +
> +       strscpy(ddc->adap.name, "mediatek-hdmi-ddc-v2", sizeof(ddc->adap.name));
> +       ddc->adap.owner = THIS_MODULE;
> +       ddc->adap.algo = &mtk_hdmi_ddc_v2_algorithm;
> +       ddc->adap.retries = 3;
> +       ddc->adap.dev.of_node = dev->of_node;
> +       ddc->adap.algo_data = ddc;
> +       ddc->adap.dev.parent = &pdev->dev;
> +
> +       ret = devm_pm_runtime_enable(&pdev->dev);
> +       if (ret)
> +               return dev_err_probe(&pdev->dev, ret, "Cannot enable Runtime PM\n");
> +
> +       pm_runtime_get_sync(dev);
> +
> +       ret = devm_i2c_add_adapter(dev, &ddc->adap);
> +       if (ret < 0)
> +               return dev_err_probe(dev, ret, "Cannot add DDC I2C adapter\n");
> +
> +       platform_set_drvdata(pdev, ddc);
> +       return 0;
> +}
> +
> +static const struct of_device_id mtk_hdmi_ddc_v2_match[] = {
> +       { .compatible = "mediatek,mt8195-hdmi-ddc" },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mtk_hdmi_ddc_v2_match);
> +
> +struct platform_driver mtk_hdmi_ddc_v2_driver = {
> +       .probe = mtk_hdmi_ddc_v2_probe,
> +       .driver = {
> +               .name = "mediatek-hdmi-ddc-v2",
> +               .of_match_table = mtk_hdmi_ddc_v2_match,
> +       },
> +};
> +module_platform_driver(mtk_hdmi_ddc_v2_driver);
> +
> +MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@xxxxxxxxxxxxx>");
> +MODULE_AUTHOR("Can Zeng <can.zeng@xxxxxxxxxxxx>");
> +MODULE_DESCRIPTION("MediaTek HDMIv2 DDC Driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h b/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h
> new file mode 100644
> index 000000000000..521b35c7e14d
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_regs_v2.h
> @@ -0,0 +1,263 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) 2021 MediaTek Inc.
> + * Copyright (c) 2021 BayLibre, SAS
> + * Copyright (c) 2024 Collabora Ltd.
> + *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@xxxxxxxxxxxxx>
> + */
> +
> +#ifndef _MTK_HDMI_REGS_H
> +#define _MTK_HDMI_REGS_H
> +
> +/* HDMI_TOP Config */
> +#define TOP_CFG00                      0x000
> +#define  HDMI2_ON                      BIT(2)
> +#define  HDMI_MODE_HDMI                        BIT(3)
> +#define  SCR_ON                                BIT(4)
> +#define  TMDS_PACK_MODE                        GENMASK(9, 8)
> +#define   TMDS_PACK_MODE_8BPP          0
> +#define   TMDS_PACK_MODE_10BPP         1
> +#define   TMDS_PACK_MODE_12BPP         2
> +#define   TMDS_PACK_MODE_16BPP         3

I would like the name to align, and let number to have indent.

> +#define  DEEPCOLOR_PKT_EN              BIT(12)
> +#define  HDMI_ABIST_VIDEO_FORMAT       GENMASK(21, 16)
> +#define  HDMI_ABIST_ENABLE             BIT(31)
> +#define TOP_CFG01 0x004
> +#define  CP_SET_MUTE_EN                        BIT(0)
> +#define  CP_CLR_MUTE_EN                        BIT(1)
> +#define  NULL_PKT_EN                   BIT(2)
> +#define  NULL_PKT_VSYNC_HIGH_EN                BIT(3)
> +
> +/* HDMI_TOP Audio: Channel Mapping */
> +#define TOP_AUD_MAP                    0x00c
> +#define  SD0_MAP                       GENMASK(2, 0)
> +#define  SD1_MAP                       GENMASK(6, 4)
> +#define  SD2_MAP                       GENMASK(10, 8)
> +#define  SD3_MAP                       GENMASK(14, 12)
> +#define  SD4_MAP                       GENMASK(18, 16)
> +#define  SD5_MAP                       GENMASK(22, 20)
> +#define  SD6_MAP                       GENMASK(26, 24)
> +#define  SD7_MAP                       GENMASK(30, 28)
> +
> +/* Auxiliary Video Information (AVI) Infoframe */
> +#define TOP_AVI_HEADER                 0x024
> +#define TOP_AVI_PKT00                  0x028
> +#define TOP_AVI_PKT01                  0x02C
> +#define TOP_AVI_PKT02                  0x030
> +#define TOP_AVI_PKT03                  0x034
> +#define TOP_AVI_PKT04                  0x038
> +#define TOP_AVI_PKT05                  0x03C
> +
> +/* Audio Interface Infoframe */
> +#define TOP_AIF_HEADER                 0x040
> +#define TOP_AIF_PKT00                  0x044
> +#define TOP_AIF_PKT01                  0x048
> +#define TOP_AIF_PKT02                  0x04c
> +#define TOP_AIF_PKT03                  0x050
> +
> +/* Audio SPDIF Infoframe */
> +#define TOP_SPDIF_HEADER               0x054
> +#define TOP_SPDIF_PKT00                        0x058
> +#define TOP_SPDIF_PKT01                        0x05c
> +#define TOP_SPDIF_PKT02                        0x060
> +#define TOP_SPDIF_PKT03                        0x064
> +#define TOP_SPDIF_PKT04                        0x068
> +#define TOP_SPDIF_PKT05                        0x06c
> +#define TOP_SPDIF_PKT06                        0x070
> +#define TOP_SPDIF_PKT07                        0x074
> +
> +/* Infoframes Configuration */
> +#define TOP_INFO_EN                    0x01c
> +#define  AVI_EN                                BIT(0)
> +#define  SPD_EN                                BIT(1)
> +#define  AUD_EN                                BIT(2)
> +#define  CP_EN                         BIT(5)
> +#define  VSIF_EN                       BIT(11)
> +#define  AVI_EN_WR                     BIT(16)
> +#define  SPD_EN_WR                     BIT(17)
> +#define  AUD_EN_WR                     BIT(18)
> +#define  CP_EN_WR                      BIT(21)
> +#define  VSIF_EN_WR                    BIT(27)
> +#define TOP_INFO_RPT                   0x020
> +#define  AVI_RPT_EN                    BIT(0)
> +#define  SPD_RPT_EN                    BIT(1)
> +#define  AUD_RPT_EN                    BIT(2)
> +#define  CP_RPT_EN                     BIT(5)
> +#define  VSIF_RPT_EN                   BIT(11)
> +
> +/* Vendor Specific Infoframe */
> +#define TOP_VSIF_HEADER                        0x174
> +#define TOP_VSIF_PKT00                 0x178
> +#define TOP_VSIF_PKT01                 0x17c
> +#define TOP_VSIF_PKT02                 0x180
> +#define TOP_VSIF_PKT03                 0x184
> +#define TOP_VSIF_PKT04                 0x188
> +#define TOP_VSIF_PKT05                 0x18c
> +#define TOP_VSIF_PKT06                 0x190
> +#define TOP_VSIF_PKT07                 0x194
> +
> +/* HDMI_TOP Misc */
> +#define TOP_MISC_CTLR                  0x1a4
> +#define  DEEP_COLOR_ADD                        BIT(4)
> +
> +/* Hardware interrupts */
> +#define TOP_INT_STA00                  0x1a8
> +#define TOP_INT_ENABLE00               0x1b0
> +#define  HTPLG_R_INT                   BIT(0)
> +#define  HTPLG_F_INT                   BIT(1)
> +#define  PORD_R_INT                    BIT(2)
> +#define  PORD_F_INT                    BIT(3)
> +#define  HDMI_VSYNC_INT                        BIT(4)
> +#define  HDMI_AUDIO_INT                        BIT(5)
> +#define  HDCP2X_RX_REAUTH_REQ_DDCM_INT BIT(25)
> +#define TOP_INT_ENABLE01               0x1b4
> +#define TOP_INT_CLR00                  0x1b8
> +#define TOP_INT_CLR01                  0x1bc
> +
> +
> +/* Video Mute */
> +#define TOP_VMUTE_CFG1                 0x1c8
> +#define  REG_VMUTE_EN                  BIT(16)
> +
> +/* HDMI Audio IP */
> +#define AIP_CTRL                       0x400
> +#define  CTS_SW_SEL                    BIT(0)
> +#define  CTS_REQ_EN                    BIT(1)
> +#define  MCLK_EN                       BIT(2)
> +#define  NO_MCLK_CTSGEN_SEL            BIT(3)
> +#define  AUD_IN_EN                     BIT(8)
> +#define  AUD_SEL_OWRT                  BIT(9)
> +#define  SPDIF_EN                      BIT(13)
> +#define  HBRA_ON                       BIT(14)
> +#define  DSD_EN                                BIT(15)
> +#define  I2S_EN                                GENMASK(19, 16)
> +#define  HBR_FROM_SPDIF                        BIT(20)
> +#define  CTS_CAL_N4                    BIT(23)
> +#define  SPDIF_INTERNAL_MODULE         BIT(24)
> +#define AIP_N_VAL                      0x404
> +#define AIP_CTS_SVAL                   0x408
> +#define AIP_SPDIF_CTRL                 0x40c
> +#define  WR_1UI_LOCK                   BIT(0)
> +#define  FS_OVERRIDE_WRITE             BIT(1)
> +#define  WR_2UI_LOCK                   BIT(2)
> +#define  MAX_1UI_WRITE                 GENMASK(15, 8)
> +#define  MAX_2UI_SPDIF_WRITE           GENMASK(23, 16)
> +#define  MAX_2UI_I2S_HI_WRITE          GENMASK(23, 20)
> +#define   MAX_2UI_I2S_LFE_CC_SWAP      BIT(1)
> +#define  MAX_2UI_I2S_LO_WRITE          GENMASK(19, 16)
> +#define  AUD_ERR_THRESH                        GENMASK(29, 24)
> +#define  I2S2DSD_EN                    BIT(30)
> +#define AIP_I2S_CTRL                   0x410
> +#define  FIFO0_MAP                     GENMASK(1, 0)
> +#define  FIFO1_MAP                     GENMASK(3, 2)
> +#define  FIFO2_MAP                     GENMASK(5, 4)
> +#define  FIFO3_MAP                     GENMASK(7, 6)
> +#define  I2S_1ST_BIT_NOSHIFT           BIT(8)
> +#define  I2S_DATA_DIR_LSB              BIT(9)
> +#define  JUSTIFY_RIGHT                 BIT(10)
> +#define  WS_HIGH                       BIT(11)
> +#define  VBIT_COMPRESSED               BIT(12)
> +#define  CBIT_ORDER_SAME               BIT(13)
> +#define  SCK_EDGE_RISE                 BIT(14)
> +#define AIP_I2S_CHST0                  0x414
> +#define AIP_I2S_CHST1                  0x418
> +#define AIP_TXCTRL                     0x424
> +#define  RST4AUDIO                     BIT(0)
> +#define  RST4AUDIO_FIFO                        BIT(1)
> +#define  RST4AUDIO_ACR                 BIT(2)
> +#define  AUD_LAYOUT_1                  BIT(4)
> +#define  AUD_MUTE_FIFO_EN              BIT(5)
> +#define  AUD_PACKET_DROP               BIT(6)
> +#define  DSD_MUTE_EN                   BIT(7)
> +#define AIP_TPI_CTRL                   0x428
> +#define  TPI_AUDIO_LOOKUP_EN           BIT(2)
> +
> +/* Video downsampling configuration */
> +#define VID_DOWNSAMPLE_CONFIG          0x8d0
> +#define  C444_C422_CONFIG_ENABLE       BIT(0)
> +#define  C422_C420_CONFIG_ENABLE       BIT(4)
> +#define  C422_C420_CONFIG_BYPASS       BIT(5)
> +#define  C422_C420_CONFIG_OUT_CB_OR_CR BIT(6)
> +#define VID_OUT_FORMAT                 0x8fc
> +#define  OUTPUT_FORMAT_DEMUX_420_ENABLE        BIT(10)
> +
> +/* HDCP registers */
> +#define HDCP_TOP_CTRL                  0xc00
> +#define HDCP2X_CTRL_0                  0xc20
> +#define  HDCP2X_EN                     BIT(0)
> +#define  HDCP2X_ENCRYPT_EN             BIT(7)
> +#define  HDCP2X_HPD_OVR                        BIT(10)
> +#define  HDCP2X_HPD_SW                 BIT(11)
> +#define HDCP2X_POL_CTRL                        0xc54
> +#define  HDCP2X_DIS_POLL_EN            BIT(16)
> +#define HDCP1X_CTRL                    0xcd0
> +#define  HDCP1X_ENC_EN                 BIT(6)
> +
> +/* HDMI DDC registers */
> +#define HPD_DDC_CTRL                   0xc08
> +#define  HPD_DDC_DELAY_CNT             GENMASK(31, 16)
> +#define  HPD_DDC_HPD_DBNC_EN           BIT(2)
> +#define  HPD_DDC_PORD_DBNC_EN          BIT(3)
> +#define DDC_CTRL                       0xc10
> +#define  DDC_CTRL_ADDR                 GENMASK(7, 1)
> +#define  DDC_CTRL_OFFSET               GENMASK(15, 8)
> +#define  DDC_CTRL_DIN_CNT              GENMASK(25, 16)
> +#define  DDC_CTRL_CMD                  GENMASK(31, 28)
> +#define SCDC_CTRL                      0xc18
> +#define  SCDC_DDC_SEGMENT              GENMASK(15, 8)
> +#define HPD_DDC_STATUS                 0xc60
> +#define  HPD_STATE                     GENMASK(1, 0)
> +#define   HPD_STATE_CONNECTED          2
> +#define  HPD_PIN_STA                   BIT(4)
> +#define  PORD_PIN_STA                  BIT(5)
> +#define  DDC_I2C_IN_PROG               BIT(13)
> +#define  DDC_DATA_OUT                  GENMASK(23, 16)
> +#define SI2C_CTRL                      0xcac
> +#define  SI2C_WR                       BIT(0)
> +#define  SI2C_RD                       BIT(1)
> +#define  SI2C_CONFIRM_READ             BIT(2)
> +#define  SI2C_WDATA                    GENMASK(15, 8)
> +#define  SI2C_ADDR                     GENMASK(23, 16)
> +
> +/* HDCP DDC registers */
> +#define HDCP2X_DDCM_STATUS             0xc68
> +#define  DDC_I2C_NO_ACK                        BIT(10)
> +#define  DDC_I2C_BUS_LOW               BIT(11)
> +
> +/* HDMI TX registers */
> +#define HDMITX_CONFIG_MT8188           0xea0
> +#define HDMITX_CONFIG_MT8195           0x900
> +#define  HDMI_YUV420_MODE              BIT(10)
> +#define  HDMITX_SW_HPD                 BIT(29)
> +#define  HDMITX_SW_RSTB                        BIT(31)
> +
> +/**
> + * enum mtk_hdmi_ddc_v2_cmds - DDC_CMD register commands
> + * @DDC_CMD_READ_NOACK:      Current address read with no ACK on last byte
> + * @DDC_CMD_READ:            Current address read with ACK on last byte
> + * @DDC_CMD_SEQ_READ_NOACK:  Sequential read with no ACK on last byte
> + * @DDC_CMD_SEQ_READ:        Sequential read with ACK on last byte
> + * @DDC_CMD_ENH_READ_NOACK:  Enhanced read with no ACK on last byte
> + * @DDC_CMD_ENH_READ:        Enhanced read with ACK on last byte
> + * @DDC_CMD_SEQ_WRITE_NOACK: Sequential write ignoring ACK on last byte
> + * @DDC_CMD_SEQ_WRITE:       Sequential write requiring ACK on last byte
> + * @DDC_CMD_RSVD:            Reserved for future use
> + * @DDC_CMD_CLEAR_FIFO:      Clear DDC I2C FIFO
> + * @DDC_CMD_CLOCK_SCL:       Start clocking DDC I2C SCL
> + * @DDC_CMD_ABORT_XFER:      Abort DDC I2C transaction
> + */
> +enum mtk_hdmi_ddc_v2_cmds {
> +       DDC_CMD_READ_NOACK = 0x0,
> +       DDC_CMD_READ,
> +       DDC_CMD_SEQ_READ_NOACK,
> +       DDC_CMD_SEQ_READ,
> +       DDC_CMD_ENH_READ_NOACK,
> +       DDC_CMD_ENH_READ,
> +       DDC_CMD_SEQ_WRITE_NOACK,
> +       DDC_CMD_SEQ_WRITE = 0x07,
> +       DDC_CMD_CLEAR_FIFO = 0x09,
> +       DDC_CMD_CLOCK_SCL = 0x0a,
> +       DDC_CMD_ABORT_XFER = 0x0f
> +};
> +
> +#endif /* _MTK_HDMI_REGS_H */
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
> new file mode 100644
> index 000000000000..338a6dda2fd2
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_v2.c
> @@ -0,0 +1,1368 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * MediaTek HDMI v2 IP driver
> + *
> + * Copyright (c) 2022 MediaTek Inc.
> + * Copyright (c) 2022 BayLibre, SAS
> + * Copyright (c) 2024 Collabora Ltd.
> + *                    AngeloGioacchino Del Regno <angelogioacchino.delregno@xxxxxxxxxxxxx>
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/kernel.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/suspend.h>
> +#include <linux/units.h>
> +#include <linux/phy/phy.h>
> +
> +#include <drm/display/drm_hdmi_helper.h>
> +#include <drm/display/drm_hdmi_state_helper.h>
> +#include <drm/display/drm_scdc_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include "mtk_hdmi_common.h"
> +#include "mtk_hdmi_regs_v2.h"
> +
> +#define MTK_HDMI_V2_CLOCK_MIN  27000
> +#define MTK_HDMI_V2_CLOCK_MAX  594000
> +
> +#define HPD_PORD_HWIRQS                (HTPLG_R_INT | HTPLG_F_INT | PORD_F_INT | PORD_R_INT)
> +
> +enum mtk_hdmi_v2_clk_id {
> +       MTK_HDMI_V2_CLK_HDCP_SEL,
> +       MTK_HDMI_V2_CLK_HDCP_24M_SEL,
> +       MTK_HDMI_V2_CLK_VPP_SPLIT_HDMI,
> +       MTK_HDMI_V2_CLK_HDMI_APB_SEL,
> +       MTK_HDMI_V2_CLK_COUNT,
> +};
> +
> +const char *const mtk_hdmi_v2_clk_names[MTK_HDMI_V2_CLK_COUNT] = {
> +       [MTK_HDMI_V2_CLK_HDMI_APB_SEL] = "bus",
> +       [MTK_HDMI_V2_CLK_HDCP_SEL] = "hdcp",
> +       [MTK_HDMI_V2_CLK_HDCP_24M_SEL] = "hdcp24m",
> +       [MTK_HDMI_V2_CLK_VPP_SPLIT_HDMI] = "hdmi-split",
> +};
> +
> +static inline void mtk_hdmi_v2_hwirq_disable(struct mtk_hdmi *hdmi)
> +{
> +       regmap_write(hdmi->regs, TOP_INT_ENABLE00, 0);
> +       regmap_write(hdmi->regs, TOP_INT_ENABLE01, 0);
> +}
> +
> +static inline void mtk_hdmi_v2_enable_hpd_pord_irq(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       if (enable)
> +               regmap_set_bits(hdmi->regs, TOP_INT_ENABLE00, HPD_PORD_HWIRQS);
> +       else
> +               regmap_clear_bits(hdmi->regs, TOP_INT_ENABLE00, HPD_PORD_HWIRQS);
> +}
> +
> +static inline void mtk_hdmi_v2_clear_hpd_pord_irq(struct mtk_hdmi *hdmi)
> +{

This function is never used, so drop it.

> +       regmap_set_bits(hdmi->regs, TOP_INT_CLR00, HPD_PORD_HWIRQS);
> +       regmap_clear_bits(hdmi->regs, TOP_INT_CLR00, HPD_PORD_HWIRQS);
> +}
> +
> +static inline void mtk_hdmi_v2_set_sw_hpd(struct mtk_hdmi *hdmi, bool enable)
> +{

enable is always true, so remove it.

> +       if (enable) {
> +               regmap_set_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_HPD);
> +               regmap_set_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_OVR);
> +               regmap_set_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_SW);
> +       } else {
> +               regmap_clear_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_OVR);
> +               regmap_clear_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_HPD_SW);
> +               regmap_clear_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_HPD);
> +       }
> +}
> +
> +static void mtk_hdmi_v2_disable_hdcp_encrypt(struct mtk_hdmi *hdmi)
> +{
> +       regmap_clear_bits(hdmi->regs, HDCP2X_CTRL_0, HDCP2X_ENCRYPT_EN);
> +       regmap_clear_bits(hdmi->regs, HDCP1X_CTRL, HDCP1X_ENC_EN);
> +}
> +
> +static inline void mtk_hdmi_v2_enable_scrambling(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       struct drm_scdc *scdc = &hdmi->curr_conn->display_info.hdmi.scdc;
> +
> +       if (enable)
> +               regmap_set_bits(hdmi->regs, TOP_CFG00, SCR_ON | HDMI2_ON);
> +       else
> +               regmap_clear_bits(hdmi->regs, TOP_CFG00, SCR_ON | HDMI2_ON);
> +
> +       if (scdc->supported) {
> +               if (scdc->scrambling.supported)
> +                       drm_scdc_set_scrambling(hdmi->curr_conn, enable);
> +               drm_scdc_set_high_tmds_clock_ratio(hdmi->curr_conn, enable);
> +       }
> +}
> +
> +static void mtk_hdmi_v2_hw_vid_mute(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       /* If enabled, sends a black image */
> +       if (enable)
> +               regmap_set_bits(hdmi->regs, TOP_VMUTE_CFG1, REG_VMUTE_EN);
> +       else
> +               regmap_clear_bits(hdmi->regs, TOP_VMUTE_CFG1, REG_VMUTE_EN);
> +}
> +
> +static void mtk_hdmi_v2_hw_aud_mute(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       u32 aip, val;
> +
> +       if (!enable) {
> +               regmap_clear_bits(hdmi->regs, AIP_TXCTRL, AUD_MUTE_FIFO_EN);
> +               return;
> +       }
> +
> +       regmap_read(hdmi->regs, AIP_CTRL, &aip);
> +
> +       val = AUD_MUTE_FIFO_EN;
> +       if (aip & DSD_EN)
> +               val |= DSD_MUTE_EN;
> +
> +       regmap_update_bits(hdmi->regs, AIP_TXCTRL, val, val);
> +}
> +
> +static void mtk_hdmi_v2_hw_reset(struct mtk_hdmi *hdmi)
> +{
> +       regmap_clear_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_RSTB);
> +       udelay(5);
> +       regmap_set_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg, HDMITX_SW_RSTB);
> +}
> +
> +static inline u32 mtk_hdmi_v2_format_hw_packet(const u8 *buffer, u8 len)
> +{
> +       unsigned short i;
> +       u32 val = 0;
> +
> +       for (i = 0; i < len; i++)
> +               val |= buffer[i] << (i * 8);
> +
> +       return val;
> +}
> +
> +static void mtk_hdmi_v2_hw_write_audio_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
> +{
> +       regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AUD_EN | AUD_EN_WR);
> +       regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
> +
> +       regmap_write(hdmi->regs, TOP_AIF_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
> +       regmap_write(hdmi->regs, TOP_AIF_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 3));
> +       regmap_write(hdmi->regs, TOP_AIF_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 2));
> +       regmap_write(hdmi->regs, TOP_AIF_PKT02, 0);
> +       regmap_write(hdmi->regs, TOP_AIF_PKT03, 0);
> +
> +       regmap_set_bits(hdmi->regs, TOP_INFO_RPT, AUD_RPT_EN);
> +       regmap_set_bits(hdmi->regs, TOP_INFO_EN, AUD_EN | AUD_EN_WR);
> +}
> +
> +static void mtk_hdmi_v2_hw_write_avi_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
> +{
> +       regmap_clear_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
> +       regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
> +
> +       regmap_write(hdmi->regs, TOP_AVI_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
> +       regmap_write(hdmi->regs, TOP_AVI_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 4));
> +       regmap_write(hdmi->regs, TOP_AVI_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 3));
> +       regmap_write(hdmi->regs, TOP_AVI_PKT02, mtk_hdmi_v2_format_hw_packet(&buffer[10], 4));
> +       regmap_write(hdmi->regs, TOP_AVI_PKT03, mtk_hdmi_v2_format_hw_packet(&buffer[14], 3));
> +       regmap_write(hdmi->regs, TOP_AVI_PKT04, 0);
> +       regmap_write(hdmi->regs, TOP_AVI_PKT05, 0);
> +
> +       regmap_set_bits(hdmi->regs, TOP_INFO_RPT, AVI_RPT_EN);
> +       regmap_set_bits(hdmi->regs, TOP_INFO_EN, AVI_EN_WR | AVI_EN);
> +}
> +
> +static void mtk_hdmi_v2_hw_write_spd_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
> +{
> +       regmap_clear_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
> +       regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
> +
> +       regmap_write(hdmi->regs, TOP_SPDIF_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
> +       regmap_write(hdmi->regs, TOP_SPDIF_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 4));
> +       regmap_write(hdmi->regs, TOP_SPDIF_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 3));
> +       regmap_write(hdmi->regs, TOP_SPDIF_PKT02, mtk_hdmi_v2_format_hw_packet(&buffer[10], 4));
> +       regmap_write(hdmi->regs, TOP_SPDIF_PKT03, mtk_hdmi_v2_format_hw_packet(&buffer[14], 3));
> +       regmap_write(hdmi->regs, TOP_SPDIF_PKT04, mtk_hdmi_v2_format_hw_packet(&buffer[17], 4));
> +       regmap_write(hdmi->regs, TOP_SPDIF_PKT05, mtk_hdmi_v2_format_hw_packet(&buffer[21], 3));
> +       regmap_write(hdmi->regs, TOP_SPDIF_PKT06, mtk_hdmi_v2_format_hw_packet(&buffer[24], 4));
> +       regmap_write(hdmi->regs, TOP_SPDIF_PKT07, buffer[28]);
> +
> +       regmap_set_bits(hdmi->regs, TOP_INFO_EN, SPD_EN_WR | SPD_EN);
> +       regmap_set_bits(hdmi->regs, TOP_INFO_RPT, SPD_RPT_EN);
> +}
> +
> +static void mtk_hdmi_v2_hw_write_vendor_infoframe(struct mtk_hdmi *hdmi, const u8 *buffer)
> +{
> +       regmap_clear_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
> +       regmap_clear_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
> +
> +       regmap_write(hdmi->regs, TOP_VSIF_HEADER, mtk_hdmi_v2_format_hw_packet(&buffer[0], 3));
> +       regmap_write(hdmi->regs, TOP_VSIF_PKT00, mtk_hdmi_v2_format_hw_packet(&buffer[3], 4));
> +       regmap_write(hdmi->regs, TOP_VSIF_PKT01, mtk_hdmi_v2_format_hw_packet(&buffer[7], 2));
> +       regmap_write(hdmi->regs, TOP_VSIF_PKT02, 0);
> +       regmap_write(hdmi->regs, TOP_VSIF_PKT03, 0);
> +       regmap_write(hdmi->regs, TOP_VSIF_PKT04, 0);
> +       regmap_write(hdmi->regs, TOP_VSIF_PKT05, 0);
> +       regmap_write(hdmi->regs, TOP_VSIF_PKT06, 0);
> +       regmap_write(hdmi->regs, TOP_VSIF_PKT07, 0);
> +
> +       regmap_set_bits(hdmi->regs, TOP_INFO_EN, VSIF_EN_WR | VSIF_EN);
> +       regmap_set_bits(hdmi->regs, TOP_INFO_RPT, VSIF_RPT_EN);
> +}
> +
> +static void mtk_hdmi_yuv420_downsampling(struct mtk_hdmi *hdmi, bool enable)

enable is always false, so drop it.

> +{
> +       u32 val;
> +
> +       regmap_read(hdmi->regs, VID_DOWNSAMPLE_CONFIG, &val);
> +
> +       if (enable) {
> +               regmap_set_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg,
> +                               HDMI_YUV420_MODE | HDMITX_SW_HPD);
> +
> +               val |= C444_C422_CONFIG_ENABLE | C422_C420_CONFIG_ENABLE;
> +               val |= C422_C420_CONFIG_OUT_CB_OR_CR;
> +               val &= ~C422_C420_CONFIG_BYPASS;
> +               regmap_write(hdmi->regs, VID_DOWNSAMPLE_CONFIG, val);
> +
> +               regmap_set_bits(hdmi->regs, VID_OUT_FORMAT, OUTPUT_FORMAT_DEMUX_420_ENABLE);
> +       } else {
> +               regmap_update_bits(hdmi->regs, hdmi->conf->reg_hdmi_tx_cfg,
> +                                  HDMI_YUV420_MODE | HDMITX_SW_HPD, HDMITX_SW_HPD);


[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux