diff options
author | Andrew Bresticker <abrestic@chromium.org> | 2014-04-21 18:39:10 -0400 |
---|---|---|
committer | Inki Dae <daeinki@gmail.com> | 2014-06-01 13:07:05 -0400 |
commit | b8b52471e87a713e61d26fa2f546fda0fb04e8fd (patch) | |
tree | e9bdacf10aec08ea529220fbe76c43c0a2e1535f | |
parent | fbc2063d7b76d58e47a74b845148b3a9db052f16 (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.txt | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_dp_core.c | 32 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_dp_core.h | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/exynos/exynos_dp_reg.c | 44 |
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 | ||
66 | Example: | 70 | Example: |
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 | ||
366 | void exynos_dp_reset_aux(struct exynos_dp_device *dp) | 381 | void 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 | } |