aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Bresticker <abrestic@chromium.org>2014-04-21 18:39:10 -0400
committerInki Dae <daeinki@gmail.com>2014-06-01 13:07:05 -0400
commitb8b52471e87a713e61d26fa2f546fda0fb04e8fd (patch)
treee9bdacf10aec08ea529220fbe76c43c0a2e1535f
parentfbc2063d7b76d58e47a74b845148b3a9db052f16 (diff)
drm/exynos: dp: support hotplug detection via GPIO
Certain bridge chips use a GPIO to indicate the cable status instead of the I_DP_HPD pin. This adds an optional device-tree property, "samsung,hpd-gpio", to the exynos-dp controller which indicates that the specified GPIO should be used for hotplug detection. The GPIO is then set up as an edge-triggered interrupt where the rising edge indicates hotplug-in and the falling edge indicates hotplug-out. Signed-off-by: Andrew Bresticker <abrestic@chromium.org> Signed-off-by: Rahul Sharma <rahul.sharma@samsung.com> Signed-off-by: Ajay Kumar <ajaykumar.rs@samsung.com> Acked-by: Jingoo Han <jg1.han@samsung.com> Signed-off-by: Inki Dae <inki.dae@samsung.com>
-rw-r--r--Documentation/devicetree/bindings/video/exynos_dp.txt4
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.c32
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_core.h1
-rw-r--r--drivers/gpu/drm/exynos/exynos_dp_reg.c44
4 files changed, 66 insertions, 15 deletions
diff --git a/Documentation/devicetree/bindings/video/exynos_dp.txt b/Documentation/devicetree/bindings/video/exynos_dp.txt
index 57ccdde02c3a..53dbccfa80ca 100644
--- a/Documentation/devicetree/bindings/video/exynos_dp.txt
+++ b/Documentation/devicetree/bindings/video/exynos_dp.txt
@@ -62,6 +62,10 @@ Optional properties for dp-controller:
62 -hsync-active-high: 62 -hsync-active-high:
63 HSYNC polarity configuration. 63 HSYNC polarity configuration.
64 High if defined, Low if not defined 64 High if defined, Low if not defined
65 -samsung,hpd-gpio:
66 Hotplug detect GPIO.
67 Indicates which GPIO should be used for hotplug
68 detection
65 69
66Example: 70Example:
67 71
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.c b/drivers/gpu/drm/exynos/exynos_dp_core.c
index 1f8914b44714..2b30c55ab050 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.c
@@ -18,6 +18,8 @@
18#include <linux/interrupt.h> 18#include <linux/interrupt.h>
19#include <linux/delay.h> 19#include <linux/delay.h>
20#include <linux/of.h> 20#include <linux/of.h>
21#include <linux/of_gpio.h>
22#include <linux/gpio.h>
21#include <linux/component.h> 23#include <linux/component.h>
22#include <linux/phy/phy.h> 24#include <linux/phy/phy.h>
23#include <video/of_display_timing.h> 25#include <video/of_display_timing.h>
@@ -1217,6 +1219,7 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
1217 struct drm_device *drm_dev = data; 1219 struct drm_device *drm_dev = data;
1218 struct resource *res; 1220 struct resource *res;
1219 struct exynos_dp_device *dp; 1221 struct exynos_dp_device *dp;
1222 unsigned int irq_flags;
1220 1223
1221 int ret = 0; 1224 int ret = 0;
1222 1225
@@ -1254,7 +1257,30 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
1254 if (IS_ERR(dp->reg_base)) 1257 if (IS_ERR(dp->reg_base))
1255 return PTR_ERR(dp->reg_base); 1258 return PTR_ERR(dp->reg_base);
1256 1259
1257 dp->irq = platform_get_irq(pdev, 0); 1260 dp->hpd_gpio = of_get_named_gpio(dev->of_node, "samsung,hpd-gpio", 0);
1261
1262 if (gpio_is_valid(dp->hpd_gpio)) {
1263 /*
1264 * Set up the hotplug GPIO from the device tree as an interrupt.
1265 * Simply specifying a different interrupt in the device tree
1266 * doesn't work since we handle hotplug rather differently when
1267 * using a GPIO. We also need the actual GPIO specifier so
1268 * that we can get the current state of the GPIO.
1269 */
1270 ret = devm_gpio_request_one(&pdev->dev, dp->hpd_gpio, GPIOF_IN,
1271 "hpd_gpio");
1272 if (ret) {
1273 dev_err(&pdev->dev, "failed to get hpd gpio\n");
1274 return ret;
1275 }
1276 dp->irq = gpio_to_irq(dp->hpd_gpio);
1277 irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
1278 } else {
1279 dp->hpd_gpio = -ENODEV;
1280 dp->irq = platform_get_irq(pdev, 0);
1281 irq_flags = 0;
1282 }
1283
1258 if (dp->irq == -ENXIO) { 1284 if (dp->irq == -ENXIO) {
1259 dev_err(&pdev->dev, "failed to get irq\n"); 1285 dev_err(&pdev->dev, "failed to get irq\n");
1260 return -ENODEV; 1286 return -ENODEV;
@@ -1266,8 +1292,8 @@ static int exynos_dp_bind(struct device *dev, struct device *master, void *data)
1266 1292
1267 exynos_dp_init_dp(dp); 1293 exynos_dp_init_dp(dp);
1268 1294
1269 ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler, 0, 1295 ret = devm_request_irq(&pdev->dev, dp->irq, exynos_dp_irq_handler,
1270 "exynos-dp", dp); 1296 irq_flags, "exynos-dp", dp);
1271 if (ret) { 1297 if (ret) {
1272 dev_err(&pdev->dev, "failed to request irq\n"); 1298 dev_err(&pdev->dev, "failed to request irq\n");
1273 return ret; 1299 return ret;
diff --git a/drivers/gpu/drm/exynos/exynos_dp_core.h b/drivers/gpu/drm/exynos/exynos_dp_core.h
index d6a900d4ee40..56fa43eb7133 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_core.h
+++ b/drivers/gpu/drm/exynos/exynos_dp_core.h
@@ -159,6 +159,7 @@ struct exynos_dp_device {
159 struct work_struct hotplug_work; 159 struct work_struct hotplug_work;
160 struct phy *phy; 160 struct phy *phy;
161 int dpms_mode; 161 int dpms_mode;
162 int hpd_gpio;
162 163
163 struct exynos_drm_panel_info panel; 164 struct exynos_drm_panel_info panel;
164}; 165};
diff --git a/drivers/gpu/drm/exynos/exynos_dp_reg.c b/drivers/gpu/drm/exynos/exynos_dp_reg.c
index b70da5052ff0..79291a2ce50d 100644
--- a/drivers/gpu/drm/exynos/exynos_dp_reg.c
+++ b/drivers/gpu/drm/exynos/exynos_dp_reg.c
@@ -13,6 +13,7 @@
13#include <linux/device.h> 13#include <linux/device.h>
14#include <linux/io.h> 14#include <linux/io.h>
15#include <linux/delay.h> 15#include <linux/delay.h>
16#include <linux/gpio.h>
16 17
17#include "exynos_dp_core.h" 18#include "exynos_dp_core.h"
18#include "exynos_dp_reg.h" 19#include "exynos_dp_reg.h"
@@ -326,6 +327,9 @@ void exynos_dp_clear_hotplug_interrupts(struct exynos_dp_device *dp)
326{ 327{
327 u32 reg; 328 u32 reg;
328 329
330 if (gpio_is_valid(dp->hpd_gpio))
331 return;
332
329 reg = HOTPLUG_CHG | HPD_LOST | PLUG; 333 reg = HOTPLUG_CHG | HPD_LOST | PLUG;
330 writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); 334 writel(reg, dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
331 335
@@ -337,6 +341,9 @@ void exynos_dp_init_hpd(struct exynos_dp_device *dp)
337{ 341{
338 u32 reg; 342 u32 reg;
339 343
344 if (gpio_is_valid(dp->hpd_gpio))
345 return;
346
340 exynos_dp_clear_hotplug_interrupts(dp); 347 exynos_dp_clear_hotplug_interrupts(dp);
341 348
342 reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); 349 reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
@@ -348,19 +355,27 @@ enum dp_irq_type exynos_dp_get_irq_type(struct exynos_dp_device *dp)
348{ 355{
349 u32 reg; 356 u32 reg;
350 357
351 /* Parse hotplug interrupt status register */ 358 if (gpio_is_valid(dp->hpd_gpio)) {
352 reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4); 359 reg = gpio_get_value(dp->hpd_gpio);
360 if (reg)
361 return DP_IRQ_TYPE_HP_CABLE_IN;
362 else
363 return DP_IRQ_TYPE_HP_CABLE_OUT;
364 } else {
365 /* Parse hotplug interrupt status register */
366 reg = readl(dp->reg_base + EXYNOS_DP_COMMON_INT_STA_4);
353 367
354 if (reg & PLUG) 368 if (reg & PLUG)
355 return DP_IRQ_TYPE_HP_CABLE_IN; 369 return DP_IRQ_TYPE_HP_CABLE_IN;
356 370
357 if (reg & HPD_LOST) 371 if (reg & HPD_LOST)
358 return DP_IRQ_TYPE_HP_CABLE_OUT; 372 return DP_IRQ_TYPE_HP_CABLE_OUT;
359 373
360 if (reg & HOTPLUG_CHG) 374 if (reg & HOTPLUG_CHG)
361 return DP_IRQ_TYPE_HP_CHANGE; 375 return DP_IRQ_TYPE_HP_CHANGE;
362 376
363 return DP_IRQ_TYPE_UNKNOWN; 377 return DP_IRQ_TYPE_UNKNOWN;
378 }
364} 379}
365 380
366void exynos_dp_reset_aux(struct exynos_dp_device *dp) 381void exynos_dp_reset_aux(struct exynos_dp_device *dp)
@@ -402,9 +417,14 @@ int exynos_dp_get_plug_in_status(struct exynos_dp_device *dp)
402{ 417{
403 u32 reg; 418 u32 reg;
404 419
405 reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3); 420 if (gpio_is_valid(dp->hpd_gpio)) {
406 if (reg & HPD_STATUS) 421 if (gpio_get_value(dp->hpd_gpio))
407 return 0; 422 return 0;
423 } else {
424 reg = readl(dp->reg_base + EXYNOS_DP_SYS_CTL_3);
425 if (reg & HPD_STATUS)
426 return 0;
427 }
408 428
409 return -EINVAL; 429 return -EINVAL;
410} 430}