drm/exynos: add support for apb mapped phys in hdmi driver
authorRahul Sharma <Rahul.Sharma@samsung.com>
Fri, 9 May 2014 06:34:18 +0000 (15:34 +0900)
committerHyungwon Hwang <human.hwang@samsung.com>
Wed, 17 Dec 2014 07:18:03 +0000 (16:18 +0900)
Previous SoCs have hdmi phys which are accessible through
dedicated i2c lines. Newer SoCs have Apb mapped hdmi phys.
Hdmi driver is modified to support apb mapped phys.

Signed-off-by: Rahul Sharma <Rahul.Sharma@samsung.com>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
drivers/gpu/drm/exynos/exynos_hdmi.c
drivers/gpu/drm/exynos/regs-hdmi.h

index 26a90e0..2eea0b4 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/of_gpio.h>
 #include <linux/phy/phy.h>
 
@@ -83,6 +84,8 @@ enum hdmi_soc_ver {
 
 struct hdmi_driver_data {
        unsigned int type;
+       const struct hdmiphy_config *phy_confs;
+       unsigned int phy_conf_count;
        unsigned int is_apb_phy:1;
        unsigned int soc_ver;
 };
@@ -211,6 +214,9 @@ struct hdmi_context {
        struct hdmi_resources           res;
 
        int                             hpd_gpio;
+       void __iomem                    *regs_hdmiphy;
+       const struct hdmiphy_config     *phy_confs;
+       unsigned int                    phy_conf_count;
        u32                             max_pixel_clock;
 
        struct hdmi_driver_data         *drv_data;
@@ -221,21 +227,6 @@ struct hdmiphy_config {
        u8 conf[32];
 };
 
-struct hdmi_driver_data exynos4210_hdmi_driver_data = {
-       .type   = HDMI_TYPE13,
-       .soc_ver = EXYNOS4,
-};
-
-struct hdmi_driver_data exynos4212_hdmi_driver_data = {
-       .type   = HDMI_TYPE14,
-       .soc_ver = EXYNOS4,
-};
-
-struct hdmi_driver_data exynos5_hdmi_driver_data = {
-       .type   = HDMI_TYPE14,
-       .soc_ver = EXYNOS5,
-};
-
 /* list of phy config settings */
 static const struct hdmiphy_config hdmiphy_v13_configs[] = {
        {
@@ -450,6 +441,30 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
        },
 };
 
+struct hdmi_driver_data exynos4210_hdmi_driver_data = {
+       .type   = HDMI_TYPE13,
+       .soc_ver = EXYNOS4,
+       .phy_confs      = hdmiphy_v13_configs,
+       .phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
+       .is_apb_phy     = 0,
+};
+
+struct hdmi_driver_data exynos4212_hdmi_driver_data = {
+       .type   = HDMI_TYPE14,
+       .soc_ver = EXYNOS4,
+       .phy_confs      = hdmiphy_v14_configs,
+       .phy_conf_count = ARRAY_SIZE(hdmiphy_v14_configs),
+       .is_apb_phy     = 0,
+};
+
+struct hdmi_driver_data exynos5_hdmi_driver_data = {
+       .type   = HDMI_TYPE14,
+       .soc_ver = EXYNOS5,
+       .phy_confs      = hdmiphy_v13_configs,
+       .phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
+       .is_apb_phy     = 0,
+};
+
 struct hdmi_infoframe {
        enum HDMI_PACKET_TYPE type;
        u8 ver;
@@ -475,6 +490,48 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
        writel(value, hdata->regs + reg_id);
 }
 
+static int hdmiphy_reg_writeb(struct hdmi_context *hdata,
+                       u32 reg_offset, u8 value)
+{
+       if (hdata->hdmiphy_port) {
+               u8 buffer[2];
+               int ret;
+
+               buffer[0] = reg_offset;
+               buffer[1] = value;
+
+               ret = i2c_master_send(hdata->hdmiphy_port, buffer, 2);
+               if (ret == 2)
+                       return 0;
+               return ret;
+       } else {
+               writeb(value, hdata->regs_hdmiphy + (reg_offset<<2));
+               return 0;
+       }
+}
+
+static int hdmiphy_reg_write_buf(struct hdmi_context *hdata,
+                       u32 reg_offset, const u8 *buf, u32 len)
+{
+       if ((reg_offset + len) > 32)
+               return -EINVAL;
+
+       if (hdata->hdmiphy_port) {
+               int ret;
+
+               ret = i2c_master_send(hdata->hdmiphy_port, buf, len);
+               if (ret == len)
+                       return 0;
+               return ret;
+       } else {
+               int i;
+               for (i = 0; i < len; i++)
+                       writeb(buf[i], hdata->regs_hdmiphy +
+                               ((reg_offset + i)<<2));
+               return 0;
+       }
+}
+
 static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
 {
 #define DUMPREG(reg_id) \
@@ -857,20 +914,10 @@ static int hdmi_get_modes(struct drm_connector *connector)
 
 static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock)
 {
-       const struct hdmiphy_config *confs;
-       int count, i;
-
-       if (hdata->drv_data->type == HDMI_TYPE13) {
-               confs = hdmiphy_v13_configs;
-               count = ARRAY_SIZE(hdmiphy_v13_configs);
-       } else if (hdata->drv_data->type == HDMI_TYPE14) {
-               confs = hdmiphy_v14_configs;
-               count = ARRAY_SIZE(hdmiphy_v14_configs);
-       } else
-               return -EINVAL;
+       int i;
 
-       for (i = 0; i < count; i++)
-               if (confs[i].pixel_clock == pixel_clock)
+       for (i = 0; i < hdata->phy_conf_count; i++)
+               if (hdata->phy_confs[i].pixel_clock == pixel_clock)
                        return i;
 
        DRM_DEBUG_KMS("Could not find phy config for %d\n", pixel_clock);
@@ -1525,18 +1572,9 @@ static void hdmiphy_poweroff(struct hdmi_context *hdata)
 
 static void hdmiphy_conf_apply(struct hdmi_context *hdata)
 {
-       const u8 *hdmiphy_data;
-       u8 buffer[32];
-       u8 operation[2];
-       u8 read_buffer[32] = {0, };
        int ret;
        int i;
 
-       if (!hdata->hdmiphy_port) {
-               DRM_ERROR("hdmiphy is not attached\n");
-               return;
-       }
-
        /* pixel clock */
        i = hdmi_find_phy_conf(hdata, hdata->mode_conf.pixel_clock);
        if (i < 0) {
@@ -1544,39 +1582,20 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
                return;
        }
 
-       if (hdata->drv_data->type == HDMI_TYPE13)
-               hdmiphy_data = hdmiphy_v13_configs[i].conf;
-       else
-               hdmiphy_data = hdmiphy_v14_configs[i].conf;
-
-       memcpy(buffer, hdmiphy_data, 32);
-       ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
-       if (ret != 32) {
-               DRM_ERROR("failed to configure HDMIPHY via I2C\n");
+       ret = hdmiphy_reg_write_buf(hdata, 0, hdata->phy_confs[i].conf, 32);
+       if (ret) {
+               DRM_ERROR("failed to configure hdmiphy\n");
                return;
        }
 
        usleep_range(10000, 12000);
 
-       /* operation mode */
-       operation[0] = 0x1f;
-       operation[1] = 0x80;
-
-       ret = i2c_master_send(hdata->hdmiphy_port, operation, 2);
-       if (ret != 2) {
-               DRM_ERROR("failed to enable hdmiphy\n");
-               return;
-       }
-
-       ret = i2c_master_recv(hdata->hdmiphy_port, read_buffer, 32);
-       if (ret < 0) {
+       ret = hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+                               HDMI_PHY_DISABLE_MODE_SET);
+       if (ret) {
                DRM_ERROR("failed to read hdmiphy config\n");
                return;
        }
-
-       for (i = 0; i < ret; i++)
-               DRM_DEBUG_KMS("hdmiphy[0x%02x] write[0x%02x] - "
-                       "recv [0x%02x]\n", i, buffer[i], read_buffer[i]);
 }
 
 static void hdmi_conf_apply(struct hdmi_context *hdata)
@@ -2139,6 +2158,8 @@ static int hdmi_probe(struct platform_device *pdev)
                return -ENODEV;
 
        hdata->drv_data = (struct hdmi_driver_data *)match->data;
+       hdata->phy_confs = hdata->drv_data->phy_confs;
+       hdata->phy_conf_count = hdata->drv_data->phy_conf_count;
 
        hdata->hpd_gpio = pdata->hpd_gpio;
        hdata->max_pixel_clock = pdata->max_pixel_clock;
@@ -2180,10 +2201,6 @@ out_get_ddc_adpt:
                return -ENODEV;
        }
 
-       /* Not support APB PHY yet. */
-       if (hdata->drv_data->is_apb_phy)
-               return -EPERM;
-
        phy_node = hdmi_legacy_phy_dt_binding(dev);
        if (phy_node)
                goto out_get_phy_port;
@@ -2197,11 +2214,20 @@ out_get_ddc_adpt:
        }
 
 out_get_phy_port:
-       hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
-       if (!hdata->hdmiphy_port) {
-               DRM_ERROR("Failed to get hdmi phy i2c client from node\n");
-               ret = -ENODEV;
-               goto err_ddc;
+       if (hdata->drv_data->is_apb_phy) {
+               hdata->regs_hdmiphy = of_iomap(phy_node, 0);
+               if (!hdata->regs_hdmiphy) {
+                       DRM_ERROR("failed to ioremap hdmi phy\n");
+                       ret = -ENOMEM;
+                       goto err_ddc;
+               }
+       } else {
+               hdata->hdmiphy_port = of_find_i2c_device_by_node(phy_node);
+               if (!hdata->hdmiphy_port) {
+                       DRM_ERROR("Failed to get hdmi phy i2c client\n");
+                       ret = -ENODEV;
+                       goto err_ddc;
+               }
        }
 
        hdata->irq = gpio_to_irq(hdata->hpd_gpio);
@@ -2229,7 +2255,8 @@ out_get_phy_port:
        return 0;
 
 err_hdmiphy:
-       put_device(&hdata->hdmiphy_port->dev);
+       if (hdata->hdmiphy_port)
+               put_device(&hdata->hdmiphy_port->dev);
 err_ddc:
        put_device(&hdata->ddc_adpt->dev);
        return ret;
@@ -2241,7 +2268,9 @@ static int hdmi_remove(struct platform_device *pdev)
        struct exynos_drm_display *display = get_hdmi_display(dev);
        struct hdmi_context *hdata = display->ctx;
 
-       put_device(&hdata->hdmiphy_port->dev);
+       if (hdata->hdmiphy_port)
+               put_device(&hdata->hdmiphy_port->dev);
+
        put_device(&hdata->ddc_adpt->dev);
        pm_runtime_disable(&pdev->dev);
 
index 5827883..9980b71 100644 (file)
 #define HDMI_TG_VACT_ST4_H             HDMI_TG_BASE(0x0074)
 #define HDMI_TG_3D                     HDMI_TG_BASE(0x00F0)
 
+/* HDMI PHY Registers Offsets*/
+#define HDMIPHY_MODE_SET_DONE          (0x7C >> 2)
+
+/* HDMI PHY Values */
+#define HDMI_PHY_DISABLE_MODE_SET      0x80
+#define HDMI_PHY_ENABLE_MODE_SET       0x00
+
 #endif /* SAMSUNG_REGS_HDMI_H */