@@ -13,6 +13,7 @@ exynosdrm-$(CONFIG_DRM_EXYNOS_DMABUF) += exynos_drm_dmabuf.o
exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \
exynos_ddc.o exynos_hdmiphy_i2c.o \
+ exynos_hdmiphy_platform.o \
exynos_drm_hdmi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
exynosdrm-$(CONFIG_DRM_EXYNOS_G2D) += exynos_drm_g2d.o
@@ -35,6 +35,7 @@
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_i2c.h>
+#include <linux/of_platform.h>
#include <drm/exynos_drm.h>
@@ -1608,10 +1609,14 @@ static int hdmi_register_phy_device(struct hdmi_context *hdata, bool i2c_dev)
{
struct device_node *np;
struct i2c_client *client;
+ struct platform_device *pdev;
int ret;
/* register hdmiphy driver */
- ret = exynos_hdmiphy_i2c_driver_register();
+ if (i2c_dev)
+ ret = exynos_hdmiphy_i2c_driver_register();
+ else
+ ret = exynos_hdmiphy_platform_driver_register();
if (ret) {
DRM_ERROR("failed to register phy driver. ret %d.\n", ret);
goto err;
@@ -1624,16 +1629,29 @@ static int hdmi_register_phy_device(struct hdmi_context *hdata, bool i2c_dev)
goto err;
}
- /* find hdmi phy on i2c bus */
- client = of_find_i2c_device_by_node(np);
- if (!client) {
- DRM_ERROR("Could not find i2c 'phy' device\n");
- ret = -ENODEV;
- goto err;
+ if (i2c_dev) {
+ /* find hdmi phy on i2c bus */
+ client = of_find_i2c_device_by_node(np);
+ if (!client) {
+ DRM_ERROR("Could not find i2c 'phy' device\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ hdata->phy_dev = &client->dev;
+ hdata->phy_ops = exynos_hdmiphy_i2c_device_get_ops(
+ hdata->phy_dev);
+ } else {
+ /* find hdmi phy on platform bus */
+ pdev = of_find_device_by_node(np);
+ if (!pdev) {
+ DRM_ERROR("Could not find platform 'phy' device\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ hdata->phy_dev = &pdev->dev;
+ hdata->phy_ops = exynos_hdmiphy_platform_device_get_ops(
+ hdata->phy_dev);
}
- hdata->phy_dev = &client->dev;
- hdata->phy_ops = exynos_hdmiphy_i2c_device_get_ops(
- hdata->phy_dev);
if (!hdata->phy_ops) {
ret = -EINVAL;
@@ -1652,6 +1670,11 @@ static struct hdmi_drv_data exynos5250_hdmi_drv_data = {
.i2c_hdmiphy = 1,
};
+static struct hdmi_drv_data exynos5420_hdmi_drv_data = {
+ .type = HDMI_TYPE14,
+ .i2c_hdmiphy = 0,
+};
+
static struct of_device_id hdmi_match_types[] = {
{
.compatible = "samsung,exynos5-hdmi",
@@ -1660,6 +1683,9 @@ static struct of_device_id hdmi_match_types[] = {
.compatible = "samsung,exynos4212-hdmi",
.data = &exynos5250_hdmi_drv_data,
}, {
+ .compatible = "samsung,exynos5420-hdmi",
+ .data = &exynos5420_hdmi_drv_data,
+ }, {
/* end node */
}
};
@@ -1767,7 +1793,10 @@ static int hdmi_probe(struct platform_device *pdev)
return 0;
err_hdmiphy:
- exynos_hdmiphy_i2c_driver_unregister();
+ if (drv->i2c_hdmiphy)
+ exynos_hdmiphy_i2c_driver_unregister();
+ else
+ exynos_hdmiphy_platform_driver_unregister();
err_ddc:
i2c_del_driver(&ddc_driver);
return ret;
@@ -1776,11 +1805,17 @@ err_ddc:
static int hdmi_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct exynos_drm_hdmi_context *ctx = get_hdmi_context(dev);
+ struct hdmi_context *hdata = ctx->ctx;
pm_runtime_disable(dev);
- /* hdmiphy i2c driver */
- exynos_hdmiphy_i2c_driver_unregister();
+ /* hdmiphy driver */
+ if (i2c_verify_client(hdata->phy_dev))
+ exynos_hdmiphy_i2c_driver_unregister();
+ else
+ exynos_hdmiphy_platform_driver_unregister();
+
/* DDC i2c driver */
i2c_del_driver(&ddc_driver);
new file mode 100644
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2013 Samsung Electronics Co.Ltd
+ * Authors:
+ * Rahul Sharma <rahul.sharma@samsung.com>
+ *
+ * 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.
+ *
+ */
+#include <drm/drmP.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#include "regs-hdmiphy.h"
+#include "exynos_hdmiphy.h"
+#include "exynos_hdmiphy_priv.h"
+
+/* default phy config settings for exynos5420 */
+static struct hdmiphy_config hdmiphy_5420_configs[] = {
+ {
+ .pixel_clock = 25200000,
+ .conf = {
+ 0x52, 0x3F, 0x55, 0x40, 0x01, 0x00, 0xC8, 0x82,
+ 0xC8, 0xBD, 0xD8, 0x45, 0xA0, 0xAC, 0x80, 0x06,
+ 0x80, 0x01, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0xF4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 27000000,
+ .conf = {
+ 0xD1, 0x22, 0x51, 0x40, 0x08, 0xFC, 0xE0, 0x98,
+ 0xE8, 0xCB, 0xD8, 0x45, 0xA0, 0xAC, 0x80, 0x06,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 27027000,
+ .conf = {
+ 0xD1, 0x2D, 0x72, 0x40, 0x64, 0x12, 0xC8, 0x43,
+ 0xE8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, 0x06,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 36000000,
+ .conf = {
+ 0x51, 0x2D, 0x55, 0x40, 0x40, 0x00, 0xC8, 0x02,
+ 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, 0x08,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0xAB, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 40000000,
+ .conf = {
+ 0xD1, 0x21, 0x31, 0x40, 0x3C, 0x28, 0xC8, 0x87,
+ 0xE8, 0xC8, 0xD8, 0x45, 0xA0, 0xAC, 0x80, 0x08,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0x9A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 65000000,
+ .conf = {
+ 0xD1, 0x36, 0x34, 0x40, 0x0C, 0x04, 0xC8, 0x82,
+ 0xE8, 0x45, 0xD9, 0x45, 0xA0, 0xAC, 0x80, 0x08,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0xBD, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 71000000,
+ .conf = {
+ 0xD1, 0x3B, 0x35, 0x40, 0x0C, 0x04, 0xC8, 0x85,
+ 0xE8, 0x63, 0xD9, 0x45, 0xA0, 0xAC, 0x80, 0x08,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0x57, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 74176000,
+ .conf = {
+ 0xD1, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0xC8, 0x81,
+ 0xE8, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80, 0x56,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 74250000,
+ .conf = {
+ 0xD1, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0xC8, 0x81,
+ 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80, 0x56,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 83500000,
+ .conf = {
+ 0xD1, 0x23, 0x11, 0x40, 0x0C, 0xFB, 0xC8, 0x85,
+ 0xE8, 0xD1, 0xD8, 0x45, 0xA0, 0xAC, 0x80, 0x08,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0x4A, 0x24, 0x00, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 106500000,
+ .conf = {
+ 0xD1, 0x2C, 0x12, 0x40, 0x0C, 0x09, 0xC8, 0x84,
+ 0xE8, 0x0A, 0xD9, 0x45, 0xA0, 0xAC, 0x80, 0x08,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0x73, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 108000000,
+ .conf = {
+ 0x51, 0x2D, 0x15, 0x40, 0x01, 0x00, 0xC8, 0x82,
+ 0xC8, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, 0x08,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0xC7, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 146250000,
+ .conf = {
+ 0xD1, 0x3D, 0x15, 0x40, 0x18, 0xFD, 0xC8, 0x83,
+ 0xE8, 0x6E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, 0x08,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0x54, 0x24, 0x01, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+ {
+ .pixel_clock = 148500000,
+ .conf = {
+ 0xD1, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0xC8, 0x81,
+ 0xE8, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80, 0x66,
+ 0x80, 0x09, 0x84, 0x05, 0x02, 0x24, 0x66, 0x54,
+ 0x4B, 0x25, 0x03, 0x00, 0x00, 0x01, 0x80,
+ },
+ },
+};
+
+static struct hdmiphy_config *hdmiphy_find_conf(struct hdmiphy_context *hdata,
+ unsigned int pixel_clk)
+{
+ int i;
+
+ for (i = 0; i < hdata->nr_confs; i++)
+ if (hdata->confs[i].pixel_clock == pixel_clk)
+ return &hdata->confs[i];
+
+ return NULL;
+}
+
+static int hdmiphy_reg_writeb(struct hdmiphy_context *hdata,
+ u32 reg_offset, u8 value)
+{
+ if (reg_offset >= HDMIPHY_REG_COUNT)
+ return -EINVAL;
+
+ writeb(value, hdata->regs + (reg_offset<<2));
+ return 0;
+}
+
+static int hdmiphy_reg_write_buf(struct hdmiphy_context *hdata,
+ u32 reg_offset, const u8 *buf, u32 len)
+{
+ int i;
+
+ if ((reg_offset + len) > HDMIPHY_REG_COUNT)
+ return -EINVAL;
+
+ for (i = 0; i < len; i++)
+ writeb(buf[i], hdata->regs +
+ ((reg_offset + i)<<2));
+ return 0;
+}
+
+static int hdmiphy_check_mode(struct device *dev,
+ struct drm_display_mode *mode)
+{
+ struct hdmiphy_context *hdata = dev_get_drvdata(dev);
+ const struct hdmiphy_config *conf;
+
+ DRM_DEBUG("%s xres=%d, yres=%d, refresh=%d, intl=%d clock=%d\n",
+ __func__, mode->hdisplay, mode->vdisplay,
+ mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ ? true : false, mode->clock * 1000);
+
+ conf = hdmiphy_find_conf(hdata, mode->clock * 1000);
+ if (!conf) {
+ DRM_DEBUG("Display Mode is not supported.\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int hdmiphy_mode_set(struct device *dev,
+ struct drm_display_mode *mode)
+{
+ struct hdmiphy_context *hdata = dev_get_drvdata(dev);
+
+ DRM_DEBUG_KMS("[%d]\n", __LINE__);
+
+ hdata->current_conf = hdmiphy_find_conf(hdata, mode->clock * 1000);
+ if (!hdata->current_conf) {
+ DRM_ERROR("Display Mode is not supported.\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int hdmiphy_commit(struct device *dev)
+{
+ struct hdmiphy_context *hdata = dev_get_drvdata(dev);
+ int ret;
+
+ DRM_DEBUG_KMS("[%d]\n", __LINE__);
+
+ ret = hdmiphy_reg_write_buf(hdata, 1, hdata->current_conf->conf,
+ HDMIPHY_REG_COUNT - 1);
+ if (ret) {
+ DRM_ERROR("failed to configure hdmiphy. ret %d.\n", ret);
+ return ret;
+ }
+
+ /* need this delay before phy can be set to operation. */
+ usleep_range(10000, 12000);
+ return 0;
+}
+
+static void hdmiphy_enable(struct device *dev, int enable)
+{
+ struct hdmiphy_context *hdata = dev_get_drvdata(dev);
+
+ DRM_DEBUG_KMS("[%d]\n", __LINE__);
+
+ if (enable)
+ hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+ HDMIPHY_MODE_EN);
+ else
+ hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE, 0);
+}
+
+static void hdmiphy_poweron(struct device *dev, int mode)
+{
+
+ DRM_DEBUG_KMS("[%d]\n", __LINE__);
+
+}
+
+struct exynos_hdmiphy_ops *exynos_hdmiphy_platform_device_get_ops
+ (struct device *dev)
+{
+ struct hdmiphy_context *hdata = dev_get_drvdata(dev);
+ DRM_DEBUG_KMS("[%d]\n", __LINE__);
+
+ if (hdata)
+ return hdata->ops;
+
+ return NULL;
+}
+
+static struct exynos_hdmiphy_ops phy_ops = {
+ .check_mode = hdmiphy_check_mode,
+ .mode_set = hdmiphy_mode_set,
+ .commit = hdmiphy_commit,
+ .enable = hdmiphy_enable,
+ .poweron = hdmiphy_poweron,
+};
+
+static struct hdmiphy_drv_data exynos5420_hdmiphy_drv_data = {
+ .confs = hdmiphy_5420_configs,
+ .count = ARRAY_SIZE(hdmiphy_5420_configs)
+};
+
+static struct of_device_id hdmiphy_platform_device_match_types[] = {
+ {
+ .compatible = "samsung,exynos5420-hdmiphy",
+ .data = &exynos5420_hdmiphy_drv_data,
+ }, {
+ /* end node */
+ }
+};
+
+static int hdmiphy_platform_device_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct hdmiphy_context *hdata;
+ struct hdmiphy_drv_data *drv;
+ struct resource *res;
+ const struct of_device_id *match;
+
+ DRM_DEBUG_KMS("[%d]\n", __LINE__);
+
+ hdata = devm_kzalloc(dev, sizeof(*hdata), GFP_KERNEL);
+ if (!hdata) {
+ DRM_ERROR("failed to allocate hdmiphy context.\n");
+ return -ENOMEM;
+ }
+
+ match = of_match_node(of_match_ptr(
+ hdmiphy_platform_device_match_types),
+ dev->of_node);
+
+ if (!match)
+ return -ENODEV;
+
+ drv = (struct hdmiphy_drv_data *)match->data;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ DRM_ERROR("failed to find phy registers\n");
+ return -ENOENT;
+ }
+
+ hdata->regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (!hdata->regs) {
+ DRM_ERROR("failed to map registers\n");
+ return -ENXIO;
+ }
+
+ hdata->confs = drv->confs;
+ hdata->nr_confs = drv->count;
+ hdata->ops = &phy_ops;
+
+ platform_set_drvdata(pdev, hdata);
+ return 0;
+}
+
+struct platform_driver hdmiphy_platform_driver = {
+ .driver = {
+ .name = "exynos-hdmiphy",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(
+ hdmiphy_platform_device_match_types),
+ },
+ .probe = hdmiphy_platform_device_probe,
+};
+
+int exynos_hdmiphy_platform_driver_register(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&hdmiphy_platform_driver);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+void exynos_hdmiphy_platform_driver_unregister(void)
+{
+ platform_driver_unregister(&hdmiphy_platform_driver);
+}
@@ -15,6 +15,7 @@
struct hdmiphy_context {
/* hdmiphy resources */
+ void __iomem *regs;
struct exynos_hdmiphy_ops *ops;
struct hdmiphy_config *confs;
unsigned int nr_confs;
Exynos5420 hdmiphy device is a platform device, unlike predecessor SoCs where it used to be a I2C device. This support is added to the hdmiphy platform driver. Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com> --- drivers/gpu/drm/exynos/Makefile | 1 + drivers/gpu/drm/exynos/exynos_hdmi.c | 61 +++- drivers/gpu/drm/exynos/exynos_hdmiphy_platform.c | 363 ++++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_hdmiphy_priv.h | 1 + 4 files changed, 413 insertions(+), 13 deletions(-) create mode 100644 drivers/gpu/drm/exynos/exynos_hdmiphy_platform.c