[PATCH v13 06/14] drm/mediatek: Add HDMI support

Daniel Kurtz djkurtz at chromium.org
Wed Mar 9 05:52:24 PST 2016


Hi Philipp & Jie,

Sorry I only now had a chance to dig deeper and review the HDMI driver.

Lots of comments inline below...

On Tue, Mar 8, 2016 at 9:27 PM, Philipp Zabel <p.zabel at pengutronix.de> wrote:
> From: Jie Qiu <jie.qiu at mediatek.com>
>
> This patch adds drivers for the HDMI bridge connected to the DPI0
> display subsystem function block, for the HDMI DDC block, and for
> the HDMI PHY to support HDMI output.
>
> Signed-off-by: Jie Qiu <jie.qiu at mediatek.com>
> Signed-off-by: Philipp Zabel <p.zabel at pengutronix.de>
> ---
>  drivers/gpu/drm/mediatek/Kconfig               |   7 +
>  drivers/gpu/drm/mediatek/Makefile              |   9 +
>  drivers/gpu/drm/mediatek/mtk_cec.c             | 245 ++++++++++
>  drivers/gpu/drm/mediatek/mtk_cec.h             |  25 +
>  drivers/gpu/drm/mediatek/mtk_drm_drv.c         |   1 +
>  drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c    | 579 ++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_hdmi.c            | 479 ++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_hdmi.h            | 221 +++++++++
>  drivers/gpu/drm/mediatek/mtk_hdmi_ddc_drv.c    | 362 ++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_hdmi_hw.c         | 652 +++++++++++++++++++++++++
>  drivers/gpu/drm/mediatek/mtk_hdmi_hw.h         |  73 +++
>  drivers/gpu/drm/mediatek/mtk_hdmi_regs.h       | 221 +++++++++
>  drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c | 505 +++++++++++++++++++
>  13 files changed, 3379 insertions(+)
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_cec.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_cec.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_ddc_drv.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_hw.c
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_hw.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_hdmi_regs.h
>  create mode 100644 drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c
>
> diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
> index 0c49a94..e2ff158 100644
> --- a/drivers/gpu/drm/mediatek/Kconfig
> +++ b/drivers/gpu/drm/mediatek/Kconfig
> @@ -12,3 +12,10 @@ config DRM_MEDIATEK
>           The module will be called mediatek-drm
>           This driver provides kernel mode setting and
>           buffer management to userspace.
> +
> +config DRM_MEDIATEK_HDMI
> +       tristate "DRM HDMI Support for Mediatek SoCs"
> +       depends on DRM_MEDIATEK
> +       select GENERIC_PHY
> +       help
> +         DRM/KMS HDMI driver for Mediatek SoCs
> diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
> index 5fcf58e..6d53bee 100644
> --- a/drivers/gpu/drm/mediatek/Makefile
> +++ b/drivers/gpu/drm/mediatek/Makefile
> @@ -12,3 +12,12 @@ mediatek-drm-y := mtk_disp_ovl.o \
>                   mtk_dpi.o
>
>  obj-$(CONFIG_DRM_MEDIATEK) += mediatek-drm.o
> +
> +mediatek-drm-hdmi-objs := mtk_cec.o \
> +                         mtk_drm_hdmi_drv.o \
> +                         mtk_hdmi.o \
> +                         mtk_hdmi_ddc_drv.o \
> +                         mtk_hdmi_hw.o \
> +                         mtk_mt8173_hdmi_phy.o
> +
> +obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
> diff --git a/drivers/gpu/drm/mediatek/mtk_cec.c b/drivers/gpu/drm/mediatek/mtk_cec.c
> new file mode 100644
> index 0000000..cba3647
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_cec.c
> @@ -0,0 +1,245 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: Jie Qiu <jie.qiu at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +
> +#include "mtk_cec.h"
> +
> +#define TR_CONFIG              0x00
> +#define CLEAR_CEC_IRQ                  BIT(15)
> +
> +#define CEC_CKGEN              0x04
> +#define CEC_32K_PDN                    BIT(19)
> +#define PDN                            BIT(16)
> +
> +#define RX_EVENT               0x54
> +#define HDMI_PORD                      BIT(25)
> +#define HDMI_HTPLG                     BIT(24)
> +#define HDMI_PORD_INT_EN               BIT(9)
> +#define HDMI_HTPLG_INT_EN              BIT(8)
> +
> +#define RX_GEN_WD              0x58
> +#define HDMI_PORD_INT_32K_STATUS       BIT(26)
> +#define RX_RISC_INT_32K_STATUS         BIT(25)
> +#define HDMI_HTPLG_INT_32K_STATUS      BIT(24)
> +#define HDMI_PORD_INT_32K_CLR          BIT(18)
> +#define RX_INT_32K_CLR                 BIT(17)
> +#define HDMI_HTPLG_INT_32K_CLR         BIT(16)
> +#define HDMI_PORD_INT_32K_STA_MASK     BIT(10)
> +#define RX_RISC_INT_32K_STA_MASK       BIT(9)
> +#define HDMI_HTPLG_INT_32K_STA_MASK    BIT(8)
> +#define HDMI_PORD_INT_32K_EN           BIT(2)
> +#define RX_INT_32K_EN                  BIT(1)
> +#define HDMI_HTPLG_INT_32K_EN          BIT(0)
> +
> +#define NORMAL_INT_CTRL                0x5C
> +#define HDMI_HTPLG_INT_STA             BIT(0)
> +#define HDMI_PORD_INT_STA              BIT(1)
> +#define HDMI_HTPLG_INT_CLR             BIT(16)
> +#define HDMI_PORD_INT_CLR              BIT(17)
> +#define HDMI_FULL_INT_CLR              BIT(20)
> +
> +struct mtk_cec {
> +       void __iomem *regs;
> +       struct clk *clk;
> +       int irq;
> +       bool hpd;
> +       void (*hpd_event)(bool hpd, struct device *dev);
> +       struct device *hdmi_dev;
> +};
> +
> +static void mtk_cec_mask(struct mtk_cec *cec, unsigned int offset,
> +                        unsigned int val, unsigned int mask)
> +{
> +       u32 tmp = readl(cec->regs + offset) & ~mask;
> +
> +       tmp |= val & mask;
> +       writel(val, cec->regs + offset);
> +}
> +
> +void mtk_cec_set_hpd_event(struct device *dev,
> +                          void (*hpd_event)(bool hpd, struct device *dev),
> +                          struct device *hdmi_dev)
> +{
> +       struct mtk_cec *cec = dev_get_drvdata(dev);
> +
> +       cec->hdmi_dev = hdmi_dev;
> +       cec->hpd_event = hpd_event;

Lock this so to prevent race with the irq?

> +}
> +
> +bool mtk_cec_hpd_high(struct device *dev)
> +{
> +       struct mtk_cec *cec = dev_get_drvdata(dev);
> +       unsigned int status;
> +
> +       status = readl(cec->regs + RX_EVENT);
> +
> +       return (status & (HDMI_PORD | HDMI_HTPLG)) == (HDMI_PORD | HDMI_HTPLG);
> +}
> +
> +int mtk_cec_irq(struct device *dev)

AFAICT, this function is never used.

> +{
> +       struct mtk_cec *cec = dev_get_drvdata(dev);
> +
> +       return cec->irq;
> +}
> +
> +static void mtk_cec_htplg_irq_enable(struct mtk_cec *cec)
> +{
> +       mtk_cec_mask(cec, CEC_CKGEN, 0, PDN);
> +       mtk_cec_mask(cec, CEC_CKGEN, CEC_32K_PDN, CEC_32K_PDN);
> +       mtk_cec_mask(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR,
> +                    HDMI_PORD_INT_32K_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, RX_INT_32K_CLR, RX_INT_32K_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, HDMI_HTPLG_INT_32K_CLR,
> +                    HDMI_HTPLG_INT_32K_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_PORD_INT_32K_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, 0, RX_INT_32K_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_HTPLG_INT_32K_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_PORD_INT_32K_EN);
> +       mtk_cec_mask(cec, RX_GEN_WD, 0, RX_INT_32K_EN);
> +       mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_HTPLG_INT_32K_EN);

This is a bit wasteful.  Can you just clear all of these bits in a single write?
(this applies to this entire file).

> +
> +       mtk_cec_mask(cec, RX_EVENT, HDMI_PORD_INT_EN, HDMI_PORD_INT_EN);
> +       mtk_cec_mask(cec, RX_EVENT, HDMI_HTPLG_INT_EN, HDMI_HTPLG_INT_EN);
> +}
> +
> +static void mtk_cec_htplg_irq_disable(struct mtk_cec *cec)
> +{

Why does irq_enable do so much more work than irq_disable?

> +       mtk_cec_mask(cec, RX_EVENT, 0, HDMI_PORD_INT_EN);
> +       mtk_cec_mask(cec, RX_EVENT, 0, HDMI_HTPLG_INT_EN);
> +}
> +
> +static void mtk_cec_clear_htplg_irq(struct mtk_cec *cec)
> +{
> +       mtk_cec_mask(cec, TR_CONFIG, CLEAR_CEC_IRQ, CLEAR_CEC_IRQ);
> +       mtk_cec_mask(cec, NORMAL_INT_CTRL, HDMI_HTPLG_INT_CLR,
> +                    HDMI_HTPLG_INT_CLR);
> +       mtk_cec_mask(cec, NORMAL_INT_CTRL, HDMI_PORD_INT_CLR,
> +                    HDMI_PORD_INT_CLR);
> +       mtk_cec_mask(cec, NORMAL_INT_CTRL, HDMI_FULL_INT_CLR,
> +                    HDMI_FULL_INT_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, HDMI_PORD_INT_32K_CLR,
> +                    HDMI_PORD_INT_32K_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, RX_INT_32K_CLR, RX_INT_32K_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, HDMI_HTPLG_INT_32K_CLR,
> +                    HDMI_HTPLG_INT_32K_CLR);
> +       udelay(5);

Do you really need this delay in the middle of the isr handler?

> +       mtk_cec_mask(cec, NORMAL_INT_CTRL, 0, HDMI_HTPLG_INT_CLR);
> +       mtk_cec_mask(cec, NORMAL_INT_CTRL, 0, HDMI_PORD_INT_CLR);
> +       mtk_cec_mask(cec, TR_CONFIG, 0, CLEAR_CEC_IRQ);
> +       mtk_cec_mask(cec, NORMAL_INT_CTRL, 0, HDMI_FULL_INT_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_PORD_INT_32K_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, 0, RX_INT_32K_CLR);
> +       mtk_cec_mask(cec, RX_GEN_WD, 0, HDMI_HTPLG_INT_32K_CLR);
> +}
> +
> +static irqreturn_t mtk_cec_htplg_isr_thread(int irq, void *arg)
> +{
> +       struct device *dev = arg;
> +       struct mtk_cec *cec = dev_get_drvdata(dev);
> +       bool hpd;
> +
> +       mtk_cec_clear_htplg_irq(cec);
> +       hpd = mtk_cec_hpd_high(dev);
> +
> +       if (cec->hpd != hpd) {
> +               dev_info(dev, "hotplug event!,cur hpd = %d, hpd = %d\n",
> +                        cec->hpd, hpd);

dev_dbg if anything

> +               cec->hpd = hpd;
> +               if (cec->hpd_event)
> +                       cec->hpd_event(hpd, cec->hdmi_dev);
> +       }
> +       return IRQ_HANDLED;
> +}
> +
> +static int mtk_cec_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct mtk_cec *cec;
> +       struct resource *res;
> +       int ret;
> +
> +       cec = devm_kzalloc(dev, sizeof(*cec), GFP_KERNEL);
> +       if (!cec)
> +               return -ENOMEM;
> +
> +       platform_set_drvdata(pdev, cec);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       cec->regs = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(cec->regs)) {
> +               ret = PTR_ERR(cec->regs);
> +               dev_err(dev, "Failed to ioremap cec: %d\n", ret);
> +               return ret;
> +       }
> +
> +       cec->clk = devm_clk_get(dev, NULL);
> +       if (IS_ERR(cec->clk)) {
> +               ret = PTR_ERR(cec->clk);
> +               dev_err(dev, "Failed to get cec clock: %d\n", ret);
> +               return ret;
> +       }
> +
> +       cec->irq = platform_get_irq(pdev, 0);
> +       if (cec->irq < 0) {
> +               dev_err(dev, "Failed to get cec irq: %d\n", cec->irq);
> +               return cec->irq;
> +       }
> +
> +       ret = devm_request_threaded_irq(dev, cec->irq, NULL,
> +                                       mtk_cec_htplg_isr_thread,
> +                                       IRQF_SHARED | IRQF_TRIGGER_LOW |
> +                                       IRQF_ONESHOT, "hdmi hpd", dev);
> +       if (ret) {
> +               dev_err(dev, "Failed to register cec irq: %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = clk_prepare_enable(cec->clk);
> +       if (ret) {
> +               dev_err(dev, "Failed to enable cec clock: %d\n", ret);
> +               return ret;
> +       }
> +
> +       mtk_cec_htplg_irq_enable(cec);
> +
> +       return 0;
> +}
> +
> +static int mtk_cec_remove(struct platform_device *pdev)
> +{
> +       struct mtk_cec *cec = platform_get_drvdata(pdev);
> +
> +       mtk_cec_htplg_irq_disable(cec);
> +       clk_disable_unprepare(cec->clk);
> +       return 0;
> +}
> +
> +static const struct of_device_id mtk_cec_of_ids[] = {
> +       { .compatible = "mediatek,mt8173-cec", },
> +       {}
> +};
> +
> +struct platform_driver mtk_cec_driver = {
> +       .probe = mtk_cec_probe,
> +       .remove = mtk_cec_remove,
> +       .driver = {
> +               .name = "mediatek-cec",
> +               .of_match_table = mtk_cec_of_ids,
> +       },
> +};
> diff --git a/drivers/gpu/drm/mediatek/mtk_cec.h b/drivers/gpu/drm/mediatek/mtk_cec.h
> new file mode 100644
> index 0000000..fe6e8fd
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_cec.h
> @@ -0,0 +1,25 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: Jie Qiu <jie.qiu at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef _MTK_CEC_H
> +#define _MTK_CEC_H
> +
> +struct device;
> +
> +void mtk_cec_set_hpd_event(struct device *dev,
> +                          void (*hotplug_event)(bool hpd, struct device *dev),
> +                          struct device *hdmi_dev);
> +bool mtk_cec_hpd_high(struct device *dev);
> +int mtk_cec_irq(struct device *dev);
> +
> +#endif /* _MTK_CEC_H */
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> index a69958c..80056d1 100644
> --- a/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_drv.c
> @@ -23,6 +23,7 @@
>  #include <linux/of_platform.h>
>  #include <linux/pm_runtime.h>
>
> +#include "mtk_cec.h"
>  #include "mtk_drm_crtc.h"
>  #include "mtk_drm_ddp.h"
>  #include "mtk_drm_ddp_comp.h"
> diff --git a/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c b/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c
> new file mode 100644
> index 0000000..ff661e0
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_drm_hdmi_drv.c
> @@ -0,0 +1,579 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: Jie Qiu <jie.qiu at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include <drm/drmP.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_edid.h>
> +#include <linux/clk.h>
> +#include <linux/i2c.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/of_platform.h>
> +#include <linux/of.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_graph.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include "mtk_cec.h"
> +#include "mtk_hdmi.h"
> +#include "mtk_hdmi_hw.h"
> +
> +static const char * const mtk_hdmi_clk_names[MTK_HDMI_CLK_COUNT] = {
> +       [MTK_HDMI_CLK_HDMI_PIXEL] = "pixel",
> +       [MTK_HDMI_CLK_HDMI_PLL] = "pll",
> +       [MTK_HDMI_CLK_AUD_BCLK] = "bclk",
> +       [MTK_HDMI_CLK_AUD_SPDIF] = "spdif",
> +};
> +
> +static const enum mtk_hdmi_clk_id mtk_hdmi_enable_clocks[] = {
> +       MTK_HDMI_CLK_AUD_BCLK,
> +       MTK_HDMI_CLK_AUD_SPDIF,
> +};
> +
> +static int mtk_hdmi_get_all_clk(struct mtk_hdmi *hdmi,
> +                               struct device_node *np)
> +{
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(mtk_hdmi_clk_names); i++) {
> +               hdmi->clk[i] = of_clk_get_by_name(np,
> +                                                 mtk_hdmi_clk_names[i]);
> +               if (IS_ERR(hdmi->clk[i]))
> +                       return PTR_ERR(hdmi->clk[i]);
> +       }
> +       return 0;
> +}
> +
> +static int mtk_hdmi_clk_enable_audio(struct mtk_hdmi *hdmi)
> +{
> +       int ret;
> +
> +       ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]);
> +       if (ret)
> +               return ret;
> +
> +       ret = clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]);
> +       if (ret)
> +               goto err;
> +
> +       return 0;
> +err:
> +       clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]);
> +       return ret;
> +}
> +
> +static void mtk_hdmi_clk_disable_audio(struct mtk_hdmi *hdmi)
> +{
> +       clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_BCLK]);
> +       clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_AUD_SPDIF]);
> +}
> +
> +static enum drm_connector_status hdmi_conn_detect(struct drm_connector *conn,
> +                                                 bool force)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
> +
> +       return mtk_hdmi_hpd_high(hdmi) ?
> +              connector_status_connected : connector_status_disconnected;
> +}
> +
> +static void hdmi_conn_destroy(struct drm_connector *conn)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
> +
> +       mtk_cec_set_hpd_event(hdmi->cec_dev, NULL, NULL);
> +
> +       drm_connector_unregister(conn);
> +       drm_connector_cleanup(conn);
> +}
> +
> +static int hdmi_conn_set_property(struct drm_connector *conn,
> +                                 struct drm_property *property, uint64_t val)
> +{
> +       return 0;
> +}
> +
> +static int mtk_hdmi_conn_get_modes(struct drm_connector *conn)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
> +       struct edid *edid;
> +       int ret;
> +
> +       if (!hdmi->ddc_adpt)
> +               return -ENODEV;
> +
> +       edid = drm_get_edid(conn, hdmi->ddc_adpt);
> +       if (!edid)
> +               return -ENODEV;
> +
> +       hdmi->dvi_mode = !drm_detect_monitor_audio(edid);
> +
> +       drm_mode_connector_update_edid_property(conn, edid);
> +
> +       ret = drm_add_edid_modes(conn, edid);
> +       drm_edid_to_eld(conn, edid);
> +       kfree(edid);
> +       return ret;
> +}
> +
> +static int mtk_hdmi_conn_mode_valid(struct drm_connector *conn,
> +                                   struct drm_display_mode *mode)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
> +
> +       dev_dbg(hdmi->dev, "xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
> +               mode->hdisplay, mode->vdisplay, mode->vrefresh,
> +               !!(mode->flags & DRM_MODE_FLAG_INTERLACE), mode->clock * 1000);
> +
> +       if (hdmi->bridge.next) {
> +               struct drm_display_mode adjusted_mode;
> +
> +               drm_mode_copy(&adjusted_mode, mode);
> +               if (!drm_bridge_mode_fixup(hdmi->bridge.next, mode,
> +                                          &adjusted_mode))
> +                       return MODE_BAD;
> +       }
> +
> +       if (mode->clock < 27000)
> +               return MODE_CLOCK_LOW;
> +       if (mode->clock > 297000)
> +               return MODE_CLOCK_HIGH;
> +
> +       return drm_mode_validate_size(mode, 0x1fff, 0x1fff);
> +}
> +
> +static struct drm_encoder *mtk_hdmi_conn_best_enc(struct drm_connector *conn)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_conn(conn);
> +
> +       return hdmi->bridge.encoder;
> +}
> +
> +static const struct drm_connector_funcs mtk_hdmi_connector_funcs = {
> +       .dpms = drm_atomic_helper_connector_dpms,
> +       .detect = hdmi_conn_detect,
> +       .fill_modes = drm_helper_probe_single_connector_modes,
> +       .destroy = hdmi_conn_destroy,
> +       .set_property = hdmi_conn_set_property,
> +       .reset = drm_atomic_helper_connector_reset,
> +       .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> +       .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static const struct drm_connector_helper_funcs
> +               mtk_hdmi_connector_helper_funcs = {
> +       .get_modes = mtk_hdmi_conn_get_modes,
> +       .mode_valid = mtk_hdmi_conn_mode_valid,
> +       .best_encoder = mtk_hdmi_conn_best_enc,
> +};
> +
> +static void mtk_hdmi_hpd_event(bool hpd, struct device *dev)
> +{
> +       struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +       if (hdmi && hdmi->bridge.encoder && hdmi->bridge.encoder->dev)
> +               drm_helper_hpd_irq_event(hdmi->bridge.encoder->dev);
> +}
> +
> +/*
> + * Bridge callbacks
> + */
> +
> +static int mtk_hdmi_bridge_attach(struct drm_bridge *bridge)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
> +       int ret;
> +
> +       ret = drm_connector_init(bridge->encoder->dev, &hdmi->conn,
> +                                &mtk_hdmi_connector_funcs,
> +                                DRM_MODE_CONNECTOR_HDMIA);
> +       if (ret) {
> +               dev_err(hdmi->dev, "Failed to initialize connector: %d\n", ret);
> +               return ret;
> +       }
> +       drm_connector_helper_add(&hdmi->conn, &mtk_hdmi_connector_helper_funcs);
> +
> +       hdmi->conn.polled = DRM_CONNECTOR_POLL_HPD;
> +       hdmi->conn.interlace_allowed = true;
> +       hdmi->conn.doublescan_allowed = false;
> +
> +       ret = drm_connector_register(&hdmi->conn);
> +       if (ret) {
> +               dev_err(hdmi->dev, "Failed to register connector: %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = drm_mode_connector_attach_encoder(&hdmi->conn,
> +                                               bridge->encoder);
> +       if (ret) {
> +               dev_err(hdmi->dev,
> +                       "Failed to attach connector to encoder: %d\n", ret);
> +               return ret;
> +       }
> +
> +       if (bridge->next) {
> +               bridge->next->encoder = bridge->encoder;
> +               ret = drm_bridge_attach(bridge->encoder->dev, bridge->next);
> +               if (ret) {
> +                       dev_err(hdmi->dev,
> +                               "Failed to attach external bridge: %d\n", ret);
> +                       return ret;
> +               }
> +       }
> +
> +       mtk_cec_set_hpd_event(hdmi->cec_dev, mtk_hdmi_hpd_event, hdmi->dev);
> +
> +       return 0;
> +}
> +
> +static bool mtk_hdmi_bridge_mode_fixup(struct drm_bridge *bridge,
> +                                      const struct drm_display_mode *mode,
> +                                      struct drm_display_mode *adjusted_mode)
> +{
> +       return true;
> +}
> +
> +static void mtk_hdmi_bridge_disable(struct drm_bridge *bridge)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
> +
> +       phy_power_off(hdmi->phy);
> +       clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]);
> +       clk_disable_unprepare(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]);

As far as I can tell, __drm_helper_disable_unused_functions() doesn't
check if crtc/encoder/bridge are disabled before calling the
->*en/disable*() callbacks.

So, these clk_disable_unprepare() may be called with the HDMI already disabled,
trigerring their WARN_ON().

So, perhaps we also need to track enabled/disabled state separately here in
the driver.

> +}
> +
> +static void mtk_hdmi_bridge_post_disable(struct drm_bridge *bridge)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
> +
> +       mtk_hdmi_power_off(hdmi);
> +}
> +
> +static void mtk_hdmi_bridge_mode_set(struct drm_bridge *bridge,
> +                                    struct drm_display_mode *mode,
> +                                    struct drm_display_mode *adjusted_mode)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
> +
> +       dev_dbg(hdmi->dev, "cur info: name:%s, hdisplay:%d\n",
> +               adjusted_mode->name, adjusted_mode->hdisplay);
> +       dev_dbg(hdmi->dev, "hsync_start:%d,hsync_end:%d, htotal:%d",
> +               adjusted_mode->hsync_start, adjusted_mode->hsync_end,
> +               adjusted_mode->htotal);
> +       dev_dbg(hdmi->dev, "hskew:%d, vdisplay:%d\n",
> +               adjusted_mode->hskew, adjusted_mode->vdisplay);
> +       dev_dbg(hdmi->dev, "vsync_start:%d, vsync_end:%d, vtotal:%d",
> +               adjusted_mode->vsync_start, adjusted_mode->vsync_end,
> +               adjusted_mode->vtotal);
> +       dev_dbg(hdmi->dev, "vscan:%d, flag:%d\n",
> +               adjusted_mode->vscan, adjusted_mode->flags);
> +
> +       drm_mode_copy(&hdmi->mode, adjusted_mode);
> +}
> +
> +static void mtk_hdmi_bridge_pre_enable(struct drm_bridge *bridge)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
> +
> +       mtk_hdmi_power_on(hdmi);
> +}
> +
> +static void mtk_hdmi_bridge_enable(struct drm_bridge *bridge)
> +{
> +       struct mtk_hdmi *hdmi = hdmi_ctx_from_bridge(bridge);
> +
> +       mtk_hdmi_output_set_display_mode(hdmi, &hdmi->mode);
> +       clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]);
> +       clk_prepare_enable(hdmi->clk[MTK_HDMI_CLK_HDMI_PIXEL]);
> +       phy_power_on(hdmi->phy);
> +}
> +
> +static const struct drm_bridge_funcs mtk_hdmi_bridge_funcs = {
> +       .attach = mtk_hdmi_bridge_attach,
> +       .mode_fixup = mtk_hdmi_bridge_mode_fixup,
> +       .disable = mtk_hdmi_bridge_disable,
> +       .post_disable = mtk_hdmi_bridge_post_disable,
> +       .mode_set = mtk_hdmi_bridge_mode_set,
> +       .pre_enable = mtk_hdmi_bridge_pre_enable,
> +       .enable = mtk_hdmi_bridge_enable,
> +};
> +
> +static int mtk_hdmi_dt_parse_pdata(struct mtk_hdmi *hdmi,
> +                                  struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct device_node *np = dev->of_node;
> +       struct device_node *cec_np, *port, *ep, *remote, *i2c_np;
> +       struct platform_device *cec_pdev;
> +       struct regmap *regmap;
> +       struct resource *mem;
> +       int ret;
> +
> +       ret = mtk_hdmi_get_all_clk(hdmi, np);
> +       if (ret) {
> +               dev_err(dev, "Failed to get clocks: %d\n", ret);
> +               return ret;
> +       }
> +
> +       /* The CEC module handles HDMI hotplug detection */
> +       cec_np = of_find_compatible_node(np->parent, NULL,
> +                                        "mediatek,mt8173-cec");
> +       if (!cec_np) {
> +               dev_err(dev, "Failed to find CEC node\n");
> +               return -EINVAL;
> +       }
> +
> +       cec_pdev = of_find_device_by_node(cec_np);
> +       if (!cec_pdev) {
> +               dev_err(hdmi->dev, "Waiting for CEC device %s\n",
> +                       cec_np->full_name);
> +               return -EPROBE_DEFER;
> +       }
> +       hdmi->cec_dev = &cec_pdev->dev;
> +
> +       /*
> +        * The mediatek,syscon-hdmi property contains a phandle link to the
> +        * MMSYS_CONFIG device and the register offset of the HDMI_SYS_CFG
> +        * registers it contains.
> +        */
> +       regmap = syscon_regmap_lookup_by_phandle(np, "mediatek,syscon-hdmi");
> +       ret = of_property_read_u32_index(np, "mediatek,syscon-hdmi", 1,
> +                                        &hdmi->sys_offset);
> +       if (IS_ERR(regmap))
> +               ret = PTR_ERR(regmap);
> +       if (ret) {
> +               ret = PTR_ERR(regmap);
> +               dev_err(dev,
> +                       "Failed to get system configuration registers: %d\n",
> +                       ret);
> +               return ret;
> +       }
> +       hdmi->sys_regmap = regmap;
> +
> +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       hdmi->regs = devm_ioremap_resource(dev, mem);
> +       if (IS_ERR(hdmi->regs)) {
> +               dev_err(dev, "Failed to ioremap hdmi_shell: %ld\n",
> +                       PTR_ERR(hdmi->regs));

What is hdmi_shell?
In any case, I don't think you need to print anything here.

> +               return PTR_ERR(hdmi->regs);
> +       }
> +
> +       port = of_graph_get_port_by_id(np, 1);
> +       if (!port) {
> +               dev_err(dev, "Missing output port node\n");
> +               return -EINVAL;
> +       }
> +
> +       ep = of_get_child_by_name(port, "endpoint");
> +       if (!ep) {
> +               dev_err(dev, "Missing endpoint node in port %s\n",
> +                       port->full_name);
> +               of_node_put(port);
> +               return -EINVAL;
> +       }
> +       of_node_put(port);
> +
> +       remote = of_graph_get_remote_port_parent(ep);
> +       if (!remote) {
> +               dev_err(dev, "Missing connector/bridge node for endpoint %s\n",
> +                       ep->full_name);
> +               of_node_put(ep);
> +               return -EINVAL;
> +       }
> +       of_node_put(ep);
> +
> +       if (!of_device_is_compatible(remote, "hdmi-connector")) {
> +               hdmi->bridge.next = of_drm_find_bridge(remote);
> +               if (!hdmi->bridge.next) {
> +                       dev_err(dev, "Waiting for external bridge\n");
> +                       of_node_put(remote);
> +                       return -EPROBE_DEFER;
> +               }
> +       }
> +
> +       i2c_np = of_parse_phandle(remote, "ddc-i2c-bus", 0);
> +       if (!i2c_np) {
> +               dev_err(dev, "Failed to find ddc-i2c-bus node in %s\n",
> +                       remote->full_name);
> +               of_node_put(remote);
> +               return -EINVAL;
> +       }
> +       of_node_put(remote);
> +
> +       hdmi->ddc_adpt = of_find_i2c_adapter_by_node(i2c_np);
> +       if (!hdmi->ddc_adpt) {
> +               dev_err(dev, "Failed to get ddc i2c adapter by node\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
> +static int mtk_drm_hdmi_probe(struct platform_device *pdev)
> +{
> +       struct mtk_hdmi *hdmi;
> +       struct device *dev = &pdev->dev;
> +       int ret;
> +
> +       hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
> +       if (!hdmi)
> +               return -ENOMEM;
> +
> +       hdmi->dev = dev;
> +
> +       ret = mtk_hdmi_dt_parse_pdata(hdmi, pdev);
> +       if (ret)
> +               return ret;
> +
> +       hdmi->phy = devm_phy_get(dev, "hdmi");
> +       if (IS_ERR(hdmi->phy)) {
> +               ret = PTR_ERR(hdmi->phy);
> +               dev_err(dev, "Failed to get HDMI PHY: %d\n", ret);
> +               return ret;
> +       }
> +
> +       platform_set_drvdata(pdev, hdmi);
> +
> +       ret = mtk_hdmi_output_init(hdmi);
> +       if (ret) {
> +               dev_err(dev, "Failed to initialize hdmi output\n");
> +               return ret;
> +       }
> +
> +       hdmi->bridge.funcs = &mtk_hdmi_bridge_funcs;
> +       hdmi->bridge.of_node = pdev->dev.of_node;
> +       ret = drm_bridge_add(&hdmi->bridge);
> +       if (ret) {
> +               dev_err(dev, "failed to add bridge, ret = %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = mtk_hdmi_clk_enable_audio(hdmi);
> +       if (ret) {
> +               dev_err(dev, "Failed to enable audio clocks: %d\n", ret);
> +               goto err_bridge_remove;
> +       }
> +
> +       dev_dbg(dev, "mediatek hdmi probe success\n");
> +       return 0;
> +
> +err_bridge_remove:
> +       drm_bridge_remove(&hdmi->bridge);
> +       return ret;
> +}
> +
> +static int mtk_drm_hdmi_remove(struct platform_device *pdev)
> +{
> +       struct mtk_hdmi *hdmi = platform_get_drvdata(pdev);
> +
> +       drm_bridge_remove(&hdmi->bridge);
> +       platform_device_unregister(hdmi->audio_pdev);

audio_pdev is not set in this patch.
Is there more audio stuff that should be removed from this patch?

> +       platform_set_drvdata(pdev, NULL);

I don't think this is necessary.

> +       mtk_hdmi_clk_disable_audio(hdmi);
> +       return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int mtk_hdmi_suspend(struct device *dev)
> +{
> +       struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
> +
> +       mtk_hdmi_power_off(hdmi);
> +       mtk_hdmi_clk_disable_audio(hdmi);
> +       phy_power_off(hdmi->phy);
> +       dev_dbg(dev, "hdmi suspend success!\n");
> +       return 0;
> +}
> +
> +static int mtk_hdmi_resume(struct device *dev)
> +{
> +       struct mtk_hdmi *hdmi = dev_get_drvdata(dev);
> +       int ret = 0;
> +
> +       ret = mtk_hdmi_clk_enable_audio(hdmi);
> +       if (ret) {
> +               dev_err(dev, "hdmi resume failed!\n");
> +               return ret;
> +       }
> +
> +       mtk_hdmi_power_on(hdmi);
> +       mtk_hdmi_output_set_display_mode(hdmi, &hdmi->mode);
> +       phy_power_on(hdmi->phy);
> +       dev_dbg(dev, "hdmi resume success!\n");
> +       return 0;
> +}
> +#endif
> +static SIMPLE_DEV_PM_OPS(mtk_hdmi_pm_ops,
> +                        mtk_hdmi_suspend, mtk_hdmi_resume);

I do not think these suspend/resume ops are needed.
The MTK DRM driver turn off connectors at suspend, and re-enables them
at resume.

> +
> +static const struct of_device_id mtk_drm_hdmi_of_ids[] = {
> +       { .compatible = "mediatek,mt8173-hdmi", },
> +       {}
> +};
> +
> +static struct platform_driver mtk_hdmi_driver = {
> +       .probe = mtk_drm_hdmi_probe,
> +       .remove = mtk_drm_hdmi_remove,
> +       .driver = {
> +               .name = "mediatek-drm-hdmi",
> +               .of_match_table = mtk_drm_hdmi_of_ids,
> +               .pm = &mtk_hdmi_pm_ops,
> +       },
> +};
> +
> +static struct platform_driver * const mtk_hdmi_drivers[] = {
> +       &mtk_hdmi_phy_driver,
> +       &mtk_hdmi_ddc_driver,
> +       &mtk_cec_driver,
> +       &mtk_hdmi_driver,
> +};
> +
> +static int __init mtk_hdmitx_init(void)
> +{
> +       int ret;
> +       int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(mtk_hdmi_drivers); i++) {
> +               ret = platform_driver_register(mtk_hdmi_drivers[i]);
> +               if (ret < 0) {
> +                       pr_err("Failed to register %s driver: %d\n",
> +                              mtk_hdmi_drivers[i]->driver.name, ret);
> +                       goto err;
> +               }
> +       }
> +
> +       return 0;
> +
> +err:
> +       while (--i >= 0)
> +               platform_driver_unregister(mtk_hdmi_drivers[i]);
> +
> +       return ret;
> +}
> +
> +static void __exit mtk_hdmitx_exit(void)
> +{
> +       int i;
> +
> +       for (i = ARRAY_SIZE(mtk_hdmi_drivers) - 1; i >= 0; i--)
> +               platform_driver_unregister(mtk_hdmi_drivers[i]);
> +}
> +
> +module_init(mtk_hdmitx_init);
> +module_exit(mtk_hdmitx_exit);
> +
> +MODULE_AUTHOR("Jie Qiu <jie.qiu at mediatek.com>");
> +MODULE_DESCRIPTION("MediaTek HDMI Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.c b/drivers/gpu/drm/mediatek/mtk_hdmi.c
> new file mode 100644
> index 0000000..30ec7b5
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.c

Hmm... what is the value in splitting this out into its own separate mtk_hdmi.c?

> @@ -0,0 +1,479 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: Jie Qiu <jie.qiu at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include <drm/drm_edid.h>
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/phy/phy.h>
> +#include "mtk_cec.h"
> +#include "mtk_hdmi.h"
> +#include "mtk_hdmi_hw.h"
> +
> +static u8 mtk_hdmi_aud_get_chnl_count(enum hdmi_aud_channel_type channel_type)
> +{
> +       switch (channel_type) {
> +       case HDMI_AUD_CHAN_TYPE_1_0:
> +       case HDMI_AUD_CHAN_TYPE_1_1:
> +       case HDMI_AUD_CHAN_TYPE_2_0:
> +               return 2;
> +       case HDMI_AUD_CHAN_TYPE_2_1:
> +       case HDMI_AUD_CHAN_TYPE_3_0:
> +               return 3;
> +       case HDMI_AUD_CHAN_TYPE_3_1:
> +       case HDMI_AUD_CHAN_TYPE_4_0:
> +       case HDMI_AUD_CHAN_TYPE_3_0_LRS:
> +               return 4;
> +       case HDMI_AUD_CHAN_TYPE_4_1:
> +       case HDMI_AUD_CHAN_TYPE_5_0:
> +       case HDMI_AUD_CHAN_TYPE_3_1_LRS:
> +       case HDMI_AUD_CHAN_TYPE_4_0_CLRS:
> +               return 5;
> +       case HDMI_AUD_CHAN_TYPE_5_1:
> +       case HDMI_AUD_CHAN_TYPE_6_0:
> +       case HDMI_AUD_CHAN_TYPE_4_1_CLRS:
> +       case HDMI_AUD_CHAN_TYPE_6_0_CS:
> +       case HDMI_AUD_CHAN_TYPE_6_0_CH:
> +       case HDMI_AUD_CHAN_TYPE_6_0_OH:
> +       case HDMI_AUD_CHAN_TYPE_6_0_CHR:
> +               return 6;
> +       case HDMI_AUD_CHAN_TYPE_6_1:
> +       case HDMI_AUD_CHAN_TYPE_6_1_CS:
> +       case HDMI_AUD_CHAN_TYPE_6_1_CH:
> +       case HDMI_AUD_CHAN_TYPE_6_1_OH:
> +       case HDMI_AUD_CHAN_TYPE_6_1_CHR:
> +       case HDMI_AUD_CHAN_TYPE_7_0:
> +       case HDMI_AUD_CHAN_TYPE_7_0_LH_RH:
> +       case HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR:
> +       case HDMI_AUD_CHAN_TYPE_7_0_LC_RC:
> +       case HDMI_AUD_CHAN_TYPE_7_0_LW_RW:
> +       case HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD:
> +       case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS:
> +       case HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS:
> +       case HDMI_AUD_CHAN_TYPE_7_0_CS_CH:
> +       case HDMI_AUD_CHAN_TYPE_7_0_CS_OH:
> +       case HDMI_AUD_CHAN_TYPE_7_0_CS_CHR:
> +       case HDMI_AUD_CHAN_TYPE_7_0_CH_OH:
> +       case HDMI_AUD_CHAN_TYPE_7_0_CH_CHR:
> +       case HDMI_AUD_CHAN_TYPE_7_0_OH_CHR:
> +       case HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR:
> +       case HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS:
> +               return 7;
> +       case HDMI_AUD_CHAN_TYPE_7_1:
> +       case HDMI_AUD_CHAN_TYPE_7_1_LH_RH:
> +       case HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR:
> +       case HDMI_AUD_CHAN_TYPE_7_1_LC_RC:
> +       case HDMI_AUD_CHAN_TYPE_7_1_LW_RW:
> +       case HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD:
> +       case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS:
> +       case HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS:
> +       case HDMI_AUD_CHAN_TYPE_7_1_CS_CH:
> +       case HDMI_AUD_CHAN_TYPE_7_1_CS_OH:
> +       case HDMI_AUD_CHAN_TYPE_7_1_CS_CHR:
> +       case HDMI_AUD_CHAN_TYPE_7_1_CH_OH:
> +       case HDMI_AUD_CHAN_TYPE_7_1_CH_CHR:
> +       case HDMI_AUD_CHAN_TYPE_7_1_OH_CHR:
> +       case HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR:
> +               return 8;
> +       default:
> +               return 2;
> +       }
> +}
> +
> +static int mtk_hdmi_video_change_vpll(struct mtk_hdmi *hdmi, u32 clock)
> +{
> +       unsigned long rate;
> +       int ret;
> +
> +       /* The DPI driver already should have set TVDPLL to the correct rate */
> +       ret = clk_set_rate(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL], clock);
> +       if (ret) {
> +               dev_err(hdmi->dev, "Failed to set PLL to %u Hz: %d\n", clock,
> +                       ret);
> +               return ret;
> +       }
> +
> +       rate = clk_get_rate(hdmi->clk[MTK_HDMI_CLK_HDMI_PLL]);
> +
> +       if (DIV_ROUND_CLOSEST(rate, 1000) != DIV_ROUND_CLOSEST(clock, 1000))
> +               dev_warn(hdmi->dev, "Want PLL %u Hz, got %lu Hz\n", clock,
> +                        rate);
> +       else
> +               dev_dbg(hdmi->dev, "Want PLL %u Hz, got %lu Hz\n", clock, rate);
> +
> +       mtk_hdmi_hw_config_sys(hdmi);
> +       mtk_hdmi_hw_set_deep_color_mode(hdmi);
> +       return 0;
> +}
> +
> +static void mtk_hdmi_video_set_display_mode(struct mtk_hdmi *hdmi,
> +                                           struct drm_display_mode *mode)
> +{
> +       mtk_hdmi_hw_reset(hdmi);
> +       mtk_hdmi_hw_enable_notice(hdmi, true);
> +       mtk_hdmi_hw_write_int_mask(hdmi, 0xff);
> +       mtk_hdmi_hw_enable_dvi_mode(hdmi, hdmi->dvi_mode);
> +       mtk_hdmi_hw_ncts_auto_write_enable(hdmi, true);
> +
> +       mtk_hdmi_hw_msic_setting(hdmi, mode);
> +}
> +
> +static int mtk_hdmi_aud_enable_packet(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       mtk_hdmi_hw_send_aud_packet(hdmi, enable);
> +       return 0;
> +}
> +
> +static int mtk_hdmi_aud_on_off_hw_ncts(struct mtk_hdmi *hdmi, bool on)
> +{
> +       mtk_hdmi_hw_ncts_enable(hdmi, on);
> +       return 0;
> +}
> +
> +static int mtk_hdmi_aud_set_input(struct mtk_hdmi *hdmi)
> +{
> +       u8 chan_count;
> +
> +       mtk_hdmi_hw_aud_set_channel_swap(hdmi, HDMI_AUD_SWAP_LFE_CC);
> +       mtk_hdmi_hw_aud_raw_data_enable(hdmi, true);
> +
> +       if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF &&
> +           hdmi->aud_param.aud_codec == HDMI_AUDIO_CODING_TYPE_DST) {
> +               mtk_hdmi_hw_aud_set_bit_num(hdmi,
> +                                           HDMI_AUDIO_SAMPLE_SIZE_24);
> +       } else if (hdmi->aud_param.aud_i2s_fmt ==
> +                       HDMI_I2S_MODE_LJT_24BIT) {
> +               hdmi->aud_param.aud_i2s_fmt = HDMI_I2S_MODE_LJT_16BIT;
> +       }
> +
> +       mtk_hdmi_hw_aud_set_i2s_fmt(hdmi,
> +                                   hdmi->aud_param.aud_i2s_fmt);
> +       mtk_hdmi_hw_aud_set_bit_num(hdmi, HDMI_AUDIO_SAMPLE_SIZE_24);
> +
> +       mtk_hdmi_hw_aud_set_high_bitrate(hdmi, false);
> +       mtk_hdmi_phy_aud_dst_normal_double_enable(hdmi, false);
> +       mtk_hdmi_hw_aud_dst_enable(hdmi, false);

These three all change the same register.  Combine them into a single helper
function that just writes GRL_AUDIO_CFG once.

> +
> +       if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_SPDIF) {
> +               mtk_hdmi_hw_aud_dsd_enable(hdmi, false);
> +               if (hdmi->aud_param.aud_codec ==
> +                       HDMI_AUDIO_CODING_TYPE_DST) {
> +                       mtk_hdmi_phy_aud_dst_normal_double_enable(hdmi,
> +                                                                 true);
> +                       mtk_hdmi_hw_aud_dst_enable(hdmi, true);
> +               }
> +
> +               chan_count = mtk_hdmi_aud_get_chnl_count
> +                                                (HDMI_AUD_CHAN_TYPE_2_0);
> +               mtk_hdmi_hw_aud_set_i2s_chan_num(hdmi,
> +                                                HDMI_AUD_CHAN_TYPE_2_0,
> +                                                chan_count);
> +               mtk_hdmi_hw_aud_set_input_type(hdmi,
> +                                              HDMI_AUD_INPUT_SPDIF);
> +       } else {
> +               mtk_hdmi_hw_aud_dsd_enable(hdmi, false);
> +               chan_count =
> +                       mtk_hdmi_aud_get_chnl_count(
> +                       hdmi->aud_param.aud_input_chan_type);
> +               mtk_hdmi_hw_aud_set_i2s_chan_num(
> +                       hdmi,
> +                       hdmi->aud_param.aud_input_chan_type,
> +                       chan_count);
> +               mtk_hdmi_hw_aud_set_input_type(hdmi,
> +                                              HDMI_AUD_INPUT_I2S);
> +       }
> +       return 0;
> +}
> +
> +static int mtk_hdmi_aud_set_src(struct mtk_hdmi *hdmi,
> +                               struct drm_display_mode *display_mode)
> +{
> +       mtk_hdmi_aud_on_off_hw_ncts(hdmi, false);
> +
> +       if (hdmi->aud_param.aud_input_type == HDMI_AUD_INPUT_I2S) {
> +               switch (hdmi->aud_param.aud_hdmi_fs) {
> +               case HDMI_AUDIO_SAMPLE_FREQUENCY_32000:
> +               case HDMI_AUDIO_SAMPLE_FREQUENCY_44100:
> +               case HDMI_AUDIO_SAMPLE_FREQUENCY_48000:
> +               case HDMI_AUDIO_SAMPLE_FREQUENCY_88200:
> +               case HDMI_AUDIO_SAMPLE_FREQUENCY_96000:
> +                       mtk_hdmi_hw_aud_src_off(hdmi);
> +                       /* mtk_hdmi_hw_aud_src_enable(hdmi, false); */

why is this commented out?

> +                       mtk_hdmi_hw_aud_set_mclk(
> +                       hdmi,
> +                       hdmi->aud_param.aud_mclk);

indentation is funny here

> +                       mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false);
> +                       break;
> +               default:
> +                       break;
> +               }
> +       } else {
> +               switch (hdmi->aud_param.iec_frame_fs) {
> +               case HDMI_IEC_32K:
> +                       hdmi->aud_param.aud_hdmi_fs =
> +                           HDMI_AUDIO_SAMPLE_FREQUENCY_32000;

indentation is funny here

> +                       mtk_hdmi_hw_aud_src_off(hdmi);
> +                       mtk_hdmi_hw_aud_set_mclk(hdmi,
> +                                                HDMI_AUD_MCLK_128FS);
> +                       mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false);
> +                       break;
> +               case HDMI_IEC_48K:
> +                       hdmi->aud_param.aud_hdmi_fs =
> +                           HDMI_AUDIO_SAMPLE_FREQUENCY_48000;

indentation is funny here

> +                       mtk_hdmi_hw_aud_src_off(hdmi);
> +                       mtk_hdmi_hw_aud_set_mclk(hdmi,
> +                                                HDMI_AUD_MCLK_128FS);
> +                       mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false);
> +                       break;
> +               case HDMI_IEC_44K:
> +                       hdmi->aud_param.aud_hdmi_fs =
> +                           HDMI_AUDIO_SAMPLE_FREQUENCY_44100;

indentation is funny here

> +                       mtk_hdmi_hw_aud_src_off(hdmi);
> +                       mtk_hdmi_hw_aud_set_mclk(hdmi,
> +                                                HDMI_AUD_MCLK_128FS);
> +                       mtk_hdmi_hw_aud_aclk_inv_enable(hdmi, false);

These clauses all seem to do the same thing; only the mclk parameter is
different.  Can you refactor them into a helper function?

> +                       break;
> +               default:
> +                       break;
> +               }
> +       }
> +       mtk_hdmi_hw_aud_set_ncts(hdmi, hdmi->aud_param.aud_hdmi_fs,
> +                                display_mode->clock);
> +
> +       mtk_hdmi_hw_aud_src_reenable(hdmi);
> +       return 0;
> +}
> +
> +static int mtk_hdmi_aud_set_chnl_status(struct mtk_hdmi *hdmi)
> +{
> +       mtk_hdmi_hw_aud_set_channel_status(
> +               hdmi,
> +          hdmi->aud_param.hdmi_l_channel_state,
> +          hdmi->aud_param.hdmi_r_channel_state,
> +          hdmi->aud_param.aud_hdmi_fs);

indentation is funny here

> +       return 0;
> +}
> +
> +static int mtk_hdmi_aud_output_config(struct mtk_hdmi *hdmi,
> +                                     struct drm_display_mode *display_mode)
> +{
> +       mtk_hdmi_hw_aud_mute(hdmi, true);
> +       mtk_hdmi_aud_enable_packet(hdmi, false);
> +
> +       mtk_hdmi_aud_set_input(hdmi);
> +       mtk_hdmi_aud_set_src(hdmi, display_mode);
> +       mtk_hdmi_aud_set_chnl_status(hdmi);
> +
> +       usleep_range(50, 100);
> +
> +       mtk_hdmi_aud_on_off_hw_ncts(hdmi, true);
> +       mtk_hdmi_aud_enable_packet(hdmi, true);
> +       mtk_hdmi_hw_aud_mute(hdmi, false);
> +       return 0;
> +}
> +
> +static int mtk_hdmi_setup_av_mute_packet(struct mtk_hdmi *hdmi)
> +{
> +       mtk_hdmi_hw_send_av_mute(hdmi);
> +       return 0;
> +}
> +
> +static int mtk_hdmi_setup_av_unmute_packet(struct mtk_hdmi *hdmi)
> +{
> +       mtk_hdmi_hw_send_av_unmute(hdmi);
> +       return 0;
> +}
> +
> +static int mtk_hdmi_setup_avi_infoframe(struct mtk_hdmi *hdmi,
> +                                       struct drm_display_mode *mode)
> +{
> +       struct hdmi_avi_infoframe frame;
> +       u8 buffer[17];
> +       ssize_t err;
> +
> +       err = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode);
> +       if (err < 0) {
> +               dev_err(hdmi->dev,
> +                       "Failed to get AVI infoframe from mode: %zd\n", err);
> +               return err;
> +       }
> +
> +       err = hdmi_avi_infoframe_pack(&frame, buffer, sizeof(buffer));
> +       if (err < 0) {
> +               dev_err(hdmi->dev, "Failed to pack AVI infoframe: %zd\n", err);
> +               return err;
> +       }
> +
> +       mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer));
> +       return 0;
> +}
> +
> +static int mtk_hdmi_setup_spd_infoframe(struct mtk_hdmi *hdmi,
> +                                       const char *vendor,
> +                                       const char *product)
> +{
> +       struct hdmi_spd_infoframe frame;
> +       u8 buffer[29];
> +       ssize_t err;
> +
> +       err = hdmi_spd_infoframe_init(&frame, vendor, product);
> +       if (err < 0) {
> +               dev_err(hdmi->dev, "Failed to initialize SPD infoframe: %zd\n",
> +                       err);
> +               return err;
> +       }
> +
> +       err = hdmi_spd_infoframe_pack(&frame, buffer, sizeof(buffer));
> +       if (err < 0) {
> +               dev_err(hdmi->dev, "Failed to pack SDP infoframe: %zd\n", err);
> +               return err;
> +       }
> +
> +       mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer));
> +       return 0;
> +}
> +
> +static int mtk_hdmi_setup_audio_infoframe(struct mtk_hdmi *hdmi)
> +{
> +       struct hdmi_audio_infoframe frame;
> +       u8 buffer[14];
> +       ssize_t err;
> +
> +       err = hdmi_audio_infoframe_init(&frame);
> +       if (err < 0) {
> +               dev_err(hdmi->dev, "Failed to setup audio infoframe: %zd\n",
> +                       err);
> +               return err;
> +       }
> +
> +       frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
> +       frame.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
> +       frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
> +       frame.channels =
> +           mtk_hdmi_aud_get_chnl_count(
> +           hdmi->aud_param.aud_input_chan_type);

indentation is funny

> +
> +       err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
> +       if (err < 0) {
> +               dev_err(hdmi->dev, "Failed to pack audio infoframe: %zd\n",
> +                       err);
> +               return err;
> +       }
> +
> +       mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer));
> +       return 0;
> +}
> +
> +static int mtk_hdmi_setup_vendor_specific_infoframe(struct mtk_hdmi *hdmi,
> +                                               struct drm_display_mode *mode)
> +{
> +       struct hdmi_vendor_infoframe frame;
> +       u8 buffer[10];
> +       ssize_t err;
> +
> +       err = drm_hdmi_vendor_infoframe_from_display_mode(&frame, mode);
> +       if (err) {
> +               dev_err(hdmi->dev,
> +                       "Failed to get vendor infoframe from mode: %zd\n", err);
> +               return err;
> +       }
> +
> +       err = hdmi_vendor_infoframe_pack(&frame, buffer, sizeof(buffer));
> +       if (err) {
> +               dev_err(hdmi->dev, "Failed to pack vendor infoframe: %zd\n",
> +                       err);
> +               return err;
> +       }
> +
> +       mtk_hdmi_hw_send_info_frame(hdmi, buffer, sizeof(buffer));
> +       return 0;
> +}
> +
> +int mtk_hdmi_hpd_high(struct mtk_hdmi *hdmi)
> +{
> +       return hdmi->cec_dev ? mtk_cec_hpd_high(hdmi->cec_dev) : false;

I don't think we would get this far if cec_dev was NULL.

> +}
> +
> +int mtk_hdmi_output_init(struct mtk_hdmi *hdmi)
> +{
> +       struct hdmi_audio_param *aud_param = &hdmi->aud_param;
> +
> +       if (hdmi->init)
> +               return -EINVAL;

This check is not needed; this function is only called once, during probe.
In fact, I don't think the "->init" field is needed at all.

> +
> +       hdmi->csp = HDMI_COLORSPACE_RGB;
> +       hdmi->output = true;
> +       aud_param->aud_codec = HDMI_AUDIO_CODING_TYPE_PCM;
> +       aud_param->aud_hdmi_fs = HDMI_AUDIO_SAMPLE_FREQUENCY_48000;
> +       aud_param->aud_sampe_size = HDMI_AUDIO_SAMPLE_SIZE_16;
> +       aud_param->aud_input_type = HDMI_AUD_INPUT_I2S;
> +       aud_param->aud_i2s_fmt = HDMI_I2S_MODE_I2S_24BIT;
> +       aud_param->aud_mclk = HDMI_AUD_MCLK_128FS;
> +       aud_param->iec_frame_fs = HDMI_IEC_48K;
> +       aud_param->aud_input_chan_type = HDMI_AUD_CHAN_TYPE_2_0;
> +       aud_param->hdmi_l_channel_state[2] = 2;
> +       aud_param->hdmi_r_channel_state[2] = 2;
> +       hdmi->init = true;
> +
> +       return 0;
> +}
> +
> +void mtk_hdmi_power_on(struct mtk_hdmi *hdmi)
> +{
> +       mtk_hdmi_hw_make_reg_writable(hdmi, true);
> +       mtk_hdmi_hw_1p4_version_enable(hdmi, true);
> +}
> +
> +void mtk_hdmi_power_off(struct mtk_hdmi *hdmi)
> +{
> +       mtk_hdmi_hw_1p4_version_enable(hdmi, true);
> +       mtk_hdmi_hw_make_reg_writable(hdmi, false);
> +}
> +
> +int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi,
> +                                    struct drm_display_mode *mode)
> +{
> +       int ret;
> +
> +       if (!hdmi->init) {

Is this possible?

> +               dev_err(hdmi->dev, "doesn't init hdmi control context!\n");
> +               return -EINVAL;
> +       }
> +
> +       mtk_hdmi_hw_vid_black(hdmi, true);
> +       mtk_hdmi_hw_aud_mute(hdmi, true);
> +       mtk_hdmi_setup_av_mute_packet(hdmi);
> +       phy_power_off(hdmi->phy);
> +
> +       ret = mtk_hdmi_video_change_vpll(hdmi,
> +                                        mode->clock * 1000);
> +       if (ret) {
> +               dev_err(hdmi->dev, "Failed to set vpll: %d\n", ret);

cleanup on error?

> +               return ret;
> +       }
> +       mtk_hdmi_video_set_display_mode(hdmi, mode);
> +
> +       phy_power_on(hdmi->phy);
> +       mtk_hdmi_aud_output_config(hdmi, mode);
> +
> +       mtk_hdmi_setup_audio_infoframe(hdmi);
> +       mtk_hdmi_setup_avi_infoframe(hdmi, mode);
> +       mtk_hdmi_setup_spd_infoframe(hdmi, "mediatek", "chromebook");

what?  No.  The "product" should refer to the MTK HDMI block.

> +       if (mode->flags & DRM_MODE_FLAG_3D_MASK)
> +               mtk_hdmi_setup_vendor_specific_infoframe(hdmi, mode);
> +
> +       mtk_hdmi_hw_vid_black(hdmi, false);
> +       mtk_hdmi_hw_aud_mute(hdmi, false);
> +       mtk_hdmi_setup_av_unmute_packet(hdmi);
> +
> +       return 0;
> +}
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi.h b/drivers/gpu/drm/mediatek/mtk_hdmi.h
> new file mode 100644
> index 0000000..9403915
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi.h
> @@ -0,0 +1,221 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: Jie Qiu <jie.qiu at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef _MTK_HDMI_CTRL_H
> +#define _MTK_HDMI_CTRL_H
> +
> +#include <drm/drm_crtc.h>
> +#include <linux/hdmi.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/types.h>
> +
> +struct clk;
> +struct device;
> +struct device_node;
> +struct i2c_adapter;
> +struct platform_device;
> +struct phy;
> +struct regmap;
> +
> +enum mtk_hdmi_clk_id {
> +       MTK_HDMI_CLK_HDMI_PIXEL,
> +       MTK_HDMI_CLK_HDMI_PLL,
> +       MTK_HDMI_CLK_AUD_BCLK,
> +       MTK_HDMI_CLK_AUD_SPDIF,
> +       MTK_HDMI_CLK_COUNT
> +};
> +
> +enum hdmi_aud_input_type {
> +       HDMI_AUD_INPUT_I2S = 0,
> +       HDMI_AUD_INPUT_SPDIF,
> +};
> +
> +enum hdmi_aud_i2s_fmt {
> +       HDMI_I2S_MODE_RJT_24BIT = 0,
> +       HDMI_I2S_MODE_RJT_16BIT,
> +       HDMI_I2S_MODE_LJT_24BIT,
> +       HDMI_I2S_MODE_LJT_16BIT,
> +       HDMI_I2S_MODE_I2S_24BIT,
> +       HDMI_I2S_MODE_I2S_16BIT
> +};
> +
> +enum hdmi_aud_mclk {
> +       HDMI_AUD_MCLK_128FS,
> +       HDMI_AUD_MCLK_192FS,
> +       HDMI_AUD_MCLK_256FS,
> +       HDMI_AUD_MCLK_384FS,
> +       HDMI_AUD_MCLK_512FS,
> +       HDMI_AUD_MCLK_768FS,
> +       HDMI_AUD_MCLK_1152FS,
> +};
> +
> +enum hdmi_aud_iec_frame_rate {
> +       HDMI_IEC_32K = 0,
> +       HDMI_IEC_96K,
> +       HDMI_IEC_192K,
> +       HDMI_IEC_768K,
> +       HDMI_IEC_44K,
> +       HDMI_IEC_88K,
> +       HDMI_IEC_176K,
> +       HDMI_IEC_705K,
> +       HDMI_IEC_16K,
> +       HDMI_IEC_22K,
> +       HDMI_IEC_24K,
> +       HDMI_IEC_48K,
> +};
> +
> +enum hdmi_aud_channel_type {
> +       HDMI_AUD_CHAN_TYPE_1_0 = 0,
> +       HDMI_AUD_CHAN_TYPE_1_1,
> +       HDMI_AUD_CHAN_TYPE_2_0,
> +       HDMI_AUD_CHAN_TYPE_2_1,
> +       HDMI_AUD_CHAN_TYPE_3_0,
> +       HDMI_AUD_CHAN_TYPE_3_1,
> +       HDMI_AUD_CHAN_TYPE_4_0,
> +       HDMI_AUD_CHAN_TYPE_4_1,
> +       HDMI_AUD_CHAN_TYPE_5_0,
> +       HDMI_AUD_CHAN_TYPE_5_1,
> +       HDMI_AUD_CHAN_TYPE_6_0,
> +       HDMI_AUD_CHAN_TYPE_6_1,
> +       HDMI_AUD_CHAN_TYPE_7_0,
> +       HDMI_AUD_CHAN_TYPE_7_1,
> +       HDMI_AUD_CHAN_TYPE_3_0_LRS,
> +       HDMI_AUD_CHAN_TYPE_3_1_LRS,
> +       HDMI_AUD_CHAN_TYPE_4_0_CLRS,
> +       HDMI_AUD_CHAN_TYPE_4_1_CLRS,
> +       HDMI_AUD_CHAN_TYPE_6_1_CS,
> +       HDMI_AUD_CHAN_TYPE_6_1_CH,
> +       HDMI_AUD_CHAN_TYPE_6_1_OH,
> +       HDMI_AUD_CHAN_TYPE_6_1_CHR,
> +       HDMI_AUD_CHAN_TYPE_7_1_LH_RH,
> +       HDMI_AUD_CHAN_TYPE_7_1_LSR_RSR,
> +       HDMI_AUD_CHAN_TYPE_7_1_LC_RC,
> +       HDMI_AUD_CHAN_TYPE_7_1_LW_RW,
> +       HDMI_AUD_CHAN_TYPE_7_1_LSD_RSD,
> +       HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS,
> +       HDMI_AUD_CHAN_TYPE_7_1_LHS_RHS,
> +       HDMI_AUD_CHAN_TYPE_7_1_CS_CH,
> +       HDMI_AUD_CHAN_TYPE_7_1_CS_OH,
> +       HDMI_AUD_CHAN_TYPE_7_1_CS_CHR,
> +       HDMI_AUD_CHAN_TYPE_7_1_CH_OH,
> +       HDMI_AUD_CHAN_TYPE_7_1_CH_CHR,
> +       HDMI_AUD_CHAN_TYPE_7_1_OH_CHR,
> +       HDMI_AUD_CHAN_TYPE_7_1_LSS_RSS_LSR_RSR,
> +       HDMI_AUD_CHAN_TYPE_6_0_CS,
> +       HDMI_AUD_CHAN_TYPE_6_0_CH,
> +       HDMI_AUD_CHAN_TYPE_6_0_OH,
> +       HDMI_AUD_CHAN_TYPE_6_0_CHR,
> +       HDMI_AUD_CHAN_TYPE_7_0_LH_RH,
> +       HDMI_AUD_CHAN_TYPE_7_0_LSR_RSR,
> +       HDMI_AUD_CHAN_TYPE_7_0_LC_RC,
> +       HDMI_AUD_CHAN_TYPE_7_0_LW_RW,
> +       HDMI_AUD_CHAN_TYPE_7_0_LSD_RSD,
> +       HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS,
> +       HDMI_AUD_CHAN_TYPE_7_0_LHS_RHS,
> +       HDMI_AUD_CHAN_TYPE_7_0_CS_CH,
> +       HDMI_AUD_CHAN_TYPE_7_0_CS_OH,
> +       HDMI_AUD_CHAN_TYPE_7_0_CS_CHR,
> +       HDMI_AUD_CHAN_TYPE_7_0_CH_OH,
> +       HDMI_AUD_CHAN_TYPE_7_0_CH_CHR,
> +       HDMI_AUD_CHAN_TYPE_7_0_OH_CHR,
> +       HDMI_AUD_CHAN_TYPE_7_0_LSS_RSS_LSR_RSR,
> +       HDMI_AUD_CHAN_TYPE_8_0_LH_RH_CS,
> +       HDMI_AUD_CHAN_TYPE_UNKNOWN = 0xFF
> +};
> +
> +enum hdmi_aud_channel_swap_type {
> +       HDMI_AUD_SWAP_LR,
> +       HDMI_AUD_SWAP_LFE_CC,
> +       HDMI_AUD_SWAP_LSRS,
> +       HDMI_AUD_SWAP_RLS_RRS,
> +       HDMI_AUD_SWAP_LR_STATUS,
> +};
> +
> +struct hdmi_audio_param {
> +       enum hdmi_audio_coding_type aud_codec;
> +       enum hdmi_audio_sample_frequency aud_hdmi_fs;
> +       enum hdmi_audio_sample_size aud_sampe_size;
> +       enum hdmi_aud_input_type aud_input_type;
> +       enum hdmi_aud_i2s_fmt aud_i2s_fmt;
> +       enum hdmi_aud_mclk aud_mclk;
> +       enum hdmi_aud_iec_frame_rate iec_frame_fs;
> +       enum hdmi_aud_channel_type aud_input_chan_type;
> +       u8 hdmi_l_channel_state[6];
> +       u8 hdmi_r_channel_state[6];
> +};
> +
> +struct mtk_hdmi {
> +       struct drm_bridge bridge;
> +       struct drm_connector conn;
> +       struct device *dev;
> +       struct phy *phy;
> +       struct device *cec_dev;
> +       struct i2c_adapter *ddc_adpt;
> +       struct clk *clk[MTK_HDMI_CLK_COUNT];
> +#if defined(CONFIG_DEBUG_FS)
> +       struct dentry *debugfs;
> +#endif

Remove all of the debugfs stuff from this patch, since it isn't implemented.

> +       struct platform_device *audio_pdev;
> +       struct drm_display_mode mode;
> +       bool dvi_mode;
> +       u32 min_clock;
> +       u32 max_clock;
> +       u32 max_hdisplay;
> +       u32 max_vdisplay;
> +       u32 ibias;
> +       u32 ibias_up;
> +       struct regmap *sys_regmap;
> +       unsigned int sys_offset;
> +       void __iomem *regs;
> +       bool init;
> +       enum hdmi_colorspace csp;
> +       bool audio_enable;
> +       bool output;
> +       struct hdmi_audio_param aud_param;
> +};
> +
> +static inline struct mtk_hdmi *hdmi_ctx_from_bridge(struct drm_bridge *b)
> +{
> +       return container_of(b, struct mtk_hdmi, bridge);
> +}
> +
> +static inline struct mtk_hdmi *hdmi_ctx_from_conn(struct drm_connector *c)
> +{
> +       return container_of(c, struct mtk_hdmi, conn);
> +}
> +
> +int mtk_hdmi_output_init(struct mtk_hdmi *hdmi);
> +int mtk_hdmi_hpd_high(struct mtk_hdmi *hdmi);
> +int mtk_hdmi_output_set_display_mode(struct mtk_hdmi *hdmi,
> +                                    struct drm_display_mode *mode);
> +void mtk_hdmi_power_on(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_power_off(struct mtk_hdmi *hdmi);
> +#if defined(CONFIG_DEBUG_FS)
> +int mtk_drm_hdmi_debugfs_init(struct mtk_hdmi *hdmi);
> +void mtk_drm_hdmi_debugfs_exit(struct mtk_hdmi *hdmi);
> +#else
> +int mtk_drm_hdmi_debugfs_init(struct mtk_hdmi *hdmi)
> +{
> +       return 0;
> +}
> +
> +void mtk_drm_hdmi_debugfs_exit(struct mtk_hdmi *hdmi)
> +{
> +}
> +#endif /* CONFIG_DEBUG_FS */
> +
> +extern struct platform_driver mtk_cec_driver;
> +extern struct platform_driver mtk_hdmi_ddc_driver;
> +extern struct platform_driver mtk_hdmi_phy_driver;
> +#endif /* _MTK_HDMI_CTRL_H */
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_drv.c b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_drv.c
> new file mode 100644
> index 0000000..22e5487
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_ddc_drv.c
> @@ -0,0 +1,362 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: Jie Qiu <jie.qiu at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/i2c.h>
> +#include <linux/time.h>
> +#include <linux/delay.h>
> +#include <linux/errno.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/of_address.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +
> +#define SIF1_CLOK              (288)
> +#define DDC_DDCMCTL0           (0x0)
> +#define DDCM_ODRAIN                    BIT(31)
> +#define DDCM_CLK_DIV_OFFSET            (16)
> +#define DDCM_CLK_DIV_MASK              (0xfff << 16)
> +#define DDCM_CS_STATUS                 BIT(4)
> +#define DDCM_SCL_STATE                 BIT(3)
> +#define DDCM_SDA_STATE                 BIT(2)
> +#define DDCM_SM0EN                     BIT(1)
> +#define DDCM_SCL_STRECH                        BIT(0)
> +#define DDC_DDCMCTL1           (0x4)
> +#define DDCM_ACK_OFFSET                        (16)
> +#define DDCM_ACK_MASK                  (0xff << 16)
> +#define DDCM_PGLEN_OFFSET              (8)
> +#define DDCM_PGLEN_MASK                        (0x7 << 8)
> +#define DDCM_SIF_MODE_OFFSET           (4)
> +#define DDCM_SIF_MODE_MASK             (0x7 << 4)
> +#define DDCM_START                     (0x1)
> +#define DDCM_WRITE_DATA                        (0x2)
> +#define DDCM_STOP                      (0x3)
> +#define DDCM_READ_DATA_NO_ACK          (0x4)
> +#define DDCM_READ_DATA_ACK             (0x5)
> +#define DDCM_TRI                       BIT(0)
> +#define DDC_DDCMD0             (0x8)
> +#define DDCM_DATA3                     (0xff << 24)
> +#define DDCM_DATA2                     (0xff << 16)
> +#define DDCM_DATA1                     (0xff << 8)
> +#define DDCM_DATA0                     (0xff << 0)
> +#define DDC_DDCMD1             (0xc)
> +#define DDCM_DATA7                     (0xff << 24)
> +#define DDCM_DATA6                     (0xff << 16)
> +#define DDCM_DATA5                     (0xff << 8)
> +#define DDCM_DATA4                     (0xff << 0)
> +
> +struct mtk_hdmi_i2c {

Throughout this driver, I think we should:

s/mtk_hdmi_i2c/mtk_hdmi_ddc

> +       struct i2c_adapter adap;
> +       struct clk *clk;
> +       void __iomem *regs;
> +};
> +
> +static inline void sif_set_bit(struct mtk_hdmi_i2c *i2c, unsigned int offset,
> +                              unsigned int val)
> +{
> +       writel(readl(i2c->regs + offset) | val, i2c->regs + offset);
> +}
> +
> +static inline void sif_clr_bit(struct mtk_hdmi_i2c *i2c, unsigned int offset,
> +                              unsigned int val)
> +{
> +       writel(readl(i2c->regs + offset) & ~val, i2c->regs + offset);
> +}
> +
> +static inline bool sif_bit_is_set(struct mtk_hdmi_i2c *i2c, unsigned int offset,
> +                                 unsigned int val)
> +{
> +       return (readl(i2c->regs + offset) & val) == val;
> +}
> +
> +static inline void sif_write_mask(struct mtk_hdmi_i2c *i2c, unsigned int offset,
> +                                 unsigned int mask, unsigned int shift,
> +                                 unsigned int val)
> +{
> +       unsigned int tmp;
> +
> +       tmp = readl(i2c->regs + offset);
> +       tmp &= ~mask;
> +       tmp |= (val << shift) & mask;
> +       writel(tmp, i2c->regs + offset);
> +}
> +
> +static inline unsigned int sif_read_mask(struct mtk_hdmi_i2c *i2c,
> +                                        unsigned int offset, unsigned int mask,
> +                                        unsigned int shift)
> +{
> +       return (readl(i2c->regs + offset) & mask) >> shift;
> +}
> +
> +static void ddcm_trigger_mode(struct mtk_hdmi_i2c *i2c, int mode)
> +{
> +       int timeout = 20 * 1000;
> +
> +       sif_write_mask(i2c, DDC_DDCMCTL1, DDCM_SIF_MODE_MASK,
> +                      DDCM_SIF_MODE_OFFSET, mode);
> +       sif_set_bit(i2c, DDC_DDCMCTL1, DDCM_TRI);
> +       while (sif_bit_is_set(i2c, DDC_DDCMCTL1, DDCM_TRI)) {
> +               timeout -= 2;
> +               udelay(2);
> +               if (timeout <= 0)
> +                       break;
> +       }

Use iopoll?


> +}
> +
> +static int mtk_hdmi_i2c_read_msg(struct mtk_hdmi_i2c *i2c, struct i2c_msg *msg)
> +{
> +       struct device *dev = i2c->adap.dev.parent;
> +       u32 remain_count, ack_count, ack_final, read_count, temp_count;
> +       u32 index = 0;
> +       u32 ack;
> +       int i;
> +
> +       ddcm_trigger_mode(i2c, DDCM_START);
> +       sif_write_mask(i2c, DDC_DDCMD0, 0xff, 0, (msg->addr << 1) | 0x01);
> +       sif_write_mask(i2c, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET,
> +                      0x00);
> +       ddcm_trigger_mode(i2c, DDCM_WRITE_DATA);
> +       ack = sif_read_mask(i2c, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET);
> +       dev_dbg(dev, "ack = 0x%x\n", ack);
> +       if (ack != 0x01) {
> +               dev_err(dev, "i2c ack err!\n");
> +               return -ENXIO;
> +       }
> +
> +       remain_count = msg->len;
> +       ack_count = (msg->len - 1) / 8;
> +       ack_final = 0;
> +
> +       while (remain_count > 0) {
> +               if (ack_count > 0) {
> +                       read_count = 8;
> +                       ack_final = 0;
> +                       ack_count--;
> +               } else {
> +                       read_count = remain_count;
> +                       ack_final = 1;
> +               }
> +
> +               sif_write_mask(i2c, DDC_DDCMCTL1, DDCM_PGLEN_MASK,
> +                              DDCM_PGLEN_OFFSET, read_count - 1);
> +               ddcm_trigger_mode(i2c, (ack_final == 1) ?
> +                                 DDCM_READ_DATA_NO_ACK :
> +                                 DDCM_READ_DATA_ACK);
> +
> +               ack = sif_read_mask(i2c, DDC_DDCMCTL1, DDCM_ACK_MASK,
> +                                   DDCM_ACK_OFFSET);
> +               temp_count = 0;
> +               while (((ack & (1 << temp_count)) != 0) && (temp_count < 8))
> +                       temp_count++;
> +               if (((ack_final == 1) && (temp_count != (read_count - 1))) ||
> +                   ((ack_final == 0) && (temp_count != read_count))) {
> +                       dev_err(dev, "Address NACK! ACK(0x%x)\n", ack);
> +                       break;
> +               }
> +
> +               for (i = read_count; i >= 1; i--) {
> +                       int shift;
> +                       int offset;
> +
> +                       if (i > 4) {
> +                               offset = DDC_DDCMD1;
> +                               shift = (i - 5) * 8;
> +                       } else {
> +                               offset = DDC_DDCMD0;
> +                               shift = (i - 1) * 8;
> +                       }
> +
> +                       msg->buf[index + i - 1] = sif_read_mask(i2c, offset,
> +                                                               0xff << shift,
> +                                                               shift);
> +               }
> +
> +               remain_count -= read_count;
> +               index += read_count;
> +       }
> +
> +       return 0;
> +}
> +
> +static int mtk_hdmi_i2c_write_msg(struct mtk_hdmi_i2c *i2c, struct i2c_msg *msg)
> +{
> +       struct device *dev = i2c->adap.dev.parent;
> +       u32 ack;
> +
> +       ddcm_trigger_mode(i2c, DDCM_START);
> +       sif_write_mask(i2c, DDC_DDCMD0, DDCM_DATA0, 0, msg->addr << 1);
> +       sif_write_mask(i2c, DDC_DDCMD0, DDCM_DATA1, 8, msg->buf[0]);
> +       sif_write_mask(i2c, DDC_DDCMCTL1, DDCM_PGLEN_MASK, DDCM_PGLEN_OFFSET,
> +                      0x1);
> +       ddcm_trigger_mode(i2c, DDCM_WRITE_DATA);
> +
> +       ack = sif_read_mask(i2c, DDC_DDCMCTL1, DDCM_ACK_MASK, DDCM_ACK_OFFSET);
> +       dev_dbg(dev, "ack = %d\n", ack);
> +
> +       if (ack != 0x03) {
> +               dev_err(dev, "i2c ack err!\n");
> +               return -EIO;
> +       }
> +
> +       return 0;
> +}
> +
> +static int mtk_hdmi_i2c_xfer(struct i2c_adapter *adapter,
> +                            struct i2c_msg *msgs, int num)
> +{
> +       struct mtk_hdmi_i2c *i2c = adapter->algo_data;
> +       struct device *dev = adapter->dev.parent;
> +       int ret;
> +       int i;
> +
> +       if (!i2c) {
> +               dev_err(dev, "invalid arguments\n");
> +               return -EINVAL;
> +       }
> +
> +       sif_set_bit(i2c, DDC_DDCMCTL0, DDCM_SCL_STRECH);
> +       sif_set_bit(i2c, DDC_DDCMCTL0, DDCM_SM0EN);
> +       sif_clr_bit(i2c, DDC_DDCMCTL0, DDCM_ODRAIN);
> +
> +       if (sif_bit_is_set(i2c, DDC_DDCMCTL1, DDCM_TRI)) {
> +               dev_err(dev, "ddc line is busy!\n");
> +               return -EBUSY;
> +       }
> +
> +       sif_write_mask(i2c, DDC_DDCMCTL0, DDCM_CLK_DIV_MASK,
> +                      DDCM_CLK_DIV_OFFSET, SIF1_CLOK);
> +
> +       for (i = 0; i < num; i++) {
> +               struct i2c_msg *msg = &msgs[i];
> +
> +               dev_dbg(dev, "i2c msg, adr:0x%x, flags:%d, len :0x%x\n",
> +                       msg->addr, msg->flags, msg->len);
> +
> +               if (msg->flags & I2C_M_RD)
> +                       ret = mtk_hdmi_i2c_read_msg(i2c, msg);
> +               else
> +                       ret = mtk_hdmi_i2c_write_msg(i2c, msg);
> +               if (ret < 0)
> +                       goto xfer_end;
> +       }
> +
> +       ddcm_trigger_mode(i2c, DDCM_STOP);
> +
> +       return i;
> +
> +xfer_end:
> +       ddcm_trigger_mode(i2c, DDCM_STOP);
> +       dev_err(dev, "ddc failed!\n");
> +       return ret;
> +}
> +
> +static u32 mtk_hdmi_i2c_func(struct i2c_adapter *adapter)
> +{
> +       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
> +}
> +
> +static const struct i2c_algorithm mtk_hdmi_i2c_algorithm = {
> +       .master_xfer = mtk_hdmi_i2c_xfer,
> +       .functionality = mtk_hdmi_i2c_func,
> +};
> +
> +static int mtk_hdmi_i2c_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct mtk_hdmi_i2c *i2c;
> +       struct resource *mem;
> +       int ret;
> +
> +       i2c = devm_kzalloc(dev, sizeof(struct mtk_hdmi_i2c), GFP_KERNEL);
> +       if (!i2c)
> +               return -ENOMEM;
> +
> +       i2c->clk = devm_clk_get(dev, "ddc-i2c");
> +       if (IS_ERR(i2c->clk)) {
> +               dev_err(dev, "get ddc_clk failed : %p ,\n", i2c->clk);

nit, no space before ':'

> +               return PTR_ERR(i2c->clk);
> +       }
> +
> +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       i2c->regs = devm_ioremap_resource(&pdev->dev, mem);
> +       if (IS_ERR(i2c->regs)) {
> +               dev_err(dev, "get memory source fail!\n");

nit: don't really need to print anything here.

> +               return PTR_ERR(i2c->regs);
> +       }
> +
> +       ret = clk_prepare_enable(i2c->clk);
> +       if (ret) {
> +               dev_err(dev, "enable ddc clk failed!\n");
> +               return ret;
> +       }
> +
> +       strlcpy(i2c->adap.name, "mediatek-hdmi-i2c", sizeof(i2c->adap.name));
> +       i2c->adap.owner = THIS_MODULE;

i2c->adap.class = I2C_CLASS_DDC;

> +       i2c->adap.algo = &mtk_hdmi_i2c_algorithm;
> +       i2c->adap.retries = 3;

why set this?

> +       i2c->adap.dev.of_node = dev->of_node;
> +       i2c->adap.algo_data = i2c;
> +       i2c->adap.dev.parent = &pdev->dev;
> +
> +       ret = i2c_add_adapter(&i2c->adap);
> +       if (ret < 0) {
> +               dev_err(dev, "failed to add bus to i2c core\n");
> +               goto err_clk_disable;
> +       }
> +
> +       platform_set_drvdata(pdev, i2c);
> +
> +       dev_dbg(dev, "i2c->adap: %p\n", &i2c->adap);
> +       dev_dbg(dev, "i2c->clk: %p\n", i2c->clk);
> +       dev_dbg(dev, "physical adr: 0x%llx, end: 0x%llx\n", mem->start,
> +               mem->end);

remove these debugging lines.

> +
> +       return 0;
> +
> +err_clk_disable:
> +       clk_disable_unprepare(i2c->clk);
> +       return ret;
> +}
> +
> +static int mtk_hdmi_i2c_remove(struct platform_device *pdev)
> +{
> +       struct mtk_hdmi_i2c *i2c = platform_get_drvdata(pdev);
> +
> +       clk_disable_unprepare(i2c->clk);
> +       i2c_del_adapter(&i2c->adap);

To match probe order, call i2c_del_adapter() first.

> +
> +       return 0;
> +}
> +
> +static const struct of_device_id mtk_hdmi_i2c_match[] = {
> +       { .compatible = "mediatek,mt8173-hdmi-ddc", },
> +       {},
> +};
> +
> +struct platform_driver mtk_hdmi_ddc_driver = {
> +       .probe = mtk_hdmi_i2c_probe,
> +       .remove = mtk_hdmi_i2c_remove,
> +       .driver = {
> +               .name = "mediatek-hdmi-ddc",
> +               .of_match_table = mtk_hdmi_i2c_match,
> +       },
> +};
> +
> +MODULE_AUTHOR("Jie Qiu <jie.qiu at mediatek.com>");
> +MODULE_DESCRIPTION("MediaTek HDMI i2c Driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c
> new file mode 100644
> index 0000000..99c7ffc
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.c

What is the value in having this as a separate mtk_hdmi_hw.c file?
If these functions were in mtk_hdmi.c, they could all be static, and
the compiler
could inline them away.


> @@ -0,0 +1,652 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: Jie Qiu <jie.qiu at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#include "mtk_hdmi_hw.h"
> +#include "mtk_hdmi_regs.h"
> +#include "mtk_hdmi.h"

I think these usually go after system includes.

> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/hdmi.h>
> +#include <linux/io.h>
> +#include <linux/regmap.h>
> +
> +static u32 mtk_hdmi_read(struct mtk_hdmi *hdmi, u32 offset)
> +{
> +       return readl(hdmi->regs + offset);
> +}
> +
> +static void mtk_hdmi_write(struct mtk_hdmi *hdmi, u32 offset, u32 val)
> +{
> +       writel(val, hdmi->regs + offset);
> +}
> +
> +static void mtk_hdmi_mask(struct mtk_hdmi *hdmi, u32 offset, u32 val, u32 mask)
> +{
> +       u32 tmp = mtk_hdmi_read(hdmi, offset) & ~mask;
> +
> +       tmp |= (val & mask);
> +       mtk_hdmi_write(hdmi, offset, tmp);
> +}
> +
> +#define NCTS_BYTES          0x07

move above the functions

> +
> +void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdmi,
> +                          bool black)
> +{
> +       mtk_hdmi_mask(hdmi, VIDEO_CFG_4, black ? GEN_RGB : NORMAL_PATH,
> +                     VIDEO_SOURCE_SEL);
> +}
> +
> +void mtk_hdmi_hw_make_reg_writable(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
> +                          HDMI_PCLK_FREE_RUN, enable ? HDMI_PCLK_FREE_RUN : 0);
> +       regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C,
> +                          HDMI_ON | ANLG_ON, enable ? (HDMI_ON | ANLG_ON) : 0);
> +}
> +
> +void mtk_hdmi_hw_1p4_version_enable(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
> +                          HDMI2P0_EN, enable ? 0 : HDMI2P0_EN);
> +}
> +
> +void mtk_hdmi_hw_aud_mute(struct mtk_hdmi *hdmi, bool mute)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_AUDIO_CFG, mute ? AUDIO_ZERO : 0, AUDIO_ZERO);
> +}
> +
> +void mtk_hdmi_hw_reset(struct mtk_hdmi *hdmi)
> +{
> +       regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C,
> +                          HDMI_RST, HDMI_RST);
> +       regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C,
> +                          HDMI_RST, 0);
> +       mtk_hdmi_mask(hdmi, GRL_CFG3, 0, CFG3_CONTROL_PACKET_DELAY);
> +       regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG1C,
> +                          ANLG_ON, ANLG_ON);
> +}
> +
> +void mtk_hdmi_hw_enable_notice(struct mtk_hdmi *hdmi, bool enable_notice)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_CFG2, enable_notice ? CFG2_NOTICE_EN : 0,
> +                     CFG2_NOTICE_EN);
> +}
> +
> +void mtk_hdmi_hw_write_int_mask(struct mtk_hdmi *hdmi, u32 int_mask)
> +{
> +       mtk_hdmi_write(hdmi, GRL_INT_MASK, int_mask);
> +}
> +
> +void mtk_hdmi_hw_enable_dvi_mode(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_CFG1, enable ? CFG1_DVI : 0, CFG1_DVI);
> +}
> +
> +void mtk_hdmi_hw_send_info_frame(struct mtk_hdmi *hdmi, u8 *buffer, u8 len)
> +{
> +       u32 ctrl_reg = GRL_CTRL;
> +       int i;
> +       u8 *frame_data;
> +       u8 frame_type;
> +       u8 frame_ver;
> +       u8 frame_len;
> +       u8 checksum;
> +       int ctrl_frame_en = 0;
> +
> +       frame_type = *buffer;
> +       buffer += 1;
> +       frame_ver = *buffer;
> +       buffer += 1;
> +       frame_len = *buffer;
> +       buffer += 1;
> +       checksum = *buffer;
> +       buffer += 1;
> +       frame_data = buffer;
> +
> +       dev_dbg(hdmi->dev,
> +               "frame_type:0x%x,frame_ver:0x%x,frame_len:0x%x,checksum:0x%x\n",
> +               frame_type, frame_ver, frame_len, checksum);
> +
> +       switch (frame_type) {
> +       case HDMI_INFOFRAME_TYPE_AVI:
> +               ctrl_frame_en = CTRL_AVI_EN;
> +               ctrl_reg = GRL_CTRL;
> +               break;
> +       case HDMI_INFOFRAME_TYPE_SPD:
> +               ctrl_frame_en = CTRL_SPD_EN;
> +               ctrl_reg = GRL_CTRL;
> +               break;
> +       case HDMI_INFOFRAME_TYPE_AUDIO:
> +               ctrl_frame_en = CTRL_AUDIO_EN;
> +               ctrl_reg = GRL_CTRL;
> +               break;
> +       case HDMI_INFOFRAME_TYPE_VENDOR:
> +               ctrl_frame_en = VS_EN;
> +               ctrl_reg = GRL_ACP_ISRC_CTRL;
> +               break;
> +       default:

Just fall through if none of the above?

> +               break;
> +       }
> +       mtk_hdmi_mask(hdmi, ctrl_reg, 0, ctrl_frame_en);
> +       mtk_hdmi_write(hdmi, GRL_INFOFRM_TYPE, frame_type);
> +       mtk_hdmi_write(hdmi, GRL_INFOFRM_VER, frame_ver);
> +       mtk_hdmi_write(hdmi, GRL_INFOFRM_LNG, frame_len);
> +
> +       mtk_hdmi_write(hdmi, GRL_IFM_PORT, checksum);
> +       for (i = 0; i < frame_len; i++)
> +               mtk_hdmi_write(hdmi, GRL_IFM_PORT, frame_data[i]);
> +
> +       mtk_hdmi_mask(hdmi, ctrl_reg, ctrl_frame_en, ctrl_frame_en);
> +}
> +
> +void mtk_hdmi_hw_send_aud_packet(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_SHIFT_R2, enable ? 0 : AUDIO_PACKET_OFF,
> +                     AUDIO_PACKET_OFF);
> +}
> +
> +void mtk_hdmi_hw_config_sys(struct mtk_hdmi *hdmi)
> +{
> +       regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
> +                          HDMI_OUT_FIFO_EN | MHL_MODE_ON, 0);
> +       mdelay(2);

Can this be msleep instead of mdelay?
It is a bit rude to hog the CPU for 2 msec.

> +       regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
> +                          HDMI_OUT_FIFO_EN | MHL_MODE_ON, HDMI_OUT_FIFO_EN);
> +}
> +
> +void mtk_hdmi_hw_set_deep_color_mode(struct mtk_hdmi *hdmi)
> +{
> +       regmap_update_bits(hdmi->sys_regmap, hdmi->sys_offset + HDMI_SYS_CFG20,
> +                          DEEP_COLOR_MODE_MASK | DEEP_COLOR_EN, COLOR_8BIT_MODE);
> +}
> +
> +void mtk_hdmi_hw_send_av_mute(struct mtk_hdmi *hdmi)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_CFG4, 0, CTRL_AVMUTE);
> +       mdelay(2);

Can this be msleep instead of mdelay?

> +       mtk_hdmi_mask(hdmi, GRL_CFG4, CTRL_AVMUTE, CTRL_AVMUTE);
> +}
> +
> +void mtk_hdmi_hw_send_av_unmute(struct mtk_hdmi *hdmi)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_CFG4, CFG4_AV_UNMUTE_EN,
> +                     CFG4_AV_UNMUTE_EN | CFG4_AV_UNMUTE_SET);
> +       mdelay(2);

Can this be msleep instead of mdelay?

> +       mtk_hdmi_mask(hdmi, GRL_CFG4, CFG4_AV_UNMUTE_SET,
> +                     CFG4_AV_UNMUTE_EN | CFG4_AV_UNMUTE_SET);
> +}
> +
> +void mtk_hdmi_hw_ncts_enable(struct mtk_hdmi *hdmi, bool on)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_CTS_CTRL, on ? 0 : CTS_CTRL_SOFT,
> +                     CTS_CTRL_SOFT);
> +}
> +
> +void mtk_hdmi_hw_ncts_auto_write_enable(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_CTS_CTRL, enable ? NCTS_WRI_ANYTIME : 0,
> +                     NCTS_WRI_ANYTIME);
> +}
> +
> +void mtk_hdmi_hw_msic_setting(struct mtk_hdmi *hdmi,
> +                             struct drm_display_mode *mode)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_CFG4, 0, CFG_MHL_MODE);
> +
> +       if (mode->flags & DRM_MODE_FLAG_INTERLACE &&
> +           mode->clock == 74250 &&
> +           mode->vdisplay == 1080)
> +               mtk_hdmi_mask(hdmi, GRL_CFG2, 0, MHL_DE_SEL);
> +       else
> +               mtk_hdmi_mask(hdmi, GRL_CFG2, MHL_DE_SEL, MHL_DE_SEL);
> +}
> +
> +void mtk_hdmi_hw_aud_set_channel_swap(struct mtk_hdmi *hdmi,
> +                                     enum hdmi_aud_channel_swap_type swap)
> +{
> +       u8 swap_bit;
> +
> +       switch (swap) {
> +       case HDMI_AUD_SWAP_LR:
> +               swap_bit = LR_SWAP;
> +               break;
> +       case HDMI_AUD_SWAP_LFE_CC:
> +               swap_bit = LFE_CC_SWAP;
> +               break;
> +       case HDMI_AUD_SWAP_LSRS:
> +               swap_bit = LSRS_SWAP;
> +               break;
> +       case HDMI_AUD_SWAP_RLS_RRS:
> +               swap_bit = RLS_RRS_SWAP;
> +               break;
> +       case HDMI_AUD_SWAP_LR_STATUS:
> +               swap_bit = LR_STATUS_SWAP;
> +               break;
> +       default:
> +               swap_bit = LFE_CC_SWAP;
> +               break;
> +       }
> +       mtk_hdmi_mask(hdmi, GRL_CH_SWAP, swap_bit, 0xff);
> +}
> +
> +void mtk_hdmi_hw_aud_raw_data_enable(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_MIX_CTRL, enable ? MIX_CTRL_FLAT : 0,
> +                     MIX_CTRL_FLAT);
> +}
> +
> +void mtk_hdmi_hw_aud_set_bit_num(struct mtk_hdmi *hdmi,
> +                                enum hdmi_audio_sample_size bit_num)
> +{
> +       u32 val = 0;

no 0 init

> +
> +       if (bit_num == HDMI_AUDIO_SAMPLE_SIZE_16)
> +               val = AOUT_16BIT;
> +       else if (bit_num == HDMI_AUDIO_SAMPLE_SIZE_20)
> +               val = AOUT_20BIT;
> +       else if (bit_num == HDMI_AUDIO_SAMPLE_SIZE_24)
> +               val = AOUT_24BIT;
> +
> +       mtk_hdmi_mask(hdmi, GRL_AOUT_BNUM_SEL, val, 0x03);
> +}
> +
> +void mtk_hdmi_hw_aud_set_i2s_fmt(struct mtk_hdmi *hdmi,
> +                                enum hdmi_aud_i2s_fmt i2s_fmt)
> +{
> +       u32 val = 0;

no 0 init

> +
> +       val = mtk_hdmi_read(hdmi, GRL_CFG0);
> +       val &= ~0x33;

#define this mask

> +
> +       switch (i2s_fmt) {
> +       case HDMI_I2S_MODE_RJT_24BIT:
> +               val |= (CFG0_I2S_MODE_RTJ | CFG0_I2S_MODE_24BIT);
> +               break;
> +       case HDMI_I2S_MODE_RJT_16BIT:
> +               val |= (CFG0_I2S_MODE_RTJ | CFG0_I2S_MODE_16BIT);
> +               break;
> +       case HDMI_I2S_MODE_LJT_24BIT:
> +               val |= (CFG0_I2S_MODE_LTJ | CFG0_I2S_MODE_24BIT);
> +               break;
> +       case HDMI_I2S_MODE_LJT_16BIT:
> +               val |= (CFG0_I2S_MODE_LTJ | CFG0_I2S_MODE_16BIT);
> +               break;
> +       case HDMI_I2S_MODE_I2S_24BIT:
> +               val |= (CFG0_I2S_MODE_I2S | CFG0_I2S_MODE_24BIT);
> +               break;
> +       case HDMI_I2S_MODE_I2S_16BIT:
> +               val |= (CFG0_I2S_MODE_I2S | CFG0_I2S_MODE_16BIT);
> +               break;
> +       default:
> +               break;
> +       }
> +       mtk_hdmi_write(hdmi, GRL_CFG0, val);
> +}
> +
> +void mtk_hdmi_hw_aud_set_high_bitrate(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_AOUT_BNUM_SEL,
> +                     enable ? HIGH_BIT_RATE_PACKET_ALIGN : 0,
> +                     HIGH_BIT_RATE_PACKET_ALIGN);
> +       mtk_hdmi_mask(hdmi, GRL_AUDIO_CFG, enable ? HIGH_BIT_RATE : 0,
> +                     HIGH_BIT_RATE);
> +}
> +
> +void mtk_hdmi_phy_aud_dst_normal_double_enable(struct mtk_hdmi *hdmi,
> +                                              bool enable)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_AUDIO_CFG, enable ? DST_NORMAL_DOUBLE : 0,
> +                     DST_NORMAL_DOUBLE);
> +}
> +
> +void mtk_hdmi_hw_aud_dst_enable(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_AUDIO_CFG, enable ? SACD_DST : 0, SACD_DST);
> +}
> +
> +void mtk_hdmi_hw_aud_dsd_enable(struct mtk_hdmi *hdmi, bool enable)
> +{
> +       mtk_hdmi_mask(hdmi, GRL_AUDIO_CFG, enable ? SACD_SEL : 0, SACD_SEL);
> +}
> +
> +void mtk_hdmi_hw_aud_set_i2s_chan_num(struct mtk_hdmi *hdmi,
> +                                     enum hdmi_aud_channel_type channel_type,
> +                                     u8 channel_count)
> +{
> +       u8 val_1, val_2, val_3, val_4;

better:
  u8 sw[3], uv;

> +
> +       if (channel_count == 2) {
> +               val_1 = 0x04;
> +               val_2 = 0x50;

Some #defines with meaningful names would be nice here.


> +       } else if (channel_count == 3 || channel_count == 4) {
> +               if (channel_count == 4 &&
> +                   (channel_type == HDMI_AUD_CHAN_TYPE_3_0_LRS ||
> +                   channel_type == HDMI_AUD_CHAN_TYPE_4_0)) {
> +                       val_1 = 0x14;
> +               } else {
> +                       val_1 = 0x0c;
> +               }
> +               val_2 = 0x50;
> +       } else if (channel_count == 6 || channel_count == 5) {
> +               if (channel_count == 6 &&
> +                   channel_type != HDMI_AUD_CHAN_TYPE_5_1 &&
> +                   channel_type != HDMI_AUD_CHAN_TYPE_4_1_CLRS) {
> +                       val_1 = 0x3c;
> +                       val_2 = 0x50;
> +               } else {
> +                       val_1 = 0x1c;
> +                       val_2 = 0x50;
> +               }
> +       } else if (channel_count == 8 || channel_count == 7) {
> +               val_1 = 0x3c;
> +               val_2 = 0x50;
> +       } else {
> +               val_1 = 0x04;
> +               val_2 = 0x50;
> +       }
> +
> +       val_3 = 0xc6;
> +       val_4 = 0xfa;
> +
> +       mtk_hdmi_write(hdmi, GRL_CH_SW0, val_2);
> +       mtk_hdmi_write(hdmi, GRL_CH_SW1, val_3);
> +       mtk_hdmi_write(hdmi, GRL_CH_SW2, val_4);
> +       mtk_hdmi_write(hdmi, GRL_I2S_UV, val_1);
> +}
> +
> +void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi,
> +                                   enum hdmi_aud_input_type input_type)
> +{
> +       u32 val = 0;

no need to 0 init

> +
> +       val = mtk_hdmi_read(hdmi, GRL_CFG1);
> +       if (input_type == HDMI_AUD_INPUT_I2S &&
> +           (val & CFG1_SPDIF) == CFG1_SPDIF) {
> +               val &= ~CFG1_SPDIF;
> +       } else if (input_type == HDMI_AUD_INPUT_SPDIF &&
> +               (val & CFG1_SPDIF) == 0) {
> +               val |= CFG1_SPDIF;
> +       }
> +       mtk_hdmi_write(hdmi, GRL_CFG1, val);
> +}
> +
> +void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi,
> +                                       u8 *l_chan_status, u8 *r_chan_status,
> +                                       enum hdmi_audio_sample_frequency
> +                                       aud_hdmi_fs)
> +{
> +       u8 l_status[5];
> +       u8 r_status[5];
> +       u8 val = 0;

no need to 0 init

> +
> +       l_status[0] = l_chan_status[0];
> +       l_status[1] = l_chan_status[1];
> +       l_status[2] = l_chan_status[2];
> +       r_status[0] = r_chan_status[0];
> +       r_status[1] = r_chan_status[1];
> +       r_status[2] = r_chan_status[2];
> +
> +       l_status[0] &= ~0x02;
> +       r_status[0] &= ~0x02;
> +
> +       val = l_chan_status[3] & 0xf0;
> +       switch (aud_hdmi_fs) {
> +       case HDMI_AUDIO_SAMPLE_FREQUENCY_32000:
> +               val |= 0x03;
> +               break;
> +       case HDMI_AUDIO_SAMPLE_FREQUENCY_44100:
> +               break;
> +       case HDMI_AUDIO_SAMPLE_FREQUENCY_88200:
> +               val |= 0x08;
> +               break;
> +       case HDMI_AUDIO_SAMPLE_FREQUENCY_96000:
> +               val |= 0x0a;
> +               break;
> +       case HDMI_AUDIO_SAMPLE_FREQUENCY_48000:
> +               val |= 0x02;
> +               break;
> +       default:
> +               val |= 0x02;
> +               break;
> +       }
> +       l_status[3] = val;
> +       r_status[3] = val;
> +
> +       val = l_chan_status[4];
> +       val |= ((~(l_status[3] & 0x0f)) << 4);
> +       l_status[4] = val;
> +       r_status[4] = val;
> +
> +       val = l_status[0];

nit: You don't need to bounce through val here.
You can just write the *_status[n] value directly.

> +       mtk_hdmi_write(hdmi, GRL_I2S_C_STA0, val);
> +       mtk_hdmi_write(hdmi, GRL_L_STATUS_0, val);
> +
> +       val = r_status[0];
> +       mtk_hdmi_write(hdmi, GRL_R_STATUS_0, val);
> +
> +       val = l_status[1];
> +       mtk_hdmi_write(hdmi, GRL_I2S_C_STA1, val);
> +       mtk_hdmi_write(hdmi, GRL_L_STATUS_1, val);
> +
> +       val = r_status[1];
> +       mtk_hdmi_write(hdmi, GRL_R_STATUS_1, val);
> +
> +       val = l_status[2];
> +       mtk_hdmi_write(hdmi, GRL_I2S_C_STA2, val);
> +       mtk_hdmi_write(hdmi, GRL_L_STATUS_2, val);
> +
> +       val = r_status[2];
> +       mtk_hdmi_write(hdmi, GRL_R_STATUS_2, val);
> +
> +       val = l_status[3];
> +       mtk_hdmi_write(hdmi, GRL_I2S_C_STA3, val);
> +       mtk_hdmi_write(hdmi, GRL_L_STATUS_3, val);
> +
> +       val = r_status[3];
> +       mtk_hdmi_write(hdmi, GRL_R_STATUS_3, val);
> +
> +       val = l_status[4];
> +       mtk_hdmi_write(hdmi, GRL_I2S_C_STA4, val);
> +       mtk_hdmi_write(hdmi, GRL_L_STATUS_4, val);
> +
> +       val = r_status[4];
> +       mtk_hdmi_write(hdmi, GRL_R_STATUS_4, val);
> +
> +       for (val = 0; val < 19; val++) {
> +               mtk_hdmi_write(hdmi, GRL_L_STATUS_5 + val * 4, 0);
> +               mtk_hdmi_write(hdmi, GRL_R_STATUS_5 + val * 4, 0);
> +       }
> +}
> +
> +void mtk_hdmi_hw_aud_src_reenable(struct mtk_hdmi *hdmi)
> +{
> +       u32 val;
> +
> +       val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL);
> +       if (val & MIX_CTRL_SRC_EN) {
> +               val &= ~MIX_CTRL_SRC_EN;
> +               mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val);
> +               usleep_range(255, 512);

Those are some very precise values for a range...

> +               val |= MIX_CTRL_SRC_EN;
> +               mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val);
> +       }
> +}
> +
> +void mtk_hdmi_hw_aud_src_off(struct mtk_hdmi *hdmi)

Perhaps *_disable() would be more consistent.

> +{
> +       u32 val;
> +
> +       val = mtk_hdmi_read(hdmi, GRL_MIX_CTRL);
> +       val &= ~MIX_CTRL_SRC_EN;
> +       mtk_hdmi_write(hdmi, GRL_MIX_CTRL, val);
> +       mtk_hdmi_write(hdmi, GRL_SHIFT_L1, 0x00);
> +}
> +
> +void mtk_hdmi_hw_aud_set_mclk(struct mtk_hdmi *hdmi, enum hdmi_aud_mclk mclk)
> +{
> +       u32 val;
> +
> +       val = mtk_hdmi_read(hdmi, GRL_CFG5);
> +       val &= CFG5_CD_RATIO_MASK;
> +
> +       switch (mclk) {
> +       case HDMI_AUD_MCLK_128FS:
> +               val |= CFG5_FS128;
> +               break;
> +       case HDMI_AUD_MCLK_256FS:
> +               val |= CFG5_FS256;
> +               break;
> +       case HDMI_AUD_MCLK_384FS:
> +               val |= CFG5_FS384;
> +               break;
> +       case HDMI_AUD_MCLK_512FS:
> +               val |= CFG5_FS512;
> +               break;
> +       case HDMI_AUD_MCLK_768FS:
> +               val |= CFG5_FS768;
> +               break;
> +       default:
> +               val |= CFG5_FS256;
> +               break;
> +       }
> +       mtk_hdmi_write(hdmi, GRL_CFG5, val);
> +}
> +
> +void mtk_hdmi_hw_aud_aclk_inv_enable(struct mtk_hdmi *hdmi, bool enable)

nit: I prefer explicit _enable() and _disable() functions w/out the
'enable' parameter.

> +{
> +       u32 val;
> +
> +       val = mtk_hdmi_read(hdmi, GRL_CFG2);
> +       if (enable)
> +               val |= 0x80;
> +       else
> +               val &= ~0x80;
> +       mtk_hdmi_write(hdmi, GRL_CFG2, val);
> +}
> +
> +struct hdmi_acr_n {
> +       unsigned int clock;
> +       unsigned int n[3];
> +};
> +
> +/* Recommended N values from HDMI specification, tables 7-1 to 7-3 */
> +static const struct hdmi_acr_n hdmi_rec_n_table[] = {
> +       /* Clock, N: 32kHz 44.1kHz 48kHz */
> +       {  25175, {  4576,  7007,  6864 } },
> +       {  74176, { 11648, 17836, 11648 } },
> +       { 148352, { 11648,  8918,  5824 } },
> +       { 296703, {  5824,  4459,  5824 } },
> +       { 297000, {  3072,  4704,  5120 } },
> +       {      0, {  4096,  6272,  6144 } }, /* all other TMDS clocks */
> +};
> +
> +/**
> + * hdmi_recommended_n() - Return N value recommended by HDMI specification
> + * @freq: audio sample rate in Hz
> + * @clock: rounded TMDS clock in kHz
> + */
> +static unsigned int hdmi_recommended_n(unsigned int freq, unsigned int clock)
> +{
> +       const struct hdmi_acr_n *recommended;
> +       unsigned int i;
> +
> +       for (i = 0; i < ARRAY_SIZE(hdmi_rec_n_table) - 1; i++) {
> +               if (clock == hdmi_rec_n_table[i].clock)
> +                       break;
> +       }
> +       recommended = hdmi_rec_n_table + i;
> +
> +       switch (freq) {
> +       case 32000:
> +               return recommended->n[0];
> +       case 44100:
> +               return recommended->n[1];
> +       case 48000:
> +               return recommended->n[2];
> +       case 88200:
> +               return recommended->n[1] * 2;
> +       case 96000:
> +               return recommended->n[2] * 2;
> +       case 176400:
> +               return recommended->n[1] * 4;
> +       case 192000:
> +               return recommended->n[2] * 4;
> +       default:
> +               return (128 * freq) / 1000;
> +       }
> +}
> +
> +static unsigned int hdmi_mode_clock_to_hz(unsigned int clock)
> +{
> +       switch (clock) {
> +       case 25175:
> +               return 25174825;        /* 25.2/1.001 MHz */
> +       case 74176:
> +               return 74175824;        /* 74.25/1.001 MHz */
> +       case 148352:
> +               return 148351648;       /* 148.5/1.001 MHz */
> +       case 296703:
> +               return 296703297;       /* 297/1.001 MHz */
> +       default:
> +               return clock * 1000;
> +       }
> +}
> +
> +static unsigned int hdmi_expected_cts(unsigned int audio_sample_rate,
> +                                     unsigned int tmds_clock, unsigned int n)
> +{
> +       return DIV_ROUND_CLOSEST_ULL((u64)hdmi_mode_clock_to_hz(tmds_clock) * n,
> +                                    128 * audio_sample_rate);
> +}

Doug Anderson may have some opinions about how N & CTS are computed.


> +
> +static void do_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int n,
> +                                   unsigned int cts)
> +{
> +       unsigned char val[NCTS_BYTES];
> +       int i;
> +
> +       mtk_hdmi_write(hdmi, GRL_NCTS, 0);
> +       mtk_hdmi_write(hdmi, GRL_NCTS, 0);
> +       mtk_hdmi_write(hdmi, GRL_NCTS, 0);
> +       memset(val, 0, sizeof(val));

not necessary, since you fill in all 7 bytes anyway.

> +
> +       val[0] = (cts >> 24) & 0xff;
> +       val[1] = (cts >> 16) & 0xff;
> +       val[2] = (cts >> 8) & 0xff;
> +       val[3] = cts & 0xff;
> +
> +       val[4] = (n >> 16) & 0xff;
> +       val[5] = (n >> 8) & 0xff;
> +       val[6] = n & 0xff;

all of these "& 0xff" are not needed, since val is an unsigned char array.

> +
> +       for (i = 0; i < NCTS_BYTES; i++)
> +               mtk_hdmi_write(hdmi, GRL_NCTS, val[i]);

What an interesting design.  We write all 10 bytes to the same register address?
In this case, why bother with val at all?
Just directly call mtk_hdmi_write() for each of the bytes above.


> +}
> +
> +void mtk_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int sample_rate,
> +                             unsigned int clock)
> +{
> +       unsigned int n, cts;
> +
> +       n = hdmi_recommended_n(sample_rate, clock);
> +       cts = hdmi_expected_cts(sample_rate, clock, n);
> +
> +       dev_dbg(hdmi->dev, "%s: sample_rate=%u, clock=%d, cts=%u, n=%u\n",
> +               __func__, sample_rate, clock, n, cts);
> +
> +       mtk_hdmi_mask(hdmi, DUMMY_304, AUDIO_I2S_NCTS_SEL_64,
> +                     AUDIO_I2S_NCTS_SEL);
> +       do_hdmi_hw_aud_set_ncts(hdmi, n, cts);
> +}
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_hw.h b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.h
> new file mode 100644
> index 0000000..9013219
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_hw.h
> @@ -0,0 +1,73 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: Jie Qiu <jie.qiu at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef _MTK_HDMI_HW_H
> +#define _MTK_HDMI_HW_H
> +
> +#include <linux/types.h>
> +#include <linux/hdmi.h>
> +#include "mtk_hdmi.h"
> +
> +void mtk_hdmi_hw_vid_black(struct mtk_hdmi *hdmi, bool black);
> +void mtk_hdmi_hw_aud_mute(struct mtk_hdmi *hdmi, bool mute);
> +void mtk_hdmi_hw_send_info_frame(struct mtk_hdmi *hdmi, u8 *buffer, u8 len);
> +void mtk_hdmi_hw_send_aud_packet(struct mtk_hdmi *hdmi, bool enable);
> +int mtk_hdmi_hw_set_clock(struct mtk_hdmi *hctx, u32 clock);
> +void mtk_hdmi_hw_config_sys(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_hw_set_deep_color_mode(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_hw_send_av_mute(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_hw_send_av_unmute(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_hw_ncts_enable(struct mtk_hdmi *hdmi, bool on);
> +void mtk_hdmi_hw_aud_set_channel_swap(struct mtk_hdmi *hdmi,
> +                                     enum hdmi_aud_channel_swap_type swap);
> +void mtk_hdmi_hw_aud_raw_data_enable(struct mtk_hdmi *hdmi, bool enable);
> +void mtk_hdmi_hw_aud_set_bit_num(struct mtk_hdmi *hdmi,
> +                                enum hdmi_audio_sample_size bit_num);
> +void mtk_hdmi_hw_aud_set_high_bitrate(struct mtk_hdmi *hdmi, bool enable);
> +void mtk_hdmi_phy_aud_dst_normal_double_enable(struct mtk_hdmi *hdmi,
> +                                              bool enable);
> +void mtk_hdmi_hw_aud_dst_enable(struct mtk_hdmi *hdmi, bool enable);
> +void mtk_hdmi_hw_aud_dsd_enable(struct mtk_hdmi *hdmi, bool enable);
> +void mtk_hdmi_hw_aud_set_i2s_fmt(struct mtk_hdmi *hdmi,
> +                                enum hdmi_aud_i2s_fmt i2s_fmt);
> +void mtk_hdmi_hw_aud_set_i2s_chan_num(struct mtk_hdmi *hdmi,
> +                                     enum hdmi_aud_channel_type channel_type,
> +                                     u8 channel_count);
> +void mtk_hdmi_hw_aud_set_input_type(struct mtk_hdmi *hdmi,
> +                                   enum hdmi_aud_input_type input_type);
> +void mtk_hdmi_hw_aud_set_channel_status(struct mtk_hdmi *hdmi,
> +                                       u8 *l_chan_status, u8 *r_chan_staus,
> +                                       enum hdmi_audio_sample_frequency
> +                                       aud_hdmi_fs);
> +void mtk_hdmi_hw_aud_src_enable(struct mtk_hdmi *hdmi, bool enable);
> +void mtk_hdmi_hw_aud_set_mclk(struct mtk_hdmi *hdmi, enum hdmi_aud_mclk mclk);
> +void mtk_hdmi_hw_aud_src_off(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_hw_aud_src_reenable(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_hw_aud_aclk_inv_enable(struct mtk_hdmi *hdmi, bool enable);
> +void mtk_hdmi_hw_aud_set_ncts(struct mtk_hdmi *hdmi, unsigned int sample_rate,
> +                             unsigned int clock);
> +bool mtk_hdmi_hw_is_hpd_high(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_hw_make_reg_writable(struct mtk_hdmi *hdmi, bool enable);
> +void mtk_hdmi_hw_reset(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_hw_enable_notice(struct mtk_hdmi *hdmi, bool enable_notice);
> +void mtk_hdmi_hw_write_int_mask(struct mtk_hdmi *hdmi, u32 int_mask);
> +void mtk_hdmi_hw_enable_dvi_mode(struct mtk_hdmi *hdmi, bool enable);
> +void mtk_hdmi_hw_ncts_auto_write_enable(struct mtk_hdmi *hdmi, bool enable);
> +void mtk_hdmi_hw_msic_setting(struct mtk_hdmi *hdmi,
> +                             struct drm_display_mode *mode);
> +void mtk_hdmi_hw_1p4_version_enable(struct mtk_hdmi *hdmi, bool enable);
> +void mtk_hdmi_hw_htplg_irq_enable(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_hw_htplg_irq_disable(struct mtk_hdmi *hdmi);
> +void mtk_hdmi_hw_clear_htplg_irq(struct mtk_hdmi *hdmi);
> +
> +#endif /* _MTK_HDMI_HW_H */
> diff --git a/drivers/gpu/drm/mediatek/mtk_hdmi_regs.h b/drivers/gpu/drm/mediatek/mtk_hdmi_regs.h
> new file mode 100644
> index 0000000..8c1d318
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_hdmi_regs.h
> @@ -0,0 +1,221 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: Jie Qiu <jie.qiu at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +#ifndef _MTK_HDMI_REGS_H
> +#define _MTK_HDMI_REGS_H
> +
> +#define GRL_INT_MASK           0x18
> +#define GRL_IFM_PORT           0x188
> +#define GRL_CH_SWAP            0x198
> +#define LR_SWAP                                BIT(0)
> +#define LFE_CC_SWAP                    BIT(1)
> +#define LSRS_SWAP                      BIT(2)
> +#define RLS_RRS_SWAP                   BIT(3)
> +#define LR_STATUS_SWAP                 BIT(4)
> +#define GRL_I2S_C_STA0         0x140
> +#define GRL_I2S_C_STA1         0x144
> +#define GRL_I2S_C_STA2         0x148
> +#define GRL_I2S_C_STA3         0x14C
> +#define GRL_I2S_C_STA4         0x150
> +#define GRL_I2S_UV             0x154
> +#define GRL_ACP_ISRC_CTRL      0x158
> +#define VS_EN                          BIT(0)
> +#define ACP_EN                         BIT(1)
> +#define ISRC1_EN                       BIT(2)
> +#define ISRC2_EN                       BIT(3)
> +#define GAMUT_EN                       BIT(4)
> +#define GRL_CTS_CTRL           0x160
> +#define CTS_CTRL_SOFT                  BIT(0)
> +#define GRL_INT                        0x14
> +#define INT_MDI                                BIT(0)
> +#define INT_HDCP                       BIT(1)
> +#define INT_FIFO_O                     BIT(2)
> +#define INT_FIFO_U                     BIT(3)
> +#define INT_IFM_ERR                    BIT(4)
> +#define INT_INF_DONE                   BIT(5)
> +#define INT_NCTS_DONE                  BIT(6)
> +#define INT_CTRL_PKT_DONE              BIT(7)
> +#define GRL_INT_MASK           0x18
> +#define GRL_CTRL               0x1C
> +#define CTRL_GEN_EN                    BIT(2)
> +#define CTRL_SPD_EN                    BIT(3)
> +#define CTRL_MPEG_EN                   BIT(4)
> +#define CTRL_AUDIO_EN                  BIT(5)
> +#define CTRL_AVI_EN                    BIT(6)
> +#define CTRL_AVMUTE                    BIT(7)
> +#define        GRL_STATUS              0x20
> +#define STATUS_HTPLG                   BIT(0)
> +#define STATUS_PORD                    BIT(1)
> +#define GRL_DIVN               0x170
> +#define NCTS_WRI_ANYTIME               BIT(6)
> +#define GRL_AUDIO_CFG          0x17C
> +#define AUDIO_ZERO                     BIT(0)
> +#define HIGH_BIT_RATE                  BIT(1)
> +#define SACD_DST                       BIT(2)
> +#define DST_NORMAL_DOUBLE              BIT(3)
> +#define DSD_INV                                BIT(4)
> +#define LR_INV                         BIT(5)
> +#define LR_MIX                         BIT(6)
> +#define SACD_SEL                       BIT(7)
> +#define GRL_NCTS               0x184
> +#define GRL_CH_SW0             0x18C
> +#define GRL_CH_SW1             0x190
> +#define GRL_CH_SW2             0x194
> +#define GRL_INFOFRM_VER                0x19C
> +#define GRL_INFOFRM_TYPE       0x1A0
> +#define GRL_INFOFRM_LNG                0x1A4
> +#define GRL_MIX_CTRL           0x1B4
> +#define MIX_CTRL_SRC_EN                        BIT(0)
> +#define BYPASS_VOLUME                  BIT(1)
> +#define MIX_CTRL_FLAT                  BIT(7)
> +#define GRL_AOUT_BNUM_SEL      0x1C4
> +#define AOUT_24BIT                     0x00
> +#define AOUT_20BIT                     0x02
> +#define AOUT_16BIT                     0x03
> +#define HIGH_BIT_RATE_PACKET_ALIGN     (0x3 << 6)
> +#define GRL_SHIFT_L1           0x1C0
> +#define GRL_SHIFT_R2           0x1B0
> +#define AUDIO_PACKET_OFF               BIT(6)
> +#define GRL_CFG0               0x24
> +#define CFG0_I2S_MODE_RTJ              0x1
> +#define CFG0_I2S_MODE_LTJ              0x0
> +#define CFG0_I2S_MODE_I2S              0x2
> +#define CFG0_I2S_MODE_24BIT            0x00
> +#define CFG0_I2S_MODE_16BIT            0x10
> +#define GRL_CFG1               0x28
> +#define CFG1_EDG_SEL                   BIT(0)
> +#define CFG1_SPDIF                     BIT(1)
> +#define CFG1_DVI                       BIT(2)
> +#define CFG1_HDCP_DEBUG                        BIT(3)
> +#define GRL_CFG2               0x2c
> +#define CFG2_NOTICE_EN                 BIT(6)
> +#define MHL_DE_SEL                     BIT(3)
> +#define GRL_CFG3               0x30
> +#define CFG3_AES_KEY_INDEX_MASK                0x3f
> +#define CFG3_CONTROL_PACKET_DELAY      BIT(6)
> +#define CFG3_KSV_LOAD_START            BIT(7)
> +#define GRL_CFG4               0x34
> +#define CFG4_AES_KEY_LOAD              BIT(4)
> +#define CFG4_AV_UNMUTE_EN              BIT(5)
> +#define CFG4_AV_UNMUTE_SET             BIT(6)
> +#define CFG_MHL_MODE                   BIT(7)
> +#define GRL_CFG5               0x38
> +#define CFG5_CD_RATIO_MASK     0x8F
> +#define CFG5_FS128                     (0x1 << 4)
> +#define CFG5_FS256                     (0x2 << 4)
> +#define CFG5_FS384                     (0x3 << 4)
> +#define CFG5_FS512                     (0x4 << 4)
> +#define CFG5_FS768                     (0x6 << 4)
> +#define DUMMY_304              0x304
> +#define CHMO_SEL                       (0x3 << 2)
> +#define CHM1_SEL                       (0x3 << 4)
> +#define CHM2_SEL                       (0x3 << 6)
> +#define AUDIO_I2S_NCTS_SEL             BIT(1)
> +#define AUDIO_I2S_NCTS_SEL_64          (1 << 1)
> +#define AUDIO_I2S_NCTS_SEL_128         (0 << 1)
> +#define NEW_GCP_CTRL                   BIT(0)
> +#define NEW_GCP_CTRL_MERGE             BIT(0)
> +#define GRL_L_STATUS_0         0x200
> +#define GRL_L_STATUS_1         0x204
> +#define GRL_L_STATUS_2         0x208
> +#define GRL_L_STATUS_3         0x20c
> +#define GRL_L_STATUS_4         0x210
> +#define GRL_L_STATUS_5         0x214
> +#define GRL_L_STATUS_6         0x218
> +#define GRL_L_STATUS_7         0x21c
> +#define GRL_L_STATUS_8         0x220
> +#define GRL_L_STATUS_9         0x224
> +#define GRL_L_STATUS_10                0x228
> +#define GRL_L_STATUS_11                0x22c
> +#define GRL_L_STATUS_12                0x230
> +#define GRL_L_STATUS_13                0x234
> +#define GRL_L_STATUS_14                0x238
> +#define GRL_L_STATUS_15                0x23c
> +#define GRL_L_STATUS_16                0x240
> +#define GRL_L_STATUS_17                0x244
> +#define GRL_L_STATUS_18                0x248
> +#define GRL_L_STATUS_19                0x24c
> +#define GRL_L_STATUS_20                0x250
> +#define GRL_L_STATUS_21                0x254
> +#define GRL_L_STATUS_22                0x258
> +#define GRL_L_STATUS_23                0x25c
> +#define GRL_R_STATUS_0         0x260
> +#define GRL_R_STATUS_1         0x264
> +#define GRL_R_STATUS_2         0x268
> +#define GRL_R_STATUS_3         0x26c
> +#define GRL_R_STATUS_4         0x270
> +#define GRL_R_STATUS_5         0x274
> +#define GRL_R_STATUS_6         0x278
> +#define GRL_R_STATUS_7         0x27c
> +#define GRL_R_STATUS_8         0x280
> +#define GRL_R_STATUS_9         0x284
> +#define GRL_R_STATUS_10                0x288
> +#define GRL_R_STATUS_11                0x28c
> +#define GRL_R_STATUS_12                0x290
> +#define GRL_R_STATUS_13                0x294
> +#define GRL_R_STATUS_14                0x298
> +#define GRL_R_STATUS_15                0x29c
> +#define GRL_R_STATUS_16                0x2a0
> +#define GRL_R_STATUS_17                0x2a4
> +#define GRL_R_STATUS_18                0x2a8
> +#define GRL_R_STATUS_19                0x2ac
> +#define GRL_R_STATUS_20                0x2b0
> +#define GRL_R_STATUS_21                0x2b4
> +#define GRL_R_STATUS_22                0x2b8
> +#define GRL_R_STATUS_23                0x2bc
> +#define GRL_ABIST_CTRL0                0x2D4
> +#define GRL_ABIST_CTRL1                0x2D8
> +#define ABIST_EN                       BIT(7)
> +#define ABIST_DATA_FMT                 (0x7 << 0)
> +#define VIDEO_CFG_0            0x380
> +#define VIDEO_CFG_1            0x384
> +#define VIDEO_CFG_2            0x388
> +#define VIDEO_CFG_3            0x38c
> +#define VIDEO_CFG_4            0x390
> +#define VIDEO_SOURCE_SEL               BIT(7)
> +#define NORMAL_PATH                    (1 << 7)
> +#define GEN_RGB                                (0 << 7)
> +
> +#define HDMI_SYS_CFG1C         0x000
> +#define HDMI_ON                                BIT(0)
> +#define HDMI_RST                       BIT(1)
> +#define ANLG_ON                                BIT(2)
> +#define CFG10_DVI                      BIT(3)
> +#define HDMI_TST                       BIT(3)
> +#define SYS_KEYMASK1                   (0xff << 8)
> +#define SYS_KEYMASK2                   (0xff << 16)
> +#define AUD_OUTSYNC_EN                 BIT(24)
> +#define AUD_OUTSYNC_PRE_EN             BIT(25)
> +#define I2CM_ON                                BIT(26)
> +#define E2PROM_TYPE_8BIT               BIT(27)
> +#define MCM_E2PROM_ON                  BIT(28)
> +#define EXT_E2PROM_ON                  BIT(29)
> +#define HTPLG_PIN_SEL_OFF              BIT(30)
> +#define AES_EFUSE_ENABLE               BIT(31)
> +#define HDMI_SYS_CFG20         0x004
> +#define DEEP_COLOR_MODE_MASK           (3 << 1)
> +#define COLOR_8BIT_MODE                        (0 << 1)
> +#define COLOR_10BIT_MODE               (1 << 1)
> +#define COLOR_12BIT_MODE               (2 << 1)
> +#define COLOR_16BIT_MODE               (3 << 1)
> +#define DEEP_COLOR_EN                  BIT(0)
> +#define HDMI_AUDIO_TEST_SEL            BIT(8)
> +#define HDMI2P0_EN                     BIT(11)
> +#define HDMI_OUT_FIFO_EN               BIT(16)
> +#define HDMI_OUT_FIFO_CLK_INV          BIT(17)
> +#define MHL_MODE_ON                    BIT(28)
> +#define MHL_PP_MODE                    BIT(29)
> +#define MHL_SYNC_AUTO_EN               BIT(30)
> +#define HDMI_PCLK_FREE_RUN             BIT(31)
> +
> +#endif
> diff --git a/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c
> new file mode 100644
> index 0000000..5d9f07f
> --- /dev/null
> +++ b/drivers/gpu/drm/mediatek/mtk_mt8173_hdmi_phy.c
> @@ -0,0 +1,505 @@
> +/*
> + * Copyright (c) 2014 MediaTek Inc.
> + * Author: Jie Qiu <jie.qiu at mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/types.h>
> +
> +#define HDMI_CON0              0x00
> +#define RG_HDMITX_PLL_EN               BIT(31)
> +#define RG_HDMITX_PLL_FBKDIV           (0x7f << 24)
> +#define PLL_FBKDIV_SHIFT               24
> +#define RG_HDMITX_PLL_FBKSEL           (0x3 << 22)
> +#define PLL_FBKSEL_SHIFT               22
> +#define RG_HDMITX_PLL_PREDIV           (0x3 << 20)
> +#define PREDIV_SHIFT                   20
> +#define RG_HDMITX_PLL_POSDIV           (0x3 << 18)
> +#define POSDIV_SHIFT                   18
> +#define RG_HDMITX_PLL_RST_DLY          (0x3 << 16)
> +#define RG_HDMITX_PLL_IR               (0xf << 12)
> +#define PLL_IR_SHIFT                   12
> +#define RG_HDMITX_PLL_IC               (0xf << 8)
> +#define PLL_IC_SHIFT                   8
> +#define RG_HDMITX_PLL_BP               (0xf << 4)
> +#define PLL_BP_SHIFT                   4
> +#define RG_HDMITX_PLL_BR               (0x3 << 2)
> +#define PLL_BR_SHIFT                   2
> +#define RG_HDMITX_PLL_BC               (0x3 << 0)
> +#define PLL_BC_SHIFT                   0
> +#define HDMI_CON1              0x04
> +#define RG_HDMITX_PLL_DIVEN            (0x7 << 29)
> +#define PLL_DIVEN_SHIFT                        29
> +#define RG_HDMITX_PLL_AUTOK_EN         BIT(28)
> +#define RG_HDMITX_PLL_AUTOK_KF         (0x3 << 26)
> +#define RG_HDMITX_PLL_AUTOK_KS         (0x3 << 24)
> +#define RG_HDMITX_PLL_AUTOK_LOAD       BIT(23)
> +#define RG_HDMITX_PLL_BAND             (0x3f << 16)
> +#define RG_HDMITX_PLL_REF_SEL          BIT(15)
> +#define RG_HDMITX_PLL_BIAS_EN          BIT(14)
> +#define RG_HDMITX_PLL_BIAS_LPF_EN      BIT(13)
> +#define RG_HDMITX_PLL_TXDIV_EN         BIT(12)
> +#define RG_HDMITX_PLL_TXDIV            (0x3 << 10)
> +#define PLL_TXDIV_SHIFT                        10
> +#define RG_HDMITX_PLL_LVROD_EN         BIT(9)
> +#define RG_HDMITX_PLL_MONVC_EN         BIT(8)
> +#define RG_HDMITX_PLL_MONCK_EN         BIT(7)
> +#define RG_HDMITX_PLL_MONREF_EN                BIT(6)
> +#define RG_HDMITX_PLL_TST_EN           BIT(5)
> +#define RG_HDMITX_PLL_TST_CK_EN                BIT(4)
> +#define RG_HDMITX_PLL_TST_SEL          (0xf << 0)
> +#define HDMI_CON2              0x08
> +#define RGS_HDMITX_PLL_AUTOK_BAND      (0x7f << 8)
> +#define RGS_HDMITX_PLL_AUTOK_FAIL      BIT(1)
> +#define RG_HDMITX_EN_TX_CKLDO          BIT(0)
> +#define HDMI_CON3              0x0c
> +#define RG_HDMITX_SER_EN               (0xf << 28)
> +#define RG_HDMITX_PRD_EN               (0xf << 24)
> +#define RG_HDMITX_PRD_IMP_EN           (0xf << 20)
> +#define RG_HDMITX_DRV_EN               (0xf << 16)
> +#define RG_HDMITX_DRV_IMP_EN           (0xf << 12)
> +#define DRV_IMP_EN_SHIFT               12
> +#define RG_HDMITX_MHLCK_FORCE          BIT(10)
> +#define RG_HDMITX_MHLCK_PPIX_EN                BIT(9)
> +#define RG_HDMITX_MHLCK_EN             BIT(8)
> +#define RG_HDMITX_SER_DIN_SEL          (0xf << 4)
> +#define RG_HDMITX_SER_5T1_BIST_EN      BIT(3)
> +#define RG_HDMITX_SER_BIST_TOG         BIT(2)
> +#define RG_HDMITX_SER_DIN_TOG          BIT(1)
> +#define RG_HDMITX_SER_CLKDIG_INV       BIT(0)
> +#define HDMI_CON4              0x10
> +#define RG_HDMITX_PRD_IBIAS_CLK                (0xf << 24)
> +#define RG_HDMITX_PRD_IBIAS_D2         (0xf << 16)
> +#define RG_HDMITX_PRD_IBIAS_D1         (0xf << 8)
> +#define RG_HDMITX_PRD_IBIAS_D0         (0xf << 0)
> +#define PRD_IBIAS_CLK_SHIFT            24
> +#define PRD_IBIAS_D2_SHIFT             16
> +#define PRD_IBIAS_D1_SHIFT             8
> +#define PRD_IBIAS_D0_SHIFT             0
> +#define HDMI_CON5              0x14
> +#define RG_HDMITX_DRV_IBIAS_CLK                (0x3f << 24)
> +#define RG_HDMITX_DRV_IBIAS_D2         (0x3f << 16)
> +#define RG_HDMITX_DRV_IBIAS_D1         (0x3f << 8)
> +#define RG_HDMITX_DRV_IBIAS_D0         (0x3f << 0)
> +#define DRV_IBIAS_CLK_SHIFT            24
> +#define DRV_IBIAS_D2_SHIFT             16
> +#define DRV_IBIAS_D1_SHIFT             8
> +#define DRV_IBIAS_D0_SHIFT             0
> +#define HDMI_CON6              0x18
> +#define RG_HDMITX_DRV_IMP_CLK          (0x3f << 24)
> +#define RG_HDMITX_DRV_IMP_D2           (0x3f << 16)
> +#define RG_HDMITX_DRV_IMP_D1           (0x3f << 8)
> +#define RG_HDMITX_DRV_IMP_D0           (0x3f << 0)
> +#define DRV_IMP_CLK_SHIFT              24
> +#define DRV_IMP_D2_SHIFT               16
> +#define DRV_IMP_D1_SHIFT               8
> +#define DRV_IMP_D0_SHIFT               0
> +#define HDMI_CON7              0x1c
> +#define RG_HDMITX_MHLCK_DRV_IBIAS      (0x1f << 27)
> +#define RG_HDMITX_SER_DIN              (0x3ff << 16)
> +#define RG_HDMITX_CHLDC_TST            (0xf << 12)
> +#define RG_HDMITX_CHLCK_TST            (0xf << 8)
> +#define RG_HDMITX_RESERVE              (0xff << 0)
> +#define HDMI_CON8              0x20
> +#define RGS_HDMITX_2T1_LEV             (0xf << 16)
> +#define RGS_HDMITX_2T1_EDG             (0xf << 12)
> +#define RGS_HDMITX_5T1_LEV             (0xf << 8)
> +#define RGS_HDMITX_5T1_EDG             (0xf << 4)
> +#define RGS_HDMITX_PLUG_TST            BIT(0)
> +
> +struct mtk_hdmi_phy {
> +       void __iomem *regs;
> +       struct device *dev;
> +       struct clk *pll;
> +       struct clk_hw pll_hw;
> +       unsigned long pll_rate;
> +       u8 drv_imp_clk;
> +       u8 drv_imp_d2;
> +       u8 drv_imp_d1;
> +       u8 drv_imp_d0;
> +       u32 ibias;
> +       u32 ibias_up;
> +};
> +
> +static const u8 PREDIV[3][4] = {
> +       {0x0, 0x0, 0x0, 0x0},   /* 27Mhz */
> +       {0x1, 0x1, 0x1, 0x1},   /* 74Mhz */
> +       {0x1, 0x1, 0x1, 0x1}    /* 148Mhz */
> +};
> +
> +static const u8 TXDIV[3][4] = {
> +       {0x3, 0x3, 0x3, 0x2},   /* 27Mhz */
> +       {0x2, 0x1, 0x1, 0x1},   /* 74Mhz */
> +       {0x1, 0x0, 0x0, 0x0}    /* 148Mhz */
> +};
> +
> +static const u8 FBKSEL[3][4] = {
> +       {0x1, 0x1, 0x1, 0x1},   /* 27Mhz */
> +       {0x1, 0x0, 0x1, 0x1},   /* 74Mhz */
> +       {0x1, 0x0, 0x1, 0x1}    /* 148Mhz */
> +};
> +
> +static const u8 FBKDIV[3][4] = {
> +       {19, 24, 29, 19},       /* 27Mhz */
> +       {19, 24, 14, 19},       /* 74Mhz */
> +       {19, 24, 14, 19}        /* 148Mhz */
> +};
> +
> +static const u8 DIVEN[3][4] = {
> +       {0x2, 0x1, 0x1, 0x2},   /* 27Mhz */
> +       {0x2, 0x2, 0x2, 0x2},   /* 74Mhz */
> +       {0x2, 0x2, 0x2, 0x2}    /* 148Mhz */
> +};
> +
> +static const u8 HTPLLBP[3][4] = {
> +       {0xc, 0xc, 0x8, 0xc},   /* 27Mhz */
> +       {0xc, 0xf, 0xf, 0xc},   /* 74Mhz */
> +       {0xc, 0xf, 0xf, 0xc}    /* 148Mhz */
> +};
> +
> +static const u8 HTPLLBC[3][4] = {
> +       {0x2, 0x3, 0x3, 0x2},   /* 27Mhz */
> +       {0x2, 0x3, 0x3, 0x2},   /* 74Mhz */
> +       {0x2, 0x3, 0x3, 0x2}    /* 148Mhz */
> +};
> +
> +static const u8 HTPLLBR[3][4] = {
> +       {0x1, 0x1, 0x0, 0x1},   /* 27Mhz */
> +       {0x1, 0x2, 0x2, 0x1},   /* 74Mhz */
> +       {0x1, 0x2, 0x2, 0x1}    /* 148Mhz */
> +};
> +
> +static void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset,
> +                             u32 val, u32 mask)
> +{
> +       u32 tmp = readl(hdmi_phy->regs  + offset) & ~mask;
> +
> +       tmp |= (val & mask);
> +       writel(tmp, hdmi_phy->regs + offset);
> +}
> +
> +static inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw)
> +{
> +       return container_of(hw, struct mtk_hdmi_phy, pll_hw);
> +}
> +
> +static int mtk_hdmi_pll_prepare(struct clk_hw *hw)
> +{
> +       struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
> +
> +       dev_dbg(hdmi_phy->dev, "prepare\n");
> +
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_AUTOK_EN,
> +                         RG_HDMITX_PLL_AUTOK_EN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV,
> +                         RG_HDMITX_PLL_POSDIV);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, 0, RG_HDMITX_MHLCK_EN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_EN,
> +                         RG_HDMITX_PLL_BIAS_EN);
> +       usleep_range(100, 150);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_EN,
> +                         RG_HDMITX_PLL_EN);
> +       usleep_range(100, 150);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN,
> +                         RG_HDMITX_PLL_BIAS_LPF_EN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_TXDIV_EN,
> +                         RG_HDMITX_PLL_TXDIV_EN);
> +
> +       return 0;
> +}
> +
> +static void mtk_hdmi_pll_unprepare(struct clk_hw *hw)
> +{
> +       struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
> +
> +       dev_dbg(hdmi_phy->dev, "prepare\n");

nit: "unprepare" (or just '"%s\n", __func__)', here and everywhere.

> +
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, 0, RG_HDMITX_PLL_TXDIV_EN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, 0, RG_HDMITX_PLL_BIAS_LPF_EN);
> +       usleep_range(100, 150);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, 0, RG_HDMITX_PLL_EN);
> +       usleep_range(100, 150);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, 0, RG_HDMITX_PLL_BIAS_EN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, 0, RG_HDMITX_PLL_POSDIV);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, 0, RG_HDMITX_PLL_AUTOK_EN);
> +       usleep_range(100, 150);
> +}
> +
> +static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +                                unsigned long parent_rate)
> +{
> +       struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
> +       unsigned int pre_div;
> +       unsigned int div;
> +
> +       dev_dbg(hdmi_phy->dev, "set rate : %lu, parent: %lu\n", rate,

nit, no space before the first ':'.

> +               parent_rate);
> +
> +       if (rate <= 27000000) {
> +               pre_div = 0;
> +               div = 3;
> +       } else if (rate <= 74250000) {
> +               pre_div = 1;
> +               div = 2;
> +       } else {
> +               pre_div = 1;
> +               div = 1;
> +       }
> +
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0,
> +                         (pre_div << PREDIV_SHIFT),
> +                         RG_HDMITX_PLL_PREDIV);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV,
> +                         RG_HDMITX_PLL_POSDIV);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0,
> +                         (0x1 << PLL_IC_SHIFT) | (0x1 << PLL_IR_SHIFT),
> +                         RG_HDMITX_PLL_IC | RG_HDMITX_PLL_IR);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1,
> +                         (div << PLL_TXDIV_SHIFT),
> +                         RG_HDMITX_PLL_TXDIV);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0,
> +                         (0x1 << PLL_FBKSEL_SHIFT) | (19 << PLL_FBKDIV_SHIFT),
> +                         RG_HDMITX_PLL_FBKSEL | RG_HDMITX_PLL_FBKDIV);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1,
> +                         (0x2 << PLL_DIVEN_SHIFT),
> +                         RG_HDMITX_PLL_DIVEN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0,
> +                         (0xc << PLL_BP_SHIFT) | (0x2 << PLL_BC_SHIFT) |
> +                         (0x1 << PLL_BR_SHIFT),
> +                         RG_HDMITX_PLL_BP | RG_HDMITX_PLL_BC |
> +                         RG_HDMITX_PLL_BR);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, 0, RG_HDMITX_PRD_IMP_EN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON4,
> +                         (0x3 << PRD_IBIAS_CLK_SHIFT) |
> +                         (0x3 << PRD_IBIAS_D2_SHIFT) |
> +                         (0x3 << PRD_IBIAS_D1_SHIFT) |
> +                         (0x3 << PRD_IBIAS_D0_SHIFT),
> +                         RG_HDMITX_PRD_IBIAS_CLK |
> +                         RG_HDMITX_PRD_IBIAS_D2 |
> +                         RG_HDMITX_PRD_IBIAS_D1 |
> +                         RG_HDMITX_PRD_IBIAS_D0);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3,
> +                         (0x0 << DRV_IMP_EN_SHIFT),
> +                         RG_HDMITX_DRV_IMP_EN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6,
> +                         (hdmi_phy->drv_imp_clk << DRV_IMP_CLK_SHIFT) |
> +                         (hdmi_phy->drv_imp_d2 << DRV_IMP_D2_SHIFT) |
> +                         (hdmi_phy->drv_imp_d1 << DRV_IMP_D1_SHIFT) |
> +                         (hdmi_phy->drv_imp_d0 << DRV_IMP_D0_SHIFT),
> +                         RG_HDMITX_DRV_IMP_CLK | RG_HDMITX_DRV_IMP_D2 |
> +                         RG_HDMITX_DRV_IMP_D1 | RG_HDMITX_DRV_IMP_D0);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON5,
> +                         (hdmi_phy->ibias << DRV_IBIAS_CLK_SHIFT) |
> +                         (hdmi_phy->ibias << DRV_IBIAS_D2_SHIFT) |
> +                         (hdmi_phy->ibias << DRV_IBIAS_D1_SHIFT) |
> +                         (hdmi_phy->ibias << DRV_IBIAS_D0_SHIFT),
> +                         RG_HDMITX_DRV_IBIAS_CLK | RG_HDMITX_DRV_IBIAS_D2 |
> +                         RG_HDMITX_DRV_IBIAS_D1 | RG_HDMITX_DRV_IBIAS_D0);
> +       return 0;
> +}
> +
> +static long mtk_hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +                                   unsigned long *parent_rate)
> +{
> +       struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
> +
> +       hdmi_phy->pll_rate = rate;
> +       if (rate <= 74250000)
> +               *parent_rate = rate;
> +       else
> +               *parent_rate = rate / 2;
> +
> +       return rate;
> +}
> +
> +static unsigned long mtk_hdmi_pll_recalc_rate(struct clk_hw *hw,
> +                                             unsigned long parent_rate)
> +{
> +       struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw);
> +
> +       return hdmi_phy->pll_rate;
> +}
> +
> +static const struct clk_ops mtk_hdmi_pll_ops = {
> +       .prepare = mtk_hdmi_pll_prepare,
> +       .unprepare = mtk_hdmi_pll_unprepare,
> +       .set_rate = mtk_hdmi_pll_set_rate,
> +       .round_rate = mtk_hdmi_pll_round_rate,
> +       .recalc_rate = mtk_hdmi_pll_recalc_rate,
> +};
> +
> +static void mtk_hdmi_phy_enable_tmds(struct mtk_hdmi_phy *hdmi_phy)
> +{
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, RG_HDMITX_SER_EN,
> +                         RG_HDMITX_SER_EN);

nit: lots of these calls might be more readable (and easier to maintain &
review) if we used two helper functions:

mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, u32 mask);
mtk_hdmi_phy_clr_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, u32 mask);

and

mtk_hdmi_set_bits()
mtk_hdmi_clr_bits()


> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, RG_HDMITX_PRD_EN,
> +                         RG_HDMITX_PRD_EN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, RG_HDMITX_DRV_EN,
> +                         RG_HDMITX_DRV_EN);
> +       usleep_range(100, 150);
> +}
> +
> +static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy)
> +{
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, 0, RG_HDMITX_DRV_EN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, 0, RG_HDMITX_PRD_EN);
> +       mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, 0, RG_HDMITX_SER_EN);
> +}
> +
> +static int mtk_hdmi_phy_power_on(struct phy *phy)
> +{
> +       struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy);
> +       int ret;
> +
> +       ret = clk_prepare_enable(hdmi_phy->pll);
> +       if (ret < 0)
> +               return ret;
> +
> +       mtk_hdmi_phy_enable_tmds(hdmi_phy);
> +
> +       return 0;
> +}
> +
> +static int mtk_hdmi_phy_power_off(struct phy *phy)
> +{
> +       struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy);
> +
> +       mtk_hdmi_phy_disable_tmds(hdmi_phy);
> +       clk_disable_unprepare(hdmi_phy->pll);
> +
> +       return 0;
> +}
> +
> +static struct phy_ops mtk_hdmi_phy_ops = {

static const

> +       .power_on = mtk_hdmi_phy_power_on,
> +       .power_off = mtk_hdmi_phy_power_off,
> +       .owner = THIS_MODULE,
> +};
> +
> +static int mtk_hdmi_phy_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct mtk_hdmi_phy *hdmi_phy;
> +       struct resource *mem;
> +       struct clk *ref_clk;
> +       const char *ref_clk_name;
> +       struct clk_init_data clk_init = {
> +               .ops = &mtk_hdmi_pll_ops,
> +               .num_parents = 1,
> +               .parent_names = (const char * const *)&ref_clk_name,
> +               .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE,
> +       };
> +       struct phy *phy;
> +       struct phy_provider *phy_provider;
> +       int ret;
> +
> +       hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL);
> +       if (!hdmi_phy)
> +               return -ENOMEM;
> +
> +       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       hdmi_phy->regs = devm_ioremap_resource(dev, mem);
> +       if (IS_ERR(hdmi_phy->regs)) {
> +               ret = PTR_ERR(hdmi_phy->regs);
> +               dev_err(dev, "Failed to get memory resource: %d\n", ret);
> +               return ret;
> +       }
> +
> +       ref_clk = devm_clk_get(dev, "pll_ref");
> +       if (IS_ERR(ref_clk)) {
> +               ret = PTR_ERR(ref_clk);
> +               dev_err(&pdev->dev, "Failed to get PLL reference clock: %d\n",
> +                       ret);
> +               return ret;
> +       }
> +       ref_clk_name = __clk_get_name(ref_clk);
> +
> +       ret = of_property_read_string(dev->of_node, "clock-output-names",
> +                                     &clk_init.name);
> +       if (ret < 0) {
> +               dev_err(dev, "Failed to read clock-output-names: %d\n", ret);
> +               return ret;
> +       }
> +
> +       hdmi_phy->pll_hw.init = &clk_init;
> +       hdmi_phy->pll = devm_clk_register(dev, &hdmi_phy->pll_hw);
> +       if (IS_ERR(hdmi_phy->pll)) {
> +               ret = PTR_ERR(hdmi_phy->pll);
> +               dev_err(dev, "Failed to register PLL: %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = of_property_read_u32(dev->of_node, "mediatek,ibias",
> +                                  &hdmi_phy->ibias);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Failed to get ibias: %d\n", ret);
> +               return ret;
> +       }
> +
> +       ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up",
> +                                  &hdmi_phy->ibias_up);
> +       if (ret < 0) {
> +               dev_err(&pdev->dev, "Failed to get ibias up: %d\n", ret);
> +               return ret;
> +       }
> +
> +       dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n");
> +       hdmi_phy->drv_imp_clk = 0x30;
> +       hdmi_phy->drv_imp_d2 = 0x30;
> +       hdmi_phy->drv_imp_d1 = 0x30;
> +       hdmi_phy->drv_imp_d0 = 0x30;
> +
> +       phy = devm_phy_create(dev, NULL, &mtk_hdmi_phy_ops);
> +       if (IS_ERR(phy)) {
> +               dev_err(dev, "Failed to create HDMI PHY\n");
> +               return PTR_ERR(phy);
> +       }
> +       phy_set_drvdata(phy, hdmi_phy);
> +
> +       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
> +       if (IS_ERR(phy_provider))
> +               return PTR_ERR(phy_provider);
> +
> +       hdmi_phy->dev = dev;
> +       return of_clk_add_provider(dev->of_node, of_clk_src_simple_get,
> +                                  hdmi_phy->pll);
> +}
> +
> +static int mtk_hdmi_phy_remove(struct platform_device *pdev)
> +{
> +       return 0;
> +}
> +
> +static const struct of_device_id mtk_hdmi_phy_match[] = {
> +       { .compatible = "mediatek,mt8173-hdmi-phy", },
> +       {},
> +};
> +
> +struct platform_driver mtk_hdmi_phy_driver = {
> +       .probe = mtk_hdmi_phy_probe,
> +       .remove = mtk_hdmi_phy_remove,
> +       .driver = {
> +               .name = "mediatek-hdmi-phy",
> +               .of_match_table = mtk_hdmi_phy_match,
> +       },
> +};
> +
> +MODULE_AUTHOR("Jie Qiu <jie.qiu at mediatek.com>");
> +MODULE_DESCRIPTION("MediaTek MT8173 HDMI PHY Driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.7.0
>



More information about the Linux-mediatek mailing list