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@xxxxxxxxxxx> --- 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 diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile index 463239b..eedd145 100644 --- a/drivers/gpu/drm/exynos/Makefile +++ b/drivers/gpu/drm/exynos/Makefile @@ -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 diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 444541d..e199d7d 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -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); diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy_platform.c b/drivers/gpu/drm/exynos/exynos_hdmiphy_platform.c new file mode 100644 index 0000000..053d854 --- /dev/null +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy_platform.c @@ -0,0 +1,363 @@ +/* + * Copyright (C) 2013 Samsung Electronics Co.Ltd + * Authors: + * Rahul Sharma <rahul.sharma@xxxxxxxxxxx> + * + * 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); +} diff --git a/drivers/gpu/drm/exynos/exynos_hdmiphy_priv.h b/drivers/gpu/drm/exynos/exynos_hdmiphy_priv.h index 4948c81..9ba46d4 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmiphy_priv.h +++ b/drivers/gpu/drm/exynos/exynos_hdmiphy_priv.h @@ -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; -- 1.7.10.4 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html