aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_dsi.c98
1 files changed, 96 insertions, 2 deletions
diff --git a/drivers/gpu/drm/exynos/exynos_drm_dsi.c b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
index 58bfb2a2a19b..a19107af1c3b 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dsi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dsi.c
@@ -16,7 +16,9 @@
16#include <drm/drm_panel.h> 16#include <drm/drm_panel.h>
17 17
18#include <linux/clk.h> 18#include <linux/clk.h>
19#include <linux/gpio/consumer.h>
19#include <linux/irq.h> 20#include <linux/irq.h>
21#include <linux/of_gpio.h>
20#include <linux/phy/phy.h> 22#include <linux/phy/phy.h>
21#include <linux/regulator/consumer.h> 23#include <linux/regulator/consumer.h>
22#include <linux/component.h> 24#include <linux/component.h>
@@ -24,6 +26,7 @@
24#include <video/mipi_display.h> 26#include <video/mipi_display.h>
25#include <video/videomode.h> 27#include <video/videomode.h>
26 28
29#include "exynos_drm_crtc.h"
27#include "exynos_drm_drv.h" 30#include "exynos_drm_drv.h"
28 31
29/* returns true iff both arguments logically differs */ 32/* returns true iff both arguments logically differs */
@@ -247,6 +250,7 @@ struct exynos_dsi {
247 struct clk *bus_clk; 250 struct clk *bus_clk;
248 struct regulator_bulk_data supplies[2]; 251 struct regulator_bulk_data supplies[2];
249 int irq; 252 int irq;
253 int te_gpio;
250 254
251 u32 pll_clk_rate; 255 u32 pll_clk_rate;
252 u32 burst_clk_rate; 256 u32 burst_clk_rate;
@@ -954,17 +958,89 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
954 return IRQ_HANDLED; 958 return IRQ_HANDLED;
955} 959}
956 960
961static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
962{
963 struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
964 struct drm_encoder *encoder = dsi->encoder;
965
966 if (dsi->state & DSIM_STATE_ENABLED)
967 exynos_drm_crtc_te_handler(encoder->crtc);
968
969 return IRQ_HANDLED;
970}
971
972static void exynos_dsi_enable_irq(struct exynos_dsi *dsi)
973{
974 enable_irq(dsi->irq);
975
976 if (gpio_is_valid(dsi->te_gpio))
977 enable_irq(gpio_to_irq(dsi->te_gpio));
978}
979
980static void exynos_dsi_disable_irq(struct exynos_dsi *dsi)
981{
982 if (gpio_is_valid(dsi->te_gpio))
983 disable_irq(gpio_to_irq(dsi->te_gpio));
984
985 disable_irq(dsi->irq);
986}
987
957static int exynos_dsi_init(struct exynos_dsi *dsi) 988static int exynos_dsi_init(struct exynos_dsi *dsi)
958{ 989{
959 exynos_dsi_enable_clock(dsi); 990 exynos_dsi_enable_clock(dsi);
960 exynos_dsi_reset(dsi); 991 exynos_dsi_reset(dsi);
961 enable_irq(dsi->irq); 992 exynos_dsi_enable_irq(dsi);
962 exynos_dsi_wait_for_reset(dsi); 993 exynos_dsi_wait_for_reset(dsi);
963 exynos_dsi_init_link(dsi); 994 exynos_dsi_init_link(dsi);
964 995
965 return 0; 996 return 0;
966} 997}
967 998
999static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi)
1000{
1001 int ret;
1002
1003 dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0);
1004 if (!gpio_is_valid(dsi->te_gpio)) {
1005 dev_err(dsi->dev, "no te-gpios specified\n");
1006 ret = dsi->te_gpio;
1007 goto out;
1008 }
1009
1010 ret = gpio_request_one(dsi->te_gpio, GPIOF_IN, "te_gpio");
1011 if (ret) {
1012 dev_err(dsi->dev, "gpio request failed with %d\n", ret);
1013 goto out;
1014 }
1015
1016 /*
1017 * This TE GPIO IRQ should not be set to IRQ_NOAUTOEN, because panel
1018 * calls drm_panel_init() first then calls mipi_dsi_attach() in probe().
1019 * It means that te_gpio is invalid when exynos_dsi_enable_irq() is
1020 * called by drm_panel_init() before panel is attached.
1021 */
1022 ret = request_threaded_irq(gpio_to_irq(dsi->te_gpio),
1023 exynos_dsi_te_irq_handler, NULL,
1024 IRQF_TRIGGER_RISING, "TE", dsi);
1025 if (ret) {
1026 dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
1027 gpio_free(dsi->te_gpio);
1028 goto out;
1029 }
1030
1031out:
1032 return ret;
1033}
1034
1035static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
1036{
1037 if (gpio_is_valid(dsi->te_gpio)) {
1038 free_irq(gpio_to_irq(dsi->te_gpio), dsi);
1039 gpio_free(dsi->te_gpio);
1040 dsi->te_gpio = -ENOENT;
1041 }
1042}
1043
968static int exynos_dsi_host_attach(struct mipi_dsi_host *host, 1044static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
969 struct mipi_dsi_device *device) 1045 struct mipi_dsi_device *device)
970{ 1046{
@@ -978,6 +1054,19 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
978 if (dsi->connector.dev) 1054 if (dsi->connector.dev)
979 drm_helper_hpd_irq_event(dsi->connector.dev); 1055 drm_helper_hpd_irq_event(dsi->connector.dev);
980 1056
1057 /*
1058 * This is a temporary solution and should be made by more generic way.
1059 *
1060 * If attached panel device is for command mode one, dsi should register
1061 * TE interrupt handler.
1062 */
1063 if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) {
1064 int ret = exynos_dsi_register_te_irq(dsi);
1065
1066 if (ret)
1067 return ret;
1068 }
1069
981 return 0; 1070 return 0;
982} 1071}
983 1072
@@ -986,6 +1075,8 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
986{ 1075{
987 struct exynos_dsi *dsi = host_to_dsi(host); 1076 struct exynos_dsi *dsi = host_to_dsi(host);
988 1077
1078 exynos_dsi_unregister_te_irq(dsi);
1079
989 dsi->panel_node = NULL; 1080 dsi->panel_node = NULL;
990 1081
991 if (dsi->connector.dev) 1082 if (dsi->connector.dev)
@@ -1099,7 +1190,7 @@ static void exynos_dsi_poweroff(struct exynos_dsi *dsi)
1099 1190
1100 exynos_dsi_disable_clock(dsi); 1191 exynos_dsi_disable_clock(dsi);
1101 1192
1102 disable_irq(dsi->irq); 1193 exynos_dsi_disable_irq(dsi);
1103 } 1194 }
1104 1195
1105 dsi->state &= ~DSIM_STATE_CMD_LPM; 1196 dsi->state &= ~DSIM_STATE_CMD_LPM;
@@ -1445,6 +1536,9 @@ static int exynos_dsi_probe(struct platform_device *pdev)
1445 goto err_del_component; 1536 goto err_del_component;
1446 } 1537 }
1447 1538
1539 /* To be checked as invalid one */
1540 dsi->te_gpio = -ENOENT;
1541
1448 init_completion(&dsi->completed); 1542 init_completion(&dsi->completed);
1449 spin_lock_init(&dsi->transfer_lock); 1543 spin_lock_init(&dsi->transfer_lock);
1450 INIT_LIST_HEAD(&dsi->transfer_list); 1544 INIT_LIST_HEAD(&dsi->transfer_list);