diff mbox

[4/5] drm/exynos: add support for apb mapped phys in hdmi driver

Message ID 1396458826-3051-5-git-send-email-rahul.sharma@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Rahul Sharma April 2, 2014, 5:13 p.m. UTC
From: Rahul Sharma <Rahul.Sharma@samsung.com>

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>
---
 drivers/gpu/drm/exynos/exynos_hdmi.c |  142 +++++++++++++++++++++-------------
 drivers/gpu/drm/exynos/regs-hdmi.h   |    7 ++
 2 files changed, 96 insertions(+), 53 deletions(-)

Comments

Tomasz Figa April 10, 2014, 5:17 p.m. UTC | #1
Hi Rahul,

On 02.04.2014 19:13, Rahul Sharma wrote:
> From: Rahul Sharma <Rahul.Sharma@samsung.com>
>
> 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>
> ---
>   drivers/gpu/drm/exynos/exynos_hdmi.c |  142 +++++++++++++++++++++-------------
>   drivers/gpu/drm/exynos/regs-hdmi.h   |    7 ++
>   2 files changed, 96 insertions(+), 53 deletions(-)
>
> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
> index 5b2cfe7..5989770 100644
> --- a/drivers/gpu/drm/exynos/exynos_hdmi.c
> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
> @@ -33,6 +33,7 @@
>   #include <linux/regulator/consumer.h>
>   #include <linux/io.h>
>   #include <linux/of.h>
> +#include <linux/of_address.h>
>   #include <linux/i2c.h>
>   #include <linux/of_gpio.h>
>   #include <linux/hdmi.h>
> @@ -68,6 +69,8 @@ enum hdmi_type {
>
>   struct hdmi_driver_data {
>   	unsigned int type;
> +	const struct hdmiphy_config *phy_confs;
> +	unsigned int phy_conf_count;
>   	unsigned int is_apb_phy:1;
>   };
>
> @@ -196,9 +199,12 @@ struct hdmi_context {
>   	struct hdmi_resources		res;
>
>   	int				hpd_gpio;
> +	void __iomem			*regs_hdmiphy;
>   	struct regmap			*pmureg;
>
>   	enum hdmi_type			type;
> +	const struct hdmiphy_config	*phy_confs;
> +	unsigned int			phy_conf_count;
>   };
>
>   struct hdmiphy_config {
> @@ -206,14 +212,6 @@ struct hdmiphy_config {
>   	u8 conf[32];
>   };
>
> -struct hdmi_driver_data exynos4212_hdmi_driver_data = {
> -	.type	= HDMI_TYPE14,
> -};
> -
> -struct hdmi_driver_data exynos5_hdmi_driver_data = {
> -	.type	= HDMI_TYPE14,
> -};
> -
>   /* list of phy config settings */
>   static const struct hdmiphy_config hdmiphy_v13_configs[] = {
>   	{
> @@ -428,6 +426,21 @@ static const struct hdmiphy_config hdmiphy_v14_configs[] = {
>   	},
>   };
>
> +
> +struct hdmi_driver_data exynos4212_hdmi_driver_data = {
> +	.type		= HDMI_TYPE14,
> +	.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,
> +	.phy_confs	= hdmiphy_v13_configs,
> +	.phy_conf_count	= ARRAY_SIZE(hdmiphy_v13_configs),
> +	.is_apb_phy	= 0,
> +};
> +
>   static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
>   {
>   	return readl(hdata->regs + reg_id);
> @@ -447,6 +460,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);

reg_offset doesn't seem to be used in I2C code path in any way. Are you 
sure this is correct?

> +		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;
> +	}
> +}

I wonder if those functions couldn't be abstracted as two callbacks in 
hdmi_driver_data struct to eliminate such if clauses as above.

--
Best regards,
Tomasz
Rahul Sharma April 11, 2014, 2:36 a.m. UTC | #2
Thanks Tomasz,

On 10 April 2014 22:47, Tomasz Figa <t.figa@samsung.com> wrote:
> Hi Rahul,
>
> On 02.04.2014 19:13, Rahul Sharma wrote:
>>
>> From: Rahul Sharma <Rahul.Sharma@samsung.com>
>>
>> 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>
>> ---
>>   drivers/gpu/drm/exynos/exynos_hdmi.c |  142
>> +++++++++++++++++++++-------------
>>   drivers/gpu/drm/exynos/regs-hdmi.h   |    7 ++
>>   2 files changed, 96 insertions(+), 53 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c
>> b/drivers/gpu/drm/exynos/exynos_hdmi.c
>> index 5b2cfe7..5989770 100644
>> --- a/drivers/gpu/drm/exynos/exynos_hdmi.c
>> +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
>> @@ -33,6 +33,7 @@
>>   #include <linux/regulator/consumer.h>
>>   #include <linux/io.h>
>>   #include <linux/of.h>
>> +#include <linux/of_address.h>
>>   #include <linux/i2c.h>
>>   #include <linux/of_gpio.h>
>>   #include <linux/hdmi.h>
>> @@ -68,6 +69,8 @@ enum hdmi_type {
>>
>>   struct hdmi_driver_data {
>>         unsigned int type;
>> +       const struct hdmiphy_config *phy_confs;
>> +       unsigned int phy_conf_count;
>>         unsigned int is_apb_phy:1;
>>   };
>>
>> @@ -196,9 +199,12 @@ struct hdmi_context {
>>         struct hdmi_resources           res;
>>
>>         int                             hpd_gpio;
>> +       void __iomem                    *regs_hdmiphy;
>>         struct regmap                   *pmureg;
>>
>>         enum hdmi_type                  type;
>> +       const struct hdmiphy_config     *phy_confs;
>> +       unsigned int                    phy_conf_count;
>>   };
>>
>>   struct hdmiphy_config {
>> @@ -206,14 +212,6 @@ struct hdmiphy_config {
>>         u8 conf[32];
>>   };
>>
>> -struct hdmi_driver_data exynos4212_hdmi_driver_data = {
>> -       .type   = HDMI_TYPE14,
>> -};
>> -
>> -struct hdmi_driver_data exynos5_hdmi_driver_data = {
>> -       .type   = HDMI_TYPE14,
>> -};
>> -
>>   /* list of phy config settings */
>>   static const struct hdmiphy_config hdmiphy_v13_configs[] = {
>>         {
>> @@ -428,6 +426,21 @@ static const struct hdmiphy_config
>> hdmiphy_v14_configs[] = {
>>         },
>>   };
>>
>> +
>> +struct hdmi_driver_data exynos4212_hdmi_driver_data = {
>> +       .type           = HDMI_TYPE14,
>> +       .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,
>> +       .phy_confs      = hdmiphy_v13_configs,
>> +       .phy_conf_count = ARRAY_SIZE(hdmiphy_v13_configs),
>> +       .is_apb_phy     = 0,
>> +};
>> +
>>   static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
>>   {
>>         return readl(hdata->regs + reg_id);
>> @@ -447,6 +460,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);
>
>
> reg_offset doesn't seem to be used in I2C code path in any way. Are you sure
> this is correct?
>

yea ... reg_offset is not required for i2c write as first buffer in
buffer is taken as offset.

>> +               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;
>> +       }
>> +}
>
>
> I wonder if those functions couldn't be abstracted as two callbacks in
> hdmi_driver_data struct to eliminate such if clauses as above.
>

hmn...I can do that... but will it help in anyway. I will end up in changing
more code in probe, adding 4 callbacks. let me know if you want me to do
that.

Regards,
Rahul Sharma.

> --
> Best regards,
> Tomasz
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/dri-devel
diff mbox

Patch

diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 5b2cfe7..5989770 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -33,6 +33,7 @@ 
 #include <linux/regulator/consumer.h>
 #include <linux/io.h>
 #include <linux/of.h>
+#include <linux/of_address.h>
 #include <linux/i2c.h>
 #include <linux/of_gpio.h>
 #include <linux/hdmi.h>
@@ -68,6 +69,8 @@  enum hdmi_type {
 
 struct hdmi_driver_data {
 	unsigned int type;
+	const struct hdmiphy_config *phy_confs;
+	unsigned int phy_conf_count;
 	unsigned int is_apb_phy:1;
 };
 
@@ -196,9 +199,12 @@  struct hdmi_context {
 	struct hdmi_resources		res;
 
 	int				hpd_gpio;
+	void __iomem			*regs_hdmiphy;
 	struct regmap			*pmureg;
 
 	enum hdmi_type			type;
+	const struct hdmiphy_config	*phy_confs;
+	unsigned int			phy_conf_count;
 };
 
 struct hdmiphy_config {
@@ -206,14 +212,6 @@  struct hdmiphy_config {
 	u8 conf[32];
 };
 
-struct hdmi_driver_data exynos4212_hdmi_driver_data = {
-	.type	= HDMI_TYPE14,
-};
-
-struct hdmi_driver_data exynos5_hdmi_driver_data = {
-	.type	= HDMI_TYPE14,
-};
-
 /* list of phy config settings */
 static const struct hdmiphy_config hdmiphy_v13_configs[] = {
 	{
@@ -428,6 +426,21 @@  static const struct hdmiphy_config hdmiphy_v14_configs[] = {
 	},
 };
 
+
+struct hdmi_driver_data exynos4212_hdmi_driver_data = {
+	.type		= HDMI_TYPE14,
+	.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,
+	.phy_confs	= hdmiphy_v13_configs,
+	.phy_conf_count	= ARRAY_SIZE(hdmiphy_v13_configs),
+	.is_apb_phy	= 0,
+};
+
 static inline u32 hdmi_reg_read(struct hdmi_context *hdata, u32 reg_id)
 {
 	return readl(hdata->regs + reg_id);
@@ -447,6 +460,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) \
@@ -850,20 +905,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->type == HDMI_TYPE13) {
-		confs = hdmiphy_v13_configs;
-		count = ARRAY_SIZE(hdmiphy_v13_configs);
-	} else if (hdata->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);
@@ -1515,17 +1560,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];
 	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) {
@@ -1533,26 +1570,17 @@  static void hdmiphy_conf_apply(struct hdmi_context *hdata)
 		return;
 	}
 
-	if (hdata->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) {
+	ret = hdmiphy_reg_writeb(hdata, HDMIPHY_MODE_SET_DONE,
+				HDMI_PHY_DISABLE_MODE_SET);
+	if (ret) {
 		DRM_ERROR("failed to enable hdmiphy\n");
 		return;
 	}
@@ -2059,6 +2087,8 @@  static int hdmi_probe(struct platform_device *pdev)
 
 	drv_data = (struct hdmi_driver_data *)match->data;
 	hdata->type = drv_data->type;
+	hdata->phy_confs = drv_data->phy_confs;
+	hdata->phy_conf_count = drv_data->phy_conf_count;
 
 	hdata->hpd_gpio = pdata->hpd_gpio;
 	hdata->dev = dev;
@@ -2092,10 +2122,6 @@  static int hdmi_probe(struct platform_device *pdev)
 		return -ENODEV;
 	}
 
-	/* Not support APB PHY yet. */
-	if (drv_data->is_apb_phy)
-		return -EPERM;
-
 	/* hdmiphy i2c driver */
 	phy_node = of_parse_phandle(dev->of_node, "phy", 0);
 	if (!phy_node) {
@@ -2103,11 +2129,21 @@  static int hdmi_probe(struct platform_device *pdev)
 		ret = -ENODEV;
 		goto err_ddc;
 	}
-	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 (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);
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h
index 9811d6f..344a5db 100644
--- a/drivers/gpu/drm/exynos/regs-hdmi.h
+++ b/drivers/gpu/drm/exynos/regs-hdmi.h
@@ -578,6 +578,13 @@ 
 #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
+
 /* PMU Registers for PHY */
 #define PMU_HDMI_PHY_CONTROL		0x700
 #define PMU_HDMI_PHY_ENABLE_BIT	(1<<0)