All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform
@ 2014-11-04 13:33 ` Andy Yan
  0 siblings, 0 replies; 20+ messages in thread
From: Andy Yan @ 2014-11-04 13:33 UTC (permalink / raw)
  To: airlied, heiko, fabio.estevam, rmk+kernel
  Cc: Greg Kroah-Hartman, Grant Likely, Rob Herring, Philipp Zabel,
	Shawn Guo, Andy yan, Josh Boyer, Sean Paul, Inki Dae,
	Dave Airlie, Arnd Bergmann, Lucas Stach, Zubair.Kakakhel,
	djkurtz, ykk, linux-kernel, dri-devel, devel, devicetree,
	linux-rockchip

From: Andy yan <andy.yan@rock-chips.com>

We found freescale imx6 and rockchip rk3288 and Ingenic JZ4780 (Xburst/MIPS)
use the interface compatible Designware HDMI IP, but they also have some
lightly difference, such as phy pll configuration, register width(imx hdmi
register is one byte, but rk3288 is 4 bytes width and can only access by word),
4K support(imx6 doesn't support 4k, but rk3288 does).

To reuse the imx-hdmi driver, we do this patch set:
patch (1): split out imx-soc code from imx-hdmi to dw_hdmi-imx.c
patch (2): move imx-hdmi to bridge/, and rename to dw-hdmi to
make this driver indepent of drm-imx . And we will add rockchip 
platform specific code dw_hdmi-rockchip.c later, this is depend
on drm-rockchip.

Andy Yan (1):
  imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi

Andy yan (1):
  move imx-hdmi to bridge/dw-hdmi

 drivers/gpu/drm/bridge/Kconfig        |    5 +
 drivers/gpu/drm/bridge/Makefile       |    1 +
 drivers/gpu/drm/bridge/dw_hdmi.c      | 1651 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi.h      | 1032 +++++++++++++++++++
 drivers/staging/imx-drm/Kconfig       |    1 +
 drivers/staging/imx-drm/Makefile      |    2 +-
 drivers/staging/imx-drm/dw_hdmi-imx.c |  214 ++++
 drivers/staging/imx-drm/imx-hdmi.c    | 1767 ---------------------------------
 drivers/staging/imx-drm/imx-hdmi.h    | 1032 -------------------
 include/drm/bridge/dw_hdmi.h          |  114 +++
 10 files changed, 3019 insertions(+), 2800 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi.h
 create mode 100644 drivers/staging/imx-drm/dw_hdmi-imx.c
 delete mode 100644 drivers/staging/imx-drm/imx-hdmi.c
 delete mode 100644 drivers/staging/imx-drm/imx-hdmi.h
 create mode 100644 include/drm/bridge/dw_hdmi.h

-- 
1.8.3.2



^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform
@ 2014-11-04 13:33 ` Andy Yan
  0 siblings, 0 replies; 20+ messages in thread
From: Andy Yan @ 2014-11-04 13:33 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, heiko-4mtYJXux2i+zQB+pC5nmwQ,
	fabio.estevam-KZfg59tc24xl57MIdRCFDg,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: Greg Kroah-Hartman, Grant Likely, Rob Herring, Philipp Zabel,
	Shawn Guo, Andy yan, Josh Boyer, Sean Paul, Inki Dae,
	Dave Airlie, Arnd Bergmann, Lucas Stach,
	Zubair.Kakakhel-1AXoQHu6uovQT0dZR+AlfA,
	djkurtz-hpIqsD4AKlfQT0dZR+AlfA, ykk-TNX95d0MmH7DzftRWevZcw,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

From: Andy yan <andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>

We found freescale imx6 and rockchip rk3288 and Ingenic JZ4780 (Xburst/MIPS)
use the interface compatible Designware HDMI IP, but they also have some
lightly difference, such as phy pll configuration, register width(imx hdmi
register is one byte, but rk3288 is 4 bytes width and can only access by word),
4K support(imx6 doesn't support 4k, but rk3288 does).

To reuse the imx-hdmi driver, we do this patch set:
patch (1): split out imx-soc code from imx-hdmi to dw_hdmi-imx.c
patch (2): move imx-hdmi to bridge/, and rename to dw-hdmi to
make this driver indepent of drm-imx . And we will add rockchip 
platform specific code dw_hdmi-rockchip.c later, this is depend
on drm-rockchip.

Andy Yan (1):
  imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi

Andy yan (1):
  move imx-hdmi to bridge/dw-hdmi

 drivers/gpu/drm/bridge/Kconfig        |    5 +
 drivers/gpu/drm/bridge/Makefile       |    1 +
 drivers/gpu/drm/bridge/dw_hdmi.c      | 1651 ++++++++++++++++++++++++++++++
 drivers/gpu/drm/bridge/dw_hdmi.h      | 1032 +++++++++++++++++++
 drivers/staging/imx-drm/Kconfig       |    1 +
 drivers/staging/imx-drm/Makefile      |    2 +-
 drivers/staging/imx-drm/dw_hdmi-imx.c |  214 ++++
 drivers/staging/imx-drm/imx-hdmi.c    | 1767 ---------------------------------
 drivers/staging/imx-drm/imx-hdmi.h    | 1032 -------------------
 include/drm/bridge/dw_hdmi.h          |  114 +++
 10 files changed, 3019 insertions(+), 2800 deletions(-)
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi.c
 create mode 100644 drivers/gpu/drm/bridge/dw_hdmi.h
 create mode 100644 drivers/staging/imx-drm/dw_hdmi-imx.c
 delete mode 100644 drivers/staging/imx-drm/imx-hdmi.c
 delete mode 100644 drivers/staging/imx-drm/imx-hdmi.h
 create mode 100644 include/drm/bridge/dw_hdmi.h

-- 
1.8.3.2


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi
@ 2014-11-04 13:33   ` Andy Yan
  0 siblings, 0 replies; 20+ messages in thread
From: Andy Yan @ 2014-11-04 13:33 UTC (permalink / raw)
  To: airlied, heiko, fabio.estevam, rmk+kernel
  Cc: Greg Kroah-Hartman, Grant Likely, Rob Herring, Philipp Zabel,
	Shawn Guo, Andy yan, Josh Boyer, Sean Paul, Inki Dae,
	Dave Airlie, Arnd Bergmann, Lucas Stach, Zubair.Kakakhel,
	djkurtz, ykk, linux-kernel, dri-devel, devel, devicetree,
	linux-rockchip

imx6 and rockchip rk3288 and JZ4780 (Ingenic Xburst/MIPS)
use the interface compatible Designware HDMI IP, but they
also have some lightly difference, such as phy pll configuration,
register width(imx hdmi register is one byte, but rk3288 is 4
bytes width), 4K support(imx6 doesn't support 4k, but rk3288 does),
clk useage,and the crtc mux configuration is also platform specific.

To reuse the imx hdmi driver, split the platform specific code out
to dw_hdmi-imx.c.

Change-Id: I85e8d08754052b118423729a01c6d17bf485f383
---
 drivers/staging/imx-drm/Makefile      |   2 +-
 drivers/staging/imx-drm/dw_hdmi-imx.c | 214 ++++++++++
 drivers/staging/imx-drm/imx-hdmi.c    | 726 ++++++++++++++--------------------
 include/drm/bridge/dw_hdmi.h          | 114 ++++++
 4 files changed, 634 insertions(+), 422 deletions(-)
 create mode 100644 drivers/staging/imx-drm/dw_hdmi-imx.c
 create mode 100644 include/drm/bridge/dw_hdmi.h

diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
index 582c438..809027d 100644
--- a/drivers/staging/imx-drm/Makefile
+++ b/drivers/staging/imx-drm/Makefile
@@ -9,4 +9,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
 
 imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o
 obj-$(CONFIG_DRM_IMX_IPUV3)	+= imx-ipuv3-crtc.o
-obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o
+obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o dw_hdmi-imx.o
diff --git a/drivers/staging/imx-drm/dw_hdmi-imx.c b/drivers/staging/imx-drm/dw_hdmi-imx.c
new file mode 100644
index 0000000..71ac859
--- /dev/null
+++ b/drivers/staging/imx-drm/dw_hdmi-imx.c
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <drm/bridge/dw_hdmi.h>
+#include <video/imx-ipu-v3.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+
+#include "imx-drm.h"
+
+struct imx_hdmi_priv {
+	struct device *dev;
+	struct clk *isfr_clk;
+	struct clk *iahb_clk;
+	struct regmap *regmap;
+};
+
+static const struct mpll_config imx_mpll_cfg[] = {
+	{
+		45250000, {
+			{ 0x01e0, 0x0000 },
+			{ 0x21e1, 0x0000 },
+			{ 0x41e2, 0x0000 }
+		},
+	}, {
+		92500000, {
+			{ 0x0140, 0x0005 },
+			{ 0x2141, 0x0005 },
+			{ 0x4142, 0x0005 },
+	},
+	}, {
+		148500000, {
+			{ 0x00a0, 0x000a },
+			{ 0x20a1, 0x000a },
+			{ 0x40a2, 0x000a },
+		},
+	}, {
+		~0UL, {
+			{ 0x00a0, 0x000a },
+			{ 0x2001, 0x000f },
+			{ 0x4002, 0x000f },
+		},
+	}
+};
+
+static const struct curr_ctrl imx_cur_ctr[] = {
+	/*      pixelclk     bpp8    bpp10   bpp12 */
+	{
+		54000000, { 0x091c, 0x091c, 0x06dc },
+	}, {
+		58400000, { 0x091c, 0x06dc, 0x06dc },
+	}, {
+		72000000, { 0x06dc, 0x06dc, 0x091c },
+	}, {
+		74250000, { 0x06dc, 0x0b5c, 0x091c },
+	}, {
+		118800000, { 0x091c, 0x091c, 0x06dc },
+	}, {
+		216000000, { 0x06dc, 0x0b5c, 0x091c },
+	}
+};
+
+static int imx_hdmi_parse_dt(struct imx_hdmi_priv *hdmi)
+{
+	struct device_node *np = hdmi->dev->of_node;
+
+	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
+	if (IS_ERR(hdmi->regmap)) {
+		dev_err(hdmi->dev, "Unable to get gpr\n");
+		return PTR_ERR(hdmi->regmap);
+	}
+
+	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
+	if (IS_ERR(hdmi->isfr_clk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI isfr clk\n");
+		return PTR_ERR(hdmi->isfr_clk);
+	}
+
+	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
+	if (IS_ERR(hdmi->iahb_clk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI iahb clk\n");
+		return PTR_ERR(hdmi->iahb_clk);
+	}
+
+	return 0;
+}
+
+static void *imx_hdmi_imx_setup(struct platform_device *pdev)
+{
+	struct imx_hdmi_priv *hdmi;
+	int ret;
+
+	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return ERR_PTR(-ENOMEM);
+	hdmi->dev = &pdev->dev;
+
+	ret = imx_hdmi_parse_dt(hdmi);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	ret = clk_prepare_enable(hdmi->isfr_clk);
+	if (ret) {
+		dev_err(hdmi->dev,
+			"Cannot enable HDMI isfr clock: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	ret = clk_prepare_enable(hdmi->iahb_clk);
+	if (ret) {
+		dev_err(hdmi->dev,
+			"Cannot enable HDMI iahb clock: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	return hdmi;
+}
+
+static void imx_hdmi_imx_exit(void *priv)
+{
+	struct imx_hdmi_priv *hdmi = (struct imx_hdmi_priv *)priv;
+
+	clk_disable_unprepare(hdmi->isfr_clk);
+
+	clk_disable_unprepare(hdmi->iahb_clk);
+}
+
+static void imx_hdmi_imx_set_crtc_mux(void *priv, struct drm_encoder *encoder)
+{
+	struct imx_hdmi_priv *hdmi = (struct imx_hdmi_priv *)priv;
+	int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
+
+	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
+			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
+			   mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
+}
+
+static void imx_hdmi_imx_encoder_prepare(struct drm_connector *connector,
+					struct drm_encoder *encoder)
+{
+	imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24);
+}
+
+static struct imx_hdmi_plat_data imx6q_hdmi_drv_data = {
+	.setup			= imx_hdmi_imx_setup,
+	.exit			= imx_hdmi_imx_exit,
+	.set_crtc_mux		= imx_hdmi_imx_set_crtc_mux,
+	.encoder_prepare	= imx_hdmi_imx_encoder_prepare,
+	.mpll_cfg		= imx_mpll_cfg,
+	.cur_ctr		= imx_cur_ctr,
+	.dev_type		= IMX6Q_HDMI,
+};
+
+static struct imx_hdmi_plat_data imx6dl_hdmi_drv_data = {
+	.setup			= imx_hdmi_imx_setup,
+	.exit			= imx_hdmi_imx_exit,
+	.set_crtc_mux		= imx_hdmi_imx_set_crtc_mux,
+	.encoder_prepare	= imx_hdmi_imx_encoder_prepare,
+	.mpll_cfg		= imx_mpll_cfg,
+	.cur_ctr		= imx_cur_ctr,
+	.dev_type		= IMX6DL_HDMI,
+};
+
+static const struct of_device_id imx_hdmi_imx_ids[] = {
+	{ .compatible = "fsl,imx6q-hdmi",
+	  .data = &imx6q_hdmi_drv_data
+	}, {
+	  .compatible = "fsl,imx6dl-hdmi",
+	  .data = &imx6dl_hdmi_drv_data
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, imx_hdmi_imx_dt_ids);
+
+static int imx_hdmi_imx_probe(struct platform_device *pdev)
+{
+	const struct imx_hdmi_plat_data *plat_data;
+	const struct of_device_id *match;
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	match = of_match_node(imx_hdmi_imx_ids, pdev->dev.of_node);
+	plat_data = match->data;
+
+	return imx_hdmi_pltfm_register(pdev, plat_data);
+}
+
+static int imx_hdmi_imx_remove(struct platform_device *pdev)
+{
+	return imx_hdmi_pltfm_unregister(pdev);
+}
+
+static struct platform_driver imx_hdmi_imx_pltfm_driver = {
+	.probe  = imx_hdmi_imx_probe,
+	.remove = imx_hdmi_imx_remove,
+	.driver = {
+		.name = "dwhdmi-imx",
+		.owner = THIS_MODULE,
+		.of_match_table = imx_hdmi_imx_ids,
+	},
+};
+
+module_platform_driver(imx_hdmi_imx_pltfm_driver);
+
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
+MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:dwhdmi-imx");
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index aaec6b2..138706c 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -16,23 +16,16 @@
 #include <linux/irq.h>
 #include <linux/delay.h>
 #include <linux/err.h>
-#include <linux/clk.h>
 #include <linux/hdmi.h>
-#include <linux/regmap.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 #include <linux/of_device.h>
-
+#include <drm/drm_of.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder_slave.h>
-#include <video/imx-ipu-v3.h>
+#include <drm/bridge/dw_hdmi.h>
 
 #include "imx-hdmi.h"
-#include "imx-drm.h"
-
-#define HDMI_EDID_LEN		512
 
 #define RGB			0
 #define YCBCR444		1
@@ -54,11 +47,6 @@ enum hdmi_datamap {
 	YCbCr422_12B = 0x12,
 };
 
-enum imx_hdmi_devtype {
-	IMX6Q_HDMI,
-	IMX6DL_HDMI,
-};
-
 static const u16 csc_coeff_default[3][4] = {
 	{ 0x2000, 0x0000, 0x0000, 0x0000 },
 	{ 0x0000, 0x2000, 0x0000, 0x0000 },
@@ -89,72 +77,44 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
 	{ 0x6756, 0x78ab, 0x2000, 0x0200 }
 };
 
-struct hdmi_vmode {
-	bool mdvi;
-	bool mhsyncpolarity;
-	bool mvsyncpolarity;
-	bool minterlaced;
-	bool mdataenablepolarity;
-
-	unsigned int mpixelclock;
-	unsigned int mpixelrepetitioninput;
-	unsigned int mpixelrepetitionoutput;
-};
-
-struct hdmi_data_info {
-	unsigned int enc_in_format;
-	unsigned int enc_out_format;
-	unsigned int enc_color_depth;
-	unsigned int colorimetry;
-	unsigned int pix_repet_factor;
-	unsigned int hdcp_enable;
-	struct hdmi_vmode video_mode;
-};
-
-struct imx_hdmi {
-	struct drm_connector connector;
-	struct drm_encoder encoder;
-
-	enum imx_hdmi_devtype dev_type;
-	struct device *dev;
-	struct clk *isfr_clk;
-	struct clk *iahb_clk;
-
-	struct hdmi_data_info hdmi_data;
-	int vic;
-
-	u8 edid[HDMI_EDID_LEN];
-	bool cable_plugin;
+/*On rockchip platform, no-word access to the hdmi
+ * register will causes an imprecise external abort
+ */
+static inline void hdmi_writel(struct imx_hdmi *hdmi, u32 val, int offset)
+{
+	writel(val, hdmi->regs + (offset << 2));
+}
 
-	bool phy_enabled;
-	struct drm_display_mode previous_mode;
+static inline u32 hdmi_readl(struct imx_hdmi *hdmi, int offset)
+{
+	return readl(hdmi->regs + (offset << 2));
+}
 
-	struct regmap *regmap;
-	struct i2c_adapter *ddc;
-	void __iomem *regs;
+static void hdmi_modl(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
+{
+	u32 val = hdmi_readl(hdmi, reg) & ~mask;
 
-	unsigned int sample_rate;
-	int ratio;
-};
+	val |= data & mask;
+	hdmi_writel(hdmi, val, reg);
+}
 
-static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di)
+static void hdmi_mask_writel(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+			     u32 shift, u32 mask)
 {
-	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
-			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
-			   ipu_di << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
+	hdmi_modl(hdmi, data << shift, mask, reg);
 }
 
-static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset)
+static inline void hdmi_writeb(struct imx_hdmi *hdmi, u32 val, int offset)
 {
 	writeb(val, hdmi->regs + offset);
 }
 
-static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset)
+static inline u32 hdmi_readb(struct imx_hdmi *hdmi, int offset)
 {
 	return readb(hdmi->regs + offset);
 }
 
-static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
+static void hdmi_modb(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
 {
 	u8 val = hdmi_readb(hdmi, reg) & ~mask;
 
@@ -162,8 +122,8 @@ static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
 	hdmi_writeb(hdmi, val, reg);
 }
 
-static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg,
-		      u8 shift, u8 mask)
+static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+		      u32 shift, u32 mask)
 {
 	hdmi_modb(hdmi, data << shift, mask, reg);
 }
@@ -171,22 +131,22 @@ static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg,
 static void hdmi_set_clock_regenerator_n(struct imx_hdmi *hdmi,
 					 unsigned int value)
 {
-	hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1);
-	hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
-	hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
+	hdmi->write(hdmi, value & 0xff, HDMI_AUD_N1);
+	hdmi->write(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
+	hdmi->write(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
 
 	/* nshift factor = 0 */
-	hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
+	hdmi->mod(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
 }
 
 static void hdmi_regenerate_cts(struct imx_hdmi *hdmi, unsigned int cts)
 {
 	/* Must be set/cleared first */
-	hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+	hdmi->mod(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
 
-	hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
-	hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
-	hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+	hdmi->write(hdmi, cts & 0xff, HDMI_AUD_CTS1);
+	hdmi->write(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
+	hdmi->write(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
 		    HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
 }
 
@@ -323,6 +283,7 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
 	}
 	if (ratio == 100)
 		return cts;
+
 	return (cts * ratio) / 100;
 }
 
@@ -338,7 +299,7 @@ static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi,
 
 	if (!clk_cts) {
 		dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n",
-			 __func__, pixel_clk);
+			__func__, pixel_clk);
 		return;
 	}
 
@@ -408,19 +369,19 @@ static void hdmi_video_sample(struct imx_hdmi *hdmi)
 	val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
 		((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
 		HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
-	hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
+	hdmi->write(hdmi, val, HDMI_TX_INVID0);
 
 	/* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
 	val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
 		HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
 		HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
-	hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
+	hdmi->write(hdmi, val, HDMI_TX_INSTUFFING);
+	hdmi->write(hdmi, 0x0, HDMI_TX_GYDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_GYDATA1);
+	hdmi->write(hdmi, 0x0, HDMI_TX_RCRDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_RCRDATA1);
+	hdmi->write(hdmi, 0x0, HDMI_TX_BCBDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_BCBDATA1);
 }
 
 static int is_color_space_conversion(struct imx_hdmi *hdmi)
@@ -477,17 +438,17 @@ static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi)
 		u16 coeff_b = (*csc_coeff)[1][i];
 		u16 coeff_c = (*csc_coeff)[2][i];
 
-		hdmi_writeb(hdmi, coeff_a & 0xff,
+		hdmi->write(hdmi, coeff_a & 0xff,
 			HDMI_CSC_COEF_A1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
-		hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
-		hdmi_writeb(hdmi, coeff_c & 0xff,
+		hdmi->write(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
+		hdmi->write(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_c & 0xff,
 			HDMI_CSC_COEF_C1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
 	}
 
-	hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
+	hdmi->mod(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
 		  HDMI_CSC_SCALE);
 }
 
@@ -515,8 +476,8 @@ static void hdmi_video_csc(struct imx_hdmi *hdmi)
 		return;
 
 	/* Configure the CSC registers */
-	hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
-	hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
+	hdmi->write(hdmi, interpolation | decimation, HDMI_CSC_CFG);
+	hdmi->mod(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
 		  HDMI_CSC_SCALE);
 
 	imx_hdmi_update_csc_coeffs(hdmi);
@@ -535,21 +496,22 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 	struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
 	u8 val, vp_conf;
 
-	if (hdmi_data->enc_out_format == RGB
-		|| hdmi_data->enc_out_format == YCBCR444) {
-		if (!hdmi_data->enc_color_depth)
+	if (hdmi_data->enc_out_format == RGB ||
+	    hdmi_data->enc_out_format == YCBCR444) {
+		if (!hdmi_data->enc_color_depth) {
 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
-		else if (hdmi_data->enc_color_depth == 8) {
+		} else if (hdmi_data->enc_color_depth == 8) {
 			color_depth = 4;
 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
-		} else if (hdmi_data->enc_color_depth == 10)
+		} else if (hdmi_data->enc_color_depth == 10) {
 			color_depth = 5;
-		else if (hdmi_data->enc_color_depth == 12)
+		} else if (hdmi_data->enc_color_depth == 12) {
 			color_depth = 6;
-		else if (hdmi_data->enc_color_depth == 16)
+		} else if (hdmi_data->enc_color_depth == 16) {
 			color_depth = 7;
-		else
+		} else {
 			return;
+		}
 	} else if (hdmi_data->enc_out_format == YCBCR422_8BITS) {
 		if (!hdmi_data->enc_color_depth ||
 		    hdmi_data->enc_color_depth == 8)
@@ -561,8 +523,9 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		else
 			return;
 		output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
-	} else
+	} else {
 		return;
+	}
 
 	/* set the packetizer registers */
 	val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
@@ -570,9 +533,9 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		((hdmi_data->pix_repet_factor <<
 		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
 		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
-	hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
+	hdmi->write(hdmi, val, HDMI_VP_PR_CD);
 
-	hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
+	hdmi->mod(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
 		  HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
 
 	/* Data from pixel repeater block */
@@ -584,14 +547,14 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 			  HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
 	}
 
-	hdmi_modb(hdmi, vp_conf,
+	hdmi->mod(hdmi, vp_conf,
 		  HDMI_VP_CONF_PR_EN_MASK |
 		  HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
 
-	hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
+	hdmi->mod(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
 		  HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
 
-	hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
+	hdmi->write(hdmi, remap_size, HDMI_VP_REMAP);
 
 	if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
 		vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
@@ -609,55 +572,55 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		return;
 	}
 
-	hdmi_modb(hdmi, vp_conf,
+	hdmi->mod(hdmi, vp_conf,
 		  HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
 		  HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF);
 
-	hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
+	hdmi->mod(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
 			HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
 		  HDMI_VP_STUFF_PP_STUFFING_MASK |
 		  HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF);
 
-	hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
+	hdmi->mod(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
 		  HDMI_VP_CONF);
 }
 
 static inline void hdmi_phy_test_clear(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
 		  HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_enable(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
 		  HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_clock(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
 		  HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_din(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_writeb(hdmi, bit, HDMI_PHY_TST1);
+	hdmi->write(hdmi, bit, HDMI_PHY_TST1);
 }
 
 static inline void hdmi_phy_test_dout(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_writeb(hdmi, bit, HDMI_PHY_TST2);
+	hdmi->write(hdmi, bit, HDMI_PHY_TST2);
 }
 
 static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec)
 {
-	while ((hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
+	while ((hdmi->read(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
 		if (msec-- == 0)
 			return false;
 		udelay(1000);
@@ -668,13 +631,13 @@ static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec)
 static void __hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
 			      unsigned char addr)
 {
-	hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
-	hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
-	hdmi_writeb(hdmi, (unsigned char)(data >> 8),
+	hdmi->write(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+	hdmi->write(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+	hdmi->write(hdmi, (unsigned char)(data >> 8),
 		HDMI_PHY_I2CM_DATAO_1_ADDR);
-	hdmi_writeb(hdmi, (unsigned char)(data >> 0),
+	hdmi->write(hdmi, (unsigned char)(data >> 0),
 		HDMI_PHY_I2CM_DATAO_0_ADDR);
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
 		HDMI_PHY_I2CM_OPERATION_ADDR);
 	hdmi_phy_wait_i2c_done(hdmi, 1000);
 }
@@ -688,116 +651,53 @@ static int hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
 
 static void imx_hdmi_phy_enable_power(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_PDZ_OFFSET,
 			 HDMI_PHY_CONF0_PDZ_MASK);
 }
 
 static void imx_hdmi_phy_enable_tmds(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_ENTMDS_OFFSET,
 			 HDMI_PHY_CONF0_ENTMDS_MASK);
 }
 
 static void imx_hdmi_phy_gen2_pddq(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
 			 HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
 }
 
 static void imx_hdmi_phy_gen2_txpwron(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
 			 HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
 }
 
 static void imx_hdmi_phy_sel_data_en_pol(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
 			 HDMI_PHY_CONF0_SELDATAENPOL_MASK);
 }
 
 static void imx_hdmi_phy_sel_interface_control(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_SELDIPIF_OFFSET,
 			 HDMI_PHY_CONF0_SELDIPIF_MASK);
 }
 
-enum {
-	RES_8,
-	RES_10,
-	RES_12,
-	RES_MAX,
-};
-
-struct mpll_config {
-	unsigned long mpixelclock;
-	struct {
-		u16 cpce;
-		u16 gmp;
-	} res[RES_MAX];
-};
-
-static const struct mpll_config mpll_config[] = {
-	{
-		45250000, {
-			{ 0x01e0, 0x0000 },
-			{ 0x21e1, 0x0000 },
-			{ 0x41e2, 0x0000 }
-		},
-	}, {
-		92500000, {
-			{ 0x0140, 0x0005 },
-			{ 0x2141, 0x0005 },
-			{ 0x4142, 0x0005 },
-		},
-	}, {
-		148500000, {
-			{ 0x00a0, 0x000a },
-			{ 0x20a1, 0x000a },
-			{ 0x40a2, 0x000a },
-		},
-	}, {
-		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
-		},
-	}
-};
-
-struct curr_ctrl {
-	unsigned long mpixelclock;
-	u16 curr[RES_MAX];
-};
-
-static const struct curr_ctrl curr_ctrl[] = {
-	/*	pixelclk     bpp8    bpp10   bpp12 */
-	{
-		 54000000, { 0x091c, 0x091c, 0x06dc },
-	}, {
-		 58400000, { 0x091c, 0x06dc, 0x06dc },
-	}, {
-		 72000000, { 0x06dc, 0x06dc, 0x091c },
-	}, {
-		 74250000, { 0x06dc, 0x0b5c, 0x091c },
-	}, {
-		118800000, { 0x091c, 0x091c, 0x06dc },
-	}, {
-		216000000, { 0x06dc, 0x0b5c, 0x091c },
-	}
-};
-
 static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 			      unsigned char res, int cscon)
 {
 	unsigned res_idx, i;
 	u8 val, msec;
+	const struct mpll_config *mpll_cfg = hdmi->plat_data->mpll_cfg;
+	const struct curr_ctrl   *curr_ctr = hdmi->plat_data->cur_ctr;
 
 	if (prep)
 		return -EINVAL;
@@ -823,7 +723,7 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	else
 		val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS;
 
-	hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL);
+	hdmi->write(hdmi, val, HDMI_MC_FLOWCTRL);
 
 	/* gen2 tx power off */
 	imx_hdmi_phy_gen2_txpwron(hdmi, 0);
@@ -832,39 +732,38 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	imx_hdmi_phy_gen2_pddq(hdmi, 1);
 
 	/* PHY reset */
-	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
-	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
+	hdmi->write(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
+	hdmi->write(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
 
-	hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
+	hdmi->write(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
 
 	hdmi_phy_test_clear(hdmi, 1);
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
 			HDMI_PHY_I2CM_SLAVE_ADDR);
 	hdmi_phy_test_clear(hdmi, 0);
 
 	/* PLL/MPLL Cfg - always match on final entry */
-	for (i = 0; i < ARRAY_SIZE(mpll_config) - 1; i++)
+	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++)
 		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    mpll_config[i].mpixelclock)
+		    mpll_cfg[i].mpixelclock)
 			break;
+	hdmi_phy_i2c_write(hdmi, mpll_cfg[i].res[res_idx].cpce, 0x06);
+	hdmi_phy_i2c_write(hdmi, mpll_cfg[i].res[res_idx].gmp, 0x15);
 
-	hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06);
-	hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15);
-
-	for (i = 0; i < ARRAY_SIZE(curr_ctrl); i++)
+	for (i = 0; curr_ctr[i].mpixelclock != (~0UL); i++)
 		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    curr_ctrl[i].mpixelclock)
+		    curr_ctr[i].mpixelclock)
 			break;
 
-	if (i >= ARRAY_SIZE(curr_ctrl)) {
+	if (curr_ctr[i].mpixelclock == (~0UL)) {
 		dev_err(hdmi->dev,
-				"Pixel clock %d - unsupported by HDMI\n",
-				hdmi->hdmi_data.video_mode.mpixelclock);
+			"Pixel clock %d - unsupported by HDMI\n",
+			hdmi->hdmi_data.video_mode.mpixelclock);
 		return -EINVAL;
 	}
 
 	/* CURRCTRL */
-	hdmi_phy_i2c_write(hdmi, curr_ctrl[i].curr[res_idx], 0x10);
+	hdmi_phy_i2c_write(hdmi, curr_ctr[i].curr[res_idx], 0x10);
 
 	hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
 	hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
@@ -890,7 +789,7 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	/*Wait for PHY PLL lock */
 	msec = 5;
 	do {
-		val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+		val = hdmi->read(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
 		if (!val)
 			break;
 
@@ -942,12 +841,12 @@ static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi)
 		de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
 
 	/* disable rx detect */
-	hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
+	hdmi->mod(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
 		  HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
 
-	hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
+	hdmi->mod(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
 
-	hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
+	hdmi->mod(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
 		  HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
 }
 
@@ -977,7 +876,7 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 		HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT |
 		HDMI_FC_AVICONF0_BAR_DATA_NO_DATA;
 
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF0);
 
 	/* AVI Data Byte 2 -Set the Aspect Ratio */
 	if (aspect_16_9) {
@@ -1009,16 +908,16 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 	}
 
 	val = colorimetry | coded_ratio | act_ratio;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF1);
 
 	/* AVI Data Byte 3 */
 	val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry |
 		HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT |
 		HDMI_FC_AVICONF2_SCALING_NONE;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF2);
 
 	/* AVI Data Byte 4 */
-	hdmi_writeb(hdmi, hdmi->vic, HDMI_FC_AVIVID);
+	hdmi->write(hdmi, hdmi->vic, HDMI_FC_AVIVID);
 
 	/* AVI Data Byte 5- set up input and output pixel repetition */
 	val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) <<
@@ -1027,22 +926,22 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 		((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput <<
 		HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
 		HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
-	hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
+	hdmi->write(hdmi, val, HDMI_FC_PRCONF);
 
 	/* IT Content and quantization range = don't care */
 	val = HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS |
 		HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF3);
 
 	/* AVI Data Bytes 6-13 */
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIETB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIETB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISBB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISBB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIELB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIELB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISRB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISRB1);
 }
 
 static void hdmi_av_composer(struct imx_hdmi *hdmi,
@@ -1091,42 +990,42 @@ static void hdmi_av_composer(struct imx_hdmi *hdmi,
 		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
 		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
 
-	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
+	hdmi->write(hdmi, inv_val, HDMI_FC_INVIDCONF);
 
 	/* Set up horizontal active pixel width */
-	hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
-	hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
+	hdmi->write(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
+	hdmi->write(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
 
 	/* Set up vertical active lines */
-	hdmi_writeb(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
-	hdmi_writeb(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
+	hdmi->write(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
+	hdmi->write(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
 
 	/* Set up horizontal blanking pixel region width */
 	hblank = mode->htotal - mode->hdisplay;
-	hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
-	hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
+	hdmi->write(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
+	hdmi->write(hdmi, hblank, HDMI_FC_INHBLANK0);
 
 	/* Set up vertical blanking pixel region width */
 	vblank = mode->vtotal - mode->vdisplay;
-	hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
+	hdmi->write(hdmi, vblank, HDMI_FC_INVBLANK);
 
 	/* Set up HSYNC active edge delay width (in pixel clks) */
 	h_de_hs = mode->hsync_start - mode->hdisplay;
-	hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
-	hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
+	hdmi->write(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
+	hdmi->write(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
 
 	/* Set up VSYNC active edge delay (in lines) */
 	v_de_vs = mode->vsync_start - mode->vdisplay;
-	hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
+	hdmi->write(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
 
 	/* Set up HSYNC active pulse width (in pixel clks) */
 	hsync_len = mode->hsync_end - mode->hsync_start;
-	hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
-	hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
+	hdmi->write(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
+	hdmi->write(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
 
 	/* Set up VSYNC active edge delay (in lines) */
 	vsync_len = mode->vsync_end - mode->vsync_start;
-	hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
+	hdmi->write(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
 }
 
 static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi)
@@ -1146,33 +1045,33 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi)
 	u8 clkdis;
 
 	/* control period minimum duration */
-	hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
-	hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
-	hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
+	hdmi->write(hdmi, 12, HDMI_FC_CTRLDUR);
+	hdmi->write(hdmi, 32, HDMI_FC_EXCTRLDUR);
+	hdmi->write(hdmi, 1, HDMI_FC_EXCTRLSPAC);
 
 	/* Set to fill TMDS data channels */
-	hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
-	hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
-	hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
+	hdmi->write(hdmi, 0x0B, HDMI_FC_CH0PREAM);
+	hdmi->write(hdmi, 0x16, HDMI_FC_CH1PREAM);
+	hdmi->write(hdmi, 0x21, HDMI_FC_CH2PREAM);
 
 	/* Enable pixel clock and tmds data path */
 	clkdis = 0x7F;
 	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 
 	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 
 	/* Enable csc path */
 	if (is_color_space_conversion(hdmi)) {
 		clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
-		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+		hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 	}
 }
 
 static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi)
 {
-	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+	hdmi->mod(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
 }
 
 /* Workaround to clear the overflow condition */
@@ -1182,27 +1081,27 @@ static void imx_hdmi_clear_overflow(struct imx_hdmi *hdmi)
 	u8 val;
 
 	/* TMDS software reset */
-	hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
+	hdmi->write(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
 
-	val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
+	val = hdmi->read(hdmi, HDMI_FC_INVIDCONF);
 	if (hdmi->dev_type == IMX6DL_HDMI) {
-		hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+		hdmi->write(hdmi, val, HDMI_FC_INVIDCONF);
 		return;
 	}
 
 	for (count = 0; count < 4; count++)
-		hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+		hdmi->write(hdmi, val, HDMI_FC_INVIDCONF);
 }
 
 static void hdmi_enable_overflow_interrupts(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, 0, HDMI_FC_MASK2);
-	hdmi_writeb(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
+	hdmi->write(hdmi, 0, HDMI_FC_MASK2);
+	hdmi->write(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
 }
 
 static void hdmi_disable_overflow_interrupts(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
+	hdmi->write(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
 		    HDMI_IH_MUTE_FC_STAT2);
 }
 
@@ -1223,21 +1122,21 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 	}
 
 	if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
-		(hdmi->vic == 21) || (hdmi->vic == 22) ||
-		(hdmi->vic == 2) || (hdmi->vic == 3) ||
-		(hdmi->vic == 17) || (hdmi->vic == 18))
+	    (hdmi->vic == 21) || (hdmi->vic == 22) ||
+	    (hdmi->vic == 2) || (hdmi->vic == 3) ||
+	    (hdmi->vic == 17) || (hdmi->vic == 18))
 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
 	else
 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
 
 	if ((hdmi->vic == 10) || (hdmi->vic == 11) ||
-		(hdmi->vic == 12) || (hdmi->vic == 13) ||
-		(hdmi->vic == 14) || (hdmi->vic == 15) ||
-		(hdmi->vic == 25) || (hdmi->vic == 26) ||
-		(hdmi->vic == 27) || (hdmi->vic == 28) ||
-		(hdmi->vic == 29) || (hdmi->vic == 30) ||
-		(hdmi->vic == 35) || (hdmi->vic == 36) ||
-		(hdmi->vic == 37) || (hdmi->vic == 38))
+	    (hdmi->vic == 12) || (hdmi->vic == 13) ||
+	    (hdmi->vic == 14) || (hdmi->vic == 15) ||
+	    (hdmi->vic == 25) || (hdmi->vic == 26) ||
+	    (hdmi->vic == 27) || (hdmi->vic == 28) ||
+	    (hdmi->vic == 29) || (hdmi->vic == 30) ||
+	    (hdmi->vic == 35) || (hdmi->vic == 36) ||
+	    (hdmi->vic == 37) || (hdmi->vic == 38))
 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
 	else
 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
@@ -1266,9 +1165,9 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 	imx_hdmi_enable_video_path(hdmi);
 
 	/* not for DVI mode */
-	if (hdmi->hdmi_data.video_mode.mdvi)
+	if (hdmi->hdmi_data.video_mode.mdvi) {
 		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
-	else {
+	} else {
 		dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
 
 		/* HDMI Initialization Step E - Configure audio */
@@ -1294,18 +1193,18 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 /* Wait until we are registered to enable interrupts */
 static int imx_hdmi_fb_registered(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
 		    HDMI_PHY_I2CM_INT_ADDR);
 
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+	hdmi->write(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
 		    HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
 		    HDMI_PHY_I2CM_CTLINT_ADDR);
 
 	/* enable cable hot plug irq */
-	hdmi_writeb(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
+	hdmi->write(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
 
 	return 0;
 }
@@ -1321,45 +1220,45 @@ static void initialize_hdmi_ih_mutes(struct imx_hdmi *hdmi)
 	 *
 	 * Disable top level interrupt bits in HDMI block
 	 */
-	ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) |
+	ih_mute = hdmi->read(hdmi, HDMI_IH_MUTE) |
 		  HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
 		  HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
 
-	hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+	hdmi->write(hdmi, ih_mute, HDMI_IH_MUTE);
 
 	/* by default mask all interrupts */
-	hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
-	hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
-	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
+	hdmi->write(hdmi, 0xff, HDMI_VP_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK0);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK1);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK2);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_MASK0);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_INT);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_SPDIFINT);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_HBR_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_GP_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_A_APIINTMSK);
+	hdmi->write(hdmi, 0xff, HDMI_CEC_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_I2CM_INT);
+	hdmi->write(hdmi, 0xff, HDMI_I2CM_CTLINT);
 
 	/* Disable interrupts in the IH_MUTE_* registers */
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
 
 	/* Enable top level interrupt bits in HDMI block */
 	ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
 		    HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
-	hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+	hdmi->write(hdmi, ih_mute, HDMI_IH_MUTE);
 }
 
 static void imx_hdmi_poweron(struct imx_hdmi *hdmi)
@@ -1378,7 +1277,7 @@ static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector
 	struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
 					     connector);
 
-	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
+	return hdmi->read(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
 		connector_status_connected : connector_status_disconnected;
 }
 
@@ -1454,21 +1353,40 @@ static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder)
 	struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
 
 	imx_hdmi_poweroff(hdmi);
-	imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24);
+
+	if (hdmi->plat_data->encoder_prepare)
+		hdmi->plat_data->encoder_prepare(&hdmi->connector, encoder);
 }
 
 static void imx_hdmi_encoder_commit(struct drm_encoder *encoder)
 {
 	struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
-	int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
 
-	imx_hdmi_set_ipu_di_mux(hdmi, mux);
+	if (hdmi->plat_data->set_crtc_mux)
+		hdmi->plat_data->set_crtc_mux(hdmi->priv, encoder);
 
 	imx_hdmi_poweron(hdmi);
 }
 
+void imx_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static int imx_hdmi_connector_mode_valid(struct drm_connector *connector,
+					 struct drm_display_mode *mode)
+{
+	struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
+					     connector);
+	if (hdmi->plat_data->mode_valid)
+		return hdmi->plat_data->mode_valid(connector, mode);
+
+	return MODE_OK;
+}
+
 static struct drm_encoder_funcs imx_hdmi_encoder_funcs = {
-	.destroy = imx_drm_encoder_destroy,
+	.destroy = drm_encoder_cleanup,
 };
 
 static struct drm_encoder_helper_funcs imx_hdmi_encoder_helper_funcs = {
@@ -1484,11 +1402,12 @@ static struct drm_connector_funcs imx_hdmi_connector_funcs = {
 	.dpms = drm_helper_connector_dpms,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.detect = imx_hdmi_connector_detect,
-	.destroy = imx_drm_connector_destroy,
+	.destroy = imx_hdmi_connector_destroy,
 };
 
 static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = {
 	.get_modes = imx_hdmi_connector_get_modes,
+	.mode_valid = imx_hdmi_connector_mode_valid,
 	.best_encoder = imx_hdmi_connector_best_encoder,
 };
 
@@ -1497,9 +1416,9 @@ static irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id)
 	struct imx_hdmi *hdmi = dev_id;
 	u8 intr_stat;
 
-	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+	intr_stat = hdmi->read(hdmi, HDMI_IH_PHY_STAT0);
 	if (intr_stat)
-		hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+		hdmi->write(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
 	return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE;
 }
@@ -1510,21 +1429,21 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id)
 	u8 intr_stat;
 	u8 phy_int_pol;
 
-	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+	intr_stat = hdmi->read(hdmi, HDMI_IH_PHY_STAT0);
 
-	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
+	phy_int_pol = hdmi->read(hdmi, HDMI_PHY_POL0);
 
 	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
 		if (phy_int_pol & HDMI_PHY_HPD) {
 			dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
+			hdmi->mod(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
 
 			imx_hdmi_poweron(hdmi);
 		} else {
 			dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
+			hdmi->mod(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
 				HDMI_PHY_POL0);
 
 			imx_hdmi_poweroff(hdmi);
@@ -1532,20 +1451,18 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id)
 		drm_helper_hpd_irq_event(hdmi->connector.dev);
 	}
 
-	hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
 	return IRQ_HANDLED;
 }
 
 static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 {
-	int ret;
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
 
-	ret = imx_drm_encoder_parse_of(drm, &hdmi->encoder,
-				       hdmi->dev->of_node);
-	if (ret)
-		return ret;
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 
 	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
 
@@ -1554,7 +1471,7 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 			 DRM_MODE_ENCODER_TMDS);
 
 	drm_connector_helper_add(&hdmi->connector,
-			&imx_hdmi_connector_helper_funcs);
+				 &imx_hdmi_connector_helper_funcs);
 	drm_connector_init(drm, &hdmi->connector, &imx_hdmi_connector_funcs,
 			   DRM_MODE_CONNECTOR_HDMIA);
 
@@ -1565,55 +1482,47 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 	return 0;
 }
 
-static struct platform_device_id imx_hdmi_devtype[] = {
-	{
-		.name = "imx6q-hdmi",
-		.driver_data = IMX6Q_HDMI,
-	}, {
-		.name = "imx6dl-hdmi",
-		.driver_data = IMX6DL_HDMI,
-	}, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(platform, imx_hdmi_devtype);
-
-static const struct of_device_id imx_hdmi_dt_ids[] = {
-{ .compatible = "fsl,imx6q-hdmi", .data = &imx_hdmi_devtype[IMX6Q_HDMI], },
-{ .compatible = "fsl,imx6dl-hdmi", .data = &imx_hdmi_devtype[IMX6DL_HDMI], },
-{ /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids);
-
 static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	const struct of_device_id *of_id =
-				of_match_device(imx_hdmi_dt_ids, dev);
+	struct imx_hdmi *hdmi = platform_get_drvdata(pdev);
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
 	struct device_node *ddc_node;
-	struct imx_hdmi *hdmi;
 	struct resource *iores;
 	int ret, irq;
-
-	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
-	if (!hdmi)
-		return -ENOMEM;
-
-	hdmi->dev = dev;
-	hdmi->sample_rate = 48000;
-	hdmi->ratio = 100;
-
-	if (of_id) {
-		const struct platform_device_id *device_id = of_id->data;
-
-		hdmi->dev_type = device_id->driver_data;
+	u32 val;
+
+	if (!of_property_read_u32(np, "reg-io-width", &val)) {
+		switch (val) {
+		case 4:
+			hdmi->write = hdmi_writel;
+			hdmi->read = hdmi_readl;
+			hdmi->mod = hdmi_modl;
+			hdmi->mask_write = hdmi_mask_writel;
+			break;
+		default:
+			hdmi->write = hdmi_writeb;
+			hdmi->read = hdmi_readb;
+			hdmi->mod = hdmi_modb;
+			hdmi->mask_write = hdmi_mask_writeb;
+			break;
+		}
+	} else {
+		hdmi->write = hdmi_writeb;
+		hdmi->read = hdmi_readb;
+		hdmi->mod = hdmi_modb;
+		hdmi->mask_write = hdmi_mask_writeb;
 	}
 
 	ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
 	if (ddc_node) {
 		hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
-		if (!hdmi->ddc)
+		if (!hdmi->ddc) {
 			dev_dbg(hdmi->dev, "failed to read ddc node\n");
+			of_node_put(ddc_node);
+			return -EPROBE_DEFER;
+		}
 
 		of_node_put(ddc_node);
 	} else {
@@ -1635,47 +1544,15 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	if (IS_ERR(hdmi->regs))
 		return PTR_ERR(hdmi->regs);
 
-	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
-	if (IS_ERR(hdmi->regmap))
-		return PTR_ERR(hdmi->regmap);
-
-	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
-	if (IS_ERR(hdmi->isfr_clk)) {
-		ret = PTR_ERR(hdmi->isfr_clk);
-		dev_err(hdmi->dev,
-			"Unable to get HDMI isfr clk: %d\n", ret);
-		return ret;
-	}
-
-	ret = clk_prepare_enable(hdmi->isfr_clk);
-	if (ret) {
-		dev_err(hdmi->dev,
-			"Cannot enable HDMI isfr clock: %d\n", ret);
-		return ret;
-	}
-
-	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
-	if (IS_ERR(hdmi->iahb_clk)) {
-		ret = PTR_ERR(hdmi->iahb_clk);
-		dev_err(hdmi->dev,
-			"Unable to get HDMI iahb clk: %d\n", ret);
-		goto err_isfr;
-	}
-
-	ret = clk_prepare_enable(hdmi->iahb_clk);
-	if (ret) {
-		dev_err(hdmi->dev,
-			"Cannot enable HDMI iahb clock: %d\n", ret);
-		goto err_isfr;
-	}
-
+	if (hdmi->plat_data->setup)
+		hdmi->priv = hdmi->plat_data->setup(pdev);
 	/* Product and revision IDs */
 	dev_info(dev,
-		"Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
-		hdmi_readb(hdmi, HDMI_DESIGN_ID),
-		hdmi_readb(hdmi, HDMI_REVISION_ID),
-		hdmi_readb(hdmi, HDMI_PRODUCT_ID0),
-		hdmi_readb(hdmi, HDMI_PRODUCT_ID1));
+		 "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
+		 hdmi->read(hdmi, HDMI_DESIGN_ID),
+		 hdmi->read(hdmi, HDMI_REVISION_ID),
+		 hdmi->read(hdmi, HDMI_PRODUCT_ID0),
+		 hdmi->read(hdmi, HDMI_PRODUCT_ID1));
 
 	initialize_hdmi_ih_mutes(hdmi);
 
@@ -1689,32 +1566,25 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	 * Configure registers related to HDMI interrupt
 	 * generation before registering IRQ.
 	 */
-	hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
+	hdmi->write(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
 
 	ret = imx_hdmi_fb_registered(hdmi);
 	if (ret)
-		goto err_iahb;
+		return ret;
 
 	ret = imx_hdmi_register(drm, hdmi);
 	if (ret)
-		goto err_iahb;
+		return ret;
 
 	/* Unmute interrupts */
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
-
-err_iahb:
-	clk_disable_unprepare(hdmi->iahb_clk);
-err_isfr:
-	clk_disable_unprepare(hdmi->isfr_clk);
-
-	return ret;
 }
 
 static void imx_hdmi_unbind(struct device *dev, struct device *master,
@@ -1723,13 +1593,12 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master,
 	struct imx_hdmi *hdmi = dev_get_drvdata(dev);
 
 	/* Disable all interrupts */
-	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
 	hdmi->encoder.funcs->destroy(&hdmi->encoder);
-
-	clk_disable_unprepare(hdmi->iahb_clk);
-	clk_disable_unprepare(hdmi->isfr_clk);
+	if (hdmi->plat_data->exit)
+		hdmi->plat_data->exit(hdmi->priv);
 	i2c_put_adapter(hdmi->ddc);
 }
 
@@ -1749,17 +1618,32 @@ static int imx_hdmi_platform_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static struct platform_driver imx_hdmi_driver = {
-	.probe  = imx_hdmi_platform_probe,
-	.remove = imx_hdmi_platform_remove,
-	.driver = {
-		.name = "imx-hdmi",
-		.owner = THIS_MODULE,
-		.of_match_table = imx_hdmi_dt_ids,
-	},
-};
+int imx_hdmi_pltfm_register(struct platform_device *pdev,
+			    const struct imx_hdmi_plat_data *plat_data)
+{
+	struct imx_hdmi *hdmi;
 
-module_platform_driver(imx_hdmi_driver);
+	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->plat_data = plat_data;
+	hdmi->dev = &pdev->dev;
+	hdmi->dev_type = plat_data->dev_type;
+	hdmi->sample_rate = 48000;
+	hdmi->ratio = 100;
+
+	platform_set_drvdata(pdev, hdmi);
+
+	return imx_hdmi_platform_probe(pdev);
+}
+EXPORT_SYMBOL_GPL(imx_hdmi_pltfm_register);
+
+int imx_hdmi_pltfm_unregister(struct platform_device *pdev)
+{
+	return imx_hdmi_platform_remove(pdev);
+}
+EXPORT_SYMBOL_GPL(imx_hdmi_pltfm_unregister);
 
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 MODULE_DESCRIPTION("i.MX6 HDMI transmitter driver");
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
new file mode 100644
index 0000000..e7e8285
--- /dev/null
+++ b/include/drm/bridge/dw_hdmi.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __DW_HDMI_H__
+#define __DW_HDMI_H__
+
+#include <drm/drmP.h>
+
+#define HDMI_EDID_LEN           512
+
+enum {
+	RES_8,
+	RES_10,
+	RES_12,
+	RES_MAX,
+};
+
+enum imx_hdmi_devtype {
+	IMX6Q_HDMI,
+	IMX6DL_HDMI,
+};
+
+struct mpll_config {
+	unsigned long mpixelclock;
+	struct {
+		u16 cpce;
+		u16 gmp;
+	} res[RES_MAX];
+};
+
+struct curr_ctrl {
+	unsigned long mpixelclock;
+	u16 curr[RES_MAX];
+};
+
+struct hdmi_vmode {
+	bool mdvi;
+	bool mhsyncpolarity;
+	bool mvsyncpolarity;
+	bool minterlaced;
+	bool mdataenablepolarity;
+
+	unsigned int mpixelclock;
+	unsigned int mpixelrepetitioninput;
+	unsigned int mpixelrepetitionoutput;
+};
+
+struct hdmi_data_info {
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int enc_color_depth;
+	unsigned int colorimetry;
+	unsigned int pix_repet_factor;
+	unsigned int hdcp_enable;
+	struct hdmi_vmode video_mode;
+};
+
+
+struct imx_hdmi {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	enum imx_hdmi_devtype dev_type;
+	struct device *dev;
+
+	struct hdmi_data_info hdmi_data;
+	const struct imx_hdmi_plat_data *plat_data;
+	void *priv;
+
+	int vic;
+
+	u8 edid[HDMI_EDID_LEN];
+	bool cable_plugin;
+
+	bool phy_enabled;
+	struct drm_display_mode previous_mode;
+
+	struct i2c_adapter *ddc;
+	void __iomem *regs;
+
+	unsigned int sample_rate;
+	int ratio;
+
+	void (*write)(struct imx_hdmi *hdmi, u32 val, int offset);
+	u32 (*read)(struct imx_hdmi *hdmi, int offset);
+	void (*mod)(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg);
+	void (*mask_write)(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+			      u32 shift, u32 mask);
+};
+
+struct imx_hdmi_plat_data {
+	void * (*setup)(struct platform_device *pdev);
+	void (*exit)(void *priv);
+	void (*set_crtc_mux)(void *priv, struct drm_encoder *encoder);
+	void (*encoder_prepare)(struct drm_connector *connector,
+				struct drm_encoder *encoder);
+	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+					   struct drm_display_mode *mode);
+	const struct mpll_config *mpll_cfg;
+	const struct curr_ctrl *cur_ctr;
+	enum imx_hdmi_devtype dev_type;
+
+};
+
+int imx_hdmi_pltfm_register(struct platform_device *pdev,
+			    const struct imx_hdmi_plat_data *plat_data);
+int imx_hdmi_pltfm_unregister(struct platform_device *pdev);
+#endif /* __IMX_HDMI_H__ */
-- 
1.8.3.2



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi
@ 2014-11-04 13:33   ` Andy Yan
  0 siblings, 0 replies; 20+ messages in thread
From: Andy Yan @ 2014-11-04 13:33 UTC (permalink / raw)
  To: airlied-cv59FeDIM0c, heiko-4mtYJXux2i+zQB+pC5nmwQ,
	fabio.estevam-KZfg59tc24xl57MIdRCFDg,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ
  Cc: Greg Kroah-Hartman, Grant Likely, Rob Herring, Philipp Zabel,
	Shawn Guo, Andy yan, Josh Boyer, Sean Paul, Inki Dae,
	Dave Airlie, Arnd Bergmann, Lucas Stach,
	Zubair.Kakakhel-1AXoQHu6uovQT0dZR+AlfA,
	djkurtz-hpIqsD4AKlfQT0dZR+AlfA, ykk-TNX95d0MmH7DzftRWevZcw,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

imx6 and rockchip rk3288 and JZ4780 (Ingenic Xburst/MIPS)
use the interface compatible Designware HDMI IP, but they
also have some lightly difference, such as phy pll configuration,
register width(imx hdmi register is one byte, but rk3288 is 4
bytes width), 4K support(imx6 doesn't support 4k, but rk3288 does),
clk useage,and the crtc mux configuration is also platform specific.

To reuse the imx hdmi driver, split the platform specific code out
to dw_hdmi-imx.c.

Change-Id: I85e8d08754052b118423729a01c6d17bf485f383
---
 drivers/staging/imx-drm/Makefile      |   2 +-
 drivers/staging/imx-drm/dw_hdmi-imx.c | 214 ++++++++++
 drivers/staging/imx-drm/imx-hdmi.c    | 726 ++++++++++++++--------------------
 include/drm/bridge/dw_hdmi.h          | 114 ++++++
 4 files changed, 634 insertions(+), 422 deletions(-)
 create mode 100644 drivers/staging/imx-drm/dw_hdmi-imx.c
 create mode 100644 include/drm/bridge/dw_hdmi.h

diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
index 582c438..809027d 100644
--- a/drivers/staging/imx-drm/Makefile
+++ b/drivers/staging/imx-drm/Makefile
@@ -9,4 +9,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
 
 imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o
 obj-$(CONFIG_DRM_IMX_IPUV3)	+= imx-ipuv3-crtc.o
-obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o
+obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o dw_hdmi-imx.o
diff --git a/drivers/staging/imx-drm/dw_hdmi-imx.c b/drivers/staging/imx-drm/dw_hdmi-imx.c
new file mode 100644
index 0000000..71ac859
--- /dev/null
+++ b/drivers/staging/imx-drm/dw_hdmi-imx.c
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <drm/bridge/dw_hdmi.h>
+#include <video/imx-ipu-v3.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+
+#include "imx-drm.h"
+
+struct imx_hdmi_priv {
+	struct device *dev;
+	struct clk *isfr_clk;
+	struct clk *iahb_clk;
+	struct regmap *regmap;
+};
+
+static const struct mpll_config imx_mpll_cfg[] = {
+	{
+		45250000, {
+			{ 0x01e0, 0x0000 },
+			{ 0x21e1, 0x0000 },
+			{ 0x41e2, 0x0000 }
+		},
+	}, {
+		92500000, {
+			{ 0x0140, 0x0005 },
+			{ 0x2141, 0x0005 },
+			{ 0x4142, 0x0005 },
+	},
+	}, {
+		148500000, {
+			{ 0x00a0, 0x000a },
+			{ 0x20a1, 0x000a },
+			{ 0x40a2, 0x000a },
+		},
+	}, {
+		~0UL, {
+			{ 0x00a0, 0x000a },
+			{ 0x2001, 0x000f },
+			{ 0x4002, 0x000f },
+		},
+	}
+};
+
+static const struct curr_ctrl imx_cur_ctr[] = {
+	/*      pixelclk     bpp8    bpp10   bpp12 */
+	{
+		54000000, { 0x091c, 0x091c, 0x06dc },
+	}, {
+		58400000, { 0x091c, 0x06dc, 0x06dc },
+	}, {
+		72000000, { 0x06dc, 0x06dc, 0x091c },
+	}, {
+		74250000, { 0x06dc, 0x0b5c, 0x091c },
+	}, {
+		118800000, { 0x091c, 0x091c, 0x06dc },
+	}, {
+		216000000, { 0x06dc, 0x0b5c, 0x091c },
+	}
+};
+
+static int imx_hdmi_parse_dt(struct imx_hdmi_priv *hdmi)
+{
+	struct device_node *np = hdmi->dev->of_node;
+
+	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
+	if (IS_ERR(hdmi->regmap)) {
+		dev_err(hdmi->dev, "Unable to get gpr\n");
+		return PTR_ERR(hdmi->regmap);
+	}
+
+	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
+	if (IS_ERR(hdmi->isfr_clk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI isfr clk\n");
+		return PTR_ERR(hdmi->isfr_clk);
+	}
+
+	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
+	if (IS_ERR(hdmi->iahb_clk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI iahb clk\n");
+		return PTR_ERR(hdmi->iahb_clk);
+	}
+
+	return 0;
+}
+
+static void *imx_hdmi_imx_setup(struct platform_device *pdev)
+{
+	struct imx_hdmi_priv *hdmi;
+	int ret;
+
+	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return ERR_PTR(-ENOMEM);
+	hdmi->dev = &pdev->dev;
+
+	ret = imx_hdmi_parse_dt(hdmi);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	ret = clk_prepare_enable(hdmi->isfr_clk);
+	if (ret) {
+		dev_err(hdmi->dev,
+			"Cannot enable HDMI isfr clock: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	ret = clk_prepare_enable(hdmi->iahb_clk);
+	if (ret) {
+		dev_err(hdmi->dev,
+			"Cannot enable HDMI iahb clock: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	return hdmi;
+}
+
+static void imx_hdmi_imx_exit(void *priv)
+{
+	struct imx_hdmi_priv *hdmi = (struct imx_hdmi_priv *)priv;
+
+	clk_disable_unprepare(hdmi->isfr_clk);
+
+	clk_disable_unprepare(hdmi->iahb_clk);
+}
+
+static void imx_hdmi_imx_set_crtc_mux(void *priv, struct drm_encoder *encoder)
+{
+	struct imx_hdmi_priv *hdmi = (struct imx_hdmi_priv *)priv;
+	int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
+
+	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
+			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
+			   mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
+}
+
+static void imx_hdmi_imx_encoder_prepare(struct drm_connector *connector,
+					struct drm_encoder *encoder)
+{
+	imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24);
+}
+
+static struct imx_hdmi_plat_data imx6q_hdmi_drv_data = {
+	.setup			= imx_hdmi_imx_setup,
+	.exit			= imx_hdmi_imx_exit,
+	.set_crtc_mux		= imx_hdmi_imx_set_crtc_mux,
+	.encoder_prepare	= imx_hdmi_imx_encoder_prepare,
+	.mpll_cfg		= imx_mpll_cfg,
+	.cur_ctr		= imx_cur_ctr,
+	.dev_type		= IMX6Q_HDMI,
+};
+
+static struct imx_hdmi_plat_data imx6dl_hdmi_drv_data = {
+	.setup			= imx_hdmi_imx_setup,
+	.exit			= imx_hdmi_imx_exit,
+	.set_crtc_mux		= imx_hdmi_imx_set_crtc_mux,
+	.encoder_prepare	= imx_hdmi_imx_encoder_prepare,
+	.mpll_cfg		= imx_mpll_cfg,
+	.cur_ctr		= imx_cur_ctr,
+	.dev_type		= IMX6DL_HDMI,
+};
+
+static const struct of_device_id imx_hdmi_imx_ids[] = {
+	{ .compatible = "fsl,imx6q-hdmi",
+	  .data = &imx6q_hdmi_drv_data
+	}, {
+	  .compatible = "fsl,imx6dl-hdmi",
+	  .data = &imx6dl_hdmi_drv_data
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, imx_hdmi_imx_dt_ids);
+
+static int imx_hdmi_imx_probe(struct platform_device *pdev)
+{
+	const struct imx_hdmi_plat_data *plat_data;
+	const struct of_device_id *match;
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	match = of_match_node(imx_hdmi_imx_ids, pdev->dev.of_node);
+	plat_data = match->data;
+
+	return imx_hdmi_pltfm_register(pdev, plat_data);
+}
+
+static int imx_hdmi_imx_remove(struct platform_device *pdev)
+{
+	return imx_hdmi_pltfm_unregister(pdev);
+}
+
+static struct platform_driver imx_hdmi_imx_pltfm_driver = {
+	.probe  = imx_hdmi_imx_probe,
+	.remove = imx_hdmi_imx_remove,
+	.driver = {
+		.name = "dwhdmi-imx",
+		.owner = THIS_MODULE,
+		.of_match_table = imx_hdmi_imx_ids,
+	},
+};
+
+module_platform_driver(imx_hdmi_imx_pltfm_driver);
+
+MODULE_AUTHOR("Andy Yan <andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>");
+MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:dwhdmi-imx");
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index aaec6b2..138706c 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -16,23 +16,16 @@
 #include <linux/irq.h>
 #include <linux/delay.h>
 #include <linux/err.h>
-#include <linux/clk.h>
 #include <linux/hdmi.h>
-#include <linux/regmap.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 #include <linux/of_device.h>
-
+#include <drm/drm_of.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder_slave.h>
-#include <video/imx-ipu-v3.h>
+#include <drm/bridge/dw_hdmi.h>
 
 #include "imx-hdmi.h"
-#include "imx-drm.h"
-
-#define HDMI_EDID_LEN		512
 
 #define RGB			0
 #define YCBCR444		1
@@ -54,11 +47,6 @@ enum hdmi_datamap {
 	YCbCr422_12B = 0x12,
 };
 
-enum imx_hdmi_devtype {
-	IMX6Q_HDMI,
-	IMX6DL_HDMI,
-};
-
 static const u16 csc_coeff_default[3][4] = {
 	{ 0x2000, 0x0000, 0x0000, 0x0000 },
 	{ 0x0000, 0x2000, 0x0000, 0x0000 },
@@ -89,72 +77,44 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
 	{ 0x6756, 0x78ab, 0x2000, 0x0200 }
 };
 
-struct hdmi_vmode {
-	bool mdvi;
-	bool mhsyncpolarity;
-	bool mvsyncpolarity;
-	bool minterlaced;
-	bool mdataenablepolarity;
-
-	unsigned int mpixelclock;
-	unsigned int mpixelrepetitioninput;
-	unsigned int mpixelrepetitionoutput;
-};
-
-struct hdmi_data_info {
-	unsigned int enc_in_format;
-	unsigned int enc_out_format;
-	unsigned int enc_color_depth;
-	unsigned int colorimetry;
-	unsigned int pix_repet_factor;
-	unsigned int hdcp_enable;
-	struct hdmi_vmode video_mode;
-};
-
-struct imx_hdmi {
-	struct drm_connector connector;
-	struct drm_encoder encoder;
-
-	enum imx_hdmi_devtype dev_type;
-	struct device *dev;
-	struct clk *isfr_clk;
-	struct clk *iahb_clk;
-
-	struct hdmi_data_info hdmi_data;
-	int vic;
-
-	u8 edid[HDMI_EDID_LEN];
-	bool cable_plugin;
+/*On rockchip platform, no-word access to the hdmi
+ * register will causes an imprecise external abort
+ */
+static inline void hdmi_writel(struct imx_hdmi *hdmi, u32 val, int offset)
+{
+	writel(val, hdmi->regs + (offset << 2));
+}
 
-	bool phy_enabled;
-	struct drm_display_mode previous_mode;
+static inline u32 hdmi_readl(struct imx_hdmi *hdmi, int offset)
+{
+	return readl(hdmi->regs + (offset << 2));
+}
 
-	struct regmap *regmap;
-	struct i2c_adapter *ddc;
-	void __iomem *regs;
+static void hdmi_modl(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
+{
+	u32 val = hdmi_readl(hdmi, reg) & ~mask;
 
-	unsigned int sample_rate;
-	int ratio;
-};
+	val |= data & mask;
+	hdmi_writel(hdmi, val, reg);
+}
 
-static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di)
+static void hdmi_mask_writel(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+			     u32 shift, u32 mask)
 {
-	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
-			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
-			   ipu_di << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
+	hdmi_modl(hdmi, data << shift, mask, reg);
 }
 
-static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset)
+static inline void hdmi_writeb(struct imx_hdmi *hdmi, u32 val, int offset)
 {
 	writeb(val, hdmi->regs + offset);
 }
 
-static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset)
+static inline u32 hdmi_readb(struct imx_hdmi *hdmi, int offset)
 {
 	return readb(hdmi->regs + offset);
 }
 
-static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
+static void hdmi_modb(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
 {
 	u8 val = hdmi_readb(hdmi, reg) & ~mask;
 
@@ -162,8 +122,8 @@ static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
 	hdmi_writeb(hdmi, val, reg);
 }
 
-static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg,
-		      u8 shift, u8 mask)
+static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+		      u32 shift, u32 mask)
 {
 	hdmi_modb(hdmi, data << shift, mask, reg);
 }
@@ -171,22 +131,22 @@ static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg,
 static void hdmi_set_clock_regenerator_n(struct imx_hdmi *hdmi,
 					 unsigned int value)
 {
-	hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1);
-	hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
-	hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
+	hdmi->write(hdmi, value & 0xff, HDMI_AUD_N1);
+	hdmi->write(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
+	hdmi->write(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
 
 	/* nshift factor = 0 */
-	hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
+	hdmi->mod(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
 }
 
 static void hdmi_regenerate_cts(struct imx_hdmi *hdmi, unsigned int cts)
 {
 	/* Must be set/cleared first */
-	hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+	hdmi->mod(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
 
-	hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
-	hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
-	hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+	hdmi->write(hdmi, cts & 0xff, HDMI_AUD_CTS1);
+	hdmi->write(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
+	hdmi->write(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
 		    HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
 }
 
@@ -323,6 +283,7 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
 	}
 	if (ratio == 100)
 		return cts;
+
 	return (cts * ratio) / 100;
 }
 
@@ -338,7 +299,7 @@ static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi,
 
 	if (!clk_cts) {
 		dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n",
-			 __func__, pixel_clk);
+			__func__, pixel_clk);
 		return;
 	}
 
@@ -408,19 +369,19 @@ static void hdmi_video_sample(struct imx_hdmi *hdmi)
 	val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
 		((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
 		HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
-	hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
+	hdmi->write(hdmi, val, HDMI_TX_INVID0);
 
 	/* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
 	val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
 		HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
 		HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
-	hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
+	hdmi->write(hdmi, val, HDMI_TX_INSTUFFING);
+	hdmi->write(hdmi, 0x0, HDMI_TX_GYDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_GYDATA1);
+	hdmi->write(hdmi, 0x0, HDMI_TX_RCRDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_RCRDATA1);
+	hdmi->write(hdmi, 0x0, HDMI_TX_BCBDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_BCBDATA1);
 }
 
 static int is_color_space_conversion(struct imx_hdmi *hdmi)
@@ -477,17 +438,17 @@ static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi)
 		u16 coeff_b = (*csc_coeff)[1][i];
 		u16 coeff_c = (*csc_coeff)[2][i];
 
-		hdmi_writeb(hdmi, coeff_a & 0xff,
+		hdmi->write(hdmi, coeff_a & 0xff,
 			HDMI_CSC_COEF_A1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
-		hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
-		hdmi_writeb(hdmi, coeff_c & 0xff,
+		hdmi->write(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
+		hdmi->write(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_c & 0xff,
 			HDMI_CSC_COEF_C1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
 	}
 
-	hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
+	hdmi->mod(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
 		  HDMI_CSC_SCALE);
 }
 
@@ -515,8 +476,8 @@ static void hdmi_video_csc(struct imx_hdmi *hdmi)
 		return;
 
 	/* Configure the CSC registers */
-	hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
-	hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
+	hdmi->write(hdmi, interpolation | decimation, HDMI_CSC_CFG);
+	hdmi->mod(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
 		  HDMI_CSC_SCALE);
 
 	imx_hdmi_update_csc_coeffs(hdmi);
@@ -535,21 +496,22 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 	struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
 	u8 val, vp_conf;
 
-	if (hdmi_data->enc_out_format == RGB
-		|| hdmi_data->enc_out_format == YCBCR444) {
-		if (!hdmi_data->enc_color_depth)
+	if (hdmi_data->enc_out_format == RGB ||
+	    hdmi_data->enc_out_format == YCBCR444) {
+		if (!hdmi_data->enc_color_depth) {
 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
-		else if (hdmi_data->enc_color_depth == 8) {
+		} else if (hdmi_data->enc_color_depth == 8) {
 			color_depth = 4;
 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
-		} else if (hdmi_data->enc_color_depth == 10)
+		} else if (hdmi_data->enc_color_depth == 10) {
 			color_depth = 5;
-		else if (hdmi_data->enc_color_depth == 12)
+		} else if (hdmi_data->enc_color_depth == 12) {
 			color_depth = 6;
-		else if (hdmi_data->enc_color_depth == 16)
+		} else if (hdmi_data->enc_color_depth == 16) {
 			color_depth = 7;
-		else
+		} else {
 			return;
+		}
 	} else if (hdmi_data->enc_out_format == YCBCR422_8BITS) {
 		if (!hdmi_data->enc_color_depth ||
 		    hdmi_data->enc_color_depth == 8)
@@ -561,8 +523,9 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		else
 			return;
 		output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
-	} else
+	} else {
 		return;
+	}
 
 	/* set the packetizer registers */
 	val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
@@ -570,9 +533,9 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		((hdmi_data->pix_repet_factor <<
 		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
 		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
-	hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
+	hdmi->write(hdmi, val, HDMI_VP_PR_CD);
 
-	hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
+	hdmi->mod(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
 		  HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
 
 	/* Data from pixel repeater block */
@@ -584,14 +547,14 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 			  HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
 	}
 
-	hdmi_modb(hdmi, vp_conf,
+	hdmi->mod(hdmi, vp_conf,
 		  HDMI_VP_CONF_PR_EN_MASK |
 		  HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
 
-	hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
+	hdmi->mod(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
 		  HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
 
-	hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
+	hdmi->write(hdmi, remap_size, HDMI_VP_REMAP);
 
 	if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
 		vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
@@ -609,55 +572,55 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		return;
 	}
 
-	hdmi_modb(hdmi, vp_conf,
+	hdmi->mod(hdmi, vp_conf,
 		  HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
 		  HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF);
 
-	hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
+	hdmi->mod(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
 			HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
 		  HDMI_VP_STUFF_PP_STUFFING_MASK |
 		  HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF);
 
-	hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
+	hdmi->mod(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
 		  HDMI_VP_CONF);
 }
 
 static inline void hdmi_phy_test_clear(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
 		  HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_enable(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
 		  HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_clock(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
 		  HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_din(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_writeb(hdmi, bit, HDMI_PHY_TST1);
+	hdmi->write(hdmi, bit, HDMI_PHY_TST1);
 }
 
 static inline void hdmi_phy_test_dout(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_writeb(hdmi, bit, HDMI_PHY_TST2);
+	hdmi->write(hdmi, bit, HDMI_PHY_TST2);
 }
 
 static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec)
 {
-	while ((hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
+	while ((hdmi->read(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
 		if (msec-- == 0)
 			return false;
 		udelay(1000);
@@ -668,13 +631,13 @@ static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec)
 static void __hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
 			      unsigned char addr)
 {
-	hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
-	hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
-	hdmi_writeb(hdmi, (unsigned char)(data >> 8),
+	hdmi->write(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+	hdmi->write(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+	hdmi->write(hdmi, (unsigned char)(data >> 8),
 		HDMI_PHY_I2CM_DATAO_1_ADDR);
-	hdmi_writeb(hdmi, (unsigned char)(data >> 0),
+	hdmi->write(hdmi, (unsigned char)(data >> 0),
 		HDMI_PHY_I2CM_DATAO_0_ADDR);
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
 		HDMI_PHY_I2CM_OPERATION_ADDR);
 	hdmi_phy_wait_i2c_done(hdmi, 1000);
 }
@@ -688,116 +651,53 @@ static int hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
 
 static void imx_hdmi_phy_enable_power(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_PDZ_OFFSET,
 			 HDMI_PHY_CONF0_PDZ_MASK);
 }
 
 static void imx_hdmi_phy_enable_tmds(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_ENTMDS_OFFSET,
 			 HDMI_PHY_CONF0_ENTMDS_MASK);
 }
 
 static void imx_hdmi_phy_gen2_pddq(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
 			 HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
 }
 
 static void imx_hdmi_phy_gen2_txpwron(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
 			 HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
 }
 
 static void imx_hdmi_phy_sel_data_en_pol(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
 			 HDMI_PHY_CONF0_SELDATAENPOL_MASK);
 }
 
 static void imx_hdmi_phy_sel_interface_control(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_SELDIPIF_OFFSET,
 			 HDMI_PHY_CONF0_SELDIPIF_MASK);
 }
 
-enum {
-	RES_8,
-	RES_10,
-	RES_12,
-	RES_MAX,
-};
-
-struct mpll_config {
-	unsigned long mpixelclock;
-	struct {
-		u16 cpce;
-		u16 gmp;
-	} res[RES_MAX];
-};
-
-static const struct mpll_config mpll_config[] = {
-	{
-		45250000, {
-			{ 0x01e0, 0x0000 },
-			{ 0x21e1, 0x0000 },
-			{ 0x41e2, 0x0000 }
-		},
-	}, {
-		92500000, {
-			{ 0x0140, 0x0005 },
-			{ 0x2141, 0x0005 },
-			{ 0x4142, 0x0005 },
-		},
-	}, {
-		148500000, {
-			{ 0x00a0, 0x000a },
-			{ 0x20a1, 0x000a },
-			{ 0x40a2, 0x000a },
-		},
-	}, {
-		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
-		},
-	}
-};
-
-struct curr_ctrl {
-	unsigned long mpixelclock;
-	u16 curr[RES_MAX];
-};
-
-static const struct curr_ctrl curr_ctrl[] = {
-	/*	pixelclk     bpp8    bpp10   bpp12 */
-	{
-		 54000000, { 0x091c, 0x091c, 0x06dc },
-	}, {
-		 58400000, { 0x091c, 0x06dc, 0x06dc },
-	}, {
-		 72000000, { 0x06dc, 0x06dc, 0x091c },
-	}, {
-		 74250000, { 0x06dc, 0x0b5c, 0x091c },
-	}, {
-		118800000, { 0x091c, 0x091c, 0x06dc },
-	}, {
-		216000000, { 0x06dc, 0x0b5c, 0x091c },
-	}
-};
-
 static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 			      unsigned char res, int cscon)
 {
 	unsigned res_idx, i;
 	u8 val, msec;
+	const struct mpll_config *mpll_cfg = hdmi->plat_data->mpll_cfg;
+	const struct curr_ctrl   *curr_ctr = hdmi->plat_data->cur_ctr;
 
 	if (prep)
 		return -EINVAL;
@@ -823,7 +723,7 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	else
 		val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS;
 
-	hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL);
+	hdmi->write(hdmi, val, HDMI_MC_FLOWCTRL);
 
 	/* gen2 tx power off */
 	imx_hdmi_phy_gen2_txpwron(hdmi, 0);
@@ -832,39 +732,38 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	imx_hdmi_phy_gen2_pddq(hdmi, 1);
 
 	/* PHY reset */
-	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
-	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
+	hdmi->write(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
+	hdmi->write(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
 
-	hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
+	hdmi->write(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
 
 	hdmi_phy_test_clear(hdmi, 1);
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
 			HDMI_PHY_I2CM_SLAVE_ADDR);
 	hdmi_phy_test_clear(hdmi, 0);
 
 	/* PLL/MPLL Cfg - always match on final entry */
-	for (i = 0; i < ARRAY_SIZE(mpll_config) - 1; i++)
+	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++)
 		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    mpll_config[i].mpixelclock)
+		    mpll_cfg[i].mpixelclock)
 			break;
+	hdmi_phy_i2c_write(hdmi, mpll_cfg[i].res[res_idx].cpce, 0x06);
+	hdmi_phy_i2c_write(hdmi, mpll_cfg[i].res[res_idx].gmp, 0x15);
 
-	hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06);
-	hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15);
-
-	for (i = 0; i < ARRAY_SIZE(curr_ctrl); i++)
+	for (i = 0; curr_ctr[i].mpixelclock != (~0UL); i++)
 		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    curr_ctrl[i].mpixelclock)
+		    curr_ctr[i].mpixelclock)
 			break;
 
-	if (i >= ARRAY_SIZE(curr_ctrl)) {
+	if (curr_ctr[i].mpixelclock == (~0UL)) {
 		dev_err(hdmi->dev,
-				"Pixel clock %d - unsupported by HDMI\n",
-				hdmi->hdmi_data.video_mode.mpixelclock);
+			"Pixel clock %d - unsupported by HDMI\n",
+			hdmi->hdmi_data.video_mode.mpixelclock);
 		return -EINVAL;
 	}
 
 	/* CURRCTRL */
-	hdmi_phy_i2c_write(hdmi, curr_ctrl[i].curr[res_idx], 0x10);
+	hdmi_phy_i2c_write(hdmi, curr_ctr[i].curr[res_idx], 0x10);
 
 	hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
 	hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
@@ -890,7 +789,7 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	/*Wait for PHY PLL lock */
 	msec = 5;
 	do {
-		val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+		val = hdmi->read(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
 		if (!val)
 			break;
 
@@ -942,12 +841,12 @@ static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi)
 		de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
 
 	/* disable rx detect */
-	hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
+	hdmi->mod(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
 		  HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
 
-	hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
+	hdmi->mod(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
 
-	hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
+	hdmi->mod(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
 		  HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
 }
 
@@ -977,7 +876,7 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 		HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT |
 		HDMI_FC_AVICONF0_BAR_DATA_NO_DATA;
 
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF0);
 
 	/* AVI Data Byte 2 -Set the Aspect Ratio */
 	if (aspect_16_9) {
@@ -1009,16 +908,16 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 	}
 
 	val = colorimetry | coded_ratio | act_ratio;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF1);
 
 	/* AVI Data Byte 3 */
 	val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry |
 		HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT |
 		HDMI_FC_AVICONF2_SCALING_NONE;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF2);
 
 	/* AVI Data Byte 4 */
-	hdmi_writeb(hdmi, hdmi->vic, HDMI_FC_AVIVID);
+	hdmi->write(hdmi, hdmi->vic, HDMI_FC_AVIVID);
 
 	/* AVI Data Byte 5- set up input and output pixel repetition */
 	val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) <<
@@ -1027,22 +926,22 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 		((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput <<
 		HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
 		HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
-	hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
+	hdmi->write(hdmi, val, HDMI_FC_PRCONF);
 
 	/* IT Content and quantization range = don't care */
 	val = HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS |
 		HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF3);
 
 	/* AVI Data Bytes 6-13 */
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIETB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIETB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISBB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISBB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIELB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIELB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISRB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISRB1);
 }
 
 static void hdmi_av_composer(struct imx_hdmi *hdmi,
@@ -1091,42 +990,42 @@ static void hdmi_av_composer(struct imx_hdmi *hdmi,
 		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
 		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
 
-	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
+	hdmi->write(hdmi, inv_val, HDMI_FC_INVIDCONF);
 
 	/* Set up horizontal active pixel width */
-	hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
-	hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
+	hdmi->write(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
+	hdmi->write(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
 
 	/* Set up vertical active lines */
-	hdmi_writeb(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
-	hdmi_writeb(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
+	hdmi->write(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
+	hdmi->write(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
 
 	/* Set up horizontal blanking pixel region width */
 	hblank = mode->htotal - mode->hdisplay;
-	hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
-	hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
+	hdmi->write(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
+	hdmi->write(hdmi, hblank, HDMI_FC_INHBLANK0);
 
 	/* Set up vertical blanking pixel region width */
 	vblank = mode->vtotal - mode->vdisplay;
-	hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
+	hdmi->write(hdmi, vblank, HDMI_FC_INVBLANK);
 
 	/* Set up HSYNC active edge delay width (in pixel clks) */
 	h_de_hs = mode->hsync_start - mode->hdisplay;
-	hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
-	hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
+	hdmi->write(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
+	hdmi->write(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
 
 	/* Set up VSYNC active edge delay (in lines) */
 	v_de_vs = mode->vsync_start - mode->vdisplay;
-	hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
+	hdmi->write(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
 
 	/* Set up HSYNC active pulse width (in pixel clks) */
 	hsync_len = mode->hsync_end - mode->hsync_start;
-	hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
-	hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
+	hdmi->write(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
+	hdmi->write(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
 
 	/* Set up VSYNC active edge delay (in lines) */
 	vsync_len = mode->vsync_end - mode->vsync_start;
-	hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
+	hdmi->write(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
 }
 
 static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi)
@@ -1146,33 +1045,33 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi)
 	u8 clkdis;
 
 	/* control period minimum duration */
-	hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
-	hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
-	hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
+	hdmi->write(hdmi, 12, HDMI_FC_CTRLDUR);
+	hdmi->write(hdmi, 32, HDMI_FC_EXCTRLDUR);
+	hdmi->write(hdmi, 1, HDMI_FC_EXCTRLSPAC);
 
 	/* Set to fill TMDS data channels */
-	hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
-	hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
-	hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
+	hdmi->write(hdmi, 0x0B, HDMI_FC_CH0PREAM);
+	hdmi->write(hdmi, 0x16, HDMI_FC_CH1PREAM);
+	hdmi->write(hdmi, 0x21, HDMI_FC_CH2PREAM);
 
 	/* Enable pixel clock and tmds data path */
 	clkdis = 0x7F;
 	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 
 	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 
 	/* Enable csc path */
 	if (is_color_space_conversion(hdmi)) {
 		clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
-		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+		hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 	}
 }
 
 static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi)
 {
-	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+	hdmi->mod(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
 }
 
 /* Workaround to clear the overflow condition */
@@ -1182,27 +1081,27 @@ static void imx_hdmi_clear_overflow(struct imx_hdmi *hdmi)
 	u8 val;
 
 	/* TMDS software reset */
-	hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
+	hdmi->write(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
 
-	val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
+	val = hdmi->read(hdmi, HDMI_FC_INVIDCONF);
 	if (hdmi->dev_type == IMX6DL_HDMI) {
-		hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+		hdmi->write(hdmi, val, HDMI_FC_INVIDCONF);
 		return;
 	}
 
 	for (count = 0; count < 4; count++)
-		hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+		hdmi->write(hdmi, val, HDMI_FC_INVIDCONF);
 }
 
 static void hdmi_enable_overflow_interrupts(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, 0, HDMI_FC_MASK2);
-	hdmi_writeb(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
+	hdmi->write(hdmi, 0, HDMI_FC_MASK2);
+	hdmi->write(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
 }
 
 static void hdmi_disable_overflow_interrupts(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
+	hdmi->write(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
 		    HDMI_IH_MUTE_FC_STAT2);
 }
 
@@ -1223,21 +1122,21 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 	}
 
 	if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
-		(hdmi->vic == 21) || (hdmi->vic == 22) ||
-		(hdmi->vic == 2) || (hdmi->vic == 3) ||
-		(hdmi->vic == 17) || (hdmi->vic == 18))
+	    (hdmi->vic == 21) || (hdmi->vic == 22) ||
+	    (hdmi->vic == 2) || (hdmi->vic == 3) ||
+	    (hdmi->vic == 17) || (hdmi->vic == 18))
 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
 	else
 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
 
 	if ((hdmi->vic == 10) || (hdmi->vic == 11) ||
-		(hdmi->vic == 12) || (hdmi->vic == 13) ||
-		(hdmi->vic == 14) || (hdmi->vic == 15) ||
-		(hdmi->vic == 25) || (hdmi->vic == 26) ||
-		(hdmi->vic == 27) || (hdmi->vic == 28) ||
-		(hdmi->vic == 29) || (hdmi->vic == 30) ||
-		(hdmi->vic == 35) || (hdmi->vic == 36) ||
-		(hdmi->vic == 37) || (hdmi->vic == 38))
+	    (hdmi->vic == 12) || (hdmi->vic == 13) ||
+	    (hdmi->vic == 14) || (hdmi->vic == 15) ||
+	    (hdmi->vic == 25) || (hdmi->vic == 26) ||
+	    (hdmi->vic == 27) || (hdmi->vic == 28) ||
+	    (hdmi->vic == 29) || (hdmi->vic == 30) ||
+	    (hdmi->vic == 35) || (hdmi->vic == 36) ||
+	    (hdmi->vic == 37) || (hdmi->vic == 38))
 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
 	else
 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
@@ -1266,9 +1165,9 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 	imx_hdmi_enable_video_path(hdmi);
 
 	/* not for DVI mode */
-	if (hdmi->hdmi_data.video_mode.mdvi)
+	if (hdmi->hdmi_data.video_mode.mdvi) {
 		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
-	else {
+	} else {
 		dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
 
 		/* HDMI Initialization Step E - Configure audio */
@@ -1294,18 +1193,18 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 /* Wait until we are registered to enable interrupts */
 static int imx_hdmi_fb_registered(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
 		    HDMI_PHY_I2CM_INT_ADDR);
 
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+	hdmi->write(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
 		    HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
 		    HDMI_PHY_I2CM_CTLINT_ADDR);
 
 	/* enable cable hot plug irq */
-	hdmi_writeb(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
+	hdmi->write(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
 
 	return 0;
 }
@@ -1321,45 +1220,45 @@ static void initialize_hdmi_ih_mutes(struct imx_hdmi *hdmi)
 	 *
 	 * Disable top level interrupt bits in HDMI block
 	 */
-	ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) |
+	ih_mute = hdmi->read(hdmi, HDMI_IH_MUTE) |
 		  HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
 		  HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
 
-	hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+	hdmi->write(hdmi, ih_mute, HDMI_IH_MUTE);
 
 	/* by default mask all interrupts */
-	hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
-	hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
-	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
+	hdmi->write(hdmi, 0xff, HDMI_VP_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK0);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK1);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK2);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_MASK0);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_INT);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_SPDIFINT);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_HBR_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_GP_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_A_APIINTMSK);
+	hdmi->write(hdmi, 0xff, HDMI_CEC_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_I2CM_INT);
+	hdmi->write(hdmi, 0xff, HDMI_I2CM_CTLINT);
 
 	/* Disable interrupts in the IH_MUTE_* registers */
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
 
 	/* Enable top level interrupt bits in HDMI block */
 	ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
 		    HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
-	hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+	hdmi->write(hdmi, ih_mute, HDMI_IH_MUTE);
 }
 
 static void imx_hdmi_poweron(struct imx_hdmi *hdmi)
@@ -1378,7 +1277,7 @@ static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector
 	struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
 					     connector);
 
-	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
+	return hdmi->read(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
 		connector_status_connected : connector_status_disconnected;
 }
 
@@ -1454,21 +1353,40 @@ static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder)
 	struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
 
 	imx_hdmi_poweroff(hdmi);
-	imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24);
+
+	if (hdmi->plat_data->encoder_prepare)
+		hdmi->plat_data->encoder_prepare(&hdmi->connector, encoder);
 }
 
 static void imx_hdmi_encoder_commit(struct drm_encoder *encoder)
 {
 	struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
-	int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
 
-	imx_hdmi_set_ipu_di_mux(hdmi, mux);
+	if (hdmi->plat_data->set_crtc_mux)
+		hdmi->plat_data->set_crtc_mux(hdmi->priv, encoder);
 
 	imx_hdmi_poweron(hdmi);
 }
 
+void imx_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static int imx_hdmi_connector_mode_valid(struct drm_connector *connector,
+					 struct drm_display_mode *mode)
+{
+	struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
+					     connector);
+	if (hdmi->plat_data->mode_valid)
+		return hdmi->plat_data->mode_valid(connector, mode);
+
+	return MODE_OK;
+}
+
 static struct drm_encoder_funcs imx_hdmi_encoder_funcs = {
-	.destroy = imx_drm_encoder_destroy,
+	.destroy = drm_encoder_cleanup,
 };
 
 static struct drm_encoder_helper_funcs imx_hdmi_encoder_helper_funcs = {
@@ -1484,11 +1402,12 @@ static struct drm_connector_funcs imx_hdmi_connector_funcs = {
 	.dpms = drm_helper_connector_dpms,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.detect = imx_hdmi_connector_detect,
-	.destroy = imx_drm_connector_destroy,
+	.destroy = imx_hdmi_connector_destroy,
 };
 
 static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = {
 	.get_modes = imx_hdmi_connector_get_modes,
+	.mode_valid = imx_hdmi_connector_mode_valid,
 	.best_encoder = imx_hdmi_connector_best_encoder,
 };
 
@@ -1497,9 +1416,9 @@ static irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id)
 	struct imx_hdmi *hdmi = dev_id;
 	u8 intr_stat;
 
-	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+	intr_stat = hdmi->read(hdmi, HDMI_IH_PHY_STAT0);
 	if (intr_stat)
-		hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+		hdmi->write(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
 	return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE;
 }
@@ -1510,21 +1429,21 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id)
 	u8 intr_stat;
 	u8 phy_int_pol;
 
-	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+	intr_stat = hdmi->read(hdmi, HDMI_IH_PHY_STAT0);
 
-	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
+	phy_int_pol = hdmi->read(hdmi, HDMI_PHY_POL0);
 
 	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
 		if (phy_int_pol & HDMI_PHY_HPD) {
 			dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
+			hdmi->mod(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
 
 			imx_hdmi_poweron(hdmi);
 		} else {
 			dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
+			hdmi->mod(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
 				HDMI_PHY_POL0);
 
 			imx_hdmi_poweroff(hdmi);
@@ -1532,20 +1451,18 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id)
 		drm_helper_hpd_irq_event(hdmi->connector.dev);
 	}
 
-	hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
 	return IRQ_HANDLED;
 }
 
 static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 {
-	int ret;
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
 
-	ret = imx_drm_encoder_parse_of(drm, &hdmi->encoder,
-				       hdmi->dev->of_node);
-	if (ret)
-		return ret;
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 
 	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
 
@@ -1554,7 +1471,7 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 			 DRM_MODE_ENCODER_TMDS);
 
 	drm_connector_helper_add(&hdmi->connector,
-			&imx_hdmi_connector_helper_funcs);
+				 &imx_hdmi_connector_helper_funcs);
 	drm_connector_init(drm, &hdmi->connector, &imx_hdmi_connector_funcs,
 			   DRM_MODE_CONNECTOR_HDMIA);
 
@@ -1565,55 +1482,47 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 	return 0;
 }
 
-static struct platform_device_id imx_hdmi_devtype[] = {
-	{
-		.name = "imx6q-hdmi",
-		.driver_data = IMX6Q_HDMI,
-	}, {
-		.name = "imx6dl-hdmi",
-		.driver_data = IMX6DL_HDMI,
-	}, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(platform, imx_hdmi_devtype);
-
-static const struct of_device_id imx_hdmi_dt_ids[] = {
-{ .compatible = "fsl,imx6q-hdmi", .data = &imx_hdmi_devtype[IMX6Q_HDMI], },
-{ .compatible = "fsl,imx6dl-hdmi", .data = &imx_hdmi_devtype[IMX6DL_HDMI], },
-{ /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids);
-
 static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	const struct of_device_id *of_id =
-				of_match_device(imx_hdmi_dt_ids, dev);
+	struct imx_hdmi *hdmi = platform_get_drvdata(pdev);
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
 	struct device_node *ddc_node;
-	struct imx_hdmi *hdmi;
 	struct resource *iores;
 	int ret, irq;
-
-	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
-	if (!hdmi)
-		return -ENOMEM;
-
-	hdmi->dev = dev;
-	hdmi->sample_rate = 48000;
-	hdmi->ratio = 100;
-
-	if (of_id) {
-		const struct platform_device_id *device_id = of_id->data;
-
-		hdmi->dev_type = device_id->driver_data;
+	u32 val;
+
+	if (!of_property_read_u32(np, "reg-io-width", &val)) {
+		switch (val) {
+		case 4:
+			hdmi->write = hdmi_writel;
+			hdmi->read = hdmi_readl;
+			hdmi->mod = hdmi_modl;
+			hdmi->mask_write = hdmi_mask_writel;
+			break;
+		default:
+			hdmi->write = hdmi_writeb;
+			hdmi->read = hdmi_readb;
+			hdmi->mod = hdmi_modb;
+			hdmi->mask_write = hdmi_mask_writeb;
+			break;
+		}
+	} else {
+		hdmi->write = hdmi_writeb;
+		hdmi->read = hdmi_readb;
+		hdmi->mod = hdmi_modb;
+		hdmi->mask_write = hdmi_mask_writeb;
 	}
 
 	ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
 	if (ddc_node) {
 		hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
-		if (!hdmi->ddc)
+		if (!hdmi->ddc) {
 			dev_dbg(hdmi->dev, "failed to read ddc node\n");
+			of_node_put(ddc_node);
+			return -EPROBE_DEFER;
+		}
 
 		of_node_put(ddc_node);
 	} else {
@@ -1635,47 +1544,15 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	if (IS_ERR(hdmi->regs))
 		return PTR_ERR(hdmi->regs);
 
-	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
-	if (IS_ERR(hdmi->regmap))
-		return PTR_ERR(hdmi->regmap);
-
-	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
-	if (IS_ERR(hdmi->isfr_clk)) {
-		ret = PTR_ERR(hdmi->isfr_clk);
-		dev_err(hdmi->dev,
-			"Unable to get HDMI isfr clk: %d\n", ret);
-		return ret;
-	}
-
-	ret = clk_prepare_enable(hdmi->isfr_clk);
-	if (ret) {
-		dev_err(hdmi->dev,
-			"Cannot enable HDMI isfr clock: %d\n", ret);
-		return ret;
-	}
-
-	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
-	if (IS_ERR(hdmi->iahb_clk)) {
-		ret = PTR_ERR(hdmi->iahb_clk);
-		dev_err(hdmi->dev,
-			"Unable to get HDMI iahb clk: %d\n", ret);
-		goto err_isfr;
-	}
-
-	ret = clk_prepare_enable(hdmi->iahb_clk);
-	if (ret) {
-		dev_err(hdmi->dev,
-			"Cannot enable HDMI iahb clock: %d\n", ret);
-		goto err_isfr;
-	}
-
+	if (hdmi->plat_data->setup)
+		hdmi->priv = hdmi->plat_data->setup(pdev);
 	/* Product and revision IDs */
 	dev_info(dev,
-		"Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
-		hdmi_readb(hdmi, HDMI_DESIGN_ID),
-		hdmi_readb(hdmi, HDMI_REVISION_ID),
-		hdmi_readb(hdmi, HDMI_PRODUCT_ID0),
-		hdmi_readb(hdmi, HDMI_PRODUCT_ID1));
+		 "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
+		 hdmi->read(hdmi, HDMI_DESIGN_ID),
+		 hdmi->read(hdmi, HDMI_REVISION_ID),
+		 hdmi->read(hdmi, HDMI_PRODUCT_ID0),
+		 hdmi->read(hdmi, HDMI_PRODUCT_ID1));
 
 	initialize_hdmi_ih_mutes(hdmi);
 
@@ -1689,32 +1566,25 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	 * Configure registers related to HDMI interrupt
 	 * generation before registering IRQ.
 	 */
-	hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
+	hdmi->write(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
 
 	ret = imx_hdmi_fb_registered(hdmi);
 	if (ret)
-		goto err_iahb;
+		return ret;
 
 	ret = imx_hdmi_register(drm, hdmi);
 	if (ret)
-		goto err_iahb;
+		return ret;
 
 	/* Unmute interrupts */
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
-
-err_iahb:
-	clk_disable_unprepare(hdmi->iahb_clk);
-err_isfr:
-	clk_disable_unprepare(hdmi->isfr_clk);
-
-	return ret;
 }
 
 static void imx_hdmi_unbind(struct device *dev, struct device *master,
@@ -1723,13 +1593,12 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master,
 	struct imx_hdmi *hdmi = dev_get_drvdata(dev);
 
 	/* Disable all interrupts */
-	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
 	hdmi->encoder.funcs->destroy(&hdmi->encoder);
-
-	clk_disable_unprepare(hdmi->iahb_clk);
-	clk_disable_unprepare(hdmi->isfr_clk);
+	if (hdmi->plat_data->exit)
+		hdmi->plat_data->exit(hdmi->priv);
 	i2c_put_adapter(hdmi->ddc);
 }
 
@@ -1749,17 +1618,32 @@ static int imx_hdmi_platform_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static struct platform_driver imx_hdmi_driver = {
-	.probe  = imx_hdmi_platform_probe,
-	.remove = imx_hdmi_platform_remove,
-	.driver = {
-		.name = "imx-hdmi",
-		.owner = THIS_MODULE,
-		.of_match_table = imx_hdmi_dt_ids,
-	},
-};
+int imx_hdmi_pltfm_register(struct platform_device *pdev,
+			    const struct imx_hdmi_plat_data *plat_data)
+{
+	struct imx_hdmi *hdmi;
 
-module_platform_driver(imx_hdmi_driver);
+	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->plat_data = plat_data;
+	hdmi->dev = &pdev->dev;
+	hdmi->dev_type = plat_data->dev_type;
+	hdmi->sample_rate = 48000;
+	hdmi->ratio = 100;
+
+	platform_set_drvdata(pdev, hdmi);
+
+	return imx_hdmi_platform_probe(pdev);
+}
+EXPORT_SYMBOL_GPL(imx_hdmi_pltfm_register);
+
+int imx_hdmi_pltfm_unregister(struct platform_device *pdev)
+{
+	return imx_hdmi_platform_remove(pdev);
+}
+EXPORT_SYMBOL_GPL(imx_hdmi_pltfm_unregister);
 
 MODULE_AUTHOR("Sascha Hauer <s.hauer-bIcnvbaLZ9MEGnE8C9+IrQ@public.gmane.org>");
 MODULE_DESCRIPTION("i.MX6 HDMI transmitter driver");
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
new file mode 100644
index 0000000..e7e8285
--- /dev/null
+++ b/include/drm/bridge/dw_hdmi.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __DW_HDMI_H__
+#define __DW_HDMI_H__
+
+#include <drm/drmP.h>
+
+#define HDMI_EDID_LEN           512
+
+enum {
+	RES_8,
+	RES_10,
+	RES_12,
+	RES_MAX,
+};
+
+enum imx_hdmi_devtype {
+	IMX6Q_HDMI,
+	IMX6DL_HDMI,
+};
+
+struct mpll_config {
+	unsigned long mpixelclock;
+	struct {
+		u16 cpce;
+		u16 gmp;
+	} res[RES_MAX];
+};
+
+struct curr_ctrl {
+	unsigned long mpixelclock;
+	u16 curr[RES_MAX];
+};
+
+struct hdmi_vmode {
+	bool mdvi;
+	bool mhsyncpolarity;
+	bool mvsyncpolarity;
+	bool minterlaced;
+	bool mdataenablepolarity;
+
+	unsigned int mpixelclock;
+	unsigned int mpixelrepetitioninput;
+	unsigned int mpixelrepetitionoutput;
+};
+
+struct hdmi_data_info {
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int enc_color_depth;
+	unsigned int colorimetry;
+	unsigned int pix_repet_factor;
+	unsigned int hdcp_enable;
+	struct hdmi_vmode video_mode;
+};
+
+
+struct imx_hdmi {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	enum imx_hdmi_devtype dev_type;
+	struct device *dev;
+
+	struct hdmi_data_info hdmi_data;
+	const struct imx_hdmi_plat_data *plat_data;
+	void *priv;
+
+	int vic;
+
+	u8 edid[HDMI_EDID_LEN];
+	bool cable_plugin;
+
+	bool phy_enabled;
+	struct drm_display_mode previous_mode;
+
+	struct i2c_adapter *ddc;
+	void __iomem *regs;
+
+	unsigned int sample_rate;
+	int ratio;
+
+	void (*write)(struct imx_hdmi *hdmi, u32 val, int offset);
+	u32 (*read)(struct imx_hdmi *hdmi, int offset);
+	void (*mod)(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg);
+	void (*mask_write)(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+			      u32 shift, u32 mask);
+};
+
+struct imx_hdmi_plat_data {
+	void * (*setup)(struct platform_device *pdev);
+	void (*exit)(void *priv);
+	void (*set_crtc_mux)(void *priv, struct drm_encoder *encoder);
+	void (*encoder_prepare)(struct drm_connector *connector,
+				struct drm_encoder *encoder);
+	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+					   struct drm_display_mode *mode);
+	const struct mpll_config *mpll_cfg;
+	const struct curr_ctrl *cur_ctr;
+	enum imx_hdmi_devtype dev_type;
+
+};
+
+int imx_hdmi_pltfm_register(struct platform_device *pdev,
+			    const struct imx_hdmi_plat_data *plat_data);
+int imx_hdmi_pltfm_unregister(struct platform_device *pdev);
+#endif /* __IMX_HDMI_H__ */
-- 
1.8.3.2


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi
  2014-11-04 13:33   ` Andy Yan
@ 2014-11-04 14:23     ` Zubair Lutfullah Kakakhel
  -1 siblings, 0 replies; 20+ messages in thread
From: Zubair Lutfullah Kakakhel @ 2014-11-04 14:23 UTC (permalink / raw)
  To: Andy Yan, airlied, heiko, fabio.estevam, rmk+kernel
  Cc: Greg Kroah-Hartman, Grant Likely, Rob Herring, Philipp Zabel,
	Shawn Guo, Josh Boyer, Sean Paul, Inki Dae, Dave Airlie,
	Arnd Bergmann, Lucas Stach, djkurtz, ykk, linux-kernel,
	dri-devel, devel, devicetree, linux-rockchip

Hi Andy,


On 04/11/14 13:33, Andy Yan wrote:
> imx6 and rockchip rk3288 and JZ4780 (Ingenic Xburst/MIPS)
> use the interface compatible Designware HDMI IP, but they
> also have some lightly difference, such as phy pll configuration,
> register width(imx hdmi register is one byte, but rk3288 is 4
> bytes width), 4K support(imx6 doesn't support 4k, but rk3288 does),
> clk useage,and the crtc mux configuration is also platform specific.
> 
> To reuse the imx hdmi driver, split the platform specific code out
> to dw_hdmi-imx.c.
> 
> Change-Id: I85e8d08754052b118423729a01c6d17bf485f383
> ---
>  drivers/staging/imx-drm/Makefile      |   2 +-
>  drivers/staging/imx-drm/dw_hdmi-imx.c | 214 ++++++++++
>  drivers/staging/imx-drm/imx-hdmi.c    | 726 ++++++++++++++--------------------
>  include/drm/bridge/dw_hdmi.h          | 114 ++++++
>  4 files changed, 634 insertions(+), 422 deletions(-)
>  create mode 100644 drivers/staging/imx-drm/dw_hdmi-imx.c
>  create mode 100644 include/drm/bridge/dw_hdmi.h
> 

This one patch does too much to be reviewed easily.

One patch is supposed to modify/add one thing at a time in the kernel.

Separating platform specific code from imx-drm/imx-hdmi is one thing.

Adding support for multi-byte register access is something different.

i.e. Something like.
1/3 split platform specific code out.
2/3 move/rename imx-hdmi outside the folder
3/3 add support for multi byte register width access.

If there are other things that are not directly relevant to the patch,
it goes in a different patch. Bug fixes are also separate.

This should result in readable patches that can be reviewed easily.

The maintainers might be able to guide on how to further split the patches if necessary.

Cheers,
ZubairLK

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi
@ 2014-11-04 14:23     ` Zubair Lutfullah Kakakhel
  0 siblings, 0 replies; 20+ messages in thread
From: Zubair Lutfullah Kakakhel @ 2014-11-04 14:23 UTC (permalink / raw)
  To: Andy Yan, airlied, heiko, fabio.estevam, rmk+kernel
  Cc: devel, devicetree, Arnd Bergmann, Josh Boyer, Greg Kroah-Hartman,
	linux-kernel, dri-devel, Inki Dae, linux-rockchip, Rob Herring,
	Sean Paul, djkurtz, Philipp Zabel, ykk, Grant Likely,
	Dave Airlie, Shawn Guo, Lucas Stach

Hi Andy,


On 04/11/14 13:33, Andy Yan wrote:
> imx6 and rockchip rk3288 and JZ4780 (Ingenic Xburst/MIPS)
> use the interface compatible Designware HDMI IP, but they
> also have some lightly difference, such as phy pll configuration,
> register width(imx hdmi register is one byte, but rk3288 is 4
> bytes width), 4K support(imx6 doesn't support 4k, but rk3288 does),
> clk useage,and the crtc mux configuration is also platform specific.
> 
> To reuse the imx hdmi driver, split the platform specific code out
> to dw_hdmi-imx.c.
> 
> Change-Id: I85e8d08754052b118423729a01c6d17bf485f383
> ---
>  drivers/staging/imx-drm/Makefile      |   2 +-
>  drivers/staging/imx-drm/dw_hdmi-imx.c | 214 ++++++++++
>  drivers/staging/imx-drm/imx-hdmi.c    | 726 ++++++++++++++--------------------
>  include/drm/bridge/dw_hdmi.h          | 114 ++++++
>  4 files changed, 634 insertions(+), 422 deletions(-)
>  create mode 100644 drivers/staging/imx-drm/dw_hdmi-imx.c
>  create mode 100644 include/drm/bridge/dw_hdmi.h
> 

This one patch does too much to be reviewed easily.

One patch is supposed to modify/add one thing at a time in the kernel.

Separating platform specific code from imx-drm/imx-hdmi is one thing.

Adding support for multi-byte register access is something different.

i.e. Something like.
1/3 split platform specific code out.
2/3 move/rename imx-hdmi outside the folder
3/3 add support for multi byte register width access.

If there are other things that are not directly relevant to the patch,
it goes in a different patch. Bug fixes are also separate.

This should result in readable patches that can be reviewed easily.

The maintainers might be able to guide on how to further split the patches if necessary.

Cheers,
ZubairLK

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform
  2014-11-04 13:33 ` Andy Yan
@ 2014-11-04 14:29   ` Russell King - ARM Linux
  -1 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2014-11-04 14:29 UTC (permalink / raw)
  To: Andy Yan
  Cc: airlied, heiko, fabio.estevam, Greg Kroah-Hartman, Grant Likely,
	Rob Herring, Philipp Zabel, Shawn Guo, Josh Boyer, Sean Paul,
	Inki Dae, Dave Airlie, Arnd Bergmann, Lucas Stach,
	Zubair.Kakakhel, djkurtz, ykk, linux-kernel, dri-devel, devel,
	devicetree, linux-rockchip

On Tue, Nov 04, 2014 at 09:33:10PM +0800, Andy Yan wrote:
> From: Andy yan <andy.yan@rock-chips.com>
> 
> We found freescale imx6 and rockchip rk3288 and Ingenic JZ4780 (Xburst/MIPS)
> use the interface compatible Designware HDMI IP, but they also have some
> lightly difference, such as phy pll configuration, register width(imx hdmi
> register is one byte, but rk3288 is 4 bytes width and can only access by word),
> 4K support(imx6 doesn't support 4k, but rk3288 does).
> 
> To reuse the imx-hdmi driver, we do this patch set:
> patch (1): split out imx-soc code from imx-hdmi to dw_hdmi-imx.c
> patch (2): move imx-hdmi to bridge/, and rename to dw-hdmi to
> make this driver indepent of drm-imx . And we will add rockchip 
> platform specific code dw_hdmi-rockchip.c later, this is depend
> on drm-rockchip.

Great - I fully agree that this needs to be renamed as it is a Designware
IP.

Should it be moved into bridge/ ?  It isn't implemented as a DRM bridge
driver at present, so this seems illogical.  Is the longer term plan to
convert it to be a DRM bridge?

Secondly, if you want HDMI audio support, you may find the patches I
maintain for the SolidRun devices useful, which add this as a standard
ALSA device.  I also have CEC support for it as well.  If you're
interested, I'll email those.

-- 
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform
@ 2014-11-04 14:29   ` Russell King - ARM Linux
  0 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2014-11-04 14:29 UTC (permalink / raw)
  To: Andy Yan
  Cc: heiko, dri-devel, ykk, devel, linux-rockchip, Grant Likely,
	Dave Airlie, devicetree, Zubair.Kakakhel, Arnd Bergmann,
	Rob Herring, fabio.estevam, Josh Boyer, Greg Kroah-Hartman,
	linux-kernel, djkurtz

On Tue, Nov 04, 2014 at 09:33:10PM +0800, Andy Yan wrote:
> From: Andy yan <andy.yan@rock-chips.com>
> 
> We found freescale imx6 and rockchip rk3288 and Ingenic JZ4780 (Xburst/MIPS)
> use the interface compatible Designware HDMI IP, but they also have some
> lightly difference, such as phy pll configuration, register width(imx hdmi
> register is one byte, but rk3288 is 4 bytes width and can only access by word),
> 4K support(imx6 doesn't support 4k, but rk3288 does).
> 
> To reuse the imx-hdmi driver, we do this patch set:
> patch (1): split out imx-soc code from imx-hdmi to dw_hdmi-imx.c
> patch (2): move imx-hdmi to bridge/, and rename to dw-hdmi to
> make this driver indepent of drm-imx . And we will add rockchip 
> platform specific code dw_hdmi-rockchip.c later, this is depend
> on drm-rockchip.

Great - I fully agree that this needs to be renamed as it is a Designware
IP.

Should it be moved into bridge/ ?  It isn't implemented as a DRM bridge
driver at present, so this seems illogical.  Is the longer term plan to
convert it to be a DRM bridge?

Secondly, if you want HDMI audio support, you may find the patches I
maintain for the SolidRun devices useful, which add this as a standard
ALSA device.  I also have CEC support for it as well.  If you're
interested, I'll email those.

-- 
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform
  2014-11-04 14:29   ` Russell King - ARM Linux
@ 2014-11-05  2:12     ` Andy Yan
  -1 siblings, 0 replies; 20+ messages in thread
From: Andy Yan @ 2014-11-05  2:12 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: airlied, heiko, fabio.estevam, Greg Kroah-Hartman, Grant Likely,
	Rob Herring, Philipp Zabel, Shawn Guo, Josh Boyer, Sean Paul,
	Inki Dae, Dave Airlie, Arnd Bergmann, Lucas Stach,
	Zubair.Kakakhel, djkurtz, ykk, linux-kernel, dri-devel, devel,
	devicetree, linux-rockchip


On 2014年11月04日 22:29, Russell King - ARM Linux wrote:
> On Tue, Nov 04, 2014 at 09:33:10PM +0800, Andy Yan wrote:
>> From: Andy yan <andy.yan@rock-chips.com>
>>
>> We found freescale imx6 and rockchip rk3288 and Ingenic JZ4780 (Xburst/MIPS)
>> use the interface compatible Designware HDMI IP, but they also have some
>> lightly difference, such as phy pll configuration, register width(imx hdmi
>> register is one byte, but rk3288 is 4 bytes width and can only access by word),
>> 4K support(imx6 doesn't support 4k, but rk3288 does).
>>
>> To reuse the imx-hdmi driver, we do this patch set:
>> patch (1): split out imx-soc code from imx-hdmi to dw_hdmi-imx.c
>> patch (2): move imx-hdmi to bridge/, and rename to dw-hdmi to
>> make this driver indepent of drm-imx . And we will add rockchip
>> platform specific code dw_hdmi-rockchip.c later, this is depend
>> on drm-rockchip.
> Great - I fully agree that this needs to be renamed as it is a Designware
> IP.
>
> Should it be moved into bridge/ ?  It isn't implemented as a DRM bridge
> driver at present, so this seems illogical.  Is the longer term plan to
> convert it to be a DRM bridge?
We have plan to convert it to DRM bridge.
>
> Secondly, if you want HDMI audio support, you may find the patches I
> maintain for the SolidRun devices useful, which add this as a standard
> ALSA device.  I also have CEC support for it as well.  If you're
> interested, I'll email those.
>
It's very great if you email those, we are also working on audio and CEC.


^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform
@ 2014-11-05  2:12     ` Andy Yan
  0 siblings, 0 replies; 20+ messages in thread
From: Andy Yan @ 2014-11-05  2:12 UTC (permalink / raw)
  To: Russell King - ARM Linux
  Cc: heiko, airlied, dri-devel, ykk, devel, linux-rockchip,
	Grant Likely, Dave Airlie, devicetree, Zubair.Kakakhel,
	Arnd Bergmann, Inki Dae, Rob Herring, Sean Paul, fabio.estevam,
	Josh Boyer, Greg Kroah-Hartman, linux-kernel, djkurtz,
	Philipp Zabel, Shawn Guo, Lucas Stach


On 2014年11月04日 22:29, Russell King - ARM Linux wrote:
> On Tue, Nov 04, 2014 at 09:33:10PM +0800, Andy Yan wrote:
>> From: Andy yan <andy.yan@rock-chips.com>
>>
>> We found freescale imx6 and rockchip rk3288 and Ingenic JZ4780 (Xburst/MIPS)
>> use the interface compatible Designware HDMI IP, but they also have some
>> lightly difference, such as phy pll configuration, register width(imx hdmi
>> register is one byte, but rk3288 is 4 bytes width and can only access by word),
>> 4K support(imx6 doesn't support 4k, but rk3288 does).
>>
>> To reuse the imx-hdmi driver, we do this patch set:
>> patch (1): split out imx-soc code from imx-hdmi to dw_hdmi-imx.c
>> patch (2): move imx-hdmi to bridge/, and rename to dw-hdmi to
>> make this driver indepent of drm-imx . And we will add rockchip
>> platform specific code dw_hdmi-rockchip.c later, this is depend
>> on drm-rockchip.
> Great - I fully agree that this needs to be renamed as it is a Designware
> IP.
>
> Should it be moved into bridge/ ?  It isn't implemented as a DRM bridge
> driver at present, so this seems illogical.  Is the longer term plan to
> convert it to be a DRM bridge?
We have plan to convert it to DRM bridge.
>
> Secondly, if you want HDMI audio support, you may find the patches I
> maintain for the SolidRun devices useful, which add this as a standard
> ALSA device.  I also have CEC support for it as well.  If you're
> interested, I'll email those.
>
It's very great if you email those, we are also working on audio and CEC.

_______________________________________________
devel mailing list
devel@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform
@ 2014-11-06  9:35     ` Kuankuan.Yang
  0 siblings, 0 replies; 20+ messages in thread
From: Kuankuan.Yang @ 2014-11-06  9:35 UTC (permalink / raw)
  To: Russell King - ARM Linux, Andy Yan
  Cc: airlied, heiko, fabio.estevam, Greg Kroah-Hartman, Grant Likely,
	Rob Herring, Philipp Zabel, Shawn Guo, Josh Boyer, Sean Paul,
	Inki Dae, Dave Airlie, Arnd Bergmann, Lucas Stach,
	Zubair.Kakakhel, djkurtz, linux-kernel, dri-devel, devel,
	devicetree, linux-rockchip

Hi Russell

I'm working on Designware hdmi-audio, also add it as a standard ALSA device.
Before I saw this email, I also planed to submit my patchs to upsteam.
I'm very grateful if you can email those patchs to us.

Best Regards.

于 2014年11月04日 22:29, Russell King - ARM Linux 写道:
> On Tue, Nov 04, 2014 at 09:33:10PM +0800, Andy Yan wrote:
>> From: Andy yan <andy.yan@rock-chips.com>
>>
>> We found freescale imx6 and rockchip rk3288 and Ingenic JZ4780 (Xburst/MIPS)
>> use the interface compatible Designware HDMI IP, but they also have some
>> lightly difference, such as phy pll configuration, register width(imx hdmi
>> register is one byte, but rk3288 is 4 bytes width and can only access by word),
>> 4K support(imx6 doesn't support 4k, but rk3288 does).
>>
>> To reuse the imx-hdmi driver, we do this patch set:
>> patch (1): split out imx-soc code from imx-hdmi to dw_hdmi-imx.c
>> patch (2): move imx-hdmi to bridge/, and rename to dw-hdmi to
>> make this driver indepent of drm-imx . And we will add rockchip
>> platform specific code dw_hdmi-rockchip.c later, this is depend
>> on drm-rockchip.
> Great - I fully agree that this needs to be renamed as it is a Designware
> IP.
>
> Should it be moved into bridge/ ?  It isn't implemented as a DRM bridge
> driver at present, so this seems illogical.  Is the longer term plan to
> convert it to be a DRM bridge?
>
> Secondly, if you want HDMI audio support, you may find the patches I
> maintain for the SolidRun devices useful, which add this as a standard
> ALSA device.  I also have CEC support for it as well.  If you're
> interested, I'll email those.
>



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform
@ 2014-11-06  9:35     ` Kuankuan.Yang
  0 siblings, 0 replies; 20+ messages in thread
From: Kuankuan.Yang @ 2014-11-06  9:35 UTC (permalink / raw)
  To: Russell King - ARM Linux, Andy Yan
  Cc: airlied-cv59FeDIM0c, heiko-4mtYJXux2i+zQB+pC5nmwQ,
	fabio.estevam-KZfg59tc24xl57MIdRCFDg, Greg Kroah-Hartman,
	Grant Likely, Rob Herring, Philipp Zabel, Shawn Guo, Josh Boyer,
	Sean Paul, Inki Dae, Dave Airlie, Arnd Bergmann, Lucas Stach,
	Zubair.Kakakhel-1AXoQHu6uovQT0dZR+AlfA,
	djkurtz-hpIqsD4AKlfQT0dZR+AlfA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	dri-devel-PD4FTy7X32lNgt0PjOBp9y5qC8QIuHrW,
	devel-gWbeCf7V1WCQmaza687I9mD2FQJk+8+b,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-rockchip-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi Russell

I'm working on Designware hdmi-audio, also add it as a standard ALSA device.
Before I saw this email, I also planed to submit my patchs to upsteam.
I'm very grateful if you can email those patchs to us.

Best Regards.

于 2014年11月04日 22:29, Russell King - ARM Linux 写道:
> On Tue, Nov 04, 2014 at 09:33:10PM +0800, Andy Yan wrote:
>> From: Andy yan <andy.yan-TNX95d0MmH7DzftRWevZcw@public.gmane.org>
>>
>> We found freescale imx6 and rockchip rk3288 and Ingenic JZ4780 (Xburst/MIPS)
>> use the interface compatible Designware HDMI IP, but they also have some
>> lightly difference, such as phy pll configuration, register width(imx hdmi
>> register is one byte, but rk3288 is 4 bytes width and can only access by word),
>> 4K support(imx6 doesn't support 4k, but rk3288 does).
>>
>> To reuse the imx-hdmi driver, we do this patch set:
>> patch (1): split out imx-soc code from imx-hdmi to dw_hdmi-imx.c
>> patch (2): move imx-hdmi to bridge/, and rename to dw-hdmi to
>> make this driver indepent of drm-imx . And we will add rockchip
>> platform specific code dw_hdmi-rockchip.c later, this is depend
>> on drm-rockchip.
> Great - I fully agree that this needs to be renamed as it is a Designware
> IP.
>
> Should it be moved into bridge/ ?  It isn't implemented as a DRM bridge
> driver at present, so this seems illogical.  Is the longer term plan to
> convert it to be a DRM bridge?
>
> Secondly, if you want HDMI audio support, you may find the patches I
> maintain for the SolidRun devices useful, which add this as a standard
> ALSA device.  I also have CEC support for it as well.  If you're
> interested, I'll email those.
>


--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform
  2014-11-06  9:35     ` Kuankuan.Yang
@ 2014-11-06 10:18       ` Russell King - ARM Linux
  -1 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2014-11-06 10:18 UTC (permalink / raw)
  To: Kuankuan.Yang
  Cc: Andy Yan, airlied, heiko, fabio.estevam, Greg Kroah-Hartman,
	Grant Likely, Rob Herring, Philipp Zabel, Shawn Guo, Josh Boyer,
	Sean Paul, Inki Dae, Dave Airlie, Arnd Bergmann, Lucas Stach,
	Zubair.Kakakhel, djkurtz, linux-kernel, dri-devel, devel,
	devicetree, linux-rockchip

[-- Attachment #1: Type: text/plain, Size: 2204 bytes --]

On Thu, Nov 06, 2014 at 05:35:40PM +0800, Kuankuan.Yang wrote:
> I'm working on Designware hdmi-audio, also add it as a standard ALSA device.
> Before I saw this email, I also planed to submit my patchs to upsteam.
> I'm very grateful if you can email those patchs to us.

I've attached the set of patches - they're part of a much bigger patch
set for supporting the Cubox-i in mainline, but should apply cleanly.
They're against a 3.17 base rather than 3.18-rc, but I do have these
rebased as 3.18-rc2 based patches too.

I've been waiting for imx-drm to move out of drivers/staging before
deciding where they should live - there seems to be no good place in the
sound/ subtree to place these (sound/soc is not the right place.)

They're being used not only with the SolidRun Cubox-i and Hummingboard,
but also the Novena project too.

One thing the audio part does not yet support is using SDMA on iMX6 to
feed updates to the audio DMA, so this driver should work with other
non-iMX6 SDMA.

Patches 21 to 23 are various attempts to try and fix a problem I've
noticed only on certain iMX6 SoCs (two different revisions of the DW IP
are used in iMX6 depending on whether it's the solo/dual-lite parts, or
the dual/quad parts.)  Inspite of the published errata, I found that the
given workarounds had no useful effect on the problem, and like most
bought-in IP, it's extremely difficult to resolve these kinds of issues
from an open source perspective without having commercial links with
manufacturers to gain access to internal documentation and/or support.

The CEC bits provide a mostly compatible interface with the current FSL
iMX BSP trees, but with a different structure to it, and hopefully less
buggily than the FSL driver.  There has been an effort to add support
for the FSL interface to libcec, but when I've looked at the library,
I've never been impressed by the code, nor by the authors handling of
anything but a clean transmission (which IMHO makes the whole thing
unsafe, especially if you have multiple devices on the CEC bus, you
need the logical ID arbitration to work properly.)

-- 
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.

[-- Attachment #2: 0018-dw-hdmi-audio-add-audio-driver.patch --]
[-- Type: text/plain, Size: 20291 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 018/107] dw-hdmi-audio: add audio driver
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Add ALSA based HDMI audio driver for imx-hdmi.  The imx-hdmi is a
Synopsis DesignWare module, so let's name it after that.  The only
buffer format supported is its own special IEC958 based format, which
is not compatible with any ALSA format.  To avoid doing too much data
manipulation within the driver, we support only ALSAs IEC958 LE, and
24-bit PCM formats for 2 to 6 channels.

This allows us to modify the buffer in place as each period is passed
for DMA without needing a separate buffer.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/Kconfig         |   8 +
 drivers/staging/imx-drm/Makefile        |   1 +
 drivers/staging/imx-drm/dw-hdmi-audio.c | 547 ++++++++++++++++++++++++++++++++
 drivers/staging/imx-drm/dw-hdmi-audio.h |  14 +
 drivers/staging/imx-drm/imx-hdmi.c      |  29 ++
 5 files changed, 599 insertions(+)
 create mode 100644 drivers/staging/imx-drm/dw-hdmi-audio.c
 create mode 100644 drivers/staging/imx-drm/dw-hdmi-audio.h

diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig
index 82fb758a29bc..008b544b9911 100644
--- a/drivers/staging/imx-drm/Kconfig
+++ b/drivers/staging/imx-drm/Kconfig
@@ -51,3 +51,11 @@ config DRM_IMX_HDMI
 	depends on DRM_IMX
 	help
 	  Choose this if you want to use HDMI on i.MX6.
+
+config DRM_DW_HDMI_AUDIO
+	tristate "Synopsis Designware Audio interface"
+	depends on DRM_IMX_HDMI != n
+	help
+	  Support the Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX HDMI driver.
diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
index 582c438d8cbd..07e65a410f8f 100644
--- a/drivers/staging/imx-drm/Makefile
+++ b/drivers/staging/imx-drm/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
 imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o
 obj-$(CONFIG_DRM_IMX_IPUV3)	+= imx-ipuv3-crtc.o
 obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AUDIO) += dw-hdmi-audio.o
diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
new file mode 100644
index 000000000000..4f9790dea6db
--- /dev/null
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -0,0 +1,547 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * 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.
+ *
+ * Written and tested against the (alleged) DW HDMI Tx found in iMX6S.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+
+#include "dw-hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	uint8_t revision;
+	uint8_t iec_offset;
+	uint8_t cs[192][8];
+};
+
+static void dw_hdmi_writel(unsigned long val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	uint32_t *src = dw->buf_src + offset;
+	uint32_t *dst = dw->buf_dst + offset;
+	uint32_t *end = dw->buf_src + offset + bytes;
+
+	do {
+		uint32_t b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static uint32_t parity(uint32_t sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	uint32_t *src = dw->buf_src + offset;
+	uint32_t *dst = dw->buf_dst + offset;
+	uint32_t *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		uint8_t *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			uint32_t sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	uint8_t cs[4];
+	unsigned ch, i, j;
+
+	cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
+	cs[1] = IEC958_AES1_CON_GENERAL;
+	cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC;
+	cs[3] = IEC958_AES3_CON_CLOCK_1000PPM;
+
+	switch (runtime->rate) {
+	case 32000:
+		cs[3] |= IEC958_AES3_CON_FS_32000;
+		break;
+	case 44100:
+		cs[3] |= IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		cs[3] |= IEC958_AES3_CON_FS_48000;
+		break;
+	case 88200:
+		cs[3] |= IEC958_AES3_CON_FS_88200;
+		break;
+	case 96000:
+		cs[3] |= IEC958_AES3_CON_FS_96000;
+		break;
+	case 176400:
+		cs[3] |= IEC958_AES3_CON_FS_176400;
+		break;
+	case 192000:
+		cs[3] |= IEC958_AES3_CON_FS_192000;
+		break;
+	}
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	dw->substream = NULL;
+
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	runtime->hw = dw_hdmi_hw;
+	snd_pcm_limit_hw_rates(runtime);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	uint8_t threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw->data.set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		dw_hdmi_stop_dma(dw);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = "dw-hdmi-audio",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.h b/drivers/staging/imx-drm/dw-hdmi-audio.h
new file mode 100644
index 000000000000..f01979d49efd
--- /dev/null
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.h
@@ -0,0 +1,14 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct imx_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct imx_hdmi *hdmi;
+	void (*set_sample_rate)(struct imx_hdmi *, unsigned);
+};
+
+#endif
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index 18c9ccd460b7..7efca1554d5a 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -29,6 +29,7 @@
 #include <drm/drm_encoder_slave.h>
 #include <video/imx-ipu-v3.h>
 
+#include "dw-hdmi-audio.h"
 #include "imx-hdmi.h"
 #include "imx-drm.h"
 
@@ -115,6 +116,7 @@ struct imx_hdmi {
 	struct drm_connector connector;
 	struct drm_encoder encoder;
 
+	struct platform_device *audio;
 	enum imx_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -361,6 +363,12 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct imx_hdmi *hdmi)
 	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock);
 }
 
+static void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned rate)
+{
+	hdmi->sample_rate = rate;
+	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock);
+}
+
 /*
  * this submodule is responsible for the video data synchronization.
  * for example, for RGB 4:4:4 input, the data map is defined as
@@ -1587,11 +1595,13 @@ MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids);
 static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
+	struct platform_device_info pdevinfo;
 	const struct of_device_id *of_id =
 				of_match_device(imx_hdmi_dt_ids, dev);
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct imx_hdmi *hdmi;
 	struct resource *iores;
 	int ret, irq;
@@ -1706,6 +1716,22 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	/* Unmute interrupts */
 	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	audio.phys = iores->start;
+	audio.base = hdmi->regs;
+	audio.irq = irq;
+	audio.hdmi = hdmi;
+	audio.set_sample_rate = imx_hdmi_set_sample_rate;
+
+	pdevinfo.name = "dw-hdmi-audio";
+	pdevinfo.data = &audio;
+	pdevinfo.size_data = sizeof(audio);
+	pdevinfo.dma_mask = DMA_BIT_MASK(32);
+	hdmi->audio = platform_device_register_full(&pdevinfo);
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1723,6 +1749,9 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master,
 {
 	struct imx_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (!IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
-- 
1.8.3.1


[-- Attachment #3: 0019-drivers-staging-imx-drm-Fix-audio-buffer-sizes.patch --]
[-- Type: text/plain, Size: 1197 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 019/107] drivers: staging: imx-drm: Fix audio buffer sizes
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

From: Sean Cross <xobs@kosagi.com>

Pulseaudio connects to dw-hdmi-audio with a period size of 1024 and a
buffer size of 4408 or so.  Because of this, it overruns its buffer and
panics.

Inform ALSA that we'd like the buffer size to be a multiple
of the period size, to prevent this problem.

Signed-off-by: Sean Cross <xobs@kosagi.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index 4f9790dea6db..ee3f8c9b5695 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -331,6 +331,7 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
 
 	runtime->hw = dw_hdmi_hw;
 	snd_pcm_limit_hw_rates(runtime);
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 
 	return 0;
 }
-- 
1.8.3.1


[-- Attachment #4: 0020-dw-hdmi-audio-parse-ELD-from-HDMI-driver.patch --]
[-- Type: text/plain, Size: 3898 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 020/107] dw-hdmi-audio: parse ELD from HDMI driver
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Parse the ELD (EDID like data) stored from the HDMI driver to restrict
the sample rates and channels which are available to ALSA.  This causes
the ALSA device to reflect the capabilities of the overall audio path,
not just what is supported at the HDMI source interface level.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 51 +++++++++++++++++++++++++++++++++
 drivers/staging/imx-drm/dw-hdmi-audio.h |  1 +
 drivers/staging/imx-drm/imx-hdmi.c      |  3 ++
 3 files changed, 55 insertions(+)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index ee3f8c9b5695..daa3302d82b3 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -300,6 +300,56 @@ static struct snd_pcm_hardware dw_hdmi_hw = {
 	.fifo_size = 0,
 };
 
+static unsigned rates_mask[] = {
+	SNDRV_PCM_RATE_32000,
+	SNDRV_PCM_RATE_44100,
+	SNDRV_PCM_RATE_48000,
+	SNDRV_PCM_RATE_88200,
+	SNDRV_PCM_RATE_96000,
+	SNDRV_PCM_RATE_176400,
+	SNDRV_PCM_RATE_192000,
+};
+
+static void dw_hdmi_parse_eld(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 *sad, *eld = dw->data.eld;
+	unsigned eld_ver,  mnl, sad_count, rates, rate_mask, i;
+	unsigned max_channels;
+
+	eld_ver = eld[0] >> 3;
+	if (eld_ver != 2 && eld_ver != 31)
+		return;
+
+	mnl = eld[4] & 0x1f;
+	if (mnl > 16)
+		return;
+
+	sad_count = eld[5] >> 4;
+	sad = eld + 20 + mnl;
+
+	/* Start from the basic audio settings */
+	max_channels = 2;
+	rates = 7;
+	while (sad_count > 0) {
+		switch (sad[0] & 0x78) {
+		case 0x08: /* PCM */
+			max_channels = max(max_channels, (sad[0] & 7) + 1u);
+			rates |= sad[1];
+			break;
+		}
+		sad += 3;
+		sad_count -= 1;
+	}
+
+	for (rate_mask = i = 0; i < ARRAY_SIZE(rates_mask); i++)
+		if (rates & 1 << i)
+			rate_mask |= rates_mask[i];
+
+	runtime->hw.rates &= rate_mask;
+	runtime->hw.channels_max = min(runtime->hw.channels_max, max_channels);
+}
+
 static int dw_hdmi_open(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -330,6 +380,7 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
 		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
 
 	runtime->hw = dw_hdmi_hw;
+	dw_hdmi_parse_eld(dw, runtime);
 	snd_pcm_limit_hw_rates(runtime);
 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 
diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.h b/drivers/staging/imx-drm/dw-hdmi-audio.h
index f01979d49efd..2d91d709381d 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.h
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.h
@@ -8,6 +8,7 @@ struct dw_hdmi_audio_data {
 	void __iomem *base;
 	int irq;
 	struct imx_hdmi *hdmi;
+	u8 *eld;
 	void (*set_sample_rate)(struct imx_hdmi *, unsigned);
 };
 
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index 7efca1554d5a..412026c3f9f9 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -1408,6 +1408,8 @@ static int imx_hdmi_connector_get_modes(struct drm_connector *connector)
 
 		drm_mode_connector_update_edid_property(connector, edid);
 		ret = drm_add_edid_modes(connector, edid);
+		/* Store the ELD */
+		drm_edid_to_eld(connector, edid);
 		kfree(edid);
 	} else {
 		dev_dbg(hdmi->dev, "failed to get edid\n");
@@ -1724,6 +1726,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	audio.base = hdmi->regs;
 	audio.irq = irq;
 	audio.hdmi = hdmi;
+	audio.eld = hdmi->connector.eld;
 	audio.set_sample_rate = imx_hdmi_set_sample_rate;
 
 	pdevinfo.name = "dw-hdmi-audio";
-- 
1.8.3.1


[-- Attachment #5: 0021-dw-hdmi-audio-try-to-fix-burbling-audio.patch --]
[-- Type: text/plain, Size: 1308 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 021/107] dw-hdmi-audio: try to fix burbling audio
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index daa3302d82b3..c2ec8213f8e1 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -61,6 +61,7 @@ enum {
 	HDMI_REVISION_ID = 0x0001,
 	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
 	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AUD_N1 = 0x3200,
 	HDMI_AHB_DMA_CONF0 = 0x3600,
 	HDMI_AHB_DMA_START = 0x3601,
 	HDMI_AHB_DMA_STOP = 0x3602,
@@ -480,6 +481,13 @@ static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 		dw->buf_offset = 0;
 		dw->substream = substream;
 		dw_hdmi_start_dma(dw);
+		if (dw->revision == 0x0a) {
+			void __iomem *base = dw->data.base;
+			unsigned val;
+
+			val = readb_relaxed(base + HDMI_AUD_N1);
+			writeb_relaxed(val, base + HDMI_AUD_N1);
+		}
 		substream->runtime->delay = substream->runtime->period_size;
 		break;
 
-- 
1.8.3.1


[-- Attachment #6: 0022-dw-hdmi-audio-try-implementing-ERR005174-to-fix-burb.patch --]
[-- Type: text/plain, Size: 2472 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 022/107] dw-hdmi-audio: try implementing ERR005174 to fix
 burbling audio
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 35 +++++++++++++++++++++++++++------
 1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index c2ec8213f8e1..77094d674a55 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -7,6 +7,7 @@
  *
  * Written and tested against the (alleged) DW HDMI Tx found in iMX6S.
  */
+#include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
@@ -68,6 +69,8 @@ enum {
 	HDMI_AHB_DMA_THRSLD = 0x3603,
 	HDMI_AHB_DMA_STRADDR0 = 0x3604,
 	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_STAT = 0x3612,
+	HDMI_AHB_DMA_STAT_FULL = BIT(1),
 	HDMI_AHB_DMA_MASK = 0x3614,
 	HDMI_AHB_DMA_POL = 0x3615,
 	HDMI_AHB_DMA_CONF1 = 0x3616,
@@ -474,20 +477,40 @@ static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
 static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct snd_dw_hdmi *dw = substream->private_data;
-	int ret = 0;
+	void __iomem *base = dw->data.base;
+	unsigned val[3];
+	unsigned timeout = 10000;
+	int ret = 0, i;
+	bool err005174;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
+		err005174 = dw->revision == 0x0a;
+		if (err005174) {
+			for (i = 2; i >= 0; i--) {
+				val[i] = readb_relaxed(base + HDMI_AUD_N1 + i);
+				writeb_relaxed(0, base + HDMI_AUD_N1 + i);
+			}
+		}
+
 		dw->buf_offset = 0;
 		dw->substream = substream;
 		dw_hdmi_start_dma(dw);
-		if (dw->revision == 0x0a) {
-			void __iomem *base = dw->data.base;
-			unsigned val;
 
-			val = readb_relaxed(base + HDMI_AUD_N1);
-			writeb_relaxed(val, base + HDMI_AUD_N1);
+		if (err005174) {
+			do {
+				if (readb_relaxed(base + HDMI_AHB_DMA_STAT) & HDMI_AHB_DMA_STAT_FULL)
+					break;
+				udelay(1);
+			} while (timeout--);
+
+			if (!(readb_relaxed(base + HDMI_AHB_DMA_STAT) & HDMI_AHB_DMA_STAT_FULL))
+				pr_info("timeout!\n");
+
+			for (i = 2; i >= 0; i--)
+				writeb_relaxed(val[i], base + HDMI_AUD_N1 + i);
 		}
+
 		substream->runtime->delay = substream->runtime->period_size;
 		break;
 
-- 
1.8.3.1


[-- Attachment #7: 0023-dw-hdmi-audio-another-attempt-at-fixing-burbling.patch --]
[-- Type: text/plain, Size: 2433 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 023/107] dw-hdmi-audio: another attempt at fixing burbling
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index 77094d674a55..967c3d6bf8cf 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -63,6 +63,7 @@ enum {
 	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
 	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
 	HDMI_AUD_N1 = 0x3200,
+	HDMI_AUD_CTS1 = 0x3203,
 	HDMI_AHB_DMA_CONF0 = 0x3600,
 	HDMI_AHB_DMA_START = 0x3601,
 	HDMI_AHB_DMA_STOP = 0x3602,
@@ -478,8 +479,7 @@ static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct snd_dw_hdmi *dw = substream->private_data;
 	void __iomem *base = dw->data.base;
-	unsigned val[3];
-	unsigned timeout = 10000;
+	unsigned n[3], cts[3];
 	int ret = 0, i;
 	bool err005174;
 
@@ -487,9 +487,11 @@ static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 	case SNDRV_PCM_TRIGGER_START:
 		err005174 = dw->revision == 0x0a;
 		if (err005174) {
-			for (i = 2; i >= 0; i--) {
-				val[i] = readb_relaxed(base + HDMI_AUD_N1 + i);
+			for (i = 2; i >= 1; i--) {
+				n[i] = readb_relaxed(base + HDMI_AUD_N1 + i);
+				cts[i] = readb_relaxed(base + HDMI_AUD_CTS1 + i);
 				writeb_relaxed(0, base + HDMI_AUD_N1 + i);
+				writeb_relaxed(0, base + HDMI_AUD_CTS1 + i);
 			}
 		}
 
@@ -498,17 +500,10 @@ static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 		dw_hdmi_start_dma(dw);
 
 		if (err005174) {
-			do {
-				if (readb_relaxed(base + HDMI_AHB_DMA_STAT) & HDMI_AHB_DMA_STAT_FULL)
-					break;
-				udelay(1);
-			} while (timeout--);
-
-			if (!(readb_relaxed(base + HDMI_AHB_DMA_STAT) & HDMI_AHB_DMA_STAT_FULL))
-				pr_info("timeout!\n");
-
-			for (i = 2; i >= 0; i--)
-				writeb_relaxed(val[i], base + HDMI_AUD_N1 + i);
+			for (i = 2; i >= 1; i--)
+				writeb_relaxed(cts[i], base + HDMI_AUD_CTS1 + i);
+			for (i = 2; i >= 1; i--)
+				writeb_relaxed(n[i], base + HDMI_AUD_N1 + i);
 		}
 
 		substream->runtime->delay = substream->runtime->period_size;
-- 
1.8.3.1


[-- Attachment #8: 0024-dw-hdmi-audio-basic-suspend-resume-support.patch --]
[-- Type: text/plain, Size: 1541 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 024/107] dw-hdmi-audio: basic suspend/resume support
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index 967c3d6bf8cf..5243d51b4069 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -610,12 +610,40 @@ static int snd_dw_hdmi_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
 static struct platform_driver snd_dw_hdmi_driver = {
 	.probe	= snd_dw_hdmi_probe,
 	.remove	= snd_dw_hdmi_remove,
 	.driver	= {
 		.name = "dw-hdmi-audio",
 		.owner = THIS_MODULE,
+		.pm = PM_OPS,
 	},
 };
 
-- 
1.8.3.1


[-- Attachment #9: 0025-cec-add-generic-HDMI-CEC-driver.patch --]
[-- Type: text/plain, Size: 13976 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 025/107] cec: add generic HDMI CEC driver
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Add a generic userspace API to support HDMI Consumer Electronics Control
interfaces.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/Kconfig              |   2 +
 drivers/Makefile             |   1 +
 drivers/cec/Kconfig          |  14 ++
 drivers/cec/Makefile         |   1 +
 drivers/cec/cec-dev.c        | 384 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/cec-dev.h      |  69 ++++++++
 include/uapi/linux/cec-dev.h |  34 ++++
 7 files changed, 505 insertions(+)
 create mode 100644 drivers/cec/Kconfig
 create mode 100644 drivers/cec/Makefile
 create mode 100644 drivers/cec/cec-dev.c
 create mode 100644 include/linux/cec-dev.h
 create mode 100644 include/uapi/linux/cec-dev.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 622fa266b29e..fa2b20ba644f 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -176,6 +176,8 @@ source "drivers/powercap/Kconfig"
 
 source "drivers/mcb/Kconfig"
 
+source "drivers/cec/Kconfig"
+
 source "drivers/ras/Kconfig"
 
 source "drivers/thunderbolt/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index ebee55537a05..b8208b267615 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -159,5 +159,6 @@ obj-$(CONFIG_NTB)		+= ntb/
 obj-$(CONFIG_FMC)		+= fmc/
 obj-$(CONFIG_POWERCAP)		+= powercap/
 obj-$(CONFIG_MCB)		+= mcb/
+obj-$(CONFIG_CEC)		+= cec/
 obj-$(CONFIG_RAS)		+= ras/
 obj-$(CONFIG_THUNDERBOLT)	+= thunderbolt/
diff --git a/drivers/cec/Kconfig b/drivers/cec/Kconfig
new file mode 100644
index 000000000000..d67cfb83de6a
--- /dev/null
+++ b/drivers/cec/Kconfig
@@ -0,0 +1,14 @@
+#
+# Consumer Electroncs Control support
+#
+
+menu "Consumer Electronics Control devices"
+
+config CEC
+	bool
+
+config HDMI_CEC_CORE
+	tristate
+	select CEC
+
+endmenu
diff --git a/drivers/cec/Makefile b/drivers/cec/Makefile
new file mode 100644
index 000000000000..b94278bc8321
--- /dev/null
+++ b/drivers/cec/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HDMI_CEC_CORE) += cec-dev.o
diff --git a/drivers/cec/cec-dev.c b/drivers/cec/cec-dev.c
new file mode 100644
index 000000000000..ba58d8217851
--- /dev/null
+++ b/drivers/cec/cec-dev.c
@@ -0,0 +1,384 @@
+/*
+ * HDMI Consumer Electronics Control
+ *
+ * This provides the user API for communication with HDMI CEC complaint
+ * devices in kernel drivers, and is based upon the protocol developed
+ * by Freescale for their i.MX SoCs.
+ *
+ * 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.
+ */
+#include <linux/cec-dev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+struct cec_event {
+	struct cec_user_event usr;
+	struct list_head node;
+};
+
+static struct class *cec_class;
+static int cec_major;
+
+static void cec_dev_send_message(struct cec_dev *cec_dev, u8 *msg,
+	size_t count)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cec_dev->lock, flags);
+	cec_dev->retries = 5;
+	cec_dev->write_busy = 1;
+	cec_dev->send_message(cec_dev, msg, count);
+	spin_unlock_irqrestore(&cec_dev->lock, flags);
+}
+
+void cec_dev_event(struct cec_dev *cec_dev, int type, u8 *msg, size_t len)
+{
+	struct cec_event *event;
+	unsigned long flags;
+
+	event = kzalloc(sizeof(*event), GFP_ATOMIC);
+	if (event) {
+		event->usr.event_type = type;
+		event->usr.msg_len = len;
+		if (msg)
+			memcpy(event->usr.msg, msg, len);
+
+		spin_lock_irqsave(&cec_dev->lock, flags);
+		list_add_tail(&event->node, &cec_dev->events);
+		spin_unlock_irqrestore(&cec_dev->lock, flags);
+		wake_up(&cec_dev->waitq);
+	}
+}
+EXPORT_SYMBOL_GPL(cec_dev_event);
+
+static int cec_dev_lock_write(struct cec_dev *cec_dev, struct file *file)
+	__acquires(cec_dev->mutex)
+{
+	int ret;
+
+	do {
+		if (file->f_flags & O_NONBLOCK) {
+			if (cec_dev->write_busy)
+				return -EAGAIN;
+		} else {
+			ret = wait_event_interruptible(cec_dev->waitq,
+						       !cec_dev->write_busy);
+			if (ret)
+				break;
+		}
+
+		ret = mutex_lock_interruptible(&cec_dev->mutex);
+		if (ret)
+			break;
+
+		if (!cec_dev->write_busy)
+			break;
+
+		mutex_unlock(&cec_dev->mutex);
+	} while (1);
+
+	return ret;
+}
+
+static ssize_t cec_dev_read(struct file *file, char __user *buf,
+	size_t count, loff_t *ppos)
+{
+	struct cec_dev *cec_dev = file->private_data;
+	ssize_t ret;
+
+	if (count > sizeof(struct cec_user_event))
+		count = sizeof(struct cec_user_event);
+
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+
+	do {
+		struct cec_event *event = NULL;
+		unsigned long flags;
+
+		spin_lock_irqsave(&cec_dev->lock, flags);
+		if (!list_empty(&cec_dev->events)) {
+			event = list_first_entry(&cec_dev->events,
+					struct cec_event, node);
+			list_del(&event->node);
+		}
+		spin_unlock_irqrestore(&cec_dev->lock, flags);
+
+		if (event) {
+			ret = __copy_to_user(buf, &event->usr, count) ?
+				 -EFAULT : count;
+			kfree(event);
+			break;
+		}
+
+		if (file->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		ret = wait_event_interruptible(cec_dev->waitq,
+					       !list_empty(&cec_dev->events));
+		if (ret)
+			break;
+	} while (1);
+
+	return ret;
+}
+
+static ssize_t cec_dev_write(struct file *file, const char __user *buf,
+	size_t count, loff_t *ppos)
+{
+	struct cec_dev *cec_dev = file->private_data;
+	u8 msg[MAX_MESSAGE_LEN];
+	int ret;
+
+	if (count > sizeof(msg))
+		return -E2BIG;
+
+	if (copy_from_user(msg, buf, count))
+		return -EFAULT;
+
+	ret = cec_dev_lock_write(cec_dev, file);
+	if (ret)
+		return ret;
+
+	cec_dev_send_message(cec_dev, msg, count);
+
+	mutex_unlock(&cec_dev->mutex);
+
+	return count;
+}
+
+static long cec_dev_ioctl(struct file *file, u_int cmd, unsigned long arg)
+{
+	struct cec_dev *cec_dev = file->private_data;
+	int ret;
+
+	switch (cmd) {
+	case HDMICEC_IOC_O_SETLOGICALADDRESS:
+	case HDMICEC_IOC_SETLOGICALADDRESS:
+		if (arg > 15) {
+			ret = -EINVAL;
+			break;
+		}
+
+		ret = cec_dev_lock_write(cec_dev, file);
+		if (ret == 0) {
+			unsigned char msg[1];
+
+			cec_dev->addresses = BIT(arg);
+			cec_dev->set_address(cec_dev, cec_dev->addresses);
+
+			/*
+			 * Send a ping message with the source and destination
+			 * set to our address; the result indicates whether
+			 * unit has chosen our address simultaneously.
+			 */
+			msg[0] = arg << 4 | arg;
+			cec_dev_send_message(cec_dev, msg, sizeof(msg));
+			mutex_unlock(&cec_dev->mutex);
+		}
+		break;
+
+	case HDMICEC_IOC_STARTDEVICE:
+		ret = mutex_lock_interruptible(&cec_dev->mutex);
+		if (ret == 0) {
+			cec_dev->addresses = BIT(15);
+			cec_dev->set_address(cec_dev, cec_dev->addresses);
+			mutex_unlock(&cec_dev->mutex);
+		}
+		break;
+
+	case HDMICEC_IOC_STOPDEVICE:
+		ret = 0;
+		break;
+
+	case HDMICEC_IOC_GETPHYADDRESS:
+		ret = put_user(cec_dev->physical, (u16 __user *)arg);
+		ret = -ENOIOCTLCMD;
+		break;
+
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+static unsigned cec_dev_poll(struct file *file, poll_table *wait)
+{
+	struct cec_dev *cec_dev = file->private_data;
+	unsigned mask = 0;
+
+	poll_wait(file, &cec_dev->waitq, wait);
+
+	if (cec_dev->write_busy == 0)
+		mask |= POLLOUT | POLLWRNORM;
+	if (!list_empty(&cec_dev->events))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+static int cec_dev_release(struct inode *inode, struct file *file)
+{
+	struct cec_dev *cec_dev = file->private_data;
+
+	mutex_lock(&cec_dev->mutex);
+	if (cec_dev->users >= 1)
+		cec_dev->users -= 1;
+	if (cec_dev->users == 0) {
+		/*
+		 * Wait for any write to complete before shutting down.
+		 * A message should complete in a maximum of 2.75ms *
+		 * 160 bits + 4.7ms, or 444.7ms.  Let's call that 500ms.
+		 * If we time out, shutdown anyway.
+		 */
+		wait_event_timeout(cec_dev->waitq, !cec_dev->write_busy,
+				   msecs_to_jiffies(500));
+
+		cec_dev->release(cec_dev);
+
+		while (!list_empty(&cec_dev->events)) {
+			struct cec_event *event;
+
+			event = list_first_entry(&cec_dev->events,
+					struct cec_event, node);
+			list_del(&event->node);
+			kfree(event);
+		}
+	}
+	mutex_unlock(&cec_dev->mutex);
+	return 0;
+}
+
+static int cec_dev_open(struct inode *inode, struct file *file)
+{
+	struct cec_dev *cec_dev = container_of(inode->i_cdev, struct cec_dev,
+					       cdev);
+	int ret = 0;
+
+	nonseekable_open(inode, file);
+
+	file->private_data = cec_dev;
+
+	ret = mutex_lock_interruptible(&cec_dev->mutex);
+	if (ret)
+		return ret;
+
+	if (cec_dev->users++ == 0) {
+		cec_dev->addresses = BIT(15);
+
+		ret = cec_dev->open(cec_dev);
+		if (ret < 0)
+			cec_dev->users = 0;
+	}
+	mutex_unlock(&cec_dev->mutex);
+
+	return ret;
+}
+
+static const struct file_operations hdmi_cec_fops = {
+	.owner = THIS_MODULE,
+	.read = cec_dev_read,
+	.write = cec_dev_write,
+	.open = cec_dev_open,
+	.unlocked_ioctl = cec_dev_ioctl,
+	.release = cec_dev_release,
+	.poll = cec_dev_poll,
+};
+
+void cec_dev_init(struct cec_dev *cec_dev, struct module *module)
+{
+	cec_dev->devn = MKDEV(cec_major, 0);
+
+	INIT_LIST_HEAD(&cec_dev->events);
+	init_waitqueue_head(&cec_dev->waitq);
+	spin_lock_init(&cec_dev->lock);
+	mutex_init(&cec_dev->mutex);
+
+	cec_dev->addresses = BIT(15);
+
+	cdev_init(&cec_dev->cdev, &hdmi_cec_fops);
+	cec_dev->cdev.owner = module;
+}
+EXPORT_SYMBOL_GPL(cec_dev_init);
+
+int cec_dev_add(struct cec_dev *cec_dev, struct device *dev, const char *name)
+{
+	struct device *cd;
+	int ret;
+
+	ret = cdev_add(&cec_dev->cdev, cec_dev->devn, 1);
+	if (ret < 0)
+		goto err_cdev;
+
+	cd = device_create(cec_class, dev, cec_dev->devn, NULL, name);
+	if (IS_ERR(cd)) {
+		ret = PTR_ERR(cd);
+		dev_err(dev, "can't create device: %d\n", ret);
+		goto err_dev;
+	}
+
+	return 0;
+
+ err_dev:
+	cdev_del(&cec_dev->cdev);
+ err_cdev:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cec_dev_add);
+
+void cec_dev_remove(struct cec_dev *cec_dev)
+{
+	device_destroy(cec_class, cec_dev->devn);
+	cdev_del(&cec_dev->cdev);
+}
+EXPORT_SYMBOL_GPL(cec_dev_remove);
+
+static int cec_init(void)
+{
+	dev_t dev;
+	int ret;
+
+	cec_class = class_create(THIS_MODULE, "hdmi-cec");
+	if (IS_ERR(cec_class)) {
+		ret = PTR_ERR(cec_class);
+		pr_err("cec: can't create cec class: %d\n", ret);
+		goto err_class;
+	}
+
+	ret = alloc_chrdev_region(&dev, 0, 1, "hdmi-cec");
+	if (ret) {
+		pr_err("cec: can't create character devices: %d\n", ret);
+		goto err_chrdev;
+	}
+
+	cec_major = MAJOR(dev);
+
+	return 0;
+
+ err_chrdev:
+	class_destroy(cec_class);
+ err_class:
+	return ret;
+}
+subsys_initcall(cec_init);
+
+static void cec_exit(void)
+{
+	unregister_chrdev_region(MKDEV(cec_major, 0), 1);
+	class_destroy(cec_class);
+}
+module_exit(cec_exit);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Generic HDMI CEC driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/cec-dev.h b/include/linux/cec-dev.h
new file mode 100644
index 000000000000..76a7d7f6a72d
--- /dev/null
+++ b/include/linux/cec-dev.h
@@ -0,0 +1,69 @@
+#ifndef _LINUX_CEC_DEV_H
+#define _LINUX_CEC_DEV_H
+
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <uapi/linux/cec-dev.h>
+
+struct device;
+
+struct cec_dev {
+	struct cdev cdev;
+	dev_t devn;
+
+	struct mutex mutex;
+	unsigned users;
+
+	spinlock_t lock;
+	wait_queue_head_t waitq;
+	struct list_head events;
+	u8 write_busy;
+
+	u8 retries;
+	u16 addresses;
+	u16 physical;
+
+	int (*open)(struct cec_dev *);
+	void (*release)(struct cec_dev *);
+	void (*send_message)(struct cec_dev *, u8 *, size_t);
+	void (*set_address)(struct cec_dev *, unsigned);
+};
+
+void cec_dev_event(struct cec_dev *cec_dev, int type, u8 *msg, size_t len);
+
+static inline void cec_dev_receive(struct cec_dev *cec_dev, u8 *msg,
+	unsigned len)
+{
+	cec_dev_event(cec_dev, MESSAGE_TYPE_RECEIVE_SUCCESS, msg, len);
+}
+
+static inline void cec_dev_send_complete(struct cec_dev *cec_dev, int ack)
+{
+	cec_dev->retries = 0;
+	cec_dev->write_busy = 0;
+
+	cec_dev_event(cec_dev, ack ? MESSAGE_TYPE_SEND_SUCCESS :
+		      MESSAGE_TYPE_NOACK, NULL, 0);
+}
+
+static inline void cec_dev_disconnect(struct cec_dev *cec_dev)
+{
+	cec_dev->physical = 0;
+	cec_dev_event(cec_dev, MESSAGE_TYPE_DISCONNECTED, NULL, 0);
+}
+
+static inline void cec_dev_connect(struct cec_dev *cec_dev, u32 phys)
+{
+	cec_dev->physical = phys;
+	cec_dev_event(cec_dev, MESSAGE_TYPE_CONNECTED, NULL, 0);
+}
+
+void cec_dev_init(struct cec_dev *cec_dev, struct module *);
+int cec_dev_add(struct cec_dev *cec_dev, struct device *, const char *name);
+void cec_dev_remove(struct cec_dev *cec_dev);
+
+#endif
diff --git a/include/uapi/linux/cec-dev.h b/include/uapi/linux/cec-dev.h
new file mode 100644
index 000000000000..fb7a41704c77
--- /dev/null
+++ b/include/uapi/linux/cec-dev.h
@@ -0,0 +1,34 @@
+#ifndef _UAPI_LINUX_CEC_DEV_H
+#define _UAPI_LINUX_CEC_DEV_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define MAX_MESSAGE_LEN 16
+
+enum {
+	HDMICEC_IOC_MAGIC = 'H',
+	/* This is wrong: we pass the argument as a number, not a pointer */
+	HDMICEC_IOC_O_SETLOGICALADDRESS	= _IOW(HDMICEC_IOC_MAGIC, 1, unsigned char),
+	HDMICEC_IOC_SETLOGICALADDRESS	= _IO(HDMICEC_IOC_MAGIC, 1),
+	HDMICEC_IOC_STARTDEVICE		= _IO(HDMICEC_IOC_MAGIC, 2),
+	HDMICEC_IOC_STOPDEVICE		= _IO(HDMICEC_IOC_MAGIC, 3),
+	HDMICEC_IOC_GETPHYADDRESS	= _IOR(HDMICEC_IOC_MAGIC, 4, unsigned char[4]),
+};
+
+enum {
+	MESSAGE_TYPE_RECEIVE_SUCCESS = 1,
+	MESSAGE_TYPE_NOACK,
+	MESSAGE_TYPE_DISCONNECTED,
+	MESSAGE_TYPE_CONNECTED,
+	MESSAGE_TYPE_SEND_SUCCESS,
+	MESSAGE_TYPE_SEND_ERROR,
+};
+
+struct cec_user_event {
+	__u32 event_type;
+	__u32 msg_len;
+	__u8 msg[MAX_MESSAGE_LEN];
+};
+
+#endif
-- 
1.8.3.1


[-- Attachment #10: 0026-imx-drm-dw-hdmi-cec-add-HDMI-CEC-driver.patch --]
[-- Type: text/plain, Size: 12835 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 026/107] imx-drm: dw-hdmi-cec: add HDMI CEC driver
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

This adds a HDMI Consumer Electronics Control driver, making use of the
generic HDMI CEC driver to provide a common user API for these devices.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/Kconfig       |   9 ++
 drivers/staging/imx-drm/Makefile      |   1 +
 drivers/staging/imx-drm/dw-hdmi-cec.c | 207 ++++++++++++++++++++++++++++++++++
 drivers/staging/imx-drm/dw-hdmi-cec.h |  16 +++
 drivers/staging/imx-drm/imx-hdmi.c    |  63 +++++++++--
 5 files changed, 286 insertions(+), 10 deletions(-)
 create mode 100644 drivers/staging/imx-drm/dw-hdmi-cec.c
 create mode 100644 drivers/staging/imx-drm/dw-hdmi-cec.h

diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig
index 008b544b9911..6238165f0b22 100644
--- a/drivers/staging/imx-drm/Kconfig
+++ b/drivers/staging/imx-drm/Kconfig
@@ -59,3 +59,12 @@ config DRM_DW_HDMI_AUDIO
 	  Support the Audio interface which is part of the Synopsis
 	  Designware HDMI block.  This is used in conjunction with
 	  the i.MX HDMI driver.
+
+config DRM_DW_HDMI_CEC
+	tristate "Synopsis Designware CEC interface"
+	depends on DRM_IMX_HDMI != n
+	select HDMI_CEC_CORE
+	help
+	  Support the CEC interface which is part of the Synposis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX HDMI driver.
diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
index 07e65a410f8f..82a368cf5979 100644
--- a/drivers/staging/imx-drm/Makefile
+++ b/drivers/staging/imx-drm/Makefile
@@ -11,3 +11,4 @@ imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o
 obj-$(CONFIG_DRM_IMX_IPUV3)	+= imx-ipuv3-crtc.o
 obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AUDIO) += dw-hdmi-audio.o
+obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
diff --git a/drivers/staging/imx-drm/dw-hdmi-cec.c b/drivers/staging/imx-drm/dw-hdmi-cec.c
new file mode 100644
index 000000000000..224e97d99b3b
--- /dev/null
+++ b/drivers/staging/imx-drm/dw-hdmi-cec.c
@@ -0,0 +1,207 @@
+/* http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/tree/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c?h=imx_3.0.35_4.1.0 */
+#include <linux/cec-dev.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "imx-hdmi.h"
+#include "dw-hdmi-cec.h"
+
+#define DEV_NAME "mxc_hdmi_cec"
+
+enum {
+	CEC_STAT_DONE		= BIT(0),
+	CEC_STAT_EOM		= BIT(1),
+	CEC_STAT_NACK		= BIT(2),
+	CEC_STAT_ARBLOST	= BIT(3),
+	CEC_STAT_ERROR_INIT	= BIT(4),
+	CEC_STAT_ERROR_FOLL	= BIT(5),
+	CEC_STAT_WAKEUP		= BIT(6),
+
+	CEC_CTRL_START		= BIT(0),
+	CEC_CTRL_NORMAL		= 1 << 1,
+};
+
+struct dw_hdmi_cec {
+	struct cec_dev cec;
+
+	struct device *dev;
+	void __iomem *base;
+	const struct dw_hdmi_cec_ops *ops;
+	void *ops_data;
+	int irq;
+};
+
+static void dw_hdmi_set_address(struct cec_dev *cec_dev, unsigned addresses)
+{
+	struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
+
+	writeb(addresses & 255, cec->base + HDMI_CEC_ADDR_L);
+	writeb(addresses >> 8, cec->base + HDMI_CEC_ADDR_H);
+}
+
+static void dw_hdmi_send_message(struct cec_dev *cec_dev, u8 *msg,
+	size_t count)
+{
+	struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
+	unsigned i;
+
+	for (i = 0; i < count; i++)
+		writeb(msg[i], cec->base + HDMI_CEC_TX_DATA0 + i);
+
+	writeb(count, cec->base + HDMI_CEC_TX_CNT);
+	writeb(CEC_CTRL_NORMAL | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
+}
+
+static irqreturn_t dw_hdmi_cec_irq(int irq, void *data)
+{
+	struct dw_hdmi_cec *cec = data;
+	struct cec_dev *cec_dev = &cec->cec;
+	unsigned stat = readb(cec->base + HDMI_IH_CEC_STAT0);
+
+	if (stat == 0)
+		return IRQ_NONE;
+
+	writeb(stat, cec->base + HDMI_IH_CEC_STAT0);
+
+	if (stat & CEC_STAT_ERROR_INIT) {
+		if (cec->cec.retries) {
+			unsigned v = readb(cec->base + HDMI_CEC_CTRL);
+			writeb(v | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
+			cec->cec.retries -= 1;
+		} else {
+			cec->cec.write_busy = 0;
+			cec_dev_event(cec_dev, MESSAGE_TYPE_SEND_ERROR, NULL, 0);
+		}
+	} else if (stat & (CEC_STAT_DONE | CEC_STAT_NACK))
+		cec_dev_send_complete(cec_dev, stat & CEC_STAT_DONE);
+
+	if (stat & CEC_STAT_EOM) {
+		unsigned len, i;
+		u8 msg[MAX_MESSAGE_LEN];
+
+		len = readb(cec->base + HDMI_CEC_RX_CNT);
+		if (len > sizeof(msg))
+			len = sizeof(msg);
+
+		for (i = 0; i < len; i++)
+			msg[i] = readb(cec->base + HDMI_CEC_RX_DATA0 + i);
+
+		writeb(0, cec->base + HDMI_CEC_LOCK);
+
+		cec_dev_receive(cec_dev, msg, len);
+	}
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(dw_hdmi_cec_irq);
+
+static void dw_hdmi_cec_release(struct cec_dev *cec_dev)
+{
+	struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
+
+	writeb(~0, cec->base + HDMI_CEC_MASK);
+	writeb(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+	writeb(0, cec->base + HDMI_CEC_POLARITY);
+
+	free_irq(cec->irq, cec);
+
+	cec->ops->disable(cec->ops_data);
+}
+
+static int dw_hdmi_cec_open(struct cec_dev *cec_dev)
+{
+	struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
+	unsigned irqs;
+	int ret;
+
+	writeb(0, cec->base + HDMI_CEC_CTRL);
+	writeb(~0, cec->base + HDMI_IH_CEC_STAT0);
+	writeb(0, cec->base + HDMI_CEC_LOCK);
+
+	ret = request_irq(cec->irq, dw_hdmi_cec_irq, IRQF_SHARED,
+			  DEV_NAME, cec);
+	if (ret < 0)
+		return ret;
+
+	dw_hdmi_set_address(cec_dev, cec_dev->addresses);
+
+	cec->ops->enable(cec->ops_data);
+
+	irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM |
+	       CEC_STAT_DONE;
+	writeb(irqs, cec->base + HDMI_CEC_POLARITY);
+	writeb(~irqs, cec->base + HDMI_CEC_MASK);
+	writeb(~irqs, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_cec_probe(struct platform_device *pdev)
+{
+	struct dw_hdmi_cec_data *data = dev_get_platdata(&pdev->dev);
+	struct dw_hdmi_cec *cec;
+
+	if (!data)
+		return -ENXIO;
+
+	cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+	if (!cec)
+		return -ENOMEM;
+
+	cec->dev = &pdev->dev;
+	cec->base = data->base;
+	cec->irq = data->irq;
+	cec->ops = data->ops;
+	cec->ops_data = data->ops_data;
+	cec->cec.open = dw_hdmi_cec_open;
+	cec->cec.release = dw_hdmi_cec_release;
+	cec->cec.send_message = dw_hdmi_send_message;
+	cec->cec.set_address = dw_hdmi_set_address;
+
+	cec_dev_init(&cec->cec, THIS_MODULE);
+
+	/* FIXME: soft-reset the CEC interface */
+
+	dw_hdmi_set_address(&cec->cec, cec->cec.addresses);
+	writeb(0, cec->base + HDMI_CEC_TX_CNT);
+	writeb(~0, cec->base + HDMI_CEC_MASK);
+	writeb(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+	writeb(0, cec->base + HDMI_CEC_POLARITY);
+
+	platform_set_drvdata(pdev, cec);
+
+	/*
+	 * Our device is just a convenience - we want to link to the real
+	 * hardware device here, so that userspace can see the association
+	 * between the HDMI hardware and its associated CEC chardev.
+	 */
+	return cec_dev_add(&cec->cec, cec->dev->parent, DEV_NAME);
+}
+
+static int dw_hdmi_cec_remove(struct platform_device *pdev)
+{
+	struct dw_hdmi_cec *cec = platform_get_drvdata(pdev);
+
+	cec_dev_remove(&cec->cec);
+
+	return 0;
+}
+
+static struct platform_driver dw_hdmi_cec_driver = {
+	.probe	= dw_hdmi_cec_probe,
+	.remove	= dw_hdmi_cec_remove,
+	.driver = {
+		.name = "dw-hdmi-cec",
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(dw_hdmi_cec_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI CEC driver for i.MX");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-cec");
diff --git a/drivers/staging/imx-drm/dw-hdmi-cec.h b/drivers/staging/imx-drm/dw-hdmi-cec.h
new file mode 100644
index 000000000000..5ff40cc237a8
--- /dev/null
+++ b/drivers/staging/imx-drm/dw-hdmi-cec.h
@@ -0,0 +1,16 @@
+#ifndef DW_HDMI_CEC_H
+#define DW_HDMI_CEC_H
+
+struct dw_hdmi_cec_ops {
+	void (*enable)(void *);
+	void (*disable)(void *);
+};
+
+struct dw_hdmi_cec_data {
+	void __iomem *base;
+	int irq;
+	const struct dw_hdmi_cec_ops *ops;
+	void *ops_data;
+};
+
+#endif
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index 412026c3f9f9..8806f6e74fe4 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -30,6 +30,7 @@
 #include <video/imx-ipu-v3.h>
 
 #include "dw-hdmi-audio.h"
+#include "dw-hdmi-cec.h"
 #include "imx-hdmi.h"
 #include "imx-drm.h"
 
@@ -117,6 +118,7 @@ struct imx_hdmi {
 	struct drm_encoder encoder;
 
 	struct platform_device *audio;
+	struct platform_device *cec;
 	enum imx_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -126,6 +128,7 @@ struct imx_hdmi {
 	int vic;
 
 	u8 edid[HDMI_EDID_LEN];
+	u8 mc_clkdis;
 	bool cable_plugin;
 
 	bool phy_enabled;
@@ -1152,8 +1155,6 @@ static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi)
 /* HDMI Initialization Step B.4 */
 static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi)
 {
-	u8 clkdis;
-
 	/* control period minimum duration */
 	hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
 	hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
@@ -1165,23 +1166,28 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi)
 	hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
 
 	/* Enable pixel clock and tmds data path */
-	clkdis = 0x7F;
-	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE |
+			   HDMI_MC_CLKDIS_CSCCLK_DISABLE |
+			   HDMI_MC_CLKDIS_AUDCLK_DISABLE |
+			   HDMI_MC_CLKDIS_PREPCLK_DISABLE |
+			   HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
-	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
 	/* Enable csc path */
 	if (is_color_space_conversion(hdmi)) {
-		clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
-		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+		hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+		hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 	}
 }
 
 static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi)
 {
-	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 }
 
 /* Workaround to clear the overflow condition */
@@ -1576,6 +1582,27 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 	return 0;
 }
 
+static void imx_hdmi_cec_enable(void *data)
+{
+	struct imx_hdmi *hdmi = data;
+
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static void imx_hdmi_cec_disable(void *data)
+{
+	struct imx_hdmi *hdmi = data;
+
+	hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static const struct dw_hdmi_cec_ops imx_hdmi_cec_ops = {
+	.enable = imx_hdmi_cec_enable,
+	.disable = imx_hdmi_cec_disable,
+};
+
 static struct platform_device_id imx_hdmi_devtype[] = {
 	{
 		.name = "imx6q-hdmi",
@@ -1604,6 +1631,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	struct device_node *np = dev->of_node;
 	struct device_node *ddc_node;
 	struct dw_hdmi_audio_data audio;
+	struct dw_hdmi_cec_data cec;
 	struct imx_hdmi *hdmi;
 	struct resource *iores;
 	int ret, irq;
@@ -1615,6 +1643,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	hdmi->dev = dev;
 	hdmi->sample_rate = 48000;
 	hdmi->ratio = 100;
+	hdmi->mc_clkdis = 0x7f;
 
 	if (of_id) {
 		const struct platform_device_id *device_id = of_id->data;
@@ -1735,6 +1764,18 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	pdevinfo.dma_mask = DMA_BIT_MASK(32);
 	hdmi->audio = platform_device_register_full(&pdevinfo);
 
+	cec.base = hdmi->regs;
+	cec.irq = irq;
+	cec.ops = &imx_hdmi_cec_ops;
+	cec.ops_data = hdmi;
+
+	pdevinfo.name = "dw-hdmi-cec";
+	pdevinfo.data = &cec;
+	pdevinfo.size_data = sizeof(cec);
+	pdevinfo.dma_mask = 0;
+
+	hdmi->cec = platform_device_register_full(&pdevinfo);
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1754,6 +1795,8 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master,
 
 	if (!IS_ERR(hdmi->audio))
 		platform_device_unregister(hdmi->audio);
+	if (!IS_ERR(hdmi->cec))
+		platform_device_unregister(hdmi->cec);
 
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform
@ 2014-11-06 10:18       ` Russell King - ARM Linux
  0 siblings, 0 replies; 20+ messages in thread
From: Russell King - ARM Linux @ 2014-11-06 10:18 UTC (permalink / raw)
  To: Kuankuan.Yang
  Cc: heiko, dri-devel, devel, linux-rockchip, Grant Likely,
	Dave Airlie, devicetree, Zubair.Kakakhel, Arnd Bergmann,
	Rob Herring, fabio.estevam, Josh Boyer, Greg Kroah-Hartman,
	linux-kernel, djkurtz, Andy Yan

[-- Attachment #1: Type: text/plain, Size: 2204 bytes --]

On Thu, Nov 06, 2014 at 05:35:40PM +0800, Kuankuan.Yang wrote:
> I'm working on Designware hdmi-audio, also add it as a standard ALSA device.
> Before I saw this email, I also planed to submit my patchs to upsteam.
> I'm very grateful if you can email those patchs to us.

I've attached the set of patches - they're part of a much bigger patch
set for supporting the Cubox-i in mainline, but should apply cleanly.
They're against a 3.17 base rather than 3.18-rc, but I do have these
rebased as 3.18-rc2 based patches too.

I've been waiting for imx-drm to move out of drivers/staging before
deciding where they should live - there seems to be no good place in the
sound/ subtree to place these (sound/soc is not the right place.)

They're being used not only with the SolidRun Cubox-i and Hummingboard,
but also the Novena project too.

One thing the audio part does not yet support is using SDMA on iMX6 to
feed updates to the audio DMA, so this driver should work with other
non-iMX6 SDMA.

Patches 21 to 23 are various attempts to try and fix a problem I've
noticed only on certain iMX6 SoCs (two different revisions of the DW IP
are used in iMX6 depending on whether it's the solo/dual-lite parts, or
the dual/quad parts.)  Inspite of the published errata, I found that the
given workarounds had no useful effect on the problem, and like most
bought-in IP, it's extremely difficult to resolve these kinds of issues
from an open source perspective without having commercial links with
manufacturers to gain access to internal documentation and/or support.

The CEC bits provide a mostly compatible interface with the current FSL
iMX BSP trees, but with a different structure to it, and hopefully less
buggily than the FSL driver.  There has been an effort to add support
for the FSL interface to libcec, but when I've looked at the library,
I've never been impressed by the code, nor by the authors handling of
anything but a clean transmission (which IMHO makes the whole thing
unsafe, especially if you have multiple devices on the CEC bus, you
need the logical ID arbitration to work properly.)

-- 
FTTC broadband for 0.8mile line: currently at 9.5Mbps down 400kbps up
according to speedtest.net.

[-- Attachment #2: 0018-dw-hdmi-audio-add-audio-driver.patch --]
[-- Type: text/plain, Size: 20291 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 018/107] dw-hdmi-audio: add audio driver
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Add ALSA based HDMI audio driver for imx-hdmi.  The imx-hdmi is a
Synopsis DesignWare module, so let's name it after that.  The only
buffer format supported is its own special IEC958 based format, which
is not compatible with any ALSA format.  To avoid doing too much data
manipulation within the driver, we support only ALSAs IEC958 LE, and
24-bit PCM formats for 2 to 6 channels.

This allows us to modify the buffer in place as each period is passed
for DMA without needing a separate buffer.

A more desirable solution would be to have this conversion in userspace,
but ALSA does not appear to allow such transformations outside of
libasound itself.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/Kconfig         |   8 +
 drivers/staging/imx-drm/Makefile        |   1 +
 drivers/staging/imx-drm/dw-hdmi-audio.c | 547 ++++++++++++++++++++++++++++++++
 drivers/staging/imx-drm/dw-hdmi-audio.h |  14 +
 drivers/staging/imx-drm/imx-hdmi.c      |  29 ++
 5 files changed, 599 insertions(+)
 create mode 100644 drivers/staging/imx-drm/dw-hdmi-audio.c
 create mode 100644 drivers/staging/imx-drm/dw-hdmi-audio.h

diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig
index 82fb758a29bc..008b544b9911 100644
--- a/drivers/staging/imx-drm/Kconfig
+++ b/drivers/staging/imx-drm/Kconfig
@@ -51,3 +51,11 @@ config DRM_IMX_HDMI
 	depends on DRM_IMX
 	help
 	  Choose this if you want to use HDMI on i.MX6.
+
+config DRM_DW_HDMI_AUDIO
+	tristate "Synopsis Designware Audio interface"
+	depends on DRM_IMX_HDMI != n
+	help
+	  Support the Audio interface which is part of the Synopsis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX HDMI driver.
diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
index 582c438d8cbd..07e65a410f8f 100644
--- a/drivers/staging/imx-drm/Makefile
+++ b/drivers/staging/imx-drm/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
 imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o
 obj-$(CONFIG_DRM_IMX_IPUV3)	+= imx-ipuv3-crtc.o
 obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o
+obj-$(CONFIG_DRM_DW_HDMI_AUDIO) += dw-hdmi-audio.o
diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
new file mode 100644
index 000000000000..4f9790dea6db
--- /dev/null
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -0,0 +1,547 @@
+/*
+ * DesignWare HDMI audio driver
+ *
+ * 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.
+ *
+ * Written and tested against the (alleged) DW HDMI Tx found in iMX6S.
+ */
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <sound/asoundef.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+
+#include "dw-hdmi-audio.h"
+
+#define DRIVER_NAME "dw-hdmi-audio"
+
+/* Provide some bits rather than bit offsets */
+enum {
+	HDMI_AHB_DMA_CONF0_SW_FIFO_RST = BIT(7),
+	HDMI_AHB_DMA_CONF0_EN_HLOCK = BIT(3),
+	HDMI_AHB_DMA_START_START = BIT(0),
+	HDMI_AHB_DMA_STOP_STOP = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_MUTE_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_IH_AHBDMAAUD_STAT0_ERROR = BIT(5),
+	HDMI_IH_AHBDMAAUD_STAT0_LOST = BIT(4),
+	HDMI_IH_AHBDMAAUD_STAT0_RETRY = BIT(3),
+	HDMI_IH_AHBDMAAUD_STAT0_DONE = BIT(2),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL = BIT(1),
+	HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY = BIT(0),
+	HDMI_IH_AHBDMAAUD_STAT0_ALL =
+		HDMI_IH_AHBDMAAUD_STAT0_ERROR |
+		HDMI_IH_AHBDMAAUD_STAT0_LOST |
+		HDMI_IH_AHBDMAAUD_STAT0_RETRY |
+		HDMI_IH_AHBDMAAUD_STAT0_DONE |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFFULL |
+		HDMI_IH_AHBDMAAUD_STAT0_BUFFEMPTY,
+	HDMI_AHB_DMA_CONF0_INCR16 = 2 << 1,
+	HDMI_AHB_DMA_CONF0_INCR8 = 1 << 1,
+	HDMI_AHB_DMA_CONF0_INCR4 = 0,
+	HDMI_AHB_DMA_CONF0_BURST_MODE = BIT(0),
+	HDMI_AHB_DMA_MASK_DONE = BIT(7),
+	HDMI_REVISION_ID = 0x0001,
+	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
+	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AHB_DMA_CONF0 = 0x3600,
+	HDMI_AHB_DMA_START = 0x3601,
+	HDMI_AHB_DMA_STOP = 0x3602,
+	HDMI_AHB_DMA_THRSLD = 0x3603,
+	HDMI_AHB_DMA_STRADDR0 = 0x3604,
+	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_MASK = 0x3614,
+	HDMI_AHB_DMA_POL = 0x3615,
+	HDMI_AHB_DMA_CONF1 = 0x3616,
+	HDMI_AHB_DMA_BUFFPOL = 0x361a,
+};
+
+struct snd_dw_hdmi {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct dw_hdmi_audio_data data;
+	struct snd_pcm_substream *substream;
+	void (*reformat)(struct snd_dw_hdmi *, size_t, size_t);
+	void *buf_src;
+	void *buf_dst;
+	dma_addr_t buf_addr;
+	unsigned buf_offset;
+	unsigned buf_period;
+	unsigned buf_size;
+	unsigned channels;
+	uint8_t revision;
+	uint8_t iec_offset;
+	uint8_t cs[192][8];
+};
+
+static void dw_hdmi_writel(unsigned long val, void __iomem *ptr)
+{
+	writeb_relaxed(val, ptr);
+	writeb_relaxed(val >> 8, ptr + 1);
+	writeb_relaxed(val >> 16, ptr + 2);
+	writeb_relaxed(val >> 24, ptr + 3);
+}
+
+/*
+ * Convert to hardware format: The userspace buffer contains IEC958 samples,
+ * with the PCUV bits in bits 31..28 and audio samples in bits 27..4.  We
+ * need these to be in bits 27..24, with the IEC B bit in bit 28, and audio
+ * samples in 23..0.
+ *
+ * Default preamble in bits 3..0: 8 = block start, 4 = even 2 = odd
+ *
+ * Ideally, we could do with having the data properly formatted in userspace.
+ */
+static void dw_hdmi_reformat_iec958(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	uint32_t *src = dw->buf_src + offset;
+	uint32_t *dst = dw->buf_dst + offset;
+	uint32_t *end = dw->buf_src + offset + bytes;
+
+	do {
+		uint32_t b, sample = *src++;
+
+		b = (sample & 8) << (28 - 3);
+
+		sample >>= 4;
+
+		*dst++ = sample | b;
+	} while (src < end);
+}
+
+static uint32_t parity(uint32_t sample)
+{
+	sample ^= sample >> 16;
+	sample ^= sample >> 8;
+	sample ^= sample >> 4;
+	sample ^= sample >> 2;
+	sample ^= sample >> 1;
+	return (sample & 1) << 27;
+}
+
+static void dw_hdmi_reformat_s24(struct snd_dw_hdmi *dw,
+	size_t offset, size_t bytes)
+{
+	uint32_t *src = dw->buf_src + offset;
+	uint32_t *dst = dw->buf_dst + offset;
+	uint32_t *end = dw->buf_src + offset + bytes;
+
+	do {
+		unsigned i;
+		uint8_t *cs;
+
+		cs = dw->cs[dw->iec_offset++];
+		if (dw->iec_offset >= 192)
+			dw->iec_offset = 0;
+
+		i = dw->channels;
+		do {
+			uint32_t sample = *src++;
+
+			sample &= ~0xff000000;
+			sample |= *cs++ << 24;
+			sample |= parity(sample & ~0xf8000000);
+
+			*dst++ = sample;
+		} while (--i);
+	} while (src < end);
+}
+
+static void dw_hdmi_create_cs(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	uint8_t cs[4];
+	unsigned ch, i, j;
+
+	cs[0] = IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_NONE;
+	cs[1] = IEC958_AES1_CON_GENERAL;
+	cs[2] = IEC958_AES2_CON_SOURCE_UNSPEC;
+	cs[3] = IEC958_AES3_CON_CLOCK_1000PPM;
+
+	switch (runtime->rate) {
+	case 32000:
+		cs[3] |= IEC958_AES3_CON_FS_32000;
+		break;
+	case 44100:
+		cs[3] |= IEC958_AES3_CON_FS_44100;
+		break;
+	case 48000:
+		cs[3] |= IEC958_AES3_CON_FS_48000;
+		break;
+	case 88200:
+		cs[3] |= IEC958_AES3_CON_FS_88200;
+		break;
+	case 96000:
+		cs[3] |= IEC958_AES3_CON_FS_96000;
+		break;
+	case 176400:
+		cs[3] |= IEC958_AES3_CON_FS_176400;
+		break;
+	case 192000:
+		cs[3] |= IEC958_AES3_CON_FS_192000;
+		break;
+	}
+
+	memset(dw->cs, 0, sizeof(dw->cs));
+
+	for (ch = 0; ch < 8; ch++) {
+		cs[2] &= ~IEC958_AES2_CON_CHANNEL;
+		cs[2] |= (ch + 1) << 4;
+
+		for (i = 0; i < ARRAY_SIZE(cs); i++) {
+			unsigned c = cs[i];
+
+			for (j = 0; j < 8; j++, c >>= 1)
+				dw->cs[i * 8 + j][ch] = (c & 1) << 2;
+		}
+	}
+	dw->cs[0][0] |= BIT(4);
+}
+
+static void dw_hdmi_start_dma(struct snd_dw_hdmi *dw)
+{
+	void __iomem *base = dw->data.base;
+	unsigned offset = dw->buf_offset;
+	unsigned period = dw->buf_period;
+	u32 start, stop;
+
+	dw->reformat(dw, offset, period);
+
+	/* Clear all irqs before enabling irqs and starting DMA */
+	writeb_relaxed(HDMI_IH_AHBDMAAUD_STAT0_ALL,
+		       base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	start = dw->buf_addr + offset;
+	stop = start + period - 1;
+
+	/* Setup the hardware start/stop addresses */
+	dw_hdmi_writel(start, base + HDMI_AHB_DMA_STRADDR0);
+	dw_hdmi_writel(stop, base + HDMI_AHB_DMA_STPADDR0);
+
+	writeb_relaxed((u8)~HDMI_AHB_DMA_MASK_DONE, base + HDMI_AHB_DMA_MASK);
+	writeb(HDMI_AHB_DMA_START_START, base + HDMI_AHB_DMA_START);
+
+	offset += period;
+	if (offset >= dw->buf_size)
+		offset = 0;
+	dw->buf_offset = offset;
+}
+
+static void dw_hdmi_stop_dma(struct snd_dw_hdmi *dw)
+{
+	dw->substream = NULL;
+
+	/* Disable interrupts before disabling DMA */
+	writeb_relaxed(~0, dw->data.base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(HDMI_AHB_DMA_STOP_STOP, dw->data.base + HDMI_AHB_DMA_STOP);
+}
+
+static irqreturn_t snd_dw_hdmi_irq(int irq, void *data)
+{
+	struct snd_dw_hdmi *dw = data;
+	struct snd_pcm_substream *substream;
+	unsigned stat;
+
+	stat = readb_relaxed(dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+	if (!stat)
+		return IRQ_NONE;
+
+	writeb_relaxed(stat, dw->data.base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	substream = dw->substream;
+	if (stat & HDMI_IH_AHBDMAAUD_STAT0_DONE && substream) {
+		snd_pcm_period_elapsed(substream);
+		if (dw->substream)
+			dw_hdmi_start_dma(dw);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static struct snd_pcm_hardware dw_hdmi_hw = {
+	.info = SNDRV_PCM_INFO_INTERLEAVED |
+		SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		SNDRV_PCM_INFO_MMAP |
+		SNDRV_PCM_INFO_MMAP_VALID,
+	.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE |
+		   SNDRV_PCM_FMTBIT_S24_LE,
+	.rates = SNDRV_PCM_RATE_32000 |
+		 SNDRV_PCM_RATE_44100 |
+		 SNDRV_PCM_RATE_48000 |
+		 SNDRV_PCM_RATE_88200 |
+		 SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_176400 |
+		 SNDRV_PCM_RATE_192000,
+	.channels_min = 2,
+	.channels_max = 8,
+	.buffer_bytes_max = 64 * 1024,
+	.period_bytes_min = 256,
+	.period_bytes_max = 8192,	/* ERR004323: must limit to 8k */
+	.periods_min = 2,
+	.periods_max = 16,
+	.fifo_size = 0,
+};
+
+static int dw_hdmi_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	void __iomem *base = dw->data.base;
+	int ret;
+
+	/* Clear FIFO */
+	writeb_relaxed(HDMI_AHB_DMA_CONF0_SW_FIFO_RST,
+		       base + HDMI_AHB_DMA_CONF0);
+
+	/* Configure interrupt polarities */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_POL);
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_BUFFPOL);
+
+	/* Keep interrupts masked, and clear any pending */
+	writeb_relaxed(~0, base + HDMI_AHB_DMA_MASK);
+	writeb_relaxed(~0, base + HDMI_IH_AHBDMAAUD_STAT0);
+
+	ret = request_irq(dw->data.irq, snd_dw_hdmi_irq, IRQF_SHARED,
+			  "dw-hdmi-audio", dw);
+	if (ret)
+		return ret;
+
+	/* Un-mute done interrupt */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL &
+		       ~HDMI_IH_MUTE_AHBDMAAUD_STAT0_DONE,
+		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	runtime->hw = dw_hdmi_hw;
+	snd_pcm_limit_hw_rates(runtime);
+
+	return 0;
+}
+
+static int dw_hdmi_close(struct snd_pcm_substream *substream)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	/* Mute all interrupts */
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       dw->data.base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+
+	free_irq(dw->data.irq, dw);
+
+	return 0;
+}
+
+static int dw_hdmi_hw_free(struct snd_pcm_substream *substream)
+{
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int dw_hdmi_hw_params(struct snd_pcm_substream *substream,
+	struct snd_pcm_hw_params *params)
+{
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(params));
+}
+
+static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+	uint8_t threshold, conf0, conf1;
+
+	/* Setup as per 3.0.5 FSL 4.1.0 BSP */
+	switch (dw->revision) {
+	case 0x0a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR4;
+		if (runtime->channels == 2)
+			threshold = 126;
+		else
+			threshold = 124;
+		break;
+	case 0x1a:
+		conf0 = HDMI_AHB_DMA_CONF0_BURST_MODE |
+			HDMI_AHB_DMA_CONF0_INCR8;
+		threshold = 128;
+		break;
+	default:
+		/* NOTREACHED */
+		return -EINVAL;
+	}
+
+	dw->data.set_sample_rate(dw->data.hdmi, runtime->rate);
+
+	/* Minimum number of bytes in the fifo. */
+	runtime->hw.fifo_size = threshold * 32;
+
+	conf0 |= HDMI_AHB_DMA_CONF0_EN_HLOCK;
+	conf1 = (1 << runtime->channels) - 1;
+
+	writeb_relaxed(threshold, dw->data.base + HDMI_AHB_DMA_THRSLD);
+	writeb_relaxed(conf0, dw->data.base + HDMI_AHB_DMA_CONF0);
+	writeb_relaxed(conf1, dw->data.base + HDMI_AHB_DMA_CONF1);
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+		dw->reformat = dw_hdmi_reformat_iec958;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dw_hdmi_create_cs(dw, runtime);
+		dw->reformat = dw_hdmi_reformat_s24;
+		break;
+	}
+	dw->iec_offset = 0;
+	dw->channels = runtime->channels;
+	dw->buf_src  = runtime->dma_area;
+	dw->buf_dst  = substream->dma_buffer.area;
+	dw->buf_addr = substream->dma_buffer.addr;
+	dw->buf_period = snd_pcm_lib_period_bytes(substream);
+	dw->buf_size = snd_pcm_lib_buffer_bytes(substream);
+
+	return 0;
+}
+
+static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_dw_hdmi *dw = substream->private_data;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		dw->buf_offset = 0;
+		dw->substream = substream;
+		dw_hdmi_start_dma(dw);
+		substream->runtime->delay = substream->runtime->period_size;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		dw_hdmi_stop_dma(dw);
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t dw_hdmi_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dw_hdmi *dw = substream->private_data;
+
+	return bytes_to_frames(runtime, dw->buf_offset);
+}
+
+static struct snd_pcm_ops snd_dw_hdmi_ops = {
+	.open = dw_hdmi_open,
+	.close = dw_hdmi_close,
+	.ioctl = snd_pcm_lib_ioctl,
+	.hw_params = dw_hdmi_hw_params,
+	.hw_free = dw_hdmi_hw_free,
+	.prepare = dw_hdmi_prepare,
+	.trigger = dw_hdmi_trigger,
+	.pointer = dw_hdmi_pointer,
+	.page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int snd_dw_hdmi_probe(struct platform_device *pdev)
+{
+	const struct dw_hdmi_audio_data *data = pdev->dev.platform_data;
+	struct device *dev = pdev->dev.parent;
+	struct snd_dw_hdmi *dw;
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	unsigned revision;
+	int ret;
+
+	writeb_relaxed(HDMI_IH_MUTE_AHBDMAAUD_STAT0_ALL,
+		       data->base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	revision = readb_relaxed(data->base + HDMI_REVISION_ID);
+	if (revision != 0x0a && revision != 0x1a) {
+		dev_err(dev, "dw-hdmi-audio: unknown revision 0x%02x\n",
+			revision);
+		return -ENXIO;
+	}
+
+	ret = snd_card_new(dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+			      THIS_MODULE, sizeof(struct snd_dw_hdmi), &card);
+	if (ret < 0)
+		return ret;
+
+	strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
+	strlcpy(card->shortname, "DW-HDMI", sizeof(card->shortname));
+	snprintf(card->longname, sizeof(card->longname),
+		 "%s rev 0x%02x, irq %d", card->shortname, revision,
+		 data->irq);
+
+	dw = card->private_data;
+	dw->card = card;
+	dw->data = *data;
+	dw->revision = revision;
+
+	ret = snd_pcm_new(card, "DW HDMI", 0, 1, 0, &pcm);
+	if (ret < 0)
+		goto err;
+
+	dw->pcm = pcm;
+	pcm->private_data = dw;
+	strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_dw_hdmi_ops);
+
+	snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+			dev, 64 * 1024, 64 * 1024);
+
+	ret = snd_card_register(card);
+	if (ret < 0)
+		goto err;
+
+	platform_set_drvdata(pdev, dw);
+
+	return 0;
+
+err:
+	snd_card_free(card);
+	return ret;
+}
+
+static int snd_dw_hdmi_remove(struct platform_device *pdev)
+{
+	struct snd_dw_hdmi *dw = platform_get_drvdata(pdev);
+
+	snd_card_free(dw->card);
+
+	return 0;
+}
+
+static struct platform_driver snd_dw_hdmi_driver = {
+	.probe	= snd_dw_hdmi_probe,
+	.remove	= snd_dw_hdmi_remove,
+	.driver	= {
+		.name = "dw-hdmi-audio",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(snd_dw_hdmi_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.h b/drivers/staging/imx-drm/dw-hdmi-audio.h
new file mode 100644
index 000000000000..f01979d49efd
--- /dev/null
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.h
@@ -0,0 +1,14 @@
+#ifndef DW_HDMI_AUDIO_H
+#define DW_HDMI_AUDIO_H
+
+struct imx_hdmi;
+
+struct dw_hdmi_audio_data {
+	phys_addr_t phys;
+	void __iomem *base;
+	int irq;
+	struct imx_hdmi *hdmi;
+	void (*set_sample_rate)(struct imx_hdmi *, unsigned);
+};
+
+#endif
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index 18c9ccd460b7..7efca1554d5a 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -29,6 +29,7 @@
 #include <drm/drm_encoder_slave.h>
 #include <video/imx-ipu-v3.h>
 
+#include "dw-hdmi-audio.h"
 #include "imx-hdmi.h"
 #include "imx-drm.h"
 
@@ -115,6 +116,7 @@ struct imx_hdmi {
 	struct drm_connector connector;
 	struct drm_encoder encoder;
 
+	struct platform_device *audio;
 	enum imx_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -361,6 +363,12 @@ static void hdmi_clk_regenerator_update_pixel_clock(struct imx_hdmi *hdmi)
 	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock);
 }
 
+static void imx_hdmi_set_sample_rate(struct imx_hdmi *hdmi, unsigned rate)
+{
+	hdmi->sample_rate = rate;
+	hdmi_set_clk_regenerator(hdmi, hdmi->hdmi_data.video_mode.mpixelclock);
+}
+
 /*
  * this submodule is responsible for the video data synchronization.
  * for example, for RGB 4:4:4 input, the data map is defined as
@@ -1587,11 +1595,13 @@ MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids);
 static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
+	struct platform_device_info pdevinfo;
 	const struct of_device_id *of_id =
 				of_match_device(imx_hdmi_dt_ids, dev);
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
 	struct device_node *ddc_node;
+	struct dw_hdmi_audio_data audio;
 	struct imx_hdmi *hdmi;
 	struct resource *iores;
 	int ret, irq;
@@ -1706,6 +1716,22 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	/* Unmute interrupts */
 	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
+	memset(&pdevinfo, 0, sizeof(pdevinfo));
+	pdevinfo.parent = dev;
+	pdevinfo.id = PLATFORM_DEVID_AUTO;
+
+	audio.phys = iores->start;
+	audio.base = hdmi->regs;
+	audio.irq = irq;
+	audio.hdmi = hdmi;
+	audio.set_sample_rate = imx_hdmi_set_sample_rate;
+
+	pdevinfo.name = "dw-hdmi-audio";
+	pdevinfo.data = &audio;
+	pdevinfo.size_data = sizeof(audio);
+	pdevinfo.dma_mask = DMA_BIT_MASK(32);
+	hdmi->audio = platform_device_register_full(&pdevinfo);
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1723,6 +1749,9 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master,
 {
 	struct imx_hdmi *hdmi = dev_get_drvdata(dev);
 
+	if (!IS_ERR(hdmi->audio))
+		platform_device_unregister(hdmi->audio);
+
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
-- 
1.8.3.1


[-- Attachment #3: 0019-drivers-staging-imx-drm-Fix-audio-buffer-sizes.patch --]
[-- Type: text/plain, Size: 1197 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 019/107] drivers: staging: imx-drm: Fix audio buffer sizes
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

From: Sean Cross <xobs@kosagi.com>

Pulseaudio connects to dw-hdmi-audio with a period size of 1024 and a
buffer size of 4408 or so.  Because of this, it overruns its buffer and
panics.

Inform ALSA that we'd like the buffer size to be a multiple
of the period size, to prevent this problem.

Signed-off-by: Sean Cross <xobs@kosagi.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index 4f9790dea6db..ee3f8c9b5695 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -331,6 +331,7 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
 
 	runtime->hw = dw_hdmi_hw;
 	snd_pcm_limit_hw_rates(runtime);
+	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 
 	return 0;
 }
-- 
1.8.3.1


[-- Attachment #4: 0020-dw-hdmi-audio-parse-ELD-from-HDMI-driver.patch --]
[-- Type: text/plain, Size: 3898 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 020/107] dw-hdmi-audio: parse ELD from HDMI driver
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Parse the ELD (EDID like data) stored from the HDMI driver to restrict
the sample rates and channels which are available to ALSA.  This causes
the ALSA device to reflect the capabilities of the overall audio path,
not just what is supported at the HDMI source interface level.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 51 +++++++++++++++++++++++++++++++++
 drivers/staging/imx-drm/dw-hdmi-audio.h |  1 +
 drivers/staging/imx-drm/imx-hdmi.c      |  3 ++
 3 files changed, 55 insertions(+)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index ee3f8c9b5695..daa3302d82b3 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -300,6 +300,56 @@ static struct snd_pcm_hardware dw_hdmi_hw = {
 	.fifo_size = 0,
 };
 
+static unsigned rates_mask[] = {
+	SNDRV_PCM_RATE_32000,
+	SNDRV_PCM_RATE_44100,
+	SNDRV_PCM_RATE_48000,
+	SNDRV_PCM_RATE_88200,
+	SNDRV_PCM_RATE_96000,
+	SNDRV_PCM_RATE_176400,
+	SNDRV_PCM_RATE_192000,
+};
+
+static void dw_hdmi_parse_eld(struct snd_dw_hdmi *dw,
+	struct snd_pcm_runtime *runtime)
+{
+	u8 *sad, *eld = dw->data.eld;
+	unsigned eld_ver,  mnl, sad_count, rates, rate_mask, i;
+	unsigned max_channels;
+
+	eld_ver = eld[0] >> 3;
+	if (eld_ver != 2 && eld_ver != 31)
+		return;
+
+	mnl = eld[4] & 0x1f;
+	if (mnl > 16)
+		return;
+
+	sad_count = eld[5] >> 4;
+	sad = eld + 20 + mnl;
+
+	/* Start from the basic audio settings */
+	max_channels = 2;
+	rates = 7;
+	while (sad_count > 0) {
+		switch (sad[0] & 0x78) {
+		case 0x08: /* PCM */
+			max_channels = max(max_channels, (sad[0] & 7) + 1u);
+			rates |= sad[1];
+			break;
+		}
+		sad += 3;
+		sad_count -= 1;
+	}
+
+	for (rate_mask = i = 0; i < ARRAY_SIZE(rates_mask); i++)
+		if (rates & 1 << i)
+			rate_mask |= rates_mask[i];
+
+	runtime->hw.rates &= rate_mask;
+	runtime->hw.channels_max = min(runtime->hw.channels_max, max_channels);
+}
+
 static int dw_hdmi_open(struct snd_pcm_substream *substream)
 {
 	struct snd_pcm_runtime *runtime = substream->runtime;
@@ -330,6 +380,7 @@ static int dw_hdmi_open(struct snd_pcm_substream *substream)
 		       base + HDMI_IH_MUTE_AHBDMAAUD_STAT0);
 
 	runtime->hw = dw_hdmi_hw;
+	dw_hdmi_parse_eld(dw, runtime);
 	snd_pcm_limit_hw_rates(runtime);
 	snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
 
diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.h b/drivers/staging/imx-drm/dw-hdmi-audio.h
index f01979d49efd..2d91d709381d 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.h
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.h
@@ -8,6 +8,7 @@ struct dw_hdmi_audio_data {
 	void __iomem *base;
 	int irq;
 	struct imx_hdmi *hdmi;
+	u8 *eld;
 	void (*set_sample_rate)(struct imx_hdmi *, unsigned);
 };
 
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index 7efca1554d5a..412026c3f9f9 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -1408,6 +1408,8 @@ static int imx_hdmi_connector_get_modes(struct drm_connector *connector)
 
 		drm_mode_connector_update_edid_property(connector, edid);
 		ret = drm_add_edid_modes(connector, edid);
+		/* Store the ELD */
+		drm_edid_to_eld(connector, edid);
 		kfree(edid);
 	} else {
 		dev_dbg(hdmi->dev, "failed to get edid\n");
@@ -1724,6 +1726,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	audio.base = hdmi->regs;
 	audio.irq = irq;
 	audio.hdmi = hdmi;
+	audio.eld = hdmi->connector.eld;
 	audio.set_sample_rate = imx_hdmi_set_sample_rate;
 
 	pdevinfo.name = "dw-hdmi-audio";
-- 
1.8.3.1


[-- Attachment #5: 0021-dw-hdmi-audio-try-to-fix-burbling-audio.patch --]
[-- Type: text/plain, Size: 1308 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 021/107] dw-hdmi-audio: try to fix burbling audio
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index daa3302d82b3..c2ec8213f8e1 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -61,6 +61,7 @@ enum {
 	HDMI_REVISION_ID = 0x0001,
 	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
 	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
+	HDMI_AUD_N1 = 0x3200,
 	HDMI_AHB_DMA_CONF0 = 0x3600,
 	HDMI_AHB_DMA_START = 0x3601,
 	HDMI_AHB_DMA_STOP = 0x3602,
@@ -480,6 +481,13 @@ static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 		dw->buf_offset = 0;
 		dw->substream = substream;
 		dw_hdmi_start_dma(dw);
+		if (dw->revision == 0x0a) {
+			void __iomem *base = dw->data.base;
+			unsigned val;
+
+			val = readb_relaxed(base + HDMI_AUD_N1);
+			writeb_relaxed(val, base + HDMI_AUD_N1);
+		}
 		substream->runtime->delay = substream->runtime->period_size;
 		break;
 
-- 
1.8.3.1


[-- Attachment #6: 0022-dw-hdmi-audio-try-implementing-ERR005174-to-fix-burb.patch --]
[-- Type: text/plain, Size: 2472 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 022/107] dw-hdmi-audio: try implementing ERR005174 to fix
 burbling audio
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 35 +++++++++++++++++++++++++++------
 1 file changed, 29 insertions(+), 6 deletions(-)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index c2ec8213f8e1..77094d674a55 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -7,6 +7,7 @@
  *
  * Written and tested against the (alleged) DW HDMI Tx found in iMX6S.
  */
+#include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
@@ -68,6 +69,8 @@ enum {
 	HDMI_AHB_DMA_THRSLD = 0x3603,
 	HDMI_AHB_DMA_STRADDR0 = 0x3604,
 	HDMI_AHB_DMA_STPADDR0 = 0x3608,
+	HDMI_AHB_DMA_STAT = 0x3612,
+	HDMI_AHB_DMA_STAT_FULL = BIT(1),
 	HDMI_AHB_DMA_MASK = 0x3614,
 	HDMI_AHB_DMA_POL = 0x3615,
 	HDMI_AHB_DMA_CONF1 = 0x3616,
@@ -474,20 +477,40 @@ static int dw_hdmi_prepare(struct snd_pcm_substream *substream)
 static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct snd_dw_hdmi *dw = substream->private_data;
-	int ret = 0;
+	void __iomem *base = dw->data.base;
+	unsigned val[3];
+	unsigned timeout = 10000;
+	int ret = 0, i;
+	bool err005174;
 
 	switch (cmd) {
 	case SNDRV_PCM_TRIGGER_START:
+		err005174 = dw->revision == 0x0a;
+		if (err005174) {
+			for (i = 2; i >= 0; i--) {
+				val[i] = readb_relaxed(base + HDMI_AUD_N1 + i);
+				writeb_relaxed(0, base + HDMI_AUD_N1 + i);
+			}
+		}
+
 		dw->buf_offset = 0;
 		dw->substream = substream;
 		dw_hdmi_start_dma(dw);
-		if (dw->revision == 0x0a) {
-			void __iomem *base = dw->data.base;
-			unsigned val;
 
-			val = readb_relaxed(base + HDMI_AUD_N1);
-			writeb_relaxed(val, base + HDMI_AUD_N1);
+		if (err005174) {
+			do {
+				if (readb_relaxed(base + HDMI_AHB_DMA_STAT) & HDMI_AHB_DMA_STAT_FULL)
+					break;
+				udelay(1);
+			} while (timeout--);
+
+			if (!(readb_relaxed(base + HDMI_AHB_DMA_STAT) & HDMI_AHB_DMA_STAT_FULL))
+				pr_info("timeout!\n");
+
+			for (i = 2; i >= 0; i--)
+				writeb_relaxed(val[i], base + HDMI_AUD_N1 + i);
 		}
+
 		substream->runtime->delay = substream->runtime->period_size;
 		break;
 
-- 
1.8.3.1


[-- Attachment #7: 0023-dw-hdmi-audio-another-attempt-at-fixing-burbling.patch --]
[-- Type: text/plain, Size: 2433 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 023/107] dw-hdmi-audio: another attempt at fixing burbling
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 25 ++++++++++---------------
 1 file changed, 10 insertions(+), 15 deletions(-)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index 77094d674a55..967c3d6bf8cf 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -63,6 +63,7 @@ enum {
 	HDMI_IH_AHBDMAAUD_STAT0 = 0x0109,
 	HDMI_IH_MUTE_AHBDMAAUD_STAT0 = 0x0189,
 	HDMI_AUD_N1 = 0x3200,
+	HDMI_AUD_CTS1 = 0x3203,
 	HDMI_AHB_DMA_CONF0 = 0x3600,
 	HDMI_AHB_DMA_START = 0x3601,
 	HDMI_AHB_DMA_STOP = 0x3602,
@@ -478,8 +479,7 @@ static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 {
 	struct snd_dw_hdmi *dw = substream->private_data;
 	void __iomem *base = dw->data.base;
-	unsigned val[3];
-	unsigned timeout = 10000;
+	unsigned n[3], cts[3];
 	int ret = 0, i;
 	bool err005174;
 
@@ -487,9 +487,11 @@ static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 	case SNDRV_PCM_TRIGGER_START:
 		err005174 = dw->revision == 0x0a;
 		if (err005174) {
-			for (i = 2; i >= 0; i--) {
-				val[i] = readb_relaxed(base + HDMI_AUD_N1 + i);
+			for (i = 2; i >= 1; i--) {
+				n[i] = readb_relaxed(base + HDMI_AUD_N1 + i);
+				cts[i] = readb_relaxed(base + HDMI_AUD_CTS1 + i);
 				writeb_relaxed(0, base + HDMI_AUD_N1 + i);
+				writeb_relaxed(0, base + HDMI_AUD_CTS1 + i);
 			}
 		}
 
@@ -498,17 +500,10 @@ static int dw_hdmi_trigger(struct snd_pcm_substream *substream, int cmd)
 		dw_hdmi_start_dma(dw);
 
 		if (err005174) {
-			do {
-				if (readb_relaxed(base + HDMI_AHB_DMA_STAT) & HDMI_AHB_DMA_STAT_FULL)
-					break;
-				udelay(1);
-			} while (timeout--);
-
-			if (!(readb_relaxed(base + HDMI_AHB_DMA_STAT) & HDMI_AHB_DMA_STAT_FULL))
-				pr_info("timeout!\n");
-
-			for (i = 2; i >= 0; i--)
-				writeb_relaxed(val[i], base + HDMI_AUD_N1 + i);
+			for (i = 2; i >= 1; i--)
+				writeb_relaxed(cts[i], base + HDMI_AUD_CTS1 + i);
+			for (i = 2; i >= 1; i--)
+				writeb_relaxed(n[i], base + HDMI_AUD_N1 + i);
 		}
 
 		substream->runtime->delay = substream->runtime->period_size;
-- 
1.8.3.1


[-- Attachment #8: 0024-dw-hdmi-audio-basic-suspend-resume-support.patch --]
[-- Type: text/plain, Size: 1541 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 024/107] dw-hdmi-audio: basic suspend/resume support
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/dw-hdmi-audio.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/staging/imx-drm/dw-hdmi-audio.c b/drivers/staging/imx-drm/dw-hdmi-audio.c
index 967c3d6bf8cf..5243d51b4069 100644
--- a/drivers/staging/imx-drm/dw-hdmi-audio.c
+++ b/drivers/staging/imx-drm/dw-hdmi-audio.c
@@ -610,12 +610,40 @@ static int snd_dw_hdmi_remove(struct platform_device *pdev)
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int snd_dw_hdmi_suspend(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
+	snd_pcm_suspend_all(dw->pcm);
+
+	return 0;
+}
+
+static int snd_dw_hdmi_resume(struct device *dev)
+{
+	struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
+
+	snd_power_change_state(dw->card, SNDRV_CTL_POWER_D0);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(snd_dw_hdmi_pm, snd_dw_hdmi_suspend,
+			 snd_dw_hdmi_resume);
+#define PM_OPS &snd_dw_hdmi_pm
+#else
+#define PM_OPS NULL
+#endif
+
 static struct platform_driver snd_dw_hdmi_driver = {
 	.probe	= snd_dw_hdmi_probe,
 	.remove	= snd_dw_hdmi_remove,
 	.driver	= {
 		.name = "dw-hdmi-audio",
 		.owner = THIS_MODULE,
+		.pm = PM_OPS,
 	},
 };
 
-- 
1.8.3.1


[-- Attachment #9: 0025-cec-add-generic-HDMI-CEC-driver.patch --]
[-- Type: text/plain, Size: 13976 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 025/107] cec: add generic HDMI CEC driver
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

Add a generic userspace API to support HDMI Consumer Electronics Control
interfaces.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/Kconfig              |   2 +
 drivers/Makefile             |   1 +
 drivers/cec/Kconfig          |  14 ++
 drivers/cec/Makefile         |   1 +
 drivers/cec/cec-dev.c        | 384 +++++++++++++++++++++++++++++++++++++++++++
 include/linux/cec-dev.h      |  69 ++++++++
 include/uapi/linux/cec-dev.h |  34 ++++
 7 files changed, 505 insertions(+)
 create mode 100644 drivers/cec/Kconfig
 create mode 100644 drivers/cec/Makefile
 create mode 100644 drivers/cec/cec-dev.c
 create mode 100644 include/linux/cec-dev.h
 create mode 100644 include/uapi/linux/cec-dev.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 622fa266b29e..fa2b20ba644f 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -176,6 +176,8 @@ source "drivers/powercap/Kconfig"
 
 source "drivers/mcb/Kconfig"
 
+source "drivers/cec/Kconfig"
+
 source "drivers/ras/Kconfig"
 
 source "drivers/thunderbolt/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index ebee55537a05..b8208b267615 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -159,5 +159,6 @@ obj-$(CONFIG_NTB)		+= ntb/
 obj-$(CONFIG_FMC)		+= fmc/
 obj-$(CONFIG_POWERCAP)		+= powercap/
 obj-$(CONFIG_MCB)		+= mcb/
+obj-$(CONFIG_CEC)		+= cec/
 obj-$(CONFIG_RAS)		+= ras/
 obj-$(CONFIG_THUNDERBOLT)	+= thunderbolt/
diff --git a/drivers/cec/Kconfig b/drivers/cec/Kconfig
new file mode 100644
index 000000000000..d67cfb83de6a
--- /dev/null
+++ b/drivers/cec/Kconfig
@@ -0,0 +1,14 @@
+#
+# Consumer Electroncs Control support
+#
+
+menu "Consumer Electronics Control devices"
+
+config CEC
+	bool
+
+config HDMI_CEC_CORE
+	tristate
+	select CEC
+
+endmenu
diff --git a/drivers/cec/Makefile b/drivers/cec/Makefile
new file mode 100644
index 000000000000..b94278bc8321
--- /dev/null
+++ b/drivers/cec/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HDMI_CEC_CORE) += cec-dev.o
diff --git a/drivers/cec/cec-dev.c b/drivers/cec/cec-dev.c
new file mode 100644
index 000000000000..ba58d8217851
--- /dev/null
+++ b/drivers/cec/cec-dev.c
@@ -0,0 +1,384 @@
+/*
+ * HDMI Consumer Electronics Control
+ *
+ * This provides the user API for communication with HDMI CEC complaint
+ * devices in kernel drivers, and is based upon the protocol developed
+ * by Freescale for their i.MX SoCs.
+ *
+ * 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.
+ */
+#include <linux/cec-dev.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+struct cec_event {
+	struct cec_user_event usr;
+	struct list_head node;
+};
+
+static struct class *cec_class;
+static int cec_major;
+
+static void cec_dev_send_message(struct cec_dev *cec_dev, u8 *msg,
+	size_t count)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cec_dev->lock, flags);
+	cec_dev->retries = 5;
+	cec_dev->write_busy = 1;
+	cec_dev->send_message(cec_dev, msg, count);
+	spin_unlock_irqrestore(&cec_dev->lock, flags);
+}
+
+void cec_dev_event(struct cec_dev *cec_dev, int type, u8 *msg, size_t len)
+{
+	struct cec_event *event;
+	unsigned long flags;
+
+	event = kzalloc(sizeof(*event), GFP_ATOMIC);
+	if (event) {
+		event->usr.event_type = type;
+		event->usr.msg_len = len;
+		if (msg)
+			memcpy(event->usr.msg, msg, len);
+
+		spin_lock_irqsave(&cec_dev->lock, flags);
+		list_add_tail(&event->node, &cec_dev->events);
+		spin_unlock_irqrestore(&cec_dev->lock, flags);
+		wake_up(&cec_dev->waitq);
+	}
+}
+EXPORT_SYMBOL_GPL(cec_dev_event);
+
+static int cec_dev_lock_write(struct cec_dev *cec_dev, struct file *file)
+	__acquires(cec_dev->mutex)
+{
+	int ret;
+
+	do {
+		if (file->f_flags & O_NONBLOCK) {
+			if (cec_dev->write_busy)
+				return -EAGAIN;
+		} else {
+			ret = wait_event_interruptible(cec_dev->waitq,
+						       !cec_dev->write_busy);
+			if (ret)
+				break;
+		}
+
+		ret = mutex_lock_interruptible(&cec_dev->mutex);
+		if (ret)
+			break;
+
+		if (!cec_dev->write_busy)
+			break;
+
+		mutex_unlock(&cec_dev->mutex);
+	} while (1);
+
+	return ret;
+}
+
+static ssize_t cec_dev_read(struct file *file, char __user *buf,
+	size_t count, loff_t *ppos)
+{
+	struct cec_dev *cec_dev = file->private_data;
+	ssize_t ret;
+
+	if (count > sizeof(struct cec_user_event))
+		count = sizeof(struct cec_user_event);
+
+	if (!access_ok(VERIFY_WRITE, buf, count))
+		return -EFAULT;
+
+	do {
+		struct cec_event *event = NULL;
+		unsigned long flags;
+
+		spin_lock_irqsave(&cec_dev->lock, flags);
+		if (!list_empty(&cec_dev->events)) {
+			event = list_first_entry(&cec_dev->events,
+					struct cec_event, node);
+			list_del(&event->node);
+		}
+		spin_unlock_irqrestore(&cec_dev->lock, flags);
+
+		if (event) {
+			ret = __copy_to_user(buf, &event->usr, count) ?
+				 -EFAULT : count;
+			kfree(event);
+			break;
+		}
+
+		if (file->f_flags & O_NONBLOCK) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		ret = wait_event_interruptible(cec_dev->waitq,
+					       !list_empty(&cec_dev->events));
+		if (ret)
+			break;
+	} while (1);
+
+	return ret;
+}
+
+static ssize_t cec_dev_write(struct file *file, const char __user *buf,
+	size_t count, loff_t *ppos)
+{
+	struct cec_dev *cec_dev = file->private_data;
+	u8 msg[MAX_MESSAGE_LEN];
+	int ret;
+
+	if (count > sizeof(msg))
+		return -E2BIG;
+
+	if (copy_from_user(msg, buf, count))
+		return -EFAULT;
+
+	ret = cec_dev_lock_write(cec_dev, file);
+	if (ret)
+		return ret;
+
+	cec_dev_send_message(cec_dev, msg, count);
+
+	mutex_unlock(&cec_dev->mutex);
+
+	return count;
+}
+
+static long cec_dev_ioctl(struct file *file, u_int cmd, unsigned long arg)
+{
+	struct cec_dev *cec_dev = file->private_data;
+	int ret;
+
+	switch (cmd) {
+	case HDMICEC_IOC_O_SETLOGICALADDRESS:
+	case HDMICEC_IOC_SETLOGICALADDRESS:
+		if (arg > 15) {
+			ret = -EINVAL;
+			break;
+		}
+
+		ret = cec_dev_lock_write(cec_dev, file);
+		if (ret == 0) {
+			unsigned char msg[1];
+
+			cec_dev->addresses = BIT(arg);
+			cec_dev->set_address(cec_dev, cec_dev->addresses);
+
+			/*
+			 * Send a ping message with the source and destination
+			 * set to our address; the result indicates whether
+			 * unit has chosen our address simultaneously.
+			 */
+			msg[0] = arg << 4 | arg;
+			cec_dev_send_message(cec_dev, msg, sizeof(msg));
+			mutex_unlock(&cec_dev->mutex);
+		}
+		break;
+
+	case HDMICEC_IOC_STARTDEVICE:
+		ret = mutex_lock_interruptible(&cec_dev->mutex);
+		if (ret == 0) {
+			cec_dev->addresses = BIT(15);
+			cec_dev->set_address(cec_dev, cec_dev->addresses);
+			mutex_unlock(&cec_dev->mutex);
+		}
+		break;
+
+	case HDMICEC_IOC_STOPDEVICE:
+		ret = 0;
+		break;
+
+	case HDMICEC_IOC_GETPHYADDRESS:
+		ret = put_user(cec_dev->physical, (u16 __user *)arg);
+		ret = -ENOIOCTLCMD;
+		break;
+
+	default:
+		ret = -ENOIOCTLCMD;
+		break;
+	}
+
+	return ret;
+}
+
+static unsigned cec_dev_poll(struct file *file, poll_table *wait)
+{
+	struct cec_dev *cec_dev = file->private_data;
+	unsigned mask = 0;
+
+	poll_wait(file, &cec_dev->waitq, wait);
+
+	if (cec_dev->write_busy == 0)
+		mask |= POLLOUT | POLLWRNORM;
+	if (!list_empty(&cec_dev->events))
+		mask |= POLLIN | POLLRDNORM;
+
+	return mask;
+}
+
+static int cec_dev_release(struct inode *inode, struct file *file)
+{
+	struct cec_dev *cec_dev = file->private_data;
+
+	mutex_lock(&cec_dev->mutex);
+	if (cec_dev->users >= 1)
+		cec_dev->users -= 1;
+	if (cec_dev->users == 0) {
+		/*
+		 * Wait for any write to complete before shutting down.
+		 * A message should complete in a maximum of 2.75ms *
+		 * 160 bits + 4.7ms, or 444.7ms.  Let's call that 500ms.
+		 * If we time out, shutdown anyway.
+		 */
+		wait_event_timeout(cec_dev->waitq, !cec_dev->write_busy,
+				   msecs_to_jiffies(500));
+
+		cec_dev->release(cec_dev);
+
+		while (!list_empty(&cec_dev->events)) {
+			struct cec_event *event;
+
+			event = list_first_entry(&cec_dev->events,
+					struct cec_event, node);
+			list_del(&event->node);
+			kfree(event);
+		}
+	}
+	mutex_unlock(&cec_dev->mutex);
+	return 0;
+}
+
+static int cec_dev_open(struct inode *inode, struct file *file)
+{
+	struct cec_dev *cec_dev = container_of(inode->i_cdev, struct cec_dev,
+					       cdev);
+	int ret = 0;
+
+	nonseekable_open(inode, file);
+
+	file->private_data = cec_dev;
+
+	ret = mutex_lock_interruptible(&cec_dev->mutex);
+	if (ret)
+		return ret;
+
+	if (cec_dev->users++ == 0) {
+		cec_dev->addresses = BIT(15);
+
+		ret = cec_dev->open(cec_dev);
+		if (ret < 0)
+			cec_dev->users = 0;
+	}
+	mutex_unlock(&cec_dev->mutex);
+
+	return ret;
+}
+
+static const struct file_operations hdmi_cec_fops = {
+	.owner = THIS_MODULE,
+	.read = cec_dev_read,
+	.write = cec_dev_write,
+	.open = cec_dev_open,
+	.unlocked_ioctl = cec_dev_ioctl,
+	.release = cec_dev_release,
+	.poll = cec_dev_poll,
+};
+
+void cec_dev_init(struct cec_dev *cec_dev, struct module *module)
+{
+	cec_dev->devn = MKDEV(cec_major, 0);
+
+	INIT_LIST_HEAD(&cec_dev->events);
+	init_waitqueue_head(&cec_dev->waitq);
+	spin_lock_init(&cec_dev->lock);
+	mutex_init(&cec_dev->mutex);
+
+	cec_dev->addresses = BIT(15);
+
+	cdev_init(&cec_dev->cdev, &hdmi_cec_fops);
+	cec_dev->cdev.owner = module;
+}
+EXPORT_SYMBOL_GPL(cec_dev_init);
+
+int cec_dev_add(struct cec_dev *cec_dev, struct device *dev, const char *name)
+{
+	struct device *cd;
+	int ret;
+
+	ret = cdev_add(&cec_dev->cdev, cec_dev->devn, 1);
+	if (ret < 0)
+		goto err_cdev;
+
+	cd = device_create(cec_class, dev, cec_dev->devn, NULL, name);
+	if (IS_ERR(cd)) {
+		ret = PTR_ERR(cd);
+		dev_err(dev, "can't create device: %d\n", ret);
+		goto err_dev;
+	}
+
+	return 0;
+
+ err_dev:
+	cdev_del(&cec_dev->cdev);
+ err_cdev:
+	return ret;
+}
+EXPORT_SYMBOL_GPL(cec_dev_add);
+
+void cec_dev_remove(struct cec_dev *cec_dev)
+{
+	device_destroy(cec_class, cec_dev->devn);
+	cdev_del(&cec_dev->cdev);
+}
+EXPORT_SYMBOL_GPL(cec_dev_remove);
+
+static int cec_init(void)
+{
+	dev_t dev;
+	int ret;
+
+	cec_class = class_create(THIS_MODULE, "hdmi-cec");
+	if (IS_ERR(cec_class)) {
+		ret = PTR_ERR(cec_class);
+		pr_err("cec: can't create cec class: %d\n", ret);
+		goto err_class;
+	}
+
+	ret = alloc_chrdev_region(&dev, 0, 1, "hdmi-cec");
+	if (ret) {
+		pr_err("cec: can't create character devices: %d\n", ret);
+		goto err_chrdev;
+	}
+
+	cec_major = MAJOR(dev);
+
+	return 0;
+
+ err_chrdev:
+	class_destroy(cec_class);
+ err_class:
+	return ret;
+}
+subsys_initcall(cec_init);
+
+static void cec_exit(void)
+{
+	unregister_chrdev_region(MKDEV(cec_major, 0), 1);
+	class_destroy(cec_class);
+}
+module_exit(cec_exit);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Generic HDMI CEC driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/cec-dev.h b/include/linux/cec-dev.h
new file mode 100644
index 000000000000..76a7d7f6a72d
--- /dev/null
+++ b/include/linux/cec-dev.h
@@ -0,0 +1,69 @@
+#ifndef _LINUX_CEC_DEV_H
+#define _LINUX_CEC_DEV_H
+
+#include <linux/cdev.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+
+#include <uapi/linux/cec-dev.h>
+
+struct device;
+
+struct cec_dev {
+	struct cdev cdev;
+	dev_t devn;
+
+	struct mutex mutex;
+	unsigned users;
+
+	spinlock_t lock;
+	wait_queue_head_t waitq;
+	struct list_head events;
+	u8 write_busy;
+
+	u8 retries;
+	u16 addresses;
+	u16 physical;
+
+	int (*open)(struct cec_dev *);
+	void (*release)(struct cec_dev *);
+	void (*send_message)(struct cec_dev *, u8 *, size_t);
+	void (*set_address)(struct cec_dev *, unsigned);
+};
+
+void cec_dev_event(struct cec_dev *cec_dev, int type, u8 *msg, size_t len);
+
+static inline void cec_dev_receive(struct cec_dev *cec_dev, u8 *msg,
+	unsigned len)
+{
+	cec_dev_event(cec_dev, MESSAGE_TYPE_RECEIVE_SUCCESS, msg, len);
+}
+
+static inline void cec_dev_send_complete(struct cec_dev *cec_dev, int ack)
+{
+	cec_dev->retries = 0;
+	cec_dev->write_busy = 0;
+
+	cec_dev_event(cec_dev, ack ? MESSAGE_TYPE_SEND_SUCCESS :
+		      MESSAGE_TYPE_NOACK, NULL, 0);
+}
+
+static inline void cec_dev_disconnect(struct cec_dev *cec_dev)
+{
+	cec_dev->physical = 0;
+	cec_dev_event(cec_dev, MESSAGE_TYPE_DISCONNECTED, NULL, 0);
+}
+
+static inline void cec_dev_connect(struct cec_dev *cec_dev, u32 phys)
+{
+	cec_dev->physical = phys;
+	cec_dev_event(cec_dev, MESSAGE_TYPE_CONNECTED, NULL, 0);
+}
+
+void cec_dev_init(struct cec_dev *cec_dev, struct module *);
+int cec_dev_add(struct cec_dev *cec_dev, struct device *, const char *name);
+void cec_dev_remove(struct cec_dev *cec_dev);
+
+#endif
diff --git a/include/uapi/linux/cec-dev.h b/include/uapi/linux/cec-dev.h
new file mode 100644
index 000000000000..fb7a41704c77
--- /dev/null
+++ b/include/uapi/linux/cec-dev.h
@@ -0,0 +1,34 @@
+#ifndef _UAPI_LINUX_CEC_DEV_H
+#define _UAPI_LINUX_CEC_DEV_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define MAX_MESSAGE_LEN 16
+
+enum {
+	HDMICEC_IOC_MAGIC = 'H',
+	/* This is wrong: we pass the argument as a number, not a pointer */
+	HDMICEC_IOC_O_SETLOGICALADDRESS	= _IOW(HDMICEC_IOC_MAGIC, 1, unsigned char),
+	HDMICEC_IOC_SETLOGICALADDRESS	= _IO(HDMICEC_IOC_MAGIC, 1),
+	HDMICEC_IOC_STARTDEVICE		= _IO(HDMICEC_IOC_MAGIC, 2),
+	HDMICEC_IOC_STOPDEVICE		= _IO(HDMICEC_IOC_MAGIC, 3),
+	HDMICEC_IOC_GETPHYADDRESS	= _IOR(HDMICEC_IOC_MAGIC, 4, unsigned char[4]),
+};
+
+enum {
+	MESSAGE_TYPE_RECEIVE_SUCCESS = 1,
+	MESSAGE_TYPE_NOACK,
+	MESSAGE_TYPE_DISCONNECTED,
+	MESSAGE_TYPE_CONNECTED,
+	MESSAGE_TYPE_SEND_SUCCESS,
+	MESSAGE_TYPE_SEND_ERROR,
+};
+
+struct cec_user_event {
+	__u32 event_type;
+	__u32 msg_len;
+	__u8 msg[MAX_MESSAGE_LEN];
+};
+
+#endif
-- 
1.8.3.1


[-- Attachment #10: 0026-imx-drm-dw-hdmi-cec-add-HDMI-CEC-driver.patch --]
[-- Type: text/plain, Size: 12835 bytes --]

From: Russell King <rmk+kernel@arm.linux.org.uk>
Subject: [PATCH 026/107] imx-drm: dw-hdmi-cec: add HDMI CEC driver
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset="utf-8"

This adds a HDMI Consumer Electronics Control driver, making use of the
generic HDMI CEC driver to provide a common user API for these devices.

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 drivers/staging/imx-drm/Kconfig       |   9 ++
 drivers/staging/imx-drm/Makefile      |   1 +
 drivers/staging/imx-drm/dw-hdmi-cec.c | 207 ++++++++++++++++++++++++++++++++++
 drivers/staging/imx-drm/dw-hdmi-cec.h |  16 +++
 drivers/staging/imx-drm/imx-hdmi.c    |  63 +++++++++--
 5 files changed, 286 insertions(+), 10 deletions(-)
 create mode 100644 drivers/staging/imx-drm/dw-hdmi-cec.c
 create mode 100644 drivers/staging/imx-drm/dw-hdmi-cec.h

diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig
index 008b544b9911..6238165f0b22 100644
--- a/drivers/staging/imx-drm/Kconfig
+++ b/drivers/staging/imx-drm/Kconfig
@@ -59,3 +59,12 @@ config DRM_DW_HDMI_AUDIO
 	  Support the Audio interface which is part of the Synopsis
 	  Designware HDMI block.  This is used in conjunction with
 	  the i.MX HDMI driver.
+
+config DRM_DW_HDMI_CEC
+	tristate "Synopsis Designware CEC interface"
+	depends on DRM_IMX_HDMI != n
+	select HDMI_CEC_CORE
+	help
+	  Support the CEC interface which is part of the Synposis
+	  Designware HDMI block.  This is used in conjunction with
+	  the i.MX HDMI driver.
diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
index 07e65a410f8f..82a368cf5979 100644
--- a/drivers/staging/imx-drm/Makefile
+++ b/drivers/staging/imx-drm/Makefile
@@ -11,3 +11,4 @@ imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o
 obj-$(CONFIG_DRM_IMX_IPUV3)	+= imx-ipuv3-crtc.o
 obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o
 obj-$(CONFIG_DRM_DW_HDMI_AUDIO) += dw-hdmi-audio.o
+obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o
diff --git a/drivers/staging/imx-drm/dw-hdmi-cec.c b/drivers/staging/imx-drm/dw-hdmi-cec.c
new file mode 100644
index 000000000000..224e97d99b3b
--- /dev/null
+++ b/drivers/staging/imx-drm/dw-hdmi-cec.c
@@ -0,0 +1,207 @@
+/* http://git.freescale.com/git/cgit.cgi/imx/linux-2.6-imx.git/tree/drivers/mxc/hdmi-cec/mxc_hdmi-cec.c?h=imx_3.0.35_4.1.0 */
+#include <linux/cec-dev.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "imx-hdmi.h"
+#include "dw-hdmi-cec.h"
+
+#define DEV_NAME "mxc_hdmi_cec"
+
+enum {
+	CEC_STAT_DONE		= BIT(0),
+	CEC_STAT_EOM		= BIT(1),
+	CEC_STAT_NACK		= BIT(2),
+	CEC_STAT_ARBLOST	= BIT(3),
+	CEC_STAT_ERROR_INIT	= BIT(4),
+	CEC_STAT_ERROR_FOLL	= BIT(5),
+	CEC_STAT_WAKEUP		= BIT(6),
+
+	CEC_CTRL_START		= BIT(0),
+	CEC_CTRL_NORMAL		= 1 << 1,
+};
+
+struct dw_hdmi_cec {
+	struct cec_dev cec;
+
+	struct device *dev;
+	void __iomem *base;
+	const struct dw_hdmi_cec_ops *ops;
+	void *ops_data;
+	int irq;
+};
+
+static void dw_hdmi_set_address(struct cec_dev *cec_dev, unsigned addresses)
+{
+	struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
+
+	writeb(addresses & 255, cec->base + HDMI_CEC_ADDR_L);
+	writeb(addresses >> 8, cec->base + HDMI_CEC_ADDR_H);
+}
+
+static void dw_hdmi_send_message(struct cec_dev *cec_dev, u8 *msg,
+	size_t count)
+{
+	struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
+	unsigned i;
+
+	for (i = 0; i < count; i++)
+		writeb(msg[i], cec->base + HDMI_CEC_TX_DATA0 + i);
+
+	writeb(count, cec->base + HDMI_CEC_TX_CNT);
+	writeb(CEC_CTRL_NORMAL | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
+}
+
+static irqreturn_t dw_hdmi_cec_irq(int irq, void *data)
+{
+	struct dw_hdmi_cec *cec = data;
+	struct cec_dev *cec_dev = &cec->cec;
+	unsigned stat = readb(cec->base + HDMI_IH_CEC_STAT0);
+
+	if (stat == 0)
+		return IRQ_NONE;
+
+	writeb(stat, cec->base + HDMI_IH_CEC_STAT0);
+
+	if (stat & CEC_STAT_ERROR_INIT) {
+		if (cec->cec.retries) {
+			unsigned v = readb(cec->base + HDMI_CEC_CTRL);
+			writeb(v | CEC_CTRL_START, cec->base + HDMI_CEC_CTRL);
+			cec->cec.retries -= 1;
+		} else {
+			cec->cec.write_busy = 0;
+			cec_dev_event(cec_dev, MESSAGE_TYPE_SEND_ERROR, NULL, 0);
+		}
+	} else if (stat & (CEC_STAT_DONE | CEC_STAT_NACK))
+		cec_dev_send_complete(cec_dev, stat & CEC_STAT_DONE);
+
+	if (stat & CEC_STAT_EOM) {
+		unsigned len, i;
+		u8 msg[MAX_MESSAGE_LEN];
+
+		len = readb(cec->base + HDMI_CEC_RX_CNT);
+		if (len > sizeof(msg))
+			len = sizeof(msg);
+
+		for (i = 0; i < len; i++)
+			msg[i] = readb(cec->base + HDMI_CEC_RX_DATA0 + i);
+
+		writeb(0, cec->base + HDMI_CEC_LOCK);
+
+		cec_dev_receive(cec_dev, msg, len);
+	}
+
+	return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(dw_hdmi_cec_irq);
+
+static void dw_hdmi_cec_release(struct cec_dev *cec_dev)
+{
+	struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
+
+	writeb(~0, cec->base + HDMI_CEC_MASK);
+	writeb(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+	writeb(0, cec->base + HDMI_CEC_POLARITY);
+
+	free_irq(cec->irq, cec);
+
+	cec->ops->disable(cec->ops_data);
+}
+
+static int dw_hdmi_cec_open(struct cec_dev *cec_dev)
+{
+	struct dw_hdmi_cec *cec = container_of(cec_dev, struct dw_hdmi_cec, cec);
+	unsigned irqs;
+	int ret;
+
+	writeb(0, cec->base + HDMI_CEC_CTRL);
+	writeb(~0, cec->base + HDMI_IH_CEC_STAT0);
+	writeb(0, cec->base + HDMI_CEC_LOCK);
+
+	ret = request_irq(cec->irq, dw_hdmi_cec_irq, IRQF_SHARED,
+			  DEV_NAME, cec);
+	if (ret < 0)
+		return ret;
+
+	dw_hdmi_set_address(cec_dev, cec_dev->addresses);
+
+	cec->ops->enable(cec->ops_data);
+
+	irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM |
+	       CEC_STAT_DONE;
+	writeb(irqs, cec->base + HDMI_CEC_POLARITY);
+	writeb(~irqs, cec->base + HDMI_CEC_MASK);
+	writeb(~irqs, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+
+	return 0;
+}
+
+static int dw_hdmi_cec_probe(struct platform_device *pdev)
+{
+	struct dw_hdmi_cec_data *data = dev_get_platdata(&pdev->dev);
+	struct dw_hdmi_cec *cec;
+
+	if (!data)
+		return -ENXIO;
+
+	cec = devm_kzalloc(&pdev->dev, sizeof(*cec), GFP_KERNEL);
+	if (!cec)
+		return -ENOMEM;
+
+	cec->dev = &pdev->dev;
+	cec->base = data->base;
+	cec->irq = data->irq;
+	cec->ops = data->ops;
+	cec->ops_data = data->ops_data;
+	cec->cec.open = dw_hdmi_cec_open;
+	cec->cec.release = dw_hdmi_cec_release;
+	cec->cec.send_message = dw_hdmi_send_message;
+	cec->cec.set_address = dw_hdmi_set_address;
+
+	cec_dev_init(&cec->cec, THIS_MODULE);
+
+	/* FIXME: soft-reset the CEC interface */
+
+	dw_hdmi_set_address(&cec->cec, cec->cec.addresses);
+	writeb(0, cec->base + HDMI_CEC_TX_CNT);
+	writeb(~0, cec->base + HDMI_CEC_MASK);
+	writeb(~0, cec->base + HDMI_IH_MUTE_CEC_STAT0);
+	writeb(0, cec->base + HDMI_CEC_POLARITY);
+
+	platform_set_drvdata(pdev, cec);
+
+	/*
+	 * Our device is just a convenience - we want to link to the real
+	 * hardware device here, so that userspace can see the association
+	 * between the HDMI hardware and its associated CEC chardev.
+	 */
+	return cec_dev_add(&cec->cec, cec->dev->parent, DEV_NAME);
+}
+
+static int dw_hdmi_cec_remove(struct platform_device *pdev)
+{
+	struct dw_hdmi_cec *cec = platform_get_drvdata(pdev);
+
+	cec_dev_remove(&cec->cec);
+
+	return 0;
+}
+
+static struct platform_driver dw_hdmi_cec_driver = {
+	.probe	= dw_hdmi_cec_probe,
+	.remove	= dw_hdmi_cec_remove,
+	.driver = {
+		.name = "dw-hdmi-cec",
+		.owner = THIS_MODULE,
+	},
+};
+module_platform_driver(dw_hdmi_cec_driver);
+
+MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
+MODULE_DESCRIPTION("Synopsis Designware HDMI CEC driver for i.MX");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "dw-hdmi-cec");
diff --git a/drivers/staging/imx-drm/dw-hdmi-cec.h b/drivers/staging/imx-drm/dw-hdmi-cec.h
new file mode 100644
index 000000000000..5ff40cc237a8
--- /dev/null
+++ b/drivers/staging/imx-drm/dw-hdmi-cec.h
@@ -0,0 +1,16 @@
+#ifndef DW_HDMI_CEC_H
+#define DW_HDMI_CEC_H
+
+struct dw_hdmi_cec_ops {
+	void (*enable)(void *);
+	void (*disable)(void *);
+};
+
+struct dw_hdmi_cec_data {
+	void __iomem *base;
+	int irq;
+	const struct dw_hdmi_cec_ops *ops;
+	void *ops_data;
+};
+
+#endif
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index 412026c3f9f9..8806f6e74fe4 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -30,6 +30,7 @@
 #include <video/imx-ipu-v3.h>
 
 #include "dw-hdmi-audio.h"
+#include "dw-hdmi-cec.h"
 #include "imx-hdmi.h"
 #include "imx-drm.h"
 
@@ -117,6 +118,7 @@ struct imx_hdmi {
 	struct drm_encoder encoder;
 
 	struct platform_device *audio;
+	struct platform_device *cec;
 	enum imx_hdmi_devtype dev_type;
 	struct device *dev;
 	struct clk *isfr_clk;
@@ -126,6 +128,7 @@ struct imx_hdmi {
 	int vic;
 
 	u8 edid[HDMI_EDID_LEN];
+	u8 mc_clkdis;
 	bool cable_plugin;
 
 	bool phy_enabled;
@@ -1152,8 +1155,6 @@ static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi)
 /* HDMI Initialization Step B.4 */
 static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi)
 {
-	u8 clkdis;
-
 	/* control period minimum duration */
 	hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
 	hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
@@ -1165,23 +1166,28 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi)
 	hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
 
 	/* Enable pixel clock and tmds data path */
-	clkdis = 0x7F;
-	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis |= HDMI_MC_CLKDIS_HDCPCLK_DISABLE |
+			   HDMI_MC_CLKDIS_CSCCLK_DISABLE |
+			   HDMI_MC_CLKDIS_AUDCLK_DISABLE |
+			   HDMI_MC_CLKDIS_PREPCLK_DISABLE |
+			   HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
-	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 
 	/* Enable csc path */
 	if (is_color_space_conversion(hdmi)) {
-		clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
-		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+		hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
+		hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 	}
 }
 
 static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi)
 {
-	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_AUDCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
 }
 
 /* Workaround to clear the overflow condition */
@@ -1576,6 +1582,27 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 	return 0;
 }
 
+static void imx_hdmi_cec_enable(void *data)
+{
+	struct imx_hdmi *hdmi = data;
+
+	hdmi->mc_clkdis &= ~HDMI_MC_CLKDIS_CECCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static void imx_hdmi_cec_disable(void *data)
+{
+	struct imx_hdmi *hdmi = data;
+
+	hdmi->mc_clkdis |= HDMI_MC_CLKDIS_CECCLK_DISABLE;
+	hdmi_writeb(hdmi, hdmi->mc_clkdis, HDMI_MC_CLKDIS);
+}
+
+static const struct dw_hdmi_cec_ops imx_hdmi_cec_ops = {
+	.enable = imx_hdmi_cec_enable,
+	.disable = imx_hdmi_cec_disable,
+};
+
 static struct platform_device_id imx_hdmi_devtype[] = {
 	{
 		.name = "imx6q-hdmi",
@@ -1604,6 +1631,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	struct device_node *np = dev->of_node;
 	struct device_node *ddc_node;
 	struct dw_hdmi_audio_data audio;
+	struct dw_hdmi_cec_data cec;
 	struct imx_hdmi *hdmi;
 	struct resource *iores;
 	int ret, irq;
@@ -1615,6 +1643,7 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	hdmi->dev = dev;
 	hdmi->sample_rate = 48000;
 	hdmi->ratio = 100;
+	hdmi->mc_clkdis = 0x7f;
 
 	if (of_id) {
 		const struct platform_device_id *device_id = of_id->data;
@@ -1735,6 +1764,18 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	pdevinfo.dma_mask = DMA_BIT_MASK(32);
 	hdmi->audio = platform_device_register_full(&pdevinfo);
 
+	cec.base = hdmi->regs;
+	cec.irq = irq;
+	cec.ops = &imx_hdmi_cec_ops;
+	cec.ops_data = hdmi;
+
+	pdevinfo.name = "dw-hdmi-cec";
+	pdevinfo.data = &cec;
+	pdevinfo.size_data = sizeof(cec);
+	pdevinfo.dma_mask = 0;
+
+	hdmi->cec = platform_device_register_full(&pdevinfo);
+
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
@@ -1754,6 +1795,8 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master,
 
 	if (!IS_ERR(hdmi->audio))
 		platform_device_unregister(hdmi->audio);
+	if (!IS_ERR(hdmi->cec))
+		platform_device_unregister(hdmi->cec);
 
 	/* Disable all interrupts */
 	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
-- 
1.8.3.1


[-- Attachment #11: Type: text/plain, Size: 159 bytes --]

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi
  2014-11-05 13:41     ` Philipp Zabel
@ 2014-11-07  2:47       ` Andy Yan
  -1 siblings, 0 replies; 20+ messages in thread
From: Andy Yan @ 2014-11-07  2:47 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: airlied, heiko, fabio.estevam, rmk+kernel, Greg Kroah-Hartman,
	Grant Likely, Rob Herring, Shawn Guo, Josh Boyer, Sean Paul,
	Inki Dae, Dave Airlie, Arnd Bergmann, Lucas Stach,
	Zubair.Kakakhel, djkurtz, ykk, linux-kernel, dri-devel, devel,
	devicetree, linux-rockchip


On 2014年11月05日 21:41, Philipp Zabel wrote:
> Hi Andy,
>
> I think separating the core from the SoC specific part is a good step
> into the right direction.
>
> Am Mittwoch, den 05.11.2014, 20:55 +0800 schrieb Andy Yan:
>> imx6 and rockchip rk3288 and JZ4780 (Ingenic Xburst/MIPS)
>> use the interface compatible Designware HDMI IP, but they
>> also have some lightly difference, such as phy pll configuration,
>> register width(imx hdmi register is one byte, but rk3288 is 4
>> bytes width), 4K support(imx6 doesn't support 4k, but rk3288 does),
>> clk useage,and the crtc mux configuration is also platform specific.
>>
>> To reuse the imx hdmi driver, split the platform specific code out
>> to dw_hdmi-imx.c.
>>
>> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> [...]
>> +static int imx_hdmi_parse_dt(struct imx_hdmi_priv *hdmi)
>> +{
>> +	struct device_node *np = hdmi->dev->of_node;
>> +
>> +	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
>> +	if (IS_ERR(hdmi->regmap)) {
>> +		dev_err(hdmi->dev, "Unable to get gpr\n");
>> +		return PTR_ERR(hdmi->regmap);
>> +	}
>> +
>> +	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
>> +	if (IS_ERR(hdmi->isfr_clk)) {
>> +		dev_err(hdmi->dev, "Unable to get HDMI isfr clk\n");
>> +		return PTR_ERR(hdmi->isfr_clk);
>> +	}
>> +
>> +	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
>> +	if (IS_ERR(hdmi->iahb_clk)) {
>> +		dev_err(hdmi->dev, "Unable to get HDMI iahb clk\n");
>> +		return PTR_ERR(hdmi->iahb_clk);
>> +	}
> Surely this IP core needs to be clocked regardless of the SoC? How are
> clocks controlled on rk3288 and jz4780?
yes, this IP core need to be clocked. But different Soc has different
usage of the clk, on freescale imx platform one clk is used for isfr, 
one is used for iahb,
but on rockchip rk3288, one clk is used for control logic , the another 
is used for hdcp.
I am not sure how are the clocks on jz4780

>
> [...]
>> +/*On rockchip platform, no-word access to the hdmi
>> + * register will causes an imprecise external abort
>> + */
>> +static inline void hdmi_writel(struct imx_hdmi *hdmi, u32 val, int offset)
>> +{
>> +	writel(val, hdmi->regs + (offset << 2));
>> +}
>>   
>> -	bool phy_enabled;
>> -	struct drm_display_mode previous_mode;
>> +static inline u32 hdmi_readl(struct imx_hdmi *hdmi, int offset)
>> +{
>> +	return readl(hdmi->regs + (offset << 2));
>> +}
>>   
>> -	struct regmap *regmap;
>> -	struct i2c_adapter *ddc;
>> -	void __iomem *regs;
>> +static void hdmi_modl(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
>> +{
>> +	u32 val = hdmi_readl(hdmi, reg) & ~mask;
>>   
>> -	unsigned int sample_rate;
>> -	int ratio;
>> -};
>> +	val |= data & mask;
>> +	hdmi_writel(hdmi, val, reg);
>> +}
>>   
>> -static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di)
>> +static void hdmi_mask_writel(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
>> +			     u32 shift, u32 mask)
>>   {
>> -	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
>> -			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
>> -			   ipu_di << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
>> +	hdmi_modl(hdmi, data << shift, mask, reg);
>>   }
>>   
>> -static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset)
>> +static inline void hdmi_writeb(struct imx_hdmi *hdmi, u32 val, int offset)
>>   {
>>   	writeb(val, hdmi->regs + offset);
>>   }
>>   
>> -static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset)
>> +static inline u32 hdmi_readb(struct imx_hdmi *hdmi, int offset)
>>   {
>>   	return readb(hdmi->regs + offset);
>>   }
>>   
>> -static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
>> +static void hdmi_modb(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
>>   {
>>   	u8 val = hdmi_readb(hdmi, reg) & ~mask;
> Do you really need to use readl instead of readb? In my opinion it would
> be better then to convert the driver to use regmap for register access
> (in a separate patch) and then let the SoC specific driver extension
> provide the regmap to the core driver.
Rockchip RK3288 can only access the hdmi register by 32bit(readl/writel)
byte access(readb/writeb) will cause  an imprecise external abort

I refactor the register access in PATCH V3, if you have any 
suggestion,please
tell me.
>
> [...]
>> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
>> new file mode 100644
>> index 0000000..e7e8285
>> --- /dev/null
>> +++ b/include/drm/bridge/dw_hdmi.h
>> @@ -0,0 +1,114 @@
>> +/*
>> + * Copyright (C) 2011 Freescale Semiconductor, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#ifndef __DW_HDMI_H__
>> +#define __DW_HDMI_H__
>> +
>> +#include <drm/drmP.h>
>> +
>> +#define HDMI_EDID_LEN           512
>> +
>> +enum {
>> +	RES_8,
>> +	RES_10,
>> +	RES_12,
>> +	RES_MAX,
>> +};
>> +
>> +enum imx_hdmi_devtype {
>> +	IMX6Q_HDMI,
>> +	IMX6DL_HDMI,
>> +};
>> +
>> +struct mpll_config {
>> +	unsigned long mpixelclock;
>> +	struct {
>> +		u16 cpce;
>> +		u16 gmp;
>> +	} res[RES_MAX];
>> +};
>> +
>> +struct curr_ctrl {
>> +	unsigned long mpixelclock;
>> +	u16 curr[RES_MAX];
>> +};
> For a header file in include/drm/bridge maybe these struct names are a
> bit too generic now.
>
> regards
> Philipp
>
>
>
>
>



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi
@ 2014-11-07  2:47       ` Andy Yan
  0 siblings, 0 replies; 20+ messages in thread
From: Andy Yan @ 2014-11-07  2:47 UTC (permalink / raw)
  To: Philipp Zabel
  Cc: heiko, airlied, dri-devel, ykk, devel, linux-rockchip,
	Grant Likely, Dave Airlie, devicetree, Zubair.Kakakhel,
	Arnd Bergmann, Inki Dae, Rob Herring, Sean Paul, rmk+kernel,
	fabio.estevam, Josh Boyer, Greg Kroah-Hartman, linux-kernel,
	djkurtz, Shawn Guo, Lucas Stach


On 2014年11月05日 21:41, Philipp Zabel wrote:
> Hi Andy,
>
> I think separating the core from the SoC specific part is a good step
> into the right direction.
>
> Am Mittwoch, den 05.11.2014, 20:55 +0800 schrieb Andy Yan:
>> imx6 and rockchip rk3288 and JZ4780 (Ingenic Xburst/MIPS)
>> use the interface compatible Designware HDMI IP, but they
>> also have some lightly difference, such as phy pll configuration,
>> register width(imx hdmi register is one byte, but rk3288 is 4
>> bytes width), 4K support(imx6 doesn't support 4k, but rk3288 does),
>> clk useage,and the crtc mux configuration is also platform specific.
>>
>> To reuse the imx hdmi driver, split the platform specific code out
>> to dw_hdmi-imx.c.
>>
>> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> [...]
>> +static int imx_hdmi_parse_dt(struct imx_hdmi_priv *hdmi)
>> +{
>> +	struct device_node *np = hdmi->dev->of_node;
>> +
>> +	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
>> +	if (IS_ERR(hdmi->regmap)) {
>> +		dev_err(hdmi->dev, "Unable to get gpr\n");
>> +		return PTR_ERR(hdmi->regmap);
>> +	}
>> +
>> +	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
>> +	if (IS_ERR(hdmi->isfr_clk)) {
>> +		dev_err(hdmi->dev, "Unable to get HDMI isfr clk\n");
>> +		return PTR_ERR(hdmi->isfr_clk);
>> +	}
>> +
>> +	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
>> +	if (IS_ERR(hdmi->iahb_clk)) {
>> +		dev_err(hdmi->dev, "Unable to get HDMI iahb clk\n");
>> +		return PTR_ERR(hdmi->iahb_clk);
>> +	}
> Surely this IP core needs to be clocked regardless of the SoC? How are
> clocks controlled on rk3288 and jz4780?
yes, this IP core need to be clocked. But different Soc has different
usage of the clk, on freescale imx platform one clk is used for isfr, 
one is used for iahb,
but on rockchip rk3288, one clk is used for control logic , the another 
is used for hdcp.
I am not sure how are the clocks on jz4780

>
> [...]
>> +/*On rockchip platform, no-word access to the hdmi
>> + * register will causes an imprecise external abort
>> + */
>> +static inline void hdmi_writel(struct imx_hdmi *hdmi, u32 val, int offset)
>> +{
>> +	writel(val, hdmi->regs + (offset << 2));
>> +}
>>   
>> -	bool phy_enabled;
>> -	struct drm_display_mode previous_mode;
>> +static inline u32 hdmi_readl(struct imx_hdmi *hdmi, int offset)
>> +{
>> +	return readl(hdmi->regs + (offset << 2));
>> +}
>>   
>> -	struct regmap *regmap;
>> -	struct i2c_adapter *ddc;
>> -	void __iomem *regs;
>> +static void hdmi_modl(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
>> +{
>> +	u32 val = hdmi_readl(hdmi, reg) & ~mask;
>>   
>> -	unsigned int sample_rate;
>> -	int ratio;
>> -};
>> +	val |= data & mask;
>> +	hdmi_writel(hdmi, val, reg);
>> +}
>>   
>> -static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di)
>> +static void hdmi_mask_writel(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
>> +			     u32 shift, u32 mask)
>>   {
>> -	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
>> -			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
>> -			   ipu_di << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
>> +	hdmi_modl(hdmi, data << shift, mask, reg);
>>   }
>>   
>> -static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset)
>> +static inline void hdmi_writeb(struct imx_hdmi *hdmi, u32 val, int offset)
>>   {
>>   	writeb(val, hdmi->regs + offset);
>>   }
>>   
>> -static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset)
>> +static inline u32 hdmi_readb(struct imx_hdmi *hdmi, int offset)
>>   {
>>   	return readb(hdmi->regs + offset);
>>   }
>>   
>> -static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
>> +static void hdmi_modb(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
>>   {
>>   	u8 val = hdmi_readb(hdmi, reg) & ~mask;
> Do you really need to use readl instead of readb? In my opinion it would
> be better then to convert the driver to use regmap for register access
> (in a separate patch) and then let the SoC specific driver extension
> provide the regmap to the core driver.
Rockchip RK3288 can only access the hdmi register by 32bit(readl/writel)
byte access(readb/writeb) will cause  an imprecise external abort

I refactor the register access in PATCH V3, if you have any 
suggestion,please
tell me.
>
> [...]
>> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
>> new file mode 100644
>> index 0000000..e7e8285
>> --- /dev/null
>> +++ b/include/drm/bridge/dw_hdmi.h
>> @@ -0,0 +1,114 @@
>> +/*
>> + * Copyright (C) 2011 Freescale Semiconductor, Inc.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + */
>> +
>> +#ifndef __DW_HDMI_H__
>> +#define __DW_HDMI_H__
>> +
>> +#include <drm/drmP.h>
>> +
>> +#define HDMI_EDID_LEN           512
>> +
>> +enum {
>> +	RES_8,
>> +	RES_10,
>> +	RES_12,
>> +	RES_MAX,
>> +};
>> +
>> +enum imx_hdmi_devtype {
>> +	IMX6Q_HDMI,
>> +	IMX6DL_HDMI,
>> +};
>> +
>> +struct mpll_config {
>> +	unsigned long mpixelclock;
>> +	struct {
>> +		u16 cpce;
>> +		u16 gmp;
>> +	} res[RES_MAX];
>> +};
>> +
>> +struct curr_ctrl {
>> +	unsigned long mpixelclock;
>> +	u16 curr[RES_MAX];
>> +};
> For a header file in include/drm/bridge maybe these struct names are a
> bit too generic now.
>
> regards
> Philipp
>
>
>
>
>


_______________________________________________
devel mailing list
devel@linuxdriverproject.org
http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi
  2014-11-05 12:55   ` Andy Yan
@ 2014-11-05 13:41     ` Philipp Zabel
  -1 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2014-11-05 13:41 UTC (permalink / raw)
  To: Andy Yan
  Cc: airlied, heiko, fabio.estevam, rmk+kernel, Greg Kroah-Hartman,
	Grant Likely, Rob Herring, Shawn Guo, Josh Boyer, Sean Paul,
	Inki Dae, Dave Airlie, Arnd Bergmann, Lucas Stach,
	Zubair.Kakakhel, djkurtz, ykk, linux-kernel, dri-devel, devel,
	devicetree, linux-rockchip

Hi Andy,

I think separating the core from the SoC specific part is a good step
into the right direction.

Am Mittwoch, den 05.11.2014, 20:55 +0800 schrieb Andy Yan:
> imx6 and rockchip rk3288 and JZ4780 (Ingenic Xburst/MIPS)
> use the interface compatible Designware HDMI IP, but they
> also have some lightly difference, such as phy pll configuration,
> register width(imx hdmi register is one byte, but rk3288 is 4
> bytes width), 4K support(imx6 doesn't support 4k, but rk3288 does),
> clk useage,and the crtc mux configuration is also platform specific.
> 
> To reuse the imx hdmi driver, split the platform specific code out
> to dw_hdmi-imx.c.
> 
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
[...]
> +static int imx_hdmi_parse_dt(struct imx_hdmi_priv *hdmi)
> +{
> +	struct device_node *np = hdmi->dev->of_node;
> +
> +	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
> +	if (IS_ERR(hdmi->regmap)) {
> +		dev_err(hdmi->dev, "Unable to get gpr\n");
> +		return PTR_ERR(hdmi->regmap);
> +	}
> +
> +	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
> +	if (IS_ERR(hdmi->isfr_clk)) {
> +		dev_err(hdmi->dev, "Unable to get HDMI isfr clk\n");
> +		return PTR_ERR(hdmi->isfr_clk);
> +	}
> +
> +	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
> +	if (IS_ERR(hdmi->iahb_clk)) {
> +		dev_err(hdmi->dev, "Unable to get HDMI iahb clk\n");
> +		return PTR_ERR(hdmi->iahb_clk);
> +	}

Surely this IP core needs to be clocked regardless of the SoC? How are
clocks controlled on rk3288 and jz4780?

[...]
> +/*On rockchip platform, no-word access to the hdmi
> + * register will causes an imprecise external abort
> + */
> +static inline void hdmi_writel(struct imx_hdmi *hdmi, u32 val, int offset)
> +{
> +	writel(val, hdmi->regs + (offset << 2));
> +}
>  
> -	bool phy_enabled;
> -	struct drm_display_mode previous_mode;
> +static inline u32 hdmi_readl(struct imx_hdmi *hdmi, int offset)
> +{
> +	return readl(hdmi->regs + (offset << 2));
> +}
>  
> -	struct regmap *regmap;
> -	struct i2c_adapter *ddc;
> -	void __iomem *regs;
> +static void hdmi_modl(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
> +{
> +	u32 val = hdmi_readl(hdmi, reg) & ~mask;
>  
> -	unsigned int sample_rate;
> -	int ratio;
> -};
> +	val |= data & mask;
> +	hdmi_writel(hdmi, val, reg);
> +}
>  
> -static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di)
> +static void hdmi_mask_writel(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
> +			     u32 shift, u32 mask)
>  {
> -	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
> -			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
> -			   ipu_di << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
> +	hdmi_modl(hdmi, data << shift, mask, reg);
>  }
>  
> -static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset)
> +static inline void hdmi_writeb(struct imx_hdmi *hdmi, u32 val, int offset)
>  {
>  	writeb(val, hdmi->regs + offset);
>  }
>  
> -static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset)
> +static inline u32 hdmi_readb(struct imx_hdmi *hdmi, int offset)
>  {
>  	return readb(hdmi->regs + offset);
>  }
>  
> -static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
> +static void hdmi_modb(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
>  {
>  	u8 val = hdmi_readb(hdmi, reg) & ~mask;

Do you really need to use readl instead of readb? In my opinion it would
be better then to convert the driver to use regmap for register access
(in a separate patch) and then let the SoC specific driver extension
provide the regmap to the core driver.

[...]
> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
> new file mode 100644
> index 0000000..e7e8285
> --- /dev/null
> +++ b/include/drm/bridge/dw_hdmi.h
> @@ -0,0 +1,114 @@
> +/*
> + * Copyright (C) 2011 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef __DW_HDMI_H__
> +#define __DW_HDMI_H__
> +
> +#include <drm/drmP.h>
> +
> +#define HDMI_EDID_LEN           512
> +
> +enum {
> +	RES_8,
> +	RES_10,
> +	RES_12,
> +	RES_MAX,
> +};
> +
> +enum imx_hdmi_devtype {
> +	IMX6Q_HDMI,
> +	IMX6DL_HDMI,
> +};
> +
> +struct mpll_config {
> +	unsigned long mpixelclock;
> +	struct {
> +		u16 cpce;
> +		u16 gmp;
> +	} res[RES_MAX];
> +};
> +
> +struct curr_ctrl {
> +	unsigned long mpixelclock;
> +	u16 curr[RES_MAX];
> +};

For a header file in include/drm/bridge maybe these struct names are a
bit too generic now.

regards
Philipp



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi
@ 2014-11-05 13:41     ` Philipp Zabel
  0 siblings, 0 replies; 20+ messages in thread
From: Philipp Zabel @ 2014-11-05 13:41 UTC (permalink / raw)
  To: Andy Yan
  Cc: heiko, airlied, dri-devel, ykk, devel, linux-rockchip,
	Grant Likely, Dave Airlie, devicetree, Zubair.Kakakhel,
	Arnd Bergmann, Inki Dae, Rob Herring, Sean Paul, rmk+kernel,
	fabio.estevam, Josh Boyer, Greg Kroah-Hartman, linux-kernel,
	djkurtz, Shawn Guo, Lucas Stach

Hi Andy,

I think separating the core from the SoC specific part is a good step
into the right direction.

Am Mittwoch, den 05.11.2014, 20:55 +0800 schrieb Andy Yan:
> imx6 and rockchip rk3288 and JZ4780 (Ingenic Xburst/MIPS)
> use the interface compatible Designware HDMI IP, but they
> also have some lightly difference, such as phy pll configuration,
> register width(imx hdmi register is one byte, but rk3288 is 4
> bytes width), 4K support(imx6 doesn't support 4k, but rk3288 does),
> clk useage,and the crtc mux configuration is also platform specific.
> 
> To reuse the imx hdmi driver, split the platform specific code out
> to dw_hdmi-imx.c.
> 
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
[...]
> +static int imx_hdmi_parse_dt(struct imx_hdmi_priv *hdmi)
> +{
> +	struct device_node *np = hdmi->dev->of_node;
> +
> +	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
> +	if (IS_ERR(hdmi->regmap)) {
> +		dev_err(hdmi->dev, "Unable to get gpr\n");
> +		return PTR_ERR(hdmi->regmap);
> +	}
> +
> +	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
> +	if (IS_ERR(hdmi->isfr_clk)) {
> +		dev_err(hdmi->dev, "Unable to get HDMI isfr clk\n");
> +		return PTR_ERR(hdmi->isfr_clk);
> +	}
> +
> +	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
> +	if (IS_ERR(hdmi->iahb_clk)) {
> +		dev_err(hdmi->dev, "Unable to get HDMI iahb clk\n");
> +		return PTR_ERR(hdmi->iahb_clk);
> +	}

Surely this IP core needs to be clocked regardless of the SoC? How are
clocks controlled on rk3288 and jz4780?

[...]
> +/*On rockchip platform, no-word access to the hdmi
> + * register will causes an imprecise external abort
> + */
> +static inline void hdmi_writel(struct imx_hdmi *hdmi, u32 val, int offset)
> +{
> +	writel(val, hdmi->regs + (offset << 2));
> +}
>  
> -	bool phy_enabled;
> -	struct drm_display_mode previous_mode;
> +static inline u32 hdmi_readl(struct imx_hdmi *hdmi, int offset)
> +{
> +	return readl(hdmi->regs + (offset << 2));
> +}
>  
> -	struct regmap *regmap;
> -	struct i2c_adapter *ddc;
> -	void __iomem *regs;
> +static void hdmi_modl(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
> +{
> +	u32 val = hdmi_readl(hdmi, reg) & ~mask;
>  
> -	unsigned int sample_rate;
> -	int ratio;
> -};
> +	val |= data & mask;
> +	hdmi_writel(hdmi, val, reg);
> +}
>  
> -static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di)
> +static void hdmi_mask_writel(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
> +			     u32 shift, u32 mask)
>  {
> -	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
> -			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
> -			   ipu_di << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
> +	hdmi_modl(hdmi, data << shift, mask, reg);
>  }
>  
> -static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset)
> +static inline void hdmi_writeb(struct imx_hdmi *hdmi, u32 val, int offset)
>  {
>  	writeb(val, hdmi->regs + offset);
>  }
>  
> -static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset)
> +static inline u32 hdmi_readb(struct imx_hdmi *hdmi, int offset)
>  {
>  	return readb(hdmi->regs + offset);
>  }
>  
> -static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
> +static void hdmi_modb(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
>  {
>  	u8 val = hdmi_readb(hdmi, reg) & ~mask;

Do you really need to use readl instead of readb? In my opinion it would
be better then to convert the driver to use regmap for register access
(in a separate patch) and then let the SoC specific driver extension
provide the regmap to the core driver.

[...]
> diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
> new file mode 100644
> index 0000000..e7e8285
> --- /dev/null
> +++ b/include/drm/bridge/dw_hdmi.h
> @@ -0,0 +1,114 @@
> +/*
> + * Copyright (C) 2011 Freescale Semiconductor, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#ifndef __DW_HDMI_H__
> +#define __DW_HDMI_H__
> +
> +#include <drm/drmP.h>
> +
> +#define HDMI_EDID_LEN           512
> +
> +enum {
> +	RES_8,
> +	RES_10,
> +	RES_12,
> +	RES_MAX,
> +};
> +
> +enum imx_hdmi_devtype {
> +	IMX6Q_HDMI,
> +	IMX6DL_HDMI,
> +};
> +
> +struct mpll_config {
> +	unsigned long mpixelclock;
> +	struct {
> +		u16 cpce;
> +		u16 gmp;
> +	} res[RES_MAX];
> +};
> +
> +struct curr_ctrl {
> +	unsigned long mpixelclock;
> +	u16 curr[RES_MAX];
> +};

For a header file in include/drm/bridge maybe these struct names are a
bit too generic now.

regards
Philipp

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi
  2014-11-05 12:54 [PATCH V2 " Andy Yan
@ 2014-11-05 12:55   ` Andy Yan
  0 siblings, 0 replies; 20+ messages in thread
From: Andy Yan @ 2014-11-05 12:55 UTC (permalink / raw)
  To: airlied, heiko, fabio.estevam, rmk+kernel
  Cc: Greg Kroah-Hartman, Grant Likely, Rob Herring, Philipp Zabel,
	Shawn Guo, Andy yan, Josh Boyer, Sean Paul, Inki Dae,
	Dave Airlie, Arnd Bergmann, Lucas Stach, Zubair.Kakakhel,
	djkurtz, ykk, linux-kernel, dri-devel, devel, devicetree,
	linux-rockchip

imx6 and rockchip rk3288 and JZ4780 (Ingenic Xburst/MIPS)
use the interface compatible Designware HDMI IP, but they
also have some lightly difference, such as phy pll configuration,
register width(imx hdmi register is one byte, but rk3288 is 4
bytes width), 4K support(imx6 doesn't support 4k, but rk3288 does),
clk useage,and the crtc mux configuration is also platform specific.

To reuse the imx hdmi driver, split the platform specific code out
to dw_hdmi-imx.c.

Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
---
 drivers/staging/imx-drm/Makefile      |   2 +-
 drivers/staging/imx-drm/dw_hdmi-imx.c | 214 ++++++++++
 drivers/staging/imx-drm/imx-hdmi.c    | 726 ++++++++++++++--------------------
 include/drm/bridge/dw_hdmi.h          | 114 ++++++
 4 files changed, 634 insertions(+), 422 deletions(-)
 create mode 100644 drivers/staging/imx-drm/dw_hdmi-imx.c
 create mode 100644 include/drm/bridge/dw_hdmi.h

diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
index 582c438..809027d 100644
--- a/drivers/staging/imx-drm/Makefile
+++ b/drivers/staging/imx-drm/Makefile
@@ -9,4 +9,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
 
 imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o
 obj-$(CONFIG_DRM_IMX_IPUV3)	+= imx-ipuv3-crtc.o
-obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o
+obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o dw_hdmi-imx.o
diff --git a/drivers/staging/imx-drm/dw_hdmi-imx.c b/drivers/staging/imx-drm/dw_hdmi-imx.c
new file mode 100644
index 0000000..71ac859
--- /dev/null
+++ b/drivers/staging/imx-drm/dw_hdmi-imx.c
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <drm/bridge/dw_hdmi.h>
+#include <video/imx-ipu-v3.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+
+#include "imx-drm.h"
+
+struct imx_hdmi_priv {
+	struct device *dev;
+	struct clk *isfr_clk;
+	struct clk *iahb_clk;
+	struct regmap *regmap;
+};
+
+static const struct mpll_config imx_mpll_cfg[] = {
+	{
+		45250000, {
+			{ 0x01e0, 0x0000 },
+			{ 0x21e1, 0x0000 },
+			{ 0x41e2, 0x0000 }
+		},
+	}, {
+		92500000, {
+			{ 0x0140, 0x0005 },
+			{ 0x2141, 0x0005 },
+			{ 0x4142, 0x0005 },
+	},
+	}, {
+		148500000, {
+			{ 0x00a0, 0x000a },
+			{ 0x20a1, 0x000a },
+			{ 0x40a2, 0x000a },
+		},
+	}, {
+		~0UL, {
+			{ 0x00a0, 0x000a },
+			{ 0x2001, 0x000f },
+			{ 0x4002, 0x000f },
+		},
+	}
+};
+
+static const struct curr_ctrl imx_cur_ctr[] = {
+	/*      pixelclk     bpp8    bpp10   bpp12 */
+	{
+		54000000, { 0x091c, 0x091c, 0x06dc },
+	}, {
+		58400000, { 0x091c, 0x06dc, 0x06dc },
+	}, {
+		72000000, { 0x06dc, 0x06dc, 0x091c },
+	}, {
+		74250000, { 0x06dc, 0x0b5c, 0x091c },
+	}, {
+		118800000, { 0x091c, 0x091c, 0x06dc },
+	}, {
+		216000000, { 0x06dc, 0x0b5c, 0x091c },
+	}
+};
+
+static int imx_hdmi_parse_dt(struct imx_hdmi_priv *hdmi)
+{
+	struct device_node *np = hdmi->dev->of_node;
+
+	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
+	if (IS_ERR(hdmi->regmap)) {
+		dev_err(hdmi->dev, "Unable to get gpr\n");
+		return PTR_ERR(hdmi->regmap);
+	}
+
+	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
+	if (IS_ERR(hdmi->isfr_clk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI isfr clk\n");
+		return PTR_ERR(hdmi->isfr_clk);
+	}
+
+	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
+	if (IS_ERR(hdmi->iahb_clk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI iahb clk\n");
+		return PTR_ERR(hdmi->iahb_clk);
+	}
+
+	return 0;
+}
+
+static void *imx_hdmi_imx_setup(struct platform_device *pdev)
+{
+	struct imx_hdmi_priv *hdmi;
+	int ret;
+
+	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return ERR_PTR(-ENOMEM);
+	hdmi->dev = &pdev->dev;
+
+	ret = imx_hdmi_parse_dt(hdmi);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	ret = clk_prepare_enable(hdmi->isfr_clk);
+	if (ret) {
+		dev_err(hdmi->dev,
+			"Cannot enable HDMI isfr clock: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	ret = clk_prepare_enable(hdmi->iahb_clk);
+	if (ret) {
+		dev_err(hdmi->dev,
+			"Cannot enable HDMI iahb clock: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	return hdmi;
+}
+
+static void imx_hdmi_imx_exit(void *priv)
+{
+	struct imx_hdmi_priv *hdmi = (struct imx_hdmi_priv *)priv;
+
+	clk_disable_unprepare(hdmi->isfr_clk);
+
+	clk_disable_unprepare(hdmi->iahb_clk);
+}
+
+static void imx_hdmi_imx_set_crtc_mux(void *priv, struct drm_encoder *encoder)
+{
+	struct imx_hdmi_priv *hdmi = (struct imx_hdmi_priv *)priv;
+	int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
+
+	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
+			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
+			   mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
+}
+
+static void imx_hdmi_imx_encoder_prepare(struct drm_connector *connector,
+					struct drm_encoder *encoder)
+{
+	imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24);
+}
+
+static struct imx_hdmi_plat_data imx6q_hdmi_drv_data = {
+	.setup			= imx_hdmi_imx_setup,
+	.exit			= imx_hdmi_imx_exit,
+	.set_crtc_mux		= imx_hdmi_imx_set_crtc_mux,
+	.encoder_prepare	= imx_hdmi_imx_encoder_prepare,
+	.mpll_cfg		= imx_mpll_cfg,
+	.cur_ctr		= imx_cur_ctr,
+	.dev_type		= IMX6Q_HDMI,
+};
+
+static struct imx_hdmi_plat_data imx6dl_hdmi_drv_data = {
+	.setup			= imx_hdmi_imx_setup,
+	.exit			= imx_hdmi_imx_exit,
+	.set_crtc_mux		= imx_hdmi_imx_set_crtc_mux,
+	.encoder_prepare	= imx_hdmi_imx_encoder_prepare,
+	.mpll_cfg		= imx_mpll_cfg,
+	.cur_ctr		= imx_cur_ctr,
+	.dev_type		= IMX6DL_HDMI,
+};
+
+static const struct of_device_id imx_hdmi_imx_ids[] = {
+	{ .compatible = "fsl,imx6q-hdmi",
+	  .data = &imx6q_hdmi_drv_data
+	}, {
+	  .compatible = "fsl,imx6dl-hdmi",
+	  .data = &imx6dl_hdmi_drv_data
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, imx_hdmi_imx_dt_ids);
+
+static int imx_hdmi_imx_probe(struct platform_device *pdev)
+{
+	const struct imx_hdmi_plat_data *plat_data;
+	const struct of_device_id *match;
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	match = of_match_node(imx_hdmi_imx_ids, pdev->dev.of_node);
+	plat_data = match->data;
+
+	return imx_hdmi_pltfm_register(pdev, plat_data);
+}
+
+static int imx_hdmi_imx_remove(struct platform_device *pdev)
+{
+	return imx_hdmi_pltfm_unregister(pdev);
+}
+
+static struct platform_driver imx_hdmi_imx_pltfm_driver = {
+	.probe  = imx_hdmi_imx_probe,
+	.remove = imx_hdmi_imx_remove,
+	.driver = {
+		.name = "dwhdmi-imx",
+		.owner = THIS_MODULE,
+		.of_match_table = imx_hdmi_imx_ids,
+	},
+};
+
+module_platform_driver(imx_hdmi_imx_pltfm_driver);
+
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
+MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:dwhdmi-imx");
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index aaec6b2..138706c 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -16,23 +16,16 @@
 #include <linux/irq.h>
 #include <linux/delay.h>
 #include <linux/err.h>
-#include <linux/clk.h>
 #include <linux/hdmi.h>
-#include <linux/regmap.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 #include <linux/of_device.h>
-
+#include <drm/drm_of.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder_slave.h>
-#include <video/imx-ipu-v3.h>
+#include <drm/bridge/dw_hdmi.h>
 
 #include "imx-hdmi.h"
-#include "imx-drm.h"
-
-#define HDMI_EDID_LEN		512
 
 #define RGB			0
 #define YCBCR444		1
@@ -54,11 +47,6 @@ enum hdmi_datamap {
 	YCbCr422_12B = 0x12,
 };
 
-enum imx_hdmi_devtype {
-	IMX6Q_HDMI,
-	IMX6DL_HDMI,
-};
-
 static const u16 csc_coeff_default[3][4] = {
 	{ 0x2000, 0x0000, 0x0000, 0x0000 },
 	{ 0x0000, 0x2000, 0x0000, 0x0000 },
@@ -89,72 +77,44 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
 	{ 0x6756, 0x78ab, 0x2000, 0x0200 }
 };
 
-struct hdmi_vmode {
-	bool mdvi;
-	bool mhsyncpolarity;
-	bool mvsyncpolarity;
-	bool minterlaced;
-	bool mdataenablepolarity;
-
-	unsigned int mpixelclock;
-	unsigned int mpixelrepetitioninput;
-	unsigned int mpixelrepetitionoutput;
-};
-
-struct hdmi_data_info {
-	unsigned int enc_in_format;
-	unsigned int enc_out_format;
-	unsigned int enc_color_depth;
-	unsigned int colorimetry;
-	unsigned int pix_repet_factor;
-	unsigned int hdcp_enable;
-	struct hdmi_vmode video_mode;
-};
-
-struct imx_hdmi {
-	struct drm_connector connector;
-	struct drm_encoder encoder;
-
-	enum imx_hdmi_devtype dev_type;
-	struct device *dev;
-	struct clk *isfr_clk;
-	struct clk *iahb_clk;
-
-	struct hdmi_data_info hdmi_data;
-	int vic;
-
-	u8 edid[HDMI_EDID_LEN];
-	bool cable_plugin;
+/*On rockchip platform, no-word access to the hdmi
+ * register will causes an imprecise external abort
+ */
+static inline void hdmi_writel(struct imx_hdmi *hdmi, u32 val, int offset)
+{
+	writel(val, hdmi->regs + (offset << 2));
+}
 
-	bool phy_enabled;
-	struct drm_display_mode previous_mode;
+static inline u32 hdmi_readl(struct imx_hdmi *hdmi, int offset)
+{
+	return readl(hdmi->regs + (offset << 2));
+}
 
-	struct regmap *regmap;
-	struct i2c_adapter *ddc;
-	void __iomem *regs;
+static void hdmi_modl(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
+{
+	u32 val = hdmi_readl(hdmi, reg) & ~mask;
 
-	unsigned int sample_rate;
-	int ratio;
-};
+	val |= data & mask;
+	hdmi_writel(hdmi, val, reg);
+}
 
-static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di)
+static void hdmi_mask_writel(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+			     u32 shift, u32 mask)
 {
-	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
-			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
-			   ipu_di << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
+	hdmi_modl(hdmi, data << shift, mask, reg);
 }
 
-static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset)
+static inline void hdmi_writeb(struct imx_hdmi *hdmi, u32 val, int offset)
 {
 	writeb(val, hdmi->regs + offset);
 }
 
-static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset)
+static inline u32 hdmi_readb(struct imx_hdmi *hdmi, int offset)
 {
 	return readb(hdmi->regs + offset);
 }
 
-static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
+static void hdmi_modb(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
 {
 	u8 val = hdmi_readb(hdmi, reg) & ~mask;
 
@@ -162,8 +122,8 @@ static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
 	hdmi_writeb(hdmi, val, reg);
 }
 
-static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg,
-		      u8 shift, u8 mask)
+static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+		      u32 shift, u32 mask)
 {
 	hdmi_modb(hdmi, data << shift, mask, reg);
 }
@@ -171,22 +131,22 @@ static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg,
 static void hdmi_set_clock_regenerator_n(struct imx_hdmi *hdmi,
 					 unsigned int value)
 {
-	hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1);
-	hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
-	hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
+	hdmi->write(hdmi, value & 0xff, HDMI_AUD_N1);
+	hdmi->write(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
+	hdmi->write(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
 
 	/* nshift factor = 0 */
-	hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
+	hdmi->mod(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
 }
 
 static void hdmi_regenerate_cts(struct imx_hdmi *hdmi, unsigned int cts)
 {
 	/* Must be set/cleared first */
-	hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+	hdmi->mod(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
 
-	hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
-	hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
-	hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+	hdmi->write(hdmi, cts & 0xff, HDMI_AUD_CTS1);
+	hdmi->write(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
+	hdmi->write(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
 		    HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
 }
 
@@ -323,6 +283,7 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
 	}
 	if (ratio == 100)
 		return cts;
+
 	return (cts * ratio) / 100;
 }
 
@@ -338,7 +299,7 @@ static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi,
 
 	if (!clk_cts) {
 		dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n",
-			 __func__, pixel_clk);
+			__func__, pixel_clk);
 		return;
 	}
 
@@ -408,19 +369,19 @@ static void hdmi_video_sample(struct imx_hdmi *hdmi)
 	val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
 		((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
 		HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
-	hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
+	hdmi->write(hdmi, val, HDMI_TX_INVID0);
 
 	/* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
 	val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
 		HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
 		HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
-	hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
+	hdmi->write(hdmi, val, HDMI_TX_INSTUFFING);
+	hdmi->write(hdmi, 0x0, HDMI_TX_GYDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_GYDATA1);
+	hdmi->write(hdmi, 0x0, HDMI_TX_RCRDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_RCRDATA1);
+	hdmi->write(hdmi, 0x0, HDMI_TX_BCBDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_BCBDATA1);
 }
 
 static int is_color_space_conversion(struct imx_hdmi *hdmi)
@@ -477,17 +438,17 @@ static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi)
 		u16 coeff_b = (*csc_coeff)[1][i];
 		u16 coeff_c = (*csc_coeff)[2][i];
 
-		hdmi_writeb(hdmi, coeff_a & 0xff,
+		hdmi->write(hdmi, coeff_a & 0xff,
 			HDMI_CSC_COEF_A1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
-		hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
-		hdmi_writeb(hdmi, coeff_c & 0xff,
+		hdmi->write(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
+		hdmi->write(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_c & 0xff,
 			HDMI_CSC_COEF_C1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
 	}
 
-	hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
+	hdmi->mod(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
 		  HDMI_CSC_SCALE);
 }
 
@@ -515,8 +476,8 @@ static void hdmi_video_csc(struct imx_hdmi *hdmi)
 		return;
 
 	/* Configure the CSC registers */
-	hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
-	hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
+	hdmi->write(hdmi, interpolation | decimation, HDMI_CSC_CFG);
+	hdmi->mod(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
 		  HDMI_CSC_SCALE);
 
 	imx_hdmi_update_csc_coeffs(hdmi);
@@ -535,21 +496,22 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 	struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
 	u8 val, vp_conf;
 
-	if (hdmi_data->enc_out_format == RGB
-		|| hdmi_data->enc_out_format == YCBCR444) {
-		if (!hdmi_data->enc_color_depth)
+	if (hdmi_data->enc_out_format == RGB ||
+	    hdmi_data->enc_out_format == YCBCR444) {
+		if (!hdmi_data->enc_color_depth) {
 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
-		else if (hdmi_data->enc_color_depth == 8) {
+		} else if (hdmi_data->enc_color_depth == 8) {
 			color_depth = 4;
 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
-		} else if (hdmi_data->enc_color_depth == 10)
+		} else if (hdmi_data->enc_color_depth == 10) {
 			color_depth = 5;
-		else if (hdmi_data->enc_color_depth == 12)
+		} else if (hdmi_data->enc_color_depth == 12) {
 			color_depth = 6;
-		else if (hdmi_data->enc_color_depth == 16)
+		} else if (hdmi_data->enc_color_depth == 16) {
 			color_depth = 7;
-		else
+		} else {
 			return;
+		}
 	} else if (hdmi_data->enc_out_format == YCBCR422_8BITS) {
 		if (!hdmi_data->enc_color_depth ||
 		    hdmi_data->enc_color_depth == 8)
@@ -561,8 +523,9 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		else
 			return;
 		output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
-	} else
+	} else {
 		return;
+	}
 
 	/* set the packetizer registers */
 	val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
@@ -570,9 +533,9 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		((hdmi_data->pix_repet_factor <<
 		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
 		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
-	hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
+	hdmi->write(hdmi, val, HDMI_VP_PR_CD);
 
-	hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
+	hdmi->mod(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
 		  HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
 
 	/* Data from pixel repeater block */
@@ -584,14 +547,14 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 			  HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
 	}
 
-	hdmi_modb(hdmi, vp_conf,
+	hdmi->mod(hdmi, vp_conf,
 		  HDMI_VP_CONF_PR_EN_MASK |
 		  HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
 
-	hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
+	hdmi->mod(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
 		  HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
 
-	hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
+	hdmi->write(hdmi, remap_size, HDMI_VP_REMAP);
 
 	if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
 		vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
@@ -609,55 +572,55 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		return;
 	}
 
-	hdmi_modb(hdmi, vp_conf,
+	hdmi->mod(hdmi, vp_conf,
 		  HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
 		  HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF);
 
-	hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
+	hdmi->mod(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
 			HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
 		  HDMI_VP_STUFF_PP_STUFFING_MASK |
 		  HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF);
 
-	hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
+	hdmi->mod(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
 		  HDMI_VP_CONF);
 }
 
 static inline void hdmi_phy_test_clear(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
 		  HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_enable(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
 		  HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_clock(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
 		  HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_din(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_writeb(hdmi, bit, HDMI_PHY_TST1);
+	hdmi->write(hdmi, bit, HDMI_PHY_TST1);
 }
 
 static inline void hdmi_phy_test_dout(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_writeb(hdmi, bit, HDMI_PHY_TST2);
+	hdmi->write(hdmi, bit, HDMI_PHY_TST2);
 }
 
 static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec)
 {
-	while ((hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
+	while ((hdmi->read(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
 		if (msec-- == 0)
 			return false;
 		udelay(1000);
@@ -668,13 +631,13 @@ static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec)
 static void __hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
 			      unsigned char addr)
 {
-	hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
-	hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
-	hdmi_writeb(hdmi, (unsigned char)(data >> 8),
+	hdmi->write(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+	hdmi->write(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+	hdmi->write(hdmi, (unsigned char)(data >> 8),
 		HDMI_PHY_I2CM_DATAO_1_ADDR);
-	hdmi_writeb(hdmi, (unsigned char)(data >> 0),
+	hdmi->write(hdmi, (unsigned char)(data >> 0),
 		HDMI_PHY_I2CM_DATAO_0_ADDR);
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
 		HDMI_PHY_I2CM_OPERATION_ADDR);
 	hdmi_phy_wait_i2c_done(hdmi, 1000);
 }
@@ -688,116 +651,53 @@ static int hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
 
 static void imx_hdmi_phy_enable_power(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_PDZ_OFFSET,
 			 HDMI_PHY_CONF0_PDZ_MASK);
 }
 
 static void imx_hdmi_phy_enable_tmds(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_ENTMDS_OFFSET,
 			 HDMI_PHY_CONF0_ENTMDS_MASK);
 }
 
 static void imx_hdmi_phy_gen2_pddq(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
 			 HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
 }
 
 static void imx_hdmi_phy_gen2_txpwron(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
 			 HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
 }
 
 static void imx_hdmi_phy_sel_data_en_pol(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
 			 HDMI_PHY_CONF0_SELDATAENPOL_MASK);
 }
 
 static void imx_hdmi_phy_sel_interface_control(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_SELDIPIF_OFFSET,
 			 HDMI_PHY_CONF0_SELDIPIF_MASK);
 }
 
-enum {
-	RES_8,
-	RES_10,
-	RES_12,
-	RES_MAX,
-};
-
-struct mpll_config {
-	unsigned long mpixelclock;
-	struct {
-		u16 cpce;
-		u16 gmp;
-	} res[RES_MAX];
-};
-
-static const struct mpll_config mpll_config[] = {
-	{
-		45250000, {
-			{ 0x01e0, 0x0000 },
-			{ 0x21e1, 0x0000 },
-			{ 0x41e2, 0x0000 }
-		},
-	}, {
-		92500000, {
-			{ 0x0140, 0x0005 },
-			{ 0x2141, 0x0005 },
-			{ 0x4142, 0x0005 },
-		},
-	}, {
-		148500000, {
-			{ 0x00a0, 0x000a },
-			{ 0x20a1, 0x000a },
-			{ 0x40a2, 0x000a },
-		},
-	}, {
-		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
-		},
-	}
-};
-
-struct curr_ctrl {
-	unsigned long mpixelclock;
-	u16 curr[RES_MAX];
-};
-
-static const struct curr_ctrl curr_ctrl[] = {
-	/*	pixelclk     bpp8    bpp10   bpp12 */
-	{
-		 54000000, { 0x091c, 0x091c, 0x06dc },
-	}, {
-		 58400000, { 0x091c, 0x06dc, 0x06dc },
-	}, {
-		 72000000, { 0x06dc, 0x06dc, 0x091c },
-	}, {
-		 74250000, { 0x06dc, 0x0b5c, 0x091c },
-	}, {
-		118800000, { 0x091c, 0x091c, 0x06dc },
-	}, {
-		216000000, { 0x06dc, 0x0b5c, 0x091c },
-	}
-};
-
 static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 			      unsigned char res, int cscon)
 {
 	unsigned res_idx, i;
 	u8 val, msec;
+	const struct mpll_config *mpll_cfg = hdmi->plat_data->mpll_cfg;
+	const struct curr_ctrl   *curr_ctr = hdmi->plat_data->cur_ctr;
 
 	if (prep)
 		return -EINVAL;
@@ -823,7 +723,7 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	else
 		val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS;
 
-	hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL);
+	hdmi->write(hdmi, val, HDMI_MC_FLOWCTRL);
 
 	/* gen2 tx power off */
 	imx_hdmi_phy_gen2_txpwron(hdmi, 0);
@@ -832,39 +732,38 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	imx_hdmi_phy_gen2_pddq(hdmi, 1);
 
 	/* PHY reset */
-	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
-	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
+	hdmi->write(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
+	hdmi->write(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
 
-	hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
+	hdmi->write(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
 
 	hdmi_phy_test_clear(hdmi, 1);
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
 			HDMI_PHY_I2CM_SLAVE_ADDR);
 	hdmi_phy_test_clear(hdmi, 0);
 
 	/* PLL/MPLL Cfg - always match on final entry */
-	for (i = 0; i < ARRAY_SIZE(mpll_config) - 1; i++)
+	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++)
 		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    mpll_config[i].mpixelclock)
+		    mpll_cfg[i].mpixelclock)
 			break;
+	hdmi_phy_i2c_write(hdmi, mpll_cfg[i].res[res_idx].cpce, 0x06);
+	hdmi_phy_i2c_write(hdmi, mpll_cfg[i].res[res_idx].gmp, 0x15);
 
-	hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06);
-	hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15);
-
-	for (i = 0; i < ARRAY_SIZE(curr_ctrl); i++)
+	for (i = 0; curr_ctr[i].mpixelclock != (~0UL); i++)
 		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    curr_ctrl[i].mpixelclock)
+		    curr_ctr[i].mpixelclock)
 			break;
 
-	if (i >= ARRAY_SIZE(curr_ctrl)) {
+	if (curr_ctr[i].mpixelclock == (~0UL)) {
 		dev_err(hdmi->dev,
-				"Pixel clock %d - unsupported by HDMI\n",
-				hdmi->hdmi_data.video_mode.mpixelclock);
+			"Pixel clock %d - unsupported by HDMI\n",
+			hdmi->hdmi_data.video_mode.mpixelclock);
 		return -EINVAL;
 	}
 
 	/* CURRCTRL */
-	hdmi_phy_i2c_write(hdmi, curr_ctrl[i].curr[res_idx], 0x10);
+	hdmi_phy_i2c_write(hdmi, curr_ctr[i].curr[res_idx], 0x10);
 
 	hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
 	hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
@@ -890,7 +789,7 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	/*Wait for PHY PLL lock */
 	msec = 5;
 	do {
-		val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+		val = hdmi->read(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
 		if (!val)
 			break;
 
@@ -942,12 +841,12 @@ static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi)
 		de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
 
 	/* disable rx detect */
-	hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
+	hdmi->mod(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
 		  HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
 
-	hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
+	hdmi->mod(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
 
-	hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
+	hdmi->mod(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
 		  HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
 }
 
@@ -977,7 +876,7 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 		HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT |
 		HDMI_FC_AVICONF0_BAR_DATA_NO_DATA;
 
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF0);
 
 	/* AVI Data Byte 2 -Set the Aspect Ratio */
 	if (aspect_16_9) {
@@ -1009,16 +908,16 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 	}
 
 	val = colorimetry | coded_ratio | act_ratio;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF1);
 
 	/* AVI Data Byte 3 */
 	val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry |
 		HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT |
 		HDMI_FC_AVICONF2_SCALING_NONE;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF2);
 
 	/* AVI Data Byte 4 */
-	hdmi_writeb(hdmi, hdmi->vic, HDMI_FC_AVIVID);
+	hdmi->write(hdmi, hdmi->vic, HDMI_FC_AVIVID);
 
 	/* AVI Data Byte 5- set up input and output pixel repetition */
 	val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) <<
@@ -1027,22 +926,22 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 		((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput <<
 		HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
 		HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
-	hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
+	hdmi->write(hdmi, val, HDMI_FC_PRCONF);
 
 	/* IT Content and quantization range = don't care */
 	val = HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS |
 		HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF3);
 
 	/* AVI Data Bytes 6-13 */
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIETB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIETB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISBB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISBB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIELB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIELB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISRB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISRB1);
 }
 
 static void hdmi_av_composer(struct imx_hdmi *hdmi,
@@ -1091,42 +990,42 @@ static void hdmi_av_composer(struct imx_hdmi *hdmi,
 		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
 		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
 
-	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
+	hdmi->write(hdmi, inv_val, HDMI_FC_INVIDCONF);
 
 	/* Set up horizontal active pixel width */
-	hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
-	hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
+	hdmi->write(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
+	hdmi->write(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
 
 	/* Set up vertical active lines */
-	hdmi_writeb(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
-	hdmi_writeb(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
+	hdmi->write(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
+	hdmi->write(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
 
 	/* Set up horizontal blanking pixel region width */
 	hblank = mode->htotal - mode->hdisplay;
-	hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
-	hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
+	hdmi->write(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
+	hdmi->write(hdmi, hblank, HDMI_FC_INHBLANK0);
 
 	/* Set up vertical blanking pixel region width */
 	vblank = mode->vtotal - mode->vdisplay;
-	hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
+	hdmi->write(hdmi, vblank, HDMI_FC_INVBLANK);
 
 	/* Set up HSYNC active edge delay width (in pixel clks) */
 	h_de_hs = mode->hsync_start - mode->hdisplay;
-	hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
-	hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
+	hdmi->write(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
+	hdmi->write(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
 
 	/* Set up VSYNC active edge delay (in lines) */
 	v_de_vs = mode->vsync_start - mode->vdisplay;
-	hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
+	hdmi->write(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
 
 	/* Set up HSYNC active pulse width (in pixel clks) */
 	hsync_len = mode->hsync_end - mode->hsync_start;
-	hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
-	hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
+	hdmi->write(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
+	hdmi->write(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
 
 	/* Set up VSYNC active edge delay (in lines) */
 	vsync_len = mode->vsync_end - mode->vsync_start;
-	hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
+	hdmi->write(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
 }
 
 static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi)
@@ -1146,33 +1045,33 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi)
 	u8 clkdis;
 
 	/* control period minimum duration */
-	hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
-	hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
-	hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
+	hdmi->write(hdmi, 12, HDMI_FC_CTRLDUR);
+	hdmi->write(hdmi, 32, HDMI_FC_EXCTRLDUR);
+	hdmi->write(hdmi, 1, HDMI_FC_EXCTRLSPAC);
 
 	/* Set to fill TMDS data channels */
-	hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
-	hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
-	hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
+	hdmi->write(hdmi, 0x0B, HDMI_FC_CH0PREAM);
+	hdmi->write(hdmi, 0x16, HDMI_FC_CH1PREAM);
+	hdmi->write(hdmi, 0x21, HDMI_FC_CH2PREAM);
 
 	/* Enable pixel clock and tmds data path */
 	clkdis = 0x7F;
 	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 
 	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 
 	/* Enable csc path */
 	if (is_color_space_conversion(hdmi)) {
 		clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
-		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+		hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 	}
 }
 
 static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi)
 {
-	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+	hdmi->mod(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
 }
 
 /* Workaround to clear the overflow condition */
@@ -1182,27 +1081,27 @@ static void imx_hdmi_clear_overflow(struct imx_hdmi *hdmi)
 	u8 val;
 
 	/* TMDS software reset */
-	hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
+	hdmi->write(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
 
-	val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
+	val = hdmi->read(hdmi, HDMI_FC_INVIDCONF);
 	if (hdmi->dev_type == IMX6DL_HDMI) {
-		hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+		hdmi->write(hdmi, val, HDMI_FC_INVIDCONF);
 		return;
 	}
 
 	for (count = 0; count < 4; count++)
-		hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+		hdmi->write(hdmi, val, HDMI_FC_INVIDCONF);
 }
 
 static void hdmi_enable_overflow_interrupts(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, 0, HDMI_FC_MASK2);
-	hdmi_writeb(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
+	hdmi->write(hdmi, 0, HDMI_FC_MASK2);
+	hdmi->write(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
 }
 
 static void hdmi_disable_overflow_interrupts(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
+	hdmi->write(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
 		    HDMI_IH_MUTE_FC_STAT2);
 }
 
@@ -1223,21 +1122,21 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 	}
 
 	if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
-		(hdmi->vic == 21) || (hdmi->vic == 22) ||
-		(hdmi->vic == 2) || (hdmi->vic == 3) ||
-		(hdmi->vic == 17) || (hdmi->vic == 18))
+	    (hdmi->vic == 21) || (hdmi->vic == 22) ||
+	    (hdmi->vic == 2) || (hdmi->vic == 3) ||
+	    (hdmi->vic == 17) || (hdmi->vic == 18))
 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
 	else
 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
 
 	if ((hdmi->vic == 10) || (hdmi->vic == 11) ||
-		(hdmi->vic == 12) || (hdmi->vic == 13) ||
-		(hdmi->vic == 14) || (hdmi->vic == 15) ||
-		(hdmi->vic == 25) || (hdmi->vic == 26) ||
-		(hdmi->vic == 27) || (hdmi->vic == 28) ||
-		(hdmi->vic == 29) || (hdmi->vic == 30) ||
-		(hdmi->vic == 35) || (hdmi->vic == 36) ||
-		(hdmi->vic == 37) || (hdmi->vic == 38))
+	    (hdmi->vic == 12) || (hdmi->vic == 13) ||
+	    (hdmi->vic == 14) || (hdmi->vic == 15) ||
+	    (hdmi->vic == 25) || (hdmi->vic == 26) ||
+	    (hdmi->vic == 27) || (hdmi->vic == 28) ||
+	    (hdmi->vic == 29) || (hdmi->vic == 30) ||
+	    (hdmi->vic == 35) || (hdmi->vic == 36) ||
+	    (hdmi->vic == 37) || (hdmi->vic == 38))
 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
 	else
 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
@@ -1266,9 +1165,9 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 	imx_hdmi_enable_video_path(hdmi);
 
 	/* not for DVI mode */
-	if (hdmi->hdmi_data.video_mode.mdvi)
+	if (hdmi->hdmi_data.video_mode.mdvi) {
 		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
-	else {
+	} else {
 		dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
 
 		/* HDMI Initialization Step E - Configure audio */
@@ -1294,18 +1193,18 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 /* Wait until we are registered to enable interrupts */
 static int imx_hdmi_fb_registered(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
 		    HDMI_PHY_I2CM_INT_ADDR);
 
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+	hdmi->write(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
 		    HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
 		    HDMI_PHY_I2CM_CTLINT_ADDR);
 
 	/* enable cable hot plug irq */
-	hdmi_writeb(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
+	hdmi->write(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
 
 	return 0;
 }
@@ -1321,45 +1220,45 @@ static void initialize_hdmi_ih_mutes(struct imx_hdmi *hdmi)
 	 *
 	 * Disable top level interrupt bits in HDMI block
 	 */
-	ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) |
+	ih_mute = hdmi->read(hdmi, HDMI_IH_MUTE) |
 		  HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
 		  HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
 
-	hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+	hdmi->write(hdmi, ih_mute, HDMI_IH_MUTE);
 
 	/* by default mask all interrupts */
-	hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
-	hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
-	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
+	hdmi->write(hdmi, 0xff, HDMI_VP_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK0);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK1);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK2);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_MASK0);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_INT);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_SPDIFINT);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_HBR_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_GP_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_A_APIINTMSK);
+	hdmi->write(hdmi, 0xff, HDMI_CEC_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_I2CM_INT);
+	hdmi->write(hdmi, 0xff, HDMI_I2CM_CTLINT);
 
 	/* Disable interrupts in the IH_MUTE_* registers */
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
 
 	/* Enable top level interrupt bits in HDMI block */
 	ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
 		    HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
-	hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+	hdmi->write(hdmi, ih_mute, HDMI_IH_MUTE);
 }
 
 static void imx_hdmi_poweron(struct imx_hdmi *hdmi)
@@ -1378,7 +1277,7 @@ static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector
 	struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
 					     connector);
 
-	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
+	return hdmi->read(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
 		connector_status_connected : connector_status_disconnected;
 }
 
@@ -1454,21 +1353,40 @@ static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder)
 	struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
 
 	imx_hdmi_poweroff(hdmi);
-	imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24);
+
+	if (hdmi->plat_data->encoder_prepare)
+		hdmi->plat_data->encoder_prepare(&hdmi->connector, encoder);
 }
 
 static void imx_hdmi_encoder_commit(struct drm_encoder *encoder)
 {
 	struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
-	int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
 
-	imx_hdmi_set_ipu_di_mux(hdmi, mux);
+	if (hdmi->plat_data->set_crtc_mux)
+		hdmi->plat_data->set_crtc_mux(hdmi->priv, encoder);
 
 	imx_hdmi_poweron(hdmi);
 }
 
+void imx_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static int imx_hdmi_connector_mode_valid(struct drm_connector *connector,
+					 struct drm_display_mode *mode)
+{
+	struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
+					     connector);
+	if (hdmi->plat_data->mode_valid)
+		return hdmi->plat_data->mode_valid(connector, mode);
+
+	return MODE_OK;
+}
+
 static struct drm_encoder_funcs imx_hdmi_encoder_funcs = {
-	.destroy = imx_drm_encoder_destroy,
+	.destroy = drm_encoder_cleanup,
 };
 
 static struct drm_encoder_helper_funcs imx_hdmi_encoder_helper_funcs = {
@@ -1484,11 +1402,12 @@ static struct drm_connector_funcs imx_hdmi_connector_funcs = {
 	.dpms = drm_helper_connector_dpms,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.detect = imx_hdmi_connector_detect,
-	.destroy = imx_drm_connector_destroy,
+	.destroy = imx_hdmi_connector_destroy,
 };
 
 static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = {
 	.get_modes = imx_hdmi_connector_get_modes,
+	.mode_valid = imx_hdmi_connector_mode_valid,
 	.best_encoder = imx_hdmi_connector_best_encoder,
 };
 
@@ -1497,9 +1416,9 @@ static irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id)
 	struct imx_hdmi *hdmi = dev_id;
 	u8 intr_stat;
 
-	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+	intr_stat = hdmi->read(hdmi, HDMI_IH_PHY_STAT0);
 	if (intr_stat)
-		hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+		hdmi->write(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
 	return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE;
 }
@@ -1510,21 +1429,21 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id)
 	u8 intr_stat;
 	u8 phy_int_pol;
 
-	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+	intr_stat = hdmi->read(hdmi, HDMI_IH_PHY_STAT0);
 
-	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
+	phy_int_pol = hdmi->read(hdmi, HDMI_PHY_POL0);
 
 	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
 		if (phy_int_pol & HDMI_PHY_HPD) {
 			dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
+			hdmi->mod(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
 
 			imx_hdmi_poweron(hdmi);
 		} else {
 			dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
+			hdmi->mod(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
 				HDMI_PHY_POL0);
 
 			imx_hdmi_poweroff(hdmi);
@@ -1532,20 +1451,18 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id)
 		drm_helper_hpd_irq_event(hdmi->connector.dev);
 	}
 
-	hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
 	return IRQ_HANDLED;
 }
 
 static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 {
-	int ret;
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
 
-	ret = imx_drm_encoder_parse_of(drm, &hdmi->encoder,
-				       hdmi->dev->of_node);
-	if (ret)
-		return ret;
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 
 	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
 
@@ -1554,7 +1471,7 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 			 DRM_MODE_ENCODER_TMDS);
 
 	drm_connector_helper_add(&hdmi->connector,
-			&imx_hdmi_connector_helper_funcs);
+				 &imx_hdmi_connector_helper_funcs);
 	drm_connector_init(drm, &hdmi->connector, &imx_hdmi_connector_funcs,
 			   DRM_MODE_CONNECTOR_HDMIA);
 
@@ -1565,55 +1482,47 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 	return 0;
 }
 
-static struct platform_device_id imx_hdmi_devtype[] = {
-	{
-		.name = "imx6q-hdmi",
-		.driver_data = IMX6Q_HDMI,
-	}, {
-		.name = "imx6dl-hdmi",
-		.driver_data = IMX6DL_HDMI,
-	}, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(platform, imx_hdmi_devtype);
-
-static const struct of_device_id imx_hdmi_dt_ids[] = {
-{ .compatible = "fsl,imx6q-hdmi", .data = &imx_hdmi_devtype[IMX6Q_HDMI], },
-{ .compatible = "fsl,imx6dl-hdmi", .data = &imx_hdmi_devtype[IMX6DL_HDMI], },
-{ /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids);
-
 static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	const struct of_device_id *of_id =
-				of_match_device(imx_hdmi_dt_ids, dev);
+	struct imx_hdmi *hdmi = platform_get_drvdata(pdev);
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
 	struct device_node *ddc_node;
-	struct imx_hdmi *hdmi;
 	struct resource *iores;
 	int ret, irq;
-
-	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
-	if (!hdmi)
-		return -ENOMEM;
-
-	hdmi->dev = dev;
-	hdmi->sample_rate = 48000;
-	hdmi->ratio = 100;
-
-	if (of_id) {
-		const struct platform_device_id *device_id = of_id->data;
-
-		hdmi->dev_type = device_id->driver_data;
+	u32 val;
+
+	if (!of_property_read_u32(np, "reg-io-width", &val)) {
+		switch (val) {
+		case 4:
+			hdmi->write = hdmi_writel;
+			hdmi->read = hdmi_readl;
+			hdmi->mod = hdmi_modl;
+			hdmi->mask_write = hdmi_mask_writel;
+			break;
+		default:
+			hdmi->write = hdmi_writeb;
+			hdmi->read = hdmi_readb;
+			hdmi->mod = hdmi_modb;
+			hdmi->mask_write = hdmi_mask_writeb;
+			break;
+		}
+	} else {
+		hdmi->write = hdmi_writeb;
+		hdmi->read = hdmi_readb;
+		hdmi->mod = hdmi_modb;
+		hdmi->mask_write = hdmi_mask_writeb;
 	}
 
 	ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
 	if (ddc_node) {
 		hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
-		if (!hdmi->ddc)
+		if (!hdmi->ddc) {
 			dev_dbg(hdmi->dev, "failed to read ddc node\n");
+			of_node_put(ddc_node);
+			return -EPROBE_DEFER;
+		}
 
 		of_node_put(ddc_node);
 	} else {
@@ -1635,47 +1544,15 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	if (IS_ERR(hdmi->regs))
 		return PTR_ERR(hdmi->regs);
 
-	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
-	if (IS_ERR(hdmi->regmap))
-		return PTR_ERR(hdmi->regmap);
-
-	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
-	if (IS_ERR(hdmi->isfr_clk)) {
-		ret = PTR_ERR(hdmi->isfr_clk);
-		dev_err(hdmi->dev,
-			"Unable to get HDMI isfr clk: %d\n", ret);
-		return ret;
-	}
-
-	ret = clk_prepare_enable(hdmi->isfr_clk);
-	if (ret) {
-		dev_err(hdmi->dev,
-			"Cannot enable HDMI isfr clock: %d\n", ret);
-		return ret;
-	}
-
-	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
-	if (IS_ERR(hdmi->iahb_clk)) {
-		ret = PTR_ERR(hdmi->iahb_clk);
-		dev_err(hdmi->dev,
-			"Unable to get HDMI iahb clk: %d\n", ret);
-		goto err_isfr;
-	}
-
-	ret = clk_prepare_enable(hdmi->iahb_clk);
-	if (ret) {
-		dev_err(hdmi->dev,
-			"Cannot enable HDMI iahb clock: %d\n", ret);
-		goto err_isfr;
-	}
-
+	if (hdmi->plat_data->setup)
+		hdmi->priv = hdmi->plat_data->setup(pdev);
 	/* Product and revision IDs */
 	dev_info(dev,
-		"Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
-		hdmi_readb(hdmi, HDMI_DESIGN_ID),
-		hdmi_readb(hdmi, HDMI_REVISION_ID),
-		hdmi_readb(hdmi, HDMI_PRODUCT_ID0),
-		hdmi_readb(hdmi, HDMI_PRODUCT_ID1));
+		 "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
+		 hdmi->read(hdmi, HDMI_DESIGN_ID),
+		 hdmi->read(hdmi, HDMI_REVISION_ID),
+		 hdmi->read(hdmi, HDMI_PRODUCT_ID0),
+		 hdmi->read(hdmi, HDMI_PRODUCT_ID1));
 
 	initialize_hdmi_ih_mutes(hdmi);
 
@@ -1689,32 +1566,25 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	 * Configure registers related to HDMI interrupt
 	 * generation before registering IRQ.
 	 */
-	hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
+	hdmi->write(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
 
 	ret = imx_hdmi_fb_registered(hdmi);
 	if (ret)
-		goto err_iahb;
+		return ret;
 
 	ret = imx_hdmi_register(drm, hdmi);
 	if (ret)
-		goto err_iahb;
+		return ret;
 
 	/* Unmute interrupts */
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
-
-err_iahb:
-	clk_disable_unprepare(hdmi->iahb_clk);
-err_isfr:
-	clk_disable_unprepare(hdmi->isfr_clk);
-
-	return ret;
 }
 
 static void imx_hdmi_unbind(struct device *dev, struct device *master,
@@ -1723,13 +1593,12 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master,
 	struct imx_hdmi *hdmi = dev_get_drvdata(dev);
 
 	/* Disable all interrupts */
-	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
 	hdmi->encoder.funcs->destroy(&hdmi->encoder);
-
-	clk_disable_unprepare(hdmi->iahb_clk);
-	clk_disable_unprepare(hdmi->isfr_clk);
+	if (hdmi->plat_data->exit)
+		hdmi->plat_data->exit(hdmi->priv);
 	i2c_put_adapter(hdmi->ddc);
 }
 
@@ -1749,17 +1618,32 @@ static int imx_hdmi_platform_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static struct platform_driver imx_hdmi_driver = {
-	.probe  = imx_hdmi_platform_probe,
-	.remove = imx_hdmi_platform_remove,
-	.driver = {
-		.name = "imx-hdmi",
-		.owner = THIS_MODULE,
-		.of_match_table = imx_hdmi_dt_ids,
-	},
-};
+int imx_hdmi_pltfm_register(struct platform_device *pdev,
+			    const struct imx_hdmi_plat_data *plat_data)
+{
+	struct imx_hdmi *hdmi;
 
-module_platform_driver(imx_hdmi_driver);
+	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->plat_data = plat_data;
+	hdmi->dev = &pdev->dev;
+	hdmi->dev_type = plat_data->dev_type;
+	hdmi->sample_rate = 48000;
+	hdmi->ratio = 100;
+
+	platform_set_drvdata(pdev, hdmi);
+
+	return imx_hdmi_platform_probe(pdev);
+}
+EXPORT_SYMBOL_GPL(imx_hdmi_pltfm_register);
+
+int imx_hdmi_pltfm_unregister(struct platform_device *pdev)
+{
+	return imx_hdmi_platform_remove(pdev);
+}
+EXPORT_SYMBOL_GPL(imx_hdmi_pltfm_unregister);
 
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 MODULE_DESCRIPTION("i.MX6 HDMI transmitter driver");
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
new file mode 100644
index 0000000..e7e8285
--- /dev/null
+++ b/include/drm/bridge/dw_hdmi.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __DW_HDMI_H__
+#define __DW_HDMI_H__
+
+#include <drm/drmP.h>
+
+#define HDMI_EDID_LEN           512
+
+enum {
+	RES_8,
+	RES_10,
+	RES_12,
+	RES_MAX,
+};
+
+enum imx_hdmi_devtype {
+	IMX6Q_HDMI,
+	IMX6DL_HDMI,
+};
+
+struct mpll_config {
+	unsigned long mpixelclock;
+	struct {
+		u16 cpce;
+		u16 gmp;
+	} res[RES_MAX];
+};
+
+struct curr_ctrl {
+	unsigned long mpixelclock;
+	u16 curr[RES_MAX];
+};
+
+struct hdmi_vmode {
+	bool mdvi;
+	bool mhsyncpolarity;
+	bool mvsyncpolarity;
+	bool minterlaced;
+	bool mdataenablepolarity;
+
+	unsigned int mpixelclock;
+	unsigned int mpixelrepetitioninput;
+	unsigned int mpixelrepetitionoutput;
+};
+
+struct hdmi_data_info {
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int enc_color_depth;
+	unsigned int colorimetry;
+	unsigned int pix_repet_factor;
+	unsigned int hdcp_enable;
+	struct hdmi_vmode video_mode;
+};
+
+
+struct imx_hdmi {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	enum imx_hdmi_devtype dev_type;
+	struct device *dev;
+
+	struct hdmi_data_info hdmi_data;
+	const struct imx_hdmi_plat_data *plat_data;
+	void *priv;
+
+	int vic;
+
+	u8 edid[HDMI_EDID_LEN];
+	bool cable_plugin;
+
+	bool phy_enabled;
+	struct drm_display_mode previous_mode;
+
+	struct i2c_adapter *ddc;
+	void __iomem *regs;
+
+	unsigned int sample_rate;
+	int ratio;
+
+	void (*write)(struct imx_hdmi *hdmi, u32 val, int offset);
+	u32 (*read)(struct imx_hdmi *hdmi, int offset);
+	void (*mod)(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg);
+	void (*mask_write)(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+			      u32 shift, u32 mask);
+};
+
+struct imx_hdmi_plat_data {
+	void * (*setup)(struct platform_device *pdev);
+	void (*exit)(void *priv);
+	void (*set_crtc_mux)(void *priv, struct drm_encoder *encoder);
+	void (*encoder_prepare)(struct drm_connector *connector,
+				struct drm_encoder *encoder);
+	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+					   struct drm_display_mode *mode);
+	const struct mpll_config *mpll_cfg;
+	const struct curr_ctrl *cur_ctr;
+	enum imx_hdmi_devtype dev_type;
+
+};
+
+int imx_hdmi_pltfm_register(struct platform_device *pdev,
+			    const struct imx_hdmi_plat_data *plat_data);
+int imx_hdmi_pltfm_unregister(struct platform_device *pdev);
+#endif /* __IMX_HDMI_H__ */
-- 
1.9.1



^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi
@ 2014-11-05 12:55   ` Andy Yan
  0 siblings, 0 replies; 20+ messages in thread
From: Andy Yan @ 2014-11-05 12:55 UTC (permalink / raw)
  To: airlied, heiko, fabio.estevam, rmk+kernel
  Cc: devel, devicetree, Zubair.Kakakhel, Arnd Bergmann, Josh Boyer,
	Greg Kroah-Hartman, ykk, linux-kernel, dri-devel, Inki Dae,
	linux-rockchip, Rob Herring, Sean Paul, djkurtz, Philipp Zabel,
	Dave Airlie, Grant Likely, Andy yan, Shawn Guo, Lucas Stach

imx6 and rockchip rk3288 and JZ4780 (Ingenic Xburst/MIPS)
use the interface compatible Designware HDMI IP, but they
also have some lightly difference, such as phy pll configuration,
register width(imx hdmi register is one byte, but rk3288 is 4
bytes width), 4K support(imx6 doesn't support 4k, but rk3288 does),
clk useage,and the crtc mux configuration is also platform specific.

To reuse the imx hdmi driver, split the platform specific code out
to dw_hdmi-imx.c.

Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
---
 drivers/staging/imx-drm/Makefile      |   2 +-
 drivers/staging/imx-drm/dw_hdmi-imx.c | 214 ++++++++++
 drivers/staging/imx-drm/imx-hdmi.c    | 726 ++++++++++++++--------------------
 include/drm/bridge/dw_hdmi.h          | 114 ++++++
 4 files changed, 634 insertions(+), 422 deletions(-)
 create mode 100644 drivers/staging/imx-drm/dw_hdmi-imx.c
 create mode 100644 include/drm/bridge/dw_hdmi.h

diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
index 582c438..809027d 100644
--- a/drivers/staging/imx-drm/Makefile
+++ b/drivers/staging/imx-drm/Makefile
@@ -9,4 +9,4 @@ obj-$(CONFIG_DRM_IMX_LDB) += imx-ldb.o
 
 imx-ipuv3-crtc-objs  := ipuv3-crtc.o ipuv3-plane.o
 obj-$(CONFIG_DRM_IMX_IPUV3)	+= imx-ipuv3-crtc.o
-obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o
+obj-$(CONFIG_DRM_IMX_HDMI) += imx-hdmi.o dw_hdmi-imx.o
diff --git a/drivers/staging/imx-drm/dw_hdmi-imx.c b/drivers/staging/imx-drm/dw_hdmi-imx.c
new file mode 100644
index 0000000..71ac859
--- /dev/null
+++ b/drivers/staging/imx-drm/dw_hdmi-imx.c
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
+#include <drm/bridge/dw_hdmi.h>
+#include <video/imx-ipu-v3.h>
+#include <linux/regmap.h>
+#include <linux/clk.h>
+
+#include "imx-drm.h"
+
+struct imx_hdmi_priv {
+	struct device *dev;
+	struct clk *isfr_clk;
+	struct clk *iahb_clk;
+	struct regmap *regmap;
+};
+
+static const struct mpll_config imx_mpll_cfg[] = {
+	{
+		45250000, {
+			{ 0x01e0, 0x0000 },
+			{ 0x21e1, 0x0000 },
+			{ 0x41e2, 0x0000 }
+		},
+	}, {
+		92500000, {
+			{ 0x0140, 0x0005 },
+			{ 0x2141, 0x0005 },
+			{ 0x4142, 0x0005 },
+	},
+	}, {
+		148500000, {
+			{ 0x00a0, 0x000a },
+			{ 0x20a1, 0x000a },
+			{ 0x40a2, 0x000a },
+		},
+	}, {
+		~0UL, {
+			{ 0x00a0, 0x000a },
+			{ 0x2001, 0x000f },
+			{ 0x4002, 0x000f },
+		},
+	}
+};
+
+static const struct curr_ctrl imx_cur_ctr[] = {
+	/*      pixelclk     bpp8    bpp10   bpp12 */
+	{
+		54000000, { 0x091c, 0x091c, 0x06dc },
+	}, {
+		58400000, { 0x091c, 0x06dc, 0x06dc },
+	}, {
+		72000000, { 0x06dc, 0x06dc, 0x091c },
+	}, {
+		74250000, { 0x06dc, 0x0b5c, 0x091c },
+	}, {
+		118800000, { 0x091c, 0x091c, 0x06dc },
+	}, {
+		216000000, { 0x06dc, 0x0b5c, 0x091c },
+	}
+};
+
+static int imx_hdmi_parse_dt(struct imx_hdmi_priv *hdmi)
+{
+	struct device_node *np = hdmi->dev->of_node;
+
+	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
+	if (IS_ERR(hdmi->regmap)) {
+		dev_err(hdmi->dev, "Unable to get gpr\n");
+		return PTR_ERR(hdmi->regmap);
+	}
+
+	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
+	if (IS_ERR(hdmi->isfr_clk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI isfr clk\n");
+		return PTR_ERR(hdmi->isfr_clk);
+	}
+
+	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
+	if (IS_ERR(hdmi->iahb_clk)) {
+		dev_err(hdmi->dev, "Unable to get HDMI iahb clk\n");
+		return PTR_ERR(hdmi->iahb_clk);
+	}
+
+	return 0;
+}
+
+static void *imx_hdmi_imx_setup(struct platform_device *pdev)
+{
+	struct imx_hdmi_priv *hdmi;
+	int ret;
+
+	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return ERR_PTR(-ENOMEM);
+	hdmi->dev = &pdev->dev;
+
+	ret = imx_hdmi_parse_dt(hdmi);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	ret = clk_prepare_enable(hdmi->isfr_clk);
+	if (ret) {
+		dev_err(hdmi->dev,
+			"Cannot enable HDMI isfr clock: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	ret = clk_prepare_enable(hdmi->iahb_clk);
+	if (ret) {
+		dev_err(hdmi->dev,
+			"Cannot enable HDMI iahb clock: %d\n", ret);
+		return ERR_PTR(ret);
+	}
+
+	return hdmi;
+}
+
+static void imx_hdmi_imx_exit(void *priv)
+{
+	struct imx_hdmi_priv *hdmi = (struct imx_hdmi_priv *)priv;
+
+	clk_disable_unprepare(hdmi->isfr_clk);
+
+	clk_disable_unprepare(hdmi->iahb_clk);
+}
+
+static void imx_hdmi_imx_set_crtc_mux(void *priv, struct drm_encoder *encoder)
+{
+	struct imx_hdmi_priv *hdmi = (struct imx_hdmi_priv *)priv;
+	int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
+
+	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
+			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
+			   mux << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
+}
+
+static void imx_hdmi_imx_encoder_prepare(struct drm_connector *connector,
+					struct drm_encoder *encoder)
+{
+	imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24);
+}
+
+static struct imx_hdmi_plat_data imx6q_hdmi_drv_data = {
+	.setup			= imx_hdmi_imx_setup,
+	.exit			= imx_hdmi_imx_exit,
+	.set_crtc_mux		= imx_hdmi_imx_set_crtc_mux,
+	.encoder_prepare	= imx_hdmi_imx_encoder_prepare,
+	.mpll_cfg		= imx_mpll_cfg,
+	.cur_ctr		= imx_cur_ctr,
+	.dev_type		= IMX6Q_HDMI,
+};
+
+static struct imx_hdmi_plat_data imx6dl_hdmi_drv_data = {
+	.setup			= imx_hdmi_imx_setup,
+	.exit			= imx_hdmi_imx_exit,
+	.set_crtc_mux		= imx_hdmi_imx_set_crtc_mux,
+	.encoder_prepare	= imx_hdmi_imx_encoder_prepare,
+	.mpll_cfg		= imx_mpll_cfg,
+	.cur_ctr		= imx_cur_ctr,
+	.dev_type		= IMX6DL_HDMI,
+};
+
+static const struct of_device_id imx_hdmi_imx_ids[] = {
+	{ .compatible = "fsl,imx6q-hdmi",
+	  .data = &imx6q_hdmi_drv_data
+	}, {
+	  .compatible = "fsl,imx6dl-hdmi",
+	  .data = &imx6dl_hdmi_drv_data
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, imx_hdmi_imx_dt_ids);
+
+static int imx_hdmi_imx_probe(struct platform_device *pdev)
+{
+	const struct imx_hdmi_plat_data *plat_data;
+	const struct of_device_id *match;
+
+	if (!pdev->dev.of_node)
+		return -ENODEV;
+
+	match = of_match_node(imx_hdmi_imx_ids, pdev->dev.of_node);
+	plat_data = match->data;
+
+	return imx_hdmi_pltfm_register(pdev, plat_data);
+}
+
+static int imx_hdmi_imx_remove(struct platform_device *pdev)
+{
+	return imx_hdmi_pltfm_unregister(pdev);
+}
+
+static struct platform_driver imx_hdmi_imx_pltfm_driver = {
+	.probe  = imx_hdmi_imx_probe,
+	.remove = imx_hdmi_imx_remove,
+	.driver = {
+		.name = "dwhdmi-imx",
+		.owner = THIS_MODULE,
+		.of_match_table = imx_hdmi_imx_ids,
+	},
+};
+
+module_platform_driver(imx_hdmi_imx_pltfm_driver);
+
+MODULE_AUTHOR("Andy Yan <andy.yan@rock-chips.com>");
+MODULE_DESCRIPTION("IMX6 Specific DW-HDMI Driver Extension");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:dwhdmi-imx");
diff --git a/drivers/staging/imx-drm/imx-hdmi.c b/drivers/staging/imx-drm/imx-hdmi.c
index aaec6b2..138706c 100644
--- a/drivers/staging/imx-drm/imx-hdmi.c
+++ b/drivers/staging/imx-drm/imx-hdmi.c
@@ -16,23 +16,16 @@
 #include <linux/irq.h>
 #include <linux/delay.h>
 #include <linux/err.h>
-#include <linux/clk.h>
 #include <linux/hdmi.h>
-#include <linux/regmap.h>
-#include <linux/mfd/syscon.h>
-#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 #include <linux/of_device.h>
-
+#include <drm/drm_of.h>
 #include <drm/drmP.h>
 #include <drm/drm_crtc_helper.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_encoder_slave.h>
-#include <video/imx-ipu-v3.h>
+#include <drm/bridge/dw_hdmi.h>
 
 #include "imx-hdmi.h"
-#include "imx-drm.h"
-
-#define HDMI_EDID_LEN		512
 
 #define RGB			0
 #define YCBCR444		1
@@ -54,11 +47,6 @@ enum hdmi_datamap {
 	YCbCr422_12B = 0x12,
 };
 
-enum imx_hdmi_devtype {
-	IMX6Q_HDMI,
-	IMX6DL_HDMI,
-};
-
 static const u16 csc_coeff_default[3][4] = {
 	{ 0x2000, 0x0000, 0x0000, 0x0000 },
 	{ 0x0000, 0x2000, 0x0000, 0x0000 },
@@ -89,72 +77,44 @@ static const u16 csc_coeff_rgb_in_eitu709[3][4] = {
 	{ 0x6756, 0x78ab, 0x2000, 0x0200 }
 };
 
-struct hdmi_vmode {
-	bool mdvi;
-	bool mhsyncpolarity;
-	bool mvsyncpolarity;
-	bool minterlaced;
-	bool mdataenablepolarity;
-
-	unsigned int mpixelclock;
-	unsigned int mpixelrepetitioninput;
-	unsigned int mpixelrepetitionoutput;
-};
-
-struct hdmi_data_info {
-	unsigned int enc_in_format;
-	unsigned int enc_out_format;
-	unsigned int enc_color_depth;
-	unsigned int colorimetry;
-	unsigned int pix_repet_factor;
-	unsigned int hdcp_enable;
-	struct hdmi_vmode video_mode;
-};
-
-struct imx_hdmi {
-	struct drm_connector connector;
-	struct drm_encoder encoder;
-
-	enum imx_hdmi_devtype dev_type;
-	struct device *dev;
-	struct clk *isfr_clk;
-	struct clk *iahb_clk;
-
-	struct hdmi_data_info hdmi_data;
-	int vic;
-
-	u8 edid[HDMI_EDID_LEN];
-	bool cable_plugin;
+/*On rockchip platform, no-word access to the hdmi
+ * register will causes an imprecise external abort
+ */
+static inline void hdmi_writel(struct imx_hdmi *hdmi, u32 val, int offset)
+{
+	writel(val, hdmi->regs + (offset << 2));
+}
 
-	bool phy_enabled;
-	struct drm_display_mode previous_mode;
+static inline u32 hdmi_readl(struct imx_hdmi *hdmi, int offset)
+{
+	return readl(hdmi->regs + (offset << 2));
+}
 
-	struct regmap *regmap;
-	struct i2c_adapter *ddc;
-	void __iomem *regs;
+static void hdmi_modl(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
+{
+	u32 val = hdmi_readl(hdmi, reg) & ~mask;
 
-	unsigned int sample_rate;
-	int ratio;
-};
+	val |= data & mask;
+	hdmi_writel(hdmi, val, reg);
+}
 
-static void imx_hdmi_set_ipu_di_mux(struct imx_hdmi *hdmi, int ipu_di)
+static void hdmi_mask_writel(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+			     u32 shift, u32 mask)
 {
-	regmap_update_bits(hdmi->regmap, IOMUXC_GPR3,
-			   IMX6Q_GPR3_HDMI_MUX_CTL_MASK,
-			   ipu_di << IMX6Q_GPR3_HDMI_MUX_CTL_SHIFT);
+	hdmi_modl(hdmi, data << shift, mask, reg);
 }
 
-static inline void hdmi_writeb(struct imx_hdmi *hdmi, u8 val, int offset)
+static inline void hdmi_writeb(struct imx_hdmi *hdmi, u32 val, int offset)
 {
 	writeb(val, hdmi->regs + offset);
 }
 
-static inline u8 hdmi_readb(struct imx_hdmi *hdmi, int offset)
+static inline u32 hdmi_readb(struct imx_hdmi *hdmi, int offset)
 {
 	return readb(hdmi->regs + offset);
 }
 
-static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
+static void hdmi_modb(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg)
 {
 	u8 val = hdmi_readb(hdmi, reg) & ~mask;
 
@@ -162,8 +122,8 @@ static void hdmi_modb(struct imx_hdmi *hdmi, u8 data, u8 mask, unsigned reg)
 	hdmi_writeb(hdmi, val, reg);
 }
 
-static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg,
-		      u8 shift, u8 mask)
+static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+		      u32 shift, u32 mask)
 {
 	hdmi_modb(hdmi, data << shift, mask, reg);
 }
@@ -171,22 +131,22 @@ static void hdmi_mask_writeb(struct imx_hdmi *hdmi, u8 data, unsigned int reg,
 static void hdmi_set_clock_regenerator_n(struct imx_hdmi *hdmi,
 					 unsigned int value)
 {
-	hdmi_writeb(hdmi, value & 0xff, HDMI_AUD_N1);
-	hdmi_writeb(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
-	hdmi_writeb(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
+	hdmi->write(hdmi, value & 0xff, HDMI_AUD_N1);
+	hdmi->write(hdmi, (value >> 8) & 0xff, HDMI_AUD_N2);
+	hdmi->write(hdmi, (value >> 16) & 0x0f, HDMI_AUD_N3);
 
 	/* nshift factor = 0 */
-	hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
+	hdmi->mod(hdmi, 0, HDMI_AUD_CTS3_N_SHIFT_MASK, HDMI_AUD_CTS3);
 }
 
 static void hdmi_regenerate_cts(struct imx_hdmi *hdmi, unsigned int cts)
 {
 	/* Must be set/cleared first */
-	hdmi_modb(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
+	hdmi->mod(hdmi, 0, HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
 
-	hdmi_writeb(hdmi, cts & 0xff, HDMI_AUD_CTS1);
-	hdmi_writeb(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
-	hdmi_writeb(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
+	hdmi->write(hdmi, cts & 0xff, HDMI_AUD_CTS1);
+	hdmi->write(hdmi, (cts >> 8) & 0xff, HDMI_AUD_CTS2);
+	hdmi->write(hdmi, ((cts >> 16) & HDMI_AUD_CTS3_AUDCTS19_16_MASK) |
 		    HDMI_AUD_CTS3_CTS_MANUAL, HDMI_AUD_CTS3);
 }
 
@@ -323,6 +283,7 @@ static unsigned int hdmi_compute_cts(unsigned int freq, unsigned long pixel_clk,
 	}
 	if (ratio == 100)
 		return cts;
+
 	return (cts * ratio) / 100;
 }
 
@@ -338,7 +299,7 @@ static void hdmi_set_clk_regenerator(struct imx_hdmi *hdmi,
 
 	if (!clk_cts) {
 		dev_dbg(hdmi->dev, "%s: pixel clock not supported: %lu\n",
-			 __func__, pixel_clk);
+			__func__, pixel_clk);
 		return;
 	}
 
@@ -408,19 +369,19 @@ static void hdmi_video_sample(struct imx_hdmi *hdmi)
 	val = HDMI_TX_INVID0_INTERNAL_DE_GENERATOR_DISABLE |
 		((color_format << HDMI_TX_INVID0_VIDEO_MAPPING_OFFSET) &
 		HDMI_TX_INVID0_VIDEO_MAPPING_MASK);
-	hdmi_writeb(hdmi, val, HDMI_TX_INVID0);
+	hdmi->write(hdmi, val, HDMI_TX_INVID0);
 
 	/* Enable TX stuffing: When DE is inactive, fix the output data to 0 */
 	val = HDMI_TX_INSTUFFING_BDBDATA_STUFFING_ENABLE |
 		HDMI_TX_INSTUFFING_RCRDATA_STUFFING_ENABLE |
 		HDMI_TX_INSTUFFING_GYDATA_STUFFING_ENABLE;
-	hdmi_writeb(hdmi, val, HDMI_TX_INSTUFFING);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_GYDATA1);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_RCRDATA1);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA0);
-	hdmi_writeb(hdmi, 0x0, HDMI_TX_BCBDATA1);
+	hdmi->write(hdmi, val, HDMI_TX_INSTUFFING);
+	hdmi->write(hdmi, 0x0, HDMI_TX_GYDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_GYDATA1);
+	hdmi->write(hdmi, 0x0, HDMI_TX_RCRDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_RCRDATA1);
+	hdmi->write(hdmi, 0x0, HDMI_TX_BCBDATA0);
+	hdmi->write(hdmi, 0x0, HDMI_TX_BCBDATA1);
 }
 
 static int is_color_space_conversion(struct imx_hdmi *hdmi)
@@ -477,17 +438,17 @@ static void imx_hdmi_update_csc_coeffs(struct imx_hdmi *hdmi)
 		u16 coeff_b = (*csc_coeff)[1][i];
 		u16 coeff_c = (*csc_coeff)[2][i];
 
-		hdmi_writeb(hdmi, coeff_a & 0xff,
+		hdmi->write(hdmi, coeff_a & 0xff,
 			HDMI_CSC_COEF_A1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
-		hdmi_writeb(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
-		hdmi_writeb(hdmi, coeff_c & 0xff,
+		hdmi->write(hdmi, coeff_a >> 8, HDMI_CSC_COEF_A1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_b & 0xff, HDMI_CSC_COEF_B1_LSB + i * 2);
+		hdmi->write(hdmi, coeff_b >> 8, HDMI_CSC_COEF_B1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_c & 0xff,
 			HDMI_CSC_COEF_C1_LSB + i * 2);
-		hdmi_writeb(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
+		hdmi->write(hdmi, coeff_c >> 8, HDMI_CSC_COEF_C1_MSB + i * 2);
 	}
 
-	hdmi_modb(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
+	hdmi->mod(hdmi, csc_scale, HDMI_CSC_SCALE_CSCSCALE_MASK,
 		  HDMI_CSC_SCALE);
 }
 
@@ -515,8 +476,8 @@ static void hdmi_video_csc(struct imx_hdmi *hdmi)
 		return;
 
 	/* Configure the CSC registers */
-	hdmi_writeb(hdmi, interpolation | decimation, HDMI_CSC_CFG);
-	hdmi_modb(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
+	hdmi->write(hdmi, interpolation | decimation, HDMI_CSC_CFG);
+	hdmi->mod(hdmi, color_depth, HDMI_CSC_SCALE_CSC_COLORDE_PTH_MASK,
 		  HDMI_CSC_SCALE);
 
 	imx_hdmi_update_csc_coeffs(hdmi);
@@ -535,21 +496,22 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 	struct hdmi_data_info *hdmi_data = &hdmi->hdmi_data;
 	u8 val, vp_conf;
 
-	if (hdmi_data->enc_out_format == RGB
-		|| hdmi_data->enc_out_format == YCBCR444) {
-		if (!hdmi_data->enc_color_depth)
+	if (hdmi_data->enc_out_format == RGB ||
+	    hdmi_data->enc_out_format == YCBCR444) {
+		if (!hdmi_data->enc_color_depth) {
 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
-		else if (hdmi_data->enc_color_depth == 8) {
+		} else if (hdmi_data->enc_color_depth == 8) {
 			color_depth = 4;
 			output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_BYPASS;
-		} else if (hdmi_data->enc_color_depth == 10)
+		} else if (hdmi_data->enc_color_depth == 10) {
 			color_depth = 5;
-		else if (hdmi_data->enc_color_depth == 12)
+		} else if (hdmi_data->enc_color_depth == 12) {
 			color_depth = 6;
-		else if (hdmi_data->enc_color_depth == 16)
+		} else if (hdmi_data->enc_color_depth == 16) {
 			color_depth = 7;
-		else
+		} else {
 			return;
+		}
 	} else if (hdmi_data->enc_out_format == YCBCR422_8BITS) {
 		if (!hdmi_data->enc_color_depth ||
 		    hdmi_data->enc_color_depth == 8)
@@ -561,8 +523,9 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		else
 			return;
 		output_select = HDMI_VP_CONF_OUTPUT_SELECTOR_YCC422;
-	} else
+	} else {
 		return;
+	}
 
 	/* set the packetizer registers */
 	val = ((color_depth << HDMI_VP_PR_CD_COLOR_DEPTH_OFFSET) &
@@ -570,9 +533,9 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		((hdmi_data->pix_repet_factor <<
 		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_OFFSET) &
 		HDMI_VP_PR_CD_DESIRED_PR_FACTOR_MASK);
-	hdmi_writeb(hdmi, val, HDMI_VP_PR_CD);
+	hdmi->write(hdmi, val, HDMI_VP_PR_CD);
 
-	hdmi_modb(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
+	hdmi->mod(hdmi, HDMI_VP_STUFF_PR_STUFFING_STUFFING_MODE,
 		  HDMI_VP_STUFF_PR_STUFFING_MASK, HDMI_VP_STUFF);
 
 	/* Data from pixel repeater block */
@@ -584,14 +547,14 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 			  HDMI_VP_CONF_BYPASS_SELECT_VID_PACKETIZER;
 	}
 
-	hdmi_modb(hdmi, vp_conf,
+	hdmi->mod(hdmi, vp_conf,
 		  HDMI_VP_CONF_PR_EN_MASK |
 		  HDMI_VP_CONF_BYPASS_SELECT_MASK, HDMI_VP_CONF);
 
-	hdmi_modb(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
+	hdmi->mod(hdmi, 1 << HDMI_VP_STUFF_IDEFAULT_PHASE_OFFSET,
 		  HDMI_VP_STUFF_IDEFAULT_PHASE_MASK, HDMI_VP_STUFF);
 
-	hdmi_writeb(hdmi, remap_size, HDMI_VP_REMAP);
+	hdmi->write(hdmi, remap_size, HDMI_VP_REMAP);
 
 	if (output_select == HDMI_VP_CONF_OUTPUT_SELECTOR_PP) {
 		vp_conf = HDMI_VP_CONF_BYPASS_EN_DISABLE |
@@ -609,55 +572,55 @@ static void hdmi_video_packetize(struct imx_hdmi *hdmi)
 		return;
 	}
 
-	hdmi_modb(hdmi, vp_conf,
+	hdmi->mod(hdmi, vp_conf,
 		  HDMI_VP_CONF_BYPASS_EN_MASK | HDMI_VP_CONF_PP_EN_ENMASK |
 		  HDMI_VP_CONF_YCC422_EN_MASK, HDMI_VP_CONF);
 
-	hdmi_modb(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
+	hdmi->mod(hdmi, HDMI_VP_STUFF_PP_STUFFING_STUFFING_MODE |
 			HDMI_VP_STUFF_YCC422_STUFFING_STUFFING_MODE,
 		  HDMI_VP_STUFF_PP_STUFFING_MASK |
 		  HDMI_VP_STUFF_YCC422_STUFFING_MASK, HDMI_VP_STUFF);
 
-	hdmi_modb(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
+	hdmi->mod(hdmi, output_select, HDMI_VP_CONF_OUTPUT_SELECTOR_MASK,
 		  HDMI_VP_CONF);
 }
 
 static inline void hdmi_phy_test_clear(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTCLR_OFFSET,
 		  HDMI_PHY_TST0_TSTCLR_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_enable(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTEN_OFFSET,
 		  HDMI_PHY_TST0_TSTEN_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_clock(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_modb(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
+	hdmi->mod(hdmi, bit << HDMI_PHY_TST0_TSTCLK_OFFSET,
 		  HDMI_PHY_TST0_TSTCLK_MASK, HDMI_PHY_TST0);
 }
 
 static inline void hdmi_phy_test_din(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_writeb(hdmi, bit, HDMI_PHY_TST1);
+	hdmi->write(hdmi, bit, HDMI_PHY_TST1);
 }
 
 static inline void hdmi_phy_test_dout(struct imx_hdmi *hdmi,
 						unsigned char bit)
 {
-	hdmi_writeb(hdmi, bit, HDMI_PHY_TST2);
+	hdmi->write(hdmi, bit, HDMI_PHY_TST2);
 }
 
 static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec)
 {
-	while ((hdmi_readb(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
+	while ((hdmi->read(hdmi, HDMI_IH_I2CMPHY_STAT0) & 0x3) == 0) {
 		if (msec-- == 0)
 			return false;
 		udelay(1000);
@@ -668,13 +631,13 @@ static bool hdmi_phy_wait_i2c_done(struct imx_hdmi *hdmi, int msec)
 static void __hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
 			      unsigned char addr)
 {
-	hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
-	hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
-	hdmi_writeb(hdmi, (unsigned char)(data >> 8),
+	hdmi->write(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
+	hdmi->write(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
+	hdmi->write(hdmi, (unsigned char)(data >> 8),
 		HDMI_PHY_I2CM_DATAO_1_ADDR);
-	hdmi_writeb(hdmi, (unsigned char)(data >> 0),
+	hdmi->write(hdmi, (unsigned char)(data >> 0),
 		HDMI_PHY_I2CM_DATAO_0_ADDR);
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_OPERATION_ADDR_WRITE,
 		HDMI_PHY_I2CM_OPERATION_ADDR);
 	hdmi_phy_wait_i2c_done(hdmi, 1000);
 }
@@ -688,116 +651,53 @@ static int hdmi_phy_i2c_write(struct imx_hdmi *hdmi, unsigned short data,
 
 static void imx_hdmi_phy_enable_power(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_PDZ_OFFSET,
 			 HDMI_PHY_CONF0_PDZ_MASK);
 }
 
 static void imx_hdmi_phy_enable_tmds(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_ENTMDS_OFFSET,
 			 HDMI_PHY_CONF0_ENTMDS_MASK);
 }
 
 static void imx_hdmi_phy_gen2_pddq(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_GEN2_PDDQ_OFFSET,
 			 HDMI_PHY_CONF0_GEN2_PDDQ_MASK);
 }
 
 static void imx_hdmi_phy_gen2_txpwron(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_GEN2_TXPWRON_OFFSET,
 			 HDMI_PHY_CONF0_GEN2_TXPWRON_MASK);
 }
 
 static void imx_hdmi_phy_sel_data_en_pol(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_SELDATAENPOL_OFFSET,
 			 HDMI_PHY_CONF0_SELDATAENPOL_MASK);
 }
 
 static void imx_hdmi_phy_sel_interface_control(struct imx_hdmi *hdmi, u8 enable)
 {
-	hdmi_mask_writeb(hdmi, enable, HDMI_PHY_CONF0,
+	hdmi->mask_write(hdmi, enable, HDMI_PHY_CONF0,
 			 HDMI_PHY_CONF0_SELDIPIF_OFFSET,
 			 HDMI_PHY_CONF0_SELDIPIF_MASK);
 }
 
-enum {
-	RES_8,
-	RES_10,
-	RES_12,
-	RES_MAX,
-};
-
-struct mpll_config {
-	unsigned long mpixelclock;
-	struct {
-		u16 cpce;
-		u16 gmp;
-	} res[RES_MAX];
-};
-
-static const struct mpll_config mpll_config[] = {
-	{
-		45250000, {
-			{ 0x01e0, 0x0000 },
-			{ 0x21e1, 0x0000 },
-			{ 0x41e2, 0x0000 }
-		},
-	}, {
-		92500000, {
-			{ 0x0140, 0x0005 },
-			{ 0x2141, 0x0005 },
-			{ 0x4142, 0x0005 },
-		},
-	}, {
-		148500000, {
-			{ 0x00a0, 0x000a },
-			{ 0x20a1, 0x000a },
-			{ 0x40a2, 0x000a },
-		},
-	}, {
-		~0UL, {
-			{ 0x00a0, 0x000a },
-			{ 0x2001, 0x000f },
-			{ 0x4002, 0x000f },
-		},
-	}
-};
-
-struct curr_ctrl {
-	unsigned long mpixelclock;
-	u16 curr[RES_MAX];
-};
-
-static const struct curr_ctrl curr_ctrl[] = {
-	/*	pixelclk     bpp8    bpp10   bpp12 */
-	{
-		 54000000, { 0x091c, 0x091c, 0x06dc },
-	}, {
-		 58400000, { 0x091c, 0x06dc, 0x06dc },
-	}, {
-		 72000000, { 0x06dc, 0x06dc, 0x091c },
-	}, {
-		 74250000, { 0x06dc, 0x0b5c, 0x091c },
-	}, {
-		118800000, { 0x091c, 0x091c, 0x06dc },
-	}, {
-		216000000, { 0x06dc, 0x0b5c, 0x091c },
-	}
-};
-
 static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 			      unsigned char res, int cscon)
 {
 	unsigned res_idx, i;
 	u8 val, msec;
+	const struct mpll_config *mpll_cfg = hdmi->plat_data->mpll_cfg;
+	const struct curr_ctrl   *curr_ctr = hdmi->plat_data->cur_ctr;
 
 	if (prep)
 		return -EINVAL;
@@ -823,7 +723,7 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	else
 		val = HDMI_MC_FLOWCTRL_FEED_THROUGH_OFF_CSC_BYPASS;
 
-	hdmi_writeb(hdmi, val, HDMI_MC_FLOWCTRL);
+	hdmi->write(hdmi, val, HDMI_MC_FLOWCTRL);
 
 	/* gen2 tx power off */
 	imx_hdmi_phy_gen2_txpwron(hdmi, 0);
@@ -832,39 +732,38 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	imx_hdmi_phy_gen2_pddq(hdmi, 1);
 
 	/* PHY reset */
-	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
-	hdmi_writeb(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
+	hdmi->write(hdmi, HDMI_MC_PHYRSTZ_DEASSERT, HDMI_MC_PHYRSTZ);
+	hdmi->write(hdmi, HDMI_MC_PHYRSTZ_ASSERT, HDMI_MC_PHYRSTZ);
 
-	hdmi_writeb(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
+	hdmi->write(hdmi, HDMI_MC_HEACPHY_RST_ASSERT, HDMI_MC_HEACPHY_RST);
 
 	hdmi_phy_test_clear(hdmi, 1);
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_SLAVE_ADDR_PHY_GEN2,
 			HDMI_PHY_I2CM_SLAVE_ADDR);
 	hdmi_phy_test_clear(hdmi, 0);
 
 	/* PLL/MPLL Cfg - always match on final entry */
-	for (i = 0; i < ARRAY_SIZE(mpll_config) - 1; i++)
+	for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++)
 		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    mpll_config[i].mpixelclock)
+		    mpll_cfg[i].mpixelclock)
 			break;
+	hdmi_phy_i2c_write(hdmi, mpll_cfg[i].res[res_idx].cpce, 0x06);
+	hdmi_phy_i2c_write(hdmi, mpll_cfg[i].res[res_idx].gmp, 0x15);
 
-	hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].cpce, 0x06);
-	hdmi_phy_i2c_write(hdmi, mpll_config[i].res[res_idx].gmp, 0x15);
-
-	for (i = 0; i < ARRAY_SIZE(curr_ctrl); i++)
+	for (i = 0; curr_ctr[i].mpixelclock != (~0UL); i++)
 		if (hdmi->hdmi_data.video_mode.mpixelclock <=
-		    curr_ctrl[i].mpixelclock)
+		    curr_ctr[i].mpixelclock)
 			break;
 
-	if (i >= ARRAY_SIZE(curr_ctrl)) {
+	if (curr_ctr[i].mpixelclock == (~0UL)) {
 		dev_err(hdmi->dev,
-				"Pixel clock %d - unsupported by HDMI\n",
-				hdmi->hdmi_data.video_mode.mpixelclock);
+			"Pixel clock %d - unsupported by HDMI\n",
+			hdmi->hdmi_data.video_mode.mpixelclock);
 		return -EINVAL;
 	}
 
 	/* CURRCTRL */
-	hdmi_phy_i2c_write(hdmi, curr_ctrl[i].curr[res_idx], 0x10);
+	hdmi_phy_i2c_write(hdmi, curr_ctr[i].curr[res_idx], 0x10);
 
 	hdmi_phy_i2c_write(hdmi, 0x0000, 0x13);  /* PLLPHBYCTRL */
 	hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
@@ -890,7 +789,7 @@ static int hdmi_phy_configure(struct imx_hdmi *hdmi, unsigned char prep,
 	/*Wait for PHY PLL lock */
 	msec = 5;
 	do {
-		val = hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
+		val = hdmi->read(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_TX_PHY_LOCK;
 		if (!val)
 			break;
 
@@ -942,12 +841,12 @@ static void hdmi_tx_hdcp_config(struct imx_hdmi *hdmi)
 		de = HDMI_A_VIDPOLCFG_DATAENPOL_ACTIVE_LOW;
 
 	/* disable rx detect */
-	hdmi_modb(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
+	hdmi->mod(hdmi, HDMI_A_HDCPCFG0_RXDETECT_DISABLE,
 		  HDMI_A_HDCPCFG0_RXDETECT_MASK, HDMI_A_HDCPCFG0);
 
-	hdmi_modb(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
+	hdmi->mod(hdmi, de, HDMI_A_VIDPOLCFG_DATAENPOL_MASK, HDMI_A_VIDPOLCFG);
 
-	hdmi_modb(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
+	hdmi->mod(hdmi, HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_DISABLE,
 		  HDMI_A_HDCPCFG1_ENCRYPTIONDISABLE_MASK, HDMI_A_HDCPCFG1);
 }
 
@@ -977,7 +876,7 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 		HDMI_FC_AVICONF0_ACTIVE_FMT_INFO_PRESENT |
 		HDMI_FC_AVICONF0_BAR_DATA_NO_DATA;
 
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF0);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF0);
 
 	/* AVI Data Byte 2 -Set the Aspect Ratio */
 	if (aspect_16_9) {
@@ -1009,16 +908,16 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 	}
 
 	val = colorimetry | coded_ratio | act_ratio;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF1);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF1);
 
 	/* AVI Data Byte 3 */
 	val = HDMI_FC_AVICONF2_IT_CONTENT_NO_DATA | ext_colorimetry |
 		HDMI_FC_AVICONF2_RGB_QUANT_DEFAULT |
 		HDMI_FC_AVICONF2_SCALING_NONE;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF2);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF2);
 
 	/* AVI Data Byte 4 */
-	hdmi_writeb(hdmi, hdmi->vic, HDMI_FC_AVIVID);
+	hdmi->write(hdmi, hdmi->vic, HDMI_FC_AVIVID);
 
 	/* AVI Data Byte 5- set up input and output pixel repetition */
 	val = (((hdmi->hdmi_data.video_mode.mpixelrepetitioninput + 1) <<
@@ -1027,22 +926,22 @@ static void hdmi_config_AVI(struct imx_hdmi *hdmi)
 		((hdmi->hdmi_data.video_mode.mpixelrepetitionoutput <<
 		HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_OFFSET) &
 		HDMI_FC_PRCONF_OUTPUT_PR_FACTOR_MASK);
-	hdmi_writeb(hdmi, val, HDMI_FC_PRCONF);
+	hdmi->write(hdmi, val, HDMI_FC_PRCONF);
 
 	/* IT Content and quantization range = don't care */
 	val = HDMI_FC_AVICONF3_IT_CONTENT_TYPE_GRAPHICS |
 		HDMI_FC_AVICONF3_QUANT_RANGE_LIMITED;
-	hdmi_writeb(hdmi, val, HDMI_FC_AVICONF3);
+	hdmi->write(hdmi, val, HDMI_FC_AVICONF3);
 
 	/* AVI Data Bytes 6-13 */
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIETB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISBB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVIELB1);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB0);
-	hdmi_writeb(hdmi, 0, HDMI_FC_AVISRB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIETB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIETB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISBB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISBB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIELB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVIELB1);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISRB0);
+	hdmi->write(hdmi, 0, HDMI_FC_AVISRB1);
 }
 
 static void hdmi_av_composer(struct imx_hdmi *hdmi,
@@ -1091,42 +990,42 @@ static void hdmi_av_composer(struct imx_hdmi *hdmi,
 		HDMI_FC_INVIDCONF_DVI_MODEZ_DVI_MODE :
 		HDMI_FC_INVIDCONF_DVI_MODEZ_HDMI_MODE);
 
-	hdmi_writeb(hdmi, inv_val, HDMI_FC_INVIDCONF);
+	hdmi->write(hdmi, inv_val, HDMI_FC_INVIDCONF);
 
 	/* Set up horizontal active pixel width */
-	hdmi_writeb(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
-	hdmi_writeb(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
+	hdmi->write(hdmi, mode->hdisplay >> 8, HDMI_FC_INHACTV1);
+	hdmi->write(hdmi, mode->hdisplay, HDMI_FC_INHACTV0);
 
 	/* Set up vertical active lines */
-	hdmi_writeb(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
-	hdmi_writeb(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
+	hdmi->write(hdmi, mode->vdisplay >> 8, HDMI_FC_INVACTV1);
+	hdmi->write(hdmi, mode->vdisplay, HDMI_FC_INVACTV0);
 
 	/* Set up horizontal blanking pixel region width */
 	hblank = mode->htotal - mode->hdisplay;
-	hdmi_writeb(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
-	hdmi_writeb(hdmi, hblank, HDMI_FC_INHBLANK0);
+	hdmi->write(hdmi, hblank >> 8, HDMI_FC_INHBLANK1);
+	hdmi->write(hdmi, hblank, HDMI_FC_INHBLANK0);
 
 	/* Set up vertical blanking pixel region width */
 	vblank = mode->vtotal - mode->vdisplay;
-	hdmi_writeb(hdmi, vblank, HDMI_FC_INVBLANK);
+	hdmi->write(hdmi, vblank, HDMI_FC_INVBLANK);
 
 	/* Set up HSYNC active edge delay width (in pixel clks) */
 	h_de_hs = mode->hsync_start - mode->hdisplay;
-	hdmi_writeb(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
-	hdmi_writeb(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
+	hdmi->write(hdmi, h_de_hs >> 8, HDMI_FC_HSYNCINDELAY1);
+	hdmi->write(hdmi, h_de_hs, HDMI_FC_HSYNCINDELAY0);
 
 	/* Set up VSYNC active edge delay (in lines) */
 	v_de_vs = mode->vsync_start - mode->vdisplay;
-	hdmi_writeb(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
+	hdmi->write(hdmi, v_de_vs, HDMI_FC_VSYNCINDELAY);
 
 	/* Set up HSYNC active pulse width (in pixel clks) */
 	hsync_len = mode->hsync_end - mode->hsync_start;
-	hdmi_writeb(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
-	hdmi_writeb(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
+	hdmi->write(hdmi, hsync_len >> 8, HDMI_FC_HSYNCINWIDTH1);
+	hdmi->write(hdmi, hsync_len, HDMI_FC_HSYNCINWIDTH0);
 
 	/* Set up VSYNC active edge delay (in lines) */
 	vsync_len = mode->vsync_end - mode->vsync_start;
-	hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
+	hdmi->write(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
 }
 
 static void imx_hdmi_phy_disable(struct imx_hdmi *hdmi)
@@ -1146,33 +1045,33 @@ static void imx_hdmi_enable_video_path(struct imx_hdmi *hdmi)
 	u8 clkdis;
 
 	/* control period minimum duration */
-	hdmi_writeb(hdmi, 12, HDMI_FC_CTRLDUR);
-	hdmi_writeb(hdmi, 32, HDMI_FC_EXCTRLDUR);
-	hdmi_writeb(hdmi, 1, HDMI_FC_EXCTRLSPAC);
+	hdmi->write(hdmi, 12, HDMI_FC_CTRLDUR);
+	hdmi->write(hdmi, 32, HDMI_FC_EXCTRLDUR);
+	hdmi->write(hdmi, 1, HDMI_FC_EXCTRLSPAC);
 
 	/* Set to fill TMDS data channels */
-	hdmi_writeb(hdmi, 0x0B, HDMI_FC_CH0PREAM);
-	hdmi_writeb(hdmi, 0x16, HDMI_FC_CH1PREAM);
-	hdmi_writeb(hdmi, 0x21, HDMI_FC_CH2PREAM);
+	hdmi->write(hdmi, 0x0B, HDMI_FC_CH0PREAM);
+	hdmi->write(hdmi, 0x16, HDMI_FC_CH1PREAM);
+	hdmi->write(hdmi, 0x21, HDMI_FC_CH2PREAM);
 
 	/* Enable pixel clock and tmds data path */
 	clkdis = 0x7F;
 	clkdis &= ~HDMI_MC_CLKDIS_PIXELCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 
 	clkdis &= ~HDMI_MC_CLKDIS_TMDSCLK_DISABLE;
-	hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+	hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 
 	/* Enable csc path */
 	if (is_color_space_conversion(hdmi)) {
 		clkdis &= ~HDMI_MC_CLKDIS_CSCCLK_DISABLE;
-		hdmi_writeb(hdmi, clkdis, HDMI_MC_CLKDIS);
+		hdmi->write(hdmi, clkdis, HDMI_MC_CLKDIS);
 	}
 }
 
 static void hdmi_enable_audio_clk(struct imx_hdmi *hdmi)
 {
-	hdmi_modb(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
+	hdmi->mod(hdmi, 0, HDMI_MC_CLKDIS_AUDCLK_DISABLE, HDMI_MC_CLKDIS);
 }
 
 /* Workaround to clear the overflow condition */
@@ -1182,27 +1081,27 @@ static void imx_hdmi_clear_overflow(struct imx_hdmi *hdmi)
 	u8 val;
 
 	/* TMDS software reset */
-	hdmi_writeb(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
+	hdmi->write(hdmi, (u8)~HDMI_MC_SWRSTZ_TMDSSWRST_REQ, HDMI_MC_SWRSTZ);
 
-	val = hdmi_readb(hdmi, HDMI_FC_INVIDCONF);
+	val = hdmi->read(hdmi, HDMI_FC_INVIDCONF);
 	if (hdmi->dev_type == IMX6DL_HDMI) {
-		hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+		hdmi->write(hdmi, val, HDMI_FC_INVIDCONF);
 		return;
 	}
 
 	for (count = 0; count < 4; count++)
-		hdmi_writeb(hdmi, val, HDMI_FC_INVIDCONF);
+		hdmi->write(hdmi, val, HDMI_FC_INVIDCONF);
 }
 
 static void hdmi_enable_overflow_interrupts(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, 0, HDMI_FC_MASK2);
-	hdmi_writeb(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
+	hdmi->write(hdmi, 0, HDMI_FC_MASK2);
+	hdmi->write(hdmi, 0, HDMI_IH_MUTE_FC_STAT2);
 }
 
 static void hdmi_disable_overflow_interrupts(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
+	hdmi->write(hdmi, HDMI_IH_MUTE_FC_STAT2_OVERFLOW_MASK,
 		    HDMI_IH_MUTE_FC_STAT2);
 }
 
@@ -1223,21 +1122,21 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 	}
 
 	if ((hdmi->vic == 6) || (hdmi->vic == 7) ||
-		(hdmi->vic == 21) || (hdmi->vic == 22) ||
-		(hdmi->vic == 2) || (hdmi->vic == 3) ||
-		(hdmi->vic == 17) || (hdmi->vic == 18))
+	    (hdmi->vic == 21) || (hdmi->vic == 22) ||
+	    (hdmi->vic == 2) || (hdmi->vic == 3) ||
+	    (hdmi->vic == 17) || (hdmi->vic == 18))
 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_601;
 	else
 		hdmi->hdmi_data.colorimetry = HDMI_COLORIMETRY_ITU_709;
 
 	if ((hdmi->vic == 10) || (hdmi->vic == 11) ||
-		(hdmi->vic == 12) || (hdmi->vic == 13) ||
-		(hdmi->vic == 14) || (hdmi->vic == 15) ||
-		(hdmi->vic == 25) || (hdmi->vic == 26) ||
-		(hdmi->vic == 27) || (hdmi->vic == 28) ||
-		(hdmi->vic == 29) || (hdmi->vic == 30) ||
-		(hdmi->vic == 35) || (hdmi->vic == 36) ||
-		(hdmi->vic == 37) || (hdmi->vic == 38))
+	    (hdmi->vic == 12) || (hdmi->vic == 13) ||
+	    (hdmi->vic == 14) || (hdmi->vic == 15) ||
+	    (hdmi->vic == 25) || (hdmi->vic == 26) ||
+	    (hdmi->vic == 27) || (hdmi->vic == 28) ||
+	    (hdmi->vic == 29) || (hdmi->vic == 30) ||
+	    (hdmi->vic == 35) || (hdmi->vic == 36) ||
+	    (hdmi->vic == 37) || (hdmi->vic == 38))
 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 1;
 	else
 		hdmi->hdmi_data.video_mode.mpixelrepetitionoutput = 0;
@@ -1266,9 +1165,9 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 	imx_hdmi_enable_video_path(hdmi);
 
 	/* not for DVI mode */
-	if (hdmi->hdmi_data.video_mode.mdvi)
+	if (hdmi->hdmi_data.video_mode.mdvi) {
 		dev_dbg(hdmi->dev, "%s DVI mode\n", __func__);
-	else {
+	} else {
 		dev_dbg(hdmi->dev, "%s CEA mode\n", __func__);
 
 		/* HDMI Initialization Step E - Configure audio */
@@ -1294,18 +1193,18 @@ static int imx_hdmi_setup(struct imx_hdmi *hdmi, struct drm_display_mode *mode)
 /* Wait until we are registered to enable interrupts */
 static int imx_hdmi_fb_registered(struct imx_hdmi *hdmi)
 {
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
+	hdmi->write(hdmi, HDMI_PHY_I2CM_INT_ADDR_DONE_POL,
 		    HDMI_PHY_I2CM_INT_ADDR);
 
-	hdmi_writeb(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
+	hdmi->write(hdmi, HDMI_PHY_I2CM_CTLINT_ADDR_NAC_POL |
 		    HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL,
 		    HDMI_PHY_I2CM_CTLINT_ADDR);
 
 	/* enable cable hot plug irq */
-	hdmi_writeb(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
+	hdmi->write(hdmi, (u8)~HDMI_PHY_HPD, HDMI_PHY_MASK0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
 
 	return 0;
 }
@@ -1321,45 +1220,45 @@ static void initialize_hdmi_ih_mutes(struct imx_hdmi *hdmi)
 	 *
 	 * Disable top level interrupt bits in HDMI block
 	 */
-	ih_mute = hdmi_readb(hdmi, HDMI_IH_MUTE) |
+	ih_mute = hdmi->read(hdmi, HDMI_IH_MUTE) |
 		  HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
 		  HDMI_IH_MUTE_MUTE_ALL_INTERRUPT;
 
-	hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+	hdmi->write(hdmi, ih_mute, HDMI_IH_MUTE);
 
 	/* by default mask all interrupts */
-	hdmi_writeb(hdmi, 0xff, HDMI_VP_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK0);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK1);
-	hdmi_writeb(hdmi, 0xff, HDMI_FC_MASK2);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_MASK0);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
-	hdmi_writeb(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_INT);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_SPDIFINT);
-	hdmi_writeb(hdmi, 0xff, HDMI_AUD_HBR_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_GP_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_A_APIINTMSK);
-	hdmi_writeb(hdmi, 0xff, HDMI_CEC_MASK);
-	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_INT);
-	hdmi_writeb(hdmi, 0xff, HDMI_I2CM_CTLINT);
+	hdmi->write(hdmi, 0xff, HDMI_VP_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK0);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK1);
+	hdmi->write(hdmi, 0xff, HDMI_FC_MASK2);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_MASK0);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_I2CM_INT_ADDR);
+	hdmi->write(hdmi, 0xff, HDMI_PHY_I2CM_CTLINT_ADDR);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_INT);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_SPDIFINT);
+	hdmi->write(hdmi, 0xff, HDMI_AUD_HBR_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_GP_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_A_APIINTMSK);
+	hdmi->write(hdmi, 0xff, HDMI_CEC_MASK);
+	hdmi->write(hdmi, 0xff, HDMI_I2CM_INT);
+	hdmi->write(hdmi, 0xff, HDMI_I2CM_CTLINT);
 
 	/* Disable interrupts in the IH_MUTE_* registers */
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
-	hdmi_writeb(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT1);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_FC_STAT2);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_AS_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_I2CM_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_CEC_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_VP_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_I2CMPHY_STAT0);
+	hdmi->write(hdmi, 0xff, HDMI_IH_MUTE_AHBDMAAUD_STAT0);
 
 	/* Enable top level interrupt bits in HDMI block */
 	ih_mute &= ~(HDMI_IH_MUTE_MUTE_WAKEUP_INTERRUPT |
 		    HDMI_IH_MUTE_MUTE_ALL_INTERRUPT);
-	hdmi_writeb(hdmi, ih_mute, HDMI_IH_MUTE);
+	hdmi->write(hdmi, ih_mute, HDMI_IH_MUTE);
 }
 
 static void imx_hdmi_poweron(struct imx_hdmi *hdmi)
@@ -1378,7 +1277,7 @@ static enum drm_connector_status imx_hdmi_connector_detect(struct drm_connector
 	struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
 					     connector);
 
-	return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
+	return hdmi->read(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
 		connector_status_connected : connector_status_disconnected;
 }
 
@@ -1454,21 +1353,40 @@ static void imx_hdmi_encoder_prepare(struct drm_encoder *encoder)
 	struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
 
 	imx_hdmi_poweroff(hdmi);
-	imx_drm_panel_format(encoder, V4L2_PIX_FMT_RGB24);
+
+	if (hdmi->plat_data->encoder_prepare)
+		hdmi->plat_data->encoder_prepare(&hdmi->connector, encoder);
 }
 
 static void imx_hdmi_encoder_commit(struct drm_encoder *encoder)
 {
 	struct imx_hdmi *hdmi = container_of(encoder, struct imx_hdmi, encoder);
-	int mux = imx_drm_encoder_get_mux_id(hdmi->dev->of_node, encoder);
 
-	imx_hdmi_set_ipu_di_mux(hdmi, mux);
+	if (hdmi->plat_data->set_crtc_mux)
+		hdmi->plat_data->set_crtc_mux(hdmi->priv, encoder);
 
 	imx_hdmi_poweron(hdmi);
 }
 
+void imx_hdmi_connector_destroy(struct drm_connector *connector)
+{
+	drm_connector_unregister(connector);
+	drm_connector_cleanup(connector);
+}
+
+static int imx_hdmi_connector_mode_valid(struct drm_connector *connector,
+					 struct drm_display_mode *mode)
+{
+	struct imx_hdmi *hdmi = container_of(connector, struct imx_hdmi,
+					     connector);
+	if (hdmi->plat_data->mode_valid)
+		return hdmi->plat_data->mode_valid(connector, mode);
+
+	return MODE_OK;
+}
+
 static struct drm_encoder_funcs imx_hdmi_encoder_funcs = {
-	.destroy = imx_drm_encoder_destroy,
+	.destroy = drm_encoder_cleanup,
 };
 
 static struct drm_encoder_helper_funcs imx_hdmi_encoder_helper_funcs = {
@@ -1484,11 +1402,12 @@ static struct drm_connector_funcs imx_hdmi_connector_funcs = {
 	.dpms = drm_helper_connector_dpms,
 	.fill_modes = drm_helper_probe_single_connector_modes,
 	.detect = imx_hdmi_connector_detect,
-	.destroy = imx_drm_connector_destroy,
+	.destroy = imx_hdmi_connector_destroy,
 };
 
 static struct drm_connector_helper_funcs imx_hdmi_connector_helper_funcs = {
 	.get_modes = imx_hdmi_connector_get_modes,
+	.mode_valid = imx_hdmi_connector_mode_valid,
 	.best_encoder = imx_hdmi_connector_best_encoder,
 };
 
@@ -1497,9 +1416,9 @@ static irqreturn_t imx_hdmi_hardirq(int irq, void *dev_id)
 	struct imx_hdmi *hdmi = dev_id;
 	u8 intr_stat;
 
-	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+	intr_stat = hdmi->read(hdmi, HDMI_IH_PHY_STAT0);
 	if (intr_stat)
-		hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+		hdmi->write(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
 	return intr_stat ? IRQ_WAKE_THREAD : IRQ_NONE;
 }
@@ -1510,21 +1429,21 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id)
 	u8 intr_stat;
 	u8 phy_int_pol;
 
-	intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
+	intr_stat = hdmi->read(hdmi, HDMI_IH_PHY_STAT0);
 
-	phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
+	phy_int_pol = hdmi->read(hdmi, HDMI_PHY_POL0);
 
 	if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
 		if (phy_int_pol & HDMI_PHY_HPD) {
 			dev_dbg(hdmi->dev, "EVENT=plugin\n");
 
-			hdmi_modb(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
+			hdmi->mod(hdmi, 0, HDMI_PHY_HPD, HDMI_PHY_POL0);
 
 			imx_hdmi_poweron(hdmi);
 		} else {
 			dev_dbg(hdmi->dev, "EVENT=plugout\n");
 
-			hdmi_modb(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
+			hdmi->mod(hdmi, HDMI_PHY_HPD, HDMI_PHY_HPD,
 				HDMI_PHY_POL0);
 
 			imx_hdmi_poweroff(hdmi);
@@ -1532,20 +1451,18 @@ static irqreturn_t imx_hdmi_irq(int irq, void *dev_id)
 		drm_helper_hpd_irq_event(hdmi->connector.dev);
 	}
 
-	hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, intr_stat, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
 	return IRQ_HANDLED;
 }
 
 static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 {
-	int ret;
+	struct drm_encoder *encoder = &hdmi->encoder;
+	struct device *dev = hdmi->dev;
 
-	ret = imx_drm_encoder_parse_of(drm, &hdmi->encoder,
-				       hdmi->dev->of_node);
-	if (ret)
-		return ret;
+	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
 
 	hdmi->connector.polled = DRM_CONNECTOR_POLL_HPD;
 
@@ -1554,7 +1471,7 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 			 DRM_MODE_ENCODER_TMDS);
 
 	drm_connector_helper_add(&hdmi->connector,
-			&imx_hdmi_connector_helper_funcs);
+				 &imx_hdmi_connector_helper_funcs);
 	drm_connector_init(drm, &hdmi->connector, &imx_hdmi_connector_funcs,
 			   DRM_MODE_CONNECTOR_HDMIA);
 
@@ -1565,55 +1482,47 @@ static int imx_hdmi_register(struct drm_device *drm, struct imx_hdmi *hdmi)
 	return 0;
 }
 
-static struct platform_device_id imx_hdmi_devtype[] = {
-	{
-		.name = "imx6q-hdmi",
-		.driver_data = IMX6Q_HDMI,
-	}, {
-		.name = "imx6dl-hdmi",
-		.driver_data = IMX6DL_HDMI,
-	}, { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(platform, imx_hdmi_devtype);
-
-static const struct of_device_id imx_hdmi_dt_ids[] = {
-{ .compatible = "fsl,imx6q-hdmi", .data = &imx_hdmi_devtype[IMX6Q_HDMI], },
-{ .compatible = "fsl,imx6dl-hdmi", .data = &imx_hdmi_devtype[IMX6DL_HDMI], },
-{ /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, imx_hdmi_dt_ids);
-
 static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
 	struct platform_device *pdev = to_platform_device(dev);
-	const struct of_device_id *of_id =
-				of_match_device(imx_hdmi_dt_ids, dev);
+	struct imx_hdmi *hdmi = platform_get_drvdata(pdev);
 	struct drm_device *drm = data;
 	struct device_node *np = dev->of_node;
 	struct device_node *ddc_node;
-	struct imx_hdmi *hdmi;
 	struct resource *iores;
 	int ret, irq;
-
-	hdmi = devm_kzalloc(dev, sizeof(*hdmi), GFP_KERNEL);
-	if (!hdmi)
-		return -ENOMEM;
-
-	hdmi->dev = dev;
-	hdmi->sample_rate = 48000;
-	hdmi->ratio = 100;
-
-	if (of_id) {
-		const struct platform_device_id *device_id = of_id->data;
-
-		hdmi->dev_type = device_id->driver_data;
+	u32 val;
+
+	if (!of_property_read_u32(np, "reg-io-width", &val)) {
+		switch (val) {
+		case 4:
+			hdmi->write = hdmi_writel;
+			hdmi->read = hdmi_readl;
+			hdmi->mod = hdmi_modl;
+			hdmi->mask_write = hdmi_mask_writel;
+			break;
+		default:
+			hdmi->write = hdmi_writeb;
+			hdmi->read = hdmi_readb;
+			hdmi->mod = hdmi_modb;
+			hdmi->mask_write = hdmi_mask_writeb;
+			break;
+		}
+	} else {
+		hdmi->write = hdmi_writeb;
+		hdmi->read = hdmi_readb;
+		hdmi->mod = hdmi_modb;
+		hdmi->mask_write = hdmi_mask_writeb;
 	}
 
 	ddc_node = of_parse_phandle(np, "ddc-i2c-bus", 0);
 	if (ddc_node) {
 		hdmi->ddc = of_find_i2c_adapter_by_node(ddc_node);
-		if (!hdmi->ddc)
+		if (!hdmi->ddc) {
 			dev_dbg(hdmi->dev, "failed to read ddc node\n");
+			of_node_put(ddc_node);
+			return -EPROBE_DEFER;
+		}
 
 		of_node_put(ddc_node);
 	} else {
@@ -1635,47 +1544,15 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	if (IS_ERR(hdmi->regs))
 		return PTR_ERR(hdmi->regs);
 
-	hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "gpr");
-	if (IS_ERR(hdmi->regmap))
-		return PTR_ERR(hdmi->regmap);
-
-	hdmi->isfr_clk = devm_clk_get(hdmi->dev, "isfr");
-	if (IS_ERR(hdmi->isfr_clk)) {
-		ret = PTR_ERR(hdmi->isfr_clk);
-		dev_err(hdmi->dev,
-			"Unable to get HDMI isfr clk: %d\n", ret);
-		return ret;
-	}
-
-	ret = clk_prepare_enable(hdmi->isfr_clk);
-	if (ret) {
-		dev_err(hdmi->dev,
-			"Cannot enable HDMI isfr clock: %d\n", ret);
-		return ret;
-	}
-
-	hdmi->iahb_clk = devm_clk_get(hdmi->dev, "iahb");
-	if (IS_ERR(hdmi->iahb_clk)) {
-		ret = PTR_ERR(hdmi->iahb_clk);
-		dev_err(hdmi->dev,
-			"Unable to get HDMI iahb clk: %d\n", ret);
-		goto err_isfr;
-	}
-
-	ret = clk_prepare_enable(hdmi->iahb_clk);
-	if (ret) {
-		dev_err(hdmi->dev,
-			"Cannot enable HDMI iahb clock: %d\n", ret);
-		goto err_isfr;
-	}
-
+	if (hdmi->plat_data->setup)
+		hdmi->priv = hdmi->plat_data->setup(pdev);
 	/* Product and revision IDs */
 	dev_info(dev,
-		"Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
-		hdmi_readb(hdmi, HDMI_DESIGN_ID),
-		hdmi_readb(hdmi, HDMI_REVISION_ID),
-		hdmi_readb(hdmi, HDMI_PRODUCT_ID0),
-		hdmi_readb(hdmi, HDMI_PRODUCT_ID1));
+		 "Detected HDMI controller 0x%x:0x%x:0x%x:0x%x\n",
+		 hdmi->read(hdmi, HDMI_DESIGN_ID),
+		 hdmi->read(hdmi, HDMI_REVISION_ID),
+		 hdmi->read(hdmi, HDMI_PRODUCT_ID0),
+		 hdmi->read(hdmi, HDMI_PRODUCT_ID1));
 
 	initialize_hdmi_ih_mutes(hdmi);
 
@@ -1689,32 +1566,25 @@ static int imx_hdmi_bind(struct device *dev, struct device *master, void *data)
 	 * Configure registers related to HDMI interrupt
 	 * generation before registering IRQ.
 	 */
-	hdmi_writeb(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
+	hdmi->write(hdmi, HDMI_PHY_HPD, HDMI_PHY_POL0);
 
 	/* Clear Hotplug interrupts */
-	hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
+	hdmi->write(hdmi, HDMI_IH_PHY_STAT0_HPD, HDMI_IH_PHY_STAT0);
 
 	ret = imx_hdmi_fb_registered(hdmi);
 	if (ret)
-		goto err_iahb;
+		return ret;
 
 	ret = imx_hdmi_register(drm, hdmi);
 	if (ret)
-		goto err_iahb;
+		return ret;
 
 	/* Unmute interrupts */
-	hdmi_writeb(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, ~HDMI_IH_PHY_STAT0_HPD, HDMI_IH_MUTE_PHY_STAT0);
 
 	dev_set_drvdata(dev, hdmi);
 
 	return 0;
-
-err_iahb:
-	clk_disable_unprepare(hdmi->iahb_clk);
-err_isfr:
-	clk_disable_unprepare(hdmi->isfr_clk);
-
-	return ret;
 }
 
 static void imx_hdmi_unbind(struct device *dev, struct device *master,
@@ -1723,13 +1593,12 @@ static void imx_hdmi_unbind(struct device *dev, struct device *master,
 	struct imx_hdmi *hdmi = dev_get_drvdata(dev);
 
 	/* Disable all interrupts */
-	hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
+	hdmi->write(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0);
 
 	hdmi->connector.funcs->destroy(&hdmi->connector);
 	hdmi->encoder.funcs->destroy(&hdmi->encoder);
-
-	clk_disable_unprepare(hdmi->iahb_clk);
-	clk_disable_unprepare(hdmi->isfr_clk);
+	if (hdmi->plat_data->exit)
+		hdmi->plat_data->exit(hdmi->priv);
 	i2c_put_adapter(hdmi->ddc);
 }
 
@@ -1749,17 +1618,32 @@ static int imx_hdmi_platform_remove(struct platform_device *pdev)
 	return 0;
 }
 
-static struct platform_driver imx_hdmi_driver = {
-	.probe  = imx_hdmi_platform_probe,
-	.remove = imx_hdmi_platform_remove,
-	.driver = {
-		.name = "imx-hdmi",
-		.owner = THIS_MODULE,
-		.of_match_table = imx_hdmi_dt_ids,
-	},
-};
+int imx_hdmi_pltfm_register(struct platform_device *pdev,
+			    const struct imx_hdmi_plat_data *plat_data)
+{
+	struct imx_hdmi *hdmi;
 
-module_platform_driver(imx_hdmi_driver);
+	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+	if (!hdmi)
+		return -ENOMEM;
+
+	hdmi->plat_data = plat_data;
+	hdmi->dev = &pdev->dev;
+	hdmi->dev_type = plat_data->dev_type;
+	hdmi->sample_rate = 48000;
+	hdmi->ratio = 100;
+
+	platform_set_drvdata(pdev, hdmi);
+
+	return imx_hdmi_platform_probe(pdev);
+}
+EXPORT_SYMBOL_GPL(imx_hdmi_pltfm_register);
+
+int imx_hdmi_pltfm_unregister(struct platform_device *pdev)
+{
+	return imx_hdmi_platform_remove(pdev);
+}
+EXPORT_SYMBOL_GPL(imx_hdmi_pltfm_unregister);
 
 MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
 MODULE_DESCRIPTION("i.MX6 HDMI transmitter driver");
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
new file mode 100644
index 0000000..e7e8285
--- /dev/null
+++ b/include/drm/bridge/dw_hdmi.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2011 Freescale Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __DW_HDMI_H__
+#define __DW_HDMI_H__
+
+#include <drm/drmP.h>
+
+#define HDMI_EDID_LEN           512
+
+enum {
+	RES_8,
+	RES_10,
+	RES_12,
+	RES_MAX,
+};
+
+enum imx_hdmi_devtype {
+	IMX6Q_HDMI,
+	IMX6DL_HDMI,
+};
+
+struct mpll_config {
+	unsigned long mpixelclock;
+	struct {
+		u16 cpce;
+		u16 gmp;
+	} res[RES_MAX];
+};
+
+struct curr_ctrl {
+	unsigned long mpixelclock;
+	u16 curr[RES_MAX];
+};
+
+struct hdmi_vmode {
+	bool mdvi;
+	bool mhsyncpolarity;
+	bool mvsyncpolarity;
+	bool minterlaced;
+	bool mdataenablepolarity;
+
+	unsigned int mpixelclock;
+	unsigned int mpixelrepetitioninput;
+	unsigned int mpixelrepetitionoutput;
+};
+
+struct hdmi_data_info {
+	unsigned int enc_in_format;
+	unsigned int enc_out_format;
+	unsigned int enc_color_depth;
+	unsigned int colorimetry;
+	unsigned int pix_repet_factor;
+	unsigned int hdcp_enable;
+	struct hdmi_vmode video_mode;
+};
+
+
+struct imx_hdmi {
+	struct drm_connector connector;
+	struct drm_encoder encoder;
+
+	enum imx_hdmi_devtype dev_type;
+	struct device *dev;
+
+	struct hdmi_data_info hdmi_data;
+	const struct imx_hdmi_plat_data *plat_data;
+	void *priv;
+
+	int vic;
+
+	u8 edid[HDMI_EDID_LEN];
+	bool cable_plugin;
+
+	bool phy_enabled;
+	struct drm_display_mode previous_mode;
+
+	struct i2c_adapter *ddc;
+	void __iomem *regs;
+
+	unsigned int sample_rate;
+	int ratio;
+
+	void (*write)(struct imx_hdmi *hdmi, u32 val, int offset);
+	u32 (*read)(struct imx_hdmi *hdmi, int offset);
+	void (*mod)(struct imx_hdmi *hdmi, u32 data, u32 mask, unsigned reg);
+	void (*mask_write)(struct imx_hdmi *hdmi, u32 data, unsigned int reg,
+			      u32 shift, u32 mask);
+};
+
+struct imx_hdmi_plat_data {
+	void * (*setup)(struct platform_device *pdev);
+	void (*exit)(void *priv);
+	void (*set_crtc_mux)(void *priv, struct drm_encoder *encoder);
+	void (*encoder_prepare)(struct drm_connector *connector,
+				struct drm_encoder *encoder);
+	enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
+					   struct drm_display_mode *mode);
+	const struct mpll_config *mpll_cfg;
+	const struct curr_ctrl *cur_ctr;
+	enum imx_hdmi_devtype dev_type;
+
+};
+
+int imx_hdmi_pltfm_register(struct platform_device *pdev,
+			    const struct imx_hdmi_plat_data *plat_data);
+int imx_hdmi_pltfm_unregister(struct platform_device *pdev);
+#endif /* __IMX_HDMI_H__ */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2014-11-07  2:48 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-11-04 13:33 [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform Andy Yan
2014-11-04 13:33 ` Andy Yan
2014-11-04 13:33 ` [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi Andy Yan
2014-11-04 13:33   ` Andy Yan
2014-11-04 14:23   ` Zubair Lutfullah Kakakhel
2014-11-04 14:23     ` Zubair Lutfullah Kakakhel
2014-11-04 14:29 ` [PATCH 0/2] make imx hdmi publicly used by dw hdmi compatible platform Russell King - ARM Linux
2014-11-04 14:29   ` Russell King - ARM Linux
2014-11-05  2:12   ` Andy Yan
2014-11-05  2:12     ` Andy Yan
2014-11-06  9:35   ` Kuankuan.Yang
2014-11-06  9:35     ` Kuankuan.Yang
2014-11-06 10:18     ` Russell King - ARM Linux
2014-11-06 10:18       ` Russell King - ARM Linux
2014-11-05 12:54 [PATCH V2 " Andy Yan
2014-11-05 12:55 ` [PATCH 1/2] imx-drm: imx-hdmi: split imx soc specific code from imx-hdmi Andy Yan
2014-11-05 12:55   ` Andy Yan
2014-11-05 13:41   ` Philipp Zabel
2014-11-05 13:41     ` Philipp Zabel
2014-11-07  2:47     ` Andy Yan
2014-11-07  2:47       ` Andy Yan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.