#include <linux/backlight.h>
 #include <linux/delay.h>
 #include <linux/gpio/consumer.h>
-#include <linux/interrupt.h>
 #include <linux/jiffies.h>
 #include <linux/module.h>
 #include <linux/sched/signal.h>
 
        /* panel HW configuration from DT or platform data */
        struct gpio_desc *reset_gpio;
-       struct gpio_desc *ext_te_gpio;
 
        struct regulator_bulk_data supplies[DCS_REGULATOR_SUPPLY_NUM];
 
        /* runtime variables */
        bool enabled;
 
-       bool te_enabled;
-
-       atomic_t do_update;
-
-       struct delayed_work te_timeout_work;
-
        bool intro_printed;
 
        struct workqueue_struct *workqueue;
 
 #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
 
-static irqreturn_t dsicm_te_isr(int irq, void *data);
-static void dsicm_te_timeout_work_callback(struct work_struct *work);
 static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable);
 
 static int dsicm_panel_reset(struct panel_drv_data *ddata);
        if (r)
                goto err;
 
-       if (ddata->ext_te_gpio)
-               disable_irq(gpiod_to_irq(ddata->ext_te_gpio));
-
        src->ops->dsi.disable(src, false, true);
 
        ddata->ulps_enabled = true;
                goto err2;
        }
 
-       if (ddata->ext_te_gpio)
-               enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
-
        dsicm_queue_ulps_work(ddata);
 
        ddata->ulps_enabled = false;
        dev_err(&ddata->dsi->dev, "failed to exit ULPS");
 
        r = dsicm_panel_reset(ddata);
-       if (!r) {
-               if (ddata->ext_te_gpio)
-                       enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
+       if (!r)
                ddata->ulps_enabled = false;
-       }
 
        dsicm_queue_ulps_work(ddata);
 
        if (r)
                goto err;
 
-       r = _dsicm_enable_te(ddata, ddata->te_enabled);
+       r = _dsicm_enable_te(ddata, true);
        if (r)
                goto err;
 
        src->ops->dsi.bus_unlock(src);
 }
 
-static irqreturn_t dsicm_te_isr(int irq, void *data)
-{
-       struct panel_drv_data *ddata = data;
-       struct omap_dss_device *src = ddata->src;
-       int old;
-       int r;
-
-       old = atomic_cmpxchg(&ddata->do_update, 1, 0);
-
-       if (old) {
-               cancel_delayed_work(&ddata->te_timeout_work);
-
-               r = src->ops->dsi.update(src, ddata->dsi->channel, dsicm_framedone_cb,
-                               ddata);
-               if (r)
-                       goto err;
-       }
-
-       return IRQ_HANDLED;
-err:
-       dev_err(&ddata->dsi->dev, "start update failed\n");
-       src->ops->dsi.bus_unlock(src);
-       return IRQ_HANDLED;
-}
-
-static void dsicm_te_timeout_work_callback(struct work_struct *work)
-{
-       struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
-                                       te_timeout_work.work);
-       struct omap_dss_device *src = ddata->src;
-
-       dev_err(&ddata->dsi->dev, "TE not received for 250ms!\n");
-
-       atomic_set(&ddata->do_update, 0);
-       src->ops->dsi.bus_unlock(src);
-}
-
 static int dsicm_update(struct omap_dss_device *dssdev,
                                    u16 x, u16 y, u16 w, u16 h)
 {
        if (r)
                goto err;
 
-       if (ddata->te_enabled && ddata->ext_te_gpio) {
-               schedule_delayed_work(&ddata->te_timeout_work,
-                               msecs_to_jiffies(250));
-               atomic_set(&ddata->do_update, 1);
-       } else {
-               r = src->ops->dsi.update(src, ddata->dsi->channel, dsicm_framedone_cb,
-                               ddata);
-               if (r)
-                       goto err;
-       }
+       r = src->ops->dsi.update(src, ddata->dsi->channel, dsicm_framedone_cb,
+                       ddata);
+       if (r)
+               goto err;
 
        /* note: no bus_unlock here. unlock is src framedone_cb */
        mutex_unlock(&ddata->lock);
        else
                r = mipi_dsi_dcs_set_tear_off(dsi);
 
-       if (!ddata->ext_te_gpio)
-               src->ops->dsi.enable_te(src, enable);
+       src->ops->dsi.enable_te(src, enable);
 
        /* possible panel bug */
        msleep(100);
                return err;
        }
 
-       ddata->ext_te_gpio = devm_gpiod_get_optional(&dsi->dev, "te",
-                                                    GPIOD_IN);
-       if (IS_ERR(ddata->ext_te_gpio)) {
-               err = PTR_ERR(ddata->ext_te_gpio);
-               dev_err(&dsi->dev, "TE gpio request failed: %d", err);
-               return err;
-       }
-
        err = of_get_display_timing(node, "panel-timing", &timing);
        if (!err) {
                videomode_from_timing(&timing, &ddata->vm);
 
        mutex_init(&ddata->lock);
 
-       atomic_set(&ddata->do_update, 0);
-
-       if (ddata->ext_te_gpio) {
-               r = devm_request_irq(dev, gpiod_to_irq(ddata->ext_te_gpio),
-                               dsicm_te_isr,
-                               IRQF_TRIGGER_RISING,
-                               "taal vsync", ddata);
-
-               if (r) {
-                       dev_err(dev, "IRQ request failed\n");
-                       goto err_reg;
-               }
-
-               INIT_DEFERRABLE_WORK(&ddata->te_timeout_work,
-                                       dsicm_te_timeout_work_callback);
-
-               dev_dbg(dev, "Using GPIO TE\n");
-       }
-
        ddata->workqueue = create_singlethread_workqueue("dsicm_wq");
        if (!ddata->workqueue) {
                r = -ENOMEM;
 
 #include <linux/device.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
+#include <linux/irq.h>
 #include <linux/delay.h>
+#include <linux/gpio/consumer.h>
 #include <linux/mutex.h>
 #include <linux/module.h>
 #include <linux/semaphore.h>
        unsigned int update_bytes;
 #endif
 
+       /* external TE GPIO */
+       struct gpio_desc *te_gpio;
+       int te_irq;
+       struct delayed_work te_timeout_work;
+       atomic_t do_ext_te_update;
+
        bool te_enabled;
        bool ulps_enabled;
 
        dsi_handle_framedone(dsi, 0);
 }
 
+static int _dsi_update(struct dsi_data *dsi)
+{
+       dsi_perf_mark_setup(dsi);
+
+#ifdef DSI_PERF_MEASURE
+       dsi->update_bytes = dsi->vm.hactive * dsi->vm.vactive *
+               mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt) / 8;
+#endif
+       dsi_update_screen_dispc(dsi);
+
+       return 0;
+}
+
 static int dsi_update(struct omap_dss_device *dssdev, int channel,
                void (*callback)(int, void *), void *data)
 {
        struct dsi_data *dsi = to_dsi_data(dssdev);
 
-       dsi_perf_mark_setup(dsi);
-
        dsi->update_channel = channel;
-
        dsi->framedone_callback = callback;
        dsi->framedone_data = data;
 
-#ifdef DSI_PERF_MEASURE
-       dsi->update_bytes = dsi->vm.hactive * dsi->vm.vactive *
-               mipi_dsi_pixel_format_to_bpp(dsi->pix_fmt) / 8;
-#endif
-       dsi_update_screen_dispc(dsi);
+       if (dsi->te_enabled && dsi->te_gpio) {
+               schedule_delayed_work(&dsi->te_timeout_work,
+                                     msecs_to_jiffies(250));
+               atomic_set(&dsi->do_ext_te_update, 1);
+       } else {
+               _dsi_update(dsi);
+       }
 
        return 0;
 }
        struct dsi_data *dsi = to_dsi_data(dssdev);
 
        dsi->te_enabled = enable;
+
+       if (dsi->te_gpio) {
+               if (enable)
+                       enable_irq(dsi->te_irq);
+               else
+                       disable_irq(dsi->te_irq);
+       }
+
        return 0;
 }
 
        },
 };
 
+static irqreturn_t omap_dsi_te_irq_handler(int irq, void *dev_id)
+{
+       struct dsi_data *dsi = (struct dsi_data *)dev_id;
+       int old;
+
+       old = atomic_cmpxchg(&dsi->do_ext_te_update, 1, 0);
+       if (old) {
+               cancel_delayed_work(&dsi->te_timeout_work);
+               _dsi_update(dsi);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static void omap_dsi_te_timeout_work_callback(struct work_struct *work)
+{
+       struct dsi_data *dsi =
+               container_of(work, struct dsi_data, te_timeout_work.work);
+       int old;
+
+       old = atomic_cmpxchg(&dsi->do_ext_te_update, 1, 0);
+       if (old) {
+               dev_err(dsi->dev, "TE not received for 250ms!\n");
+               _dsi_update(dsi);
+       }
+}
+
+static int omap_dsi_register_te_irq(struct dsi_data *dsi,
+                                   struct mipi_dsi_device *client)
+{
+       int err;
+       int te_irq;
+
+       dsi->te_gpio = gpiod_get(&client->dev, "te-gpios", GPIOD_IN);
+       if (IS_ERR(dsi->te_gpio)) {
+               err = PTR_ERR(dsi->te_gpio);
+
+               if (err == -ENOENT) {
+                       dsi->te_gpio = NULL;
+                       return 0;
+               }
+
+               dev_err(dsi->dev, "Could not get TE gpio: %d\n", err);
+               return err;
+       }
+
+       te_irq = gpiod_to_irq(dsi->te_gpio);
+       if (te_irq < 0) {
+               gpiod_put(dsi->te_gpio);
+               dsi->te_gpio = NULL;
+               return -EINVAL;
+       }
+
+       dsi->te_irq = te_irq;
+
+       irq_set_status_flags(te_irq, IRQ_NOAUTOEN);
+
+       err = request_threaded_irq(te_irq, NULL, omap_dsi_te_irq_handler,
+                                  IRQF_TRIGGER_RISING, "TE", dsi);
+       if (err) {
+               dev_err(dsi->dev, "request irq failed with %d\n", err);
+               gpiod_put(dsi->te_gpio);
+               dsi->te_gpio = NULL;
+               return err;
+       }
+
+       INIT_DEFERRABLE_WORK(&dsi->te_timeout_work,
+                            omap_dsi_te_timeout_work_callback);
+
+       dev_dbg(dsi->dev, "Using GPIO TE\n");
+
+       return 0;
+}
+
+static void omap_dsi_unregister_te_irq(struct dsi_data *dsi)
+{
+       if (dsi->te_gpio) {
+               free_irq(dsi->te_irq, dsi);
+               cancel_delayed_work(&dsi->te_timeout_work);
+               gpiod_put(dsi->te_gpio);
+               dsi->te_gpio = NULL;
+       }
+}
+
 static int omap_dsi_host_attach(struct mipi_dsi_host *host,
                                struct mipi_dsi_device *client)
 {
        struct dsi_data *dsi = host_to_omap(host);
        unsigned int channel = client->channel;
+       int r;
 
        if (channel > 3)
                return -EINVAL;
                return -EINVAL;
        }
 
-       dsi->vc[channel].dest = client;
+       atomic_set(&dsi->do_ext_te_update, 0);
 
-       dsi->pix_fmt = client->format;
-       if (client->mode_flags & MIPI_DSI_MODE_VIDEO)
+       if (client->mode_flags & MIPI_DSI_MODE_VIDEO) {
                dsi->mode = OMAP_DSS_DSI_VIDEO_MODE;
-       else
+       } else {
+               r = omap_dsi_register_te_irq(dsi, client);
+               if (r)
+                       return r;
+
                dsi->mode = OMAP_DSS_DSI_CMD_MODE;
+       }
+
+       dsi->vc[channel].dest = client;
+       dsi->pix_fmt = client->format;
 
        return 0;
 }
        if (dsi->vc[channel].dest != client)
                return -EINVAL;
 
+       omap_dsi_unregister_te_irq(dsi);
        dsi->vc[channel].dest = NULL;
        return 0;
 }