summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/omapdrm/dss/dsi.c
diff options
context:
space:
mode:
authorSebastian Reichel <sebastian.reichel@collabora.com>2020-12-15 12:45:57 +0200
committerTomi Valkeinen <tomi.valkeinen@ti.com>2020-12-15 16:08:22 +0200
commit4c1b935fea54a4832161f0be2c01ac58be16698f (patch)
tree196a87e3de4d1dc71250a6f75b39c6180379aff7 /drivers/gpu/drm/omapdrm/dss/dsi.c
parent3220034b12153252499365aef03c5a69f714d1be (diff)
drm/omap: dsi: move TE GPIO handling into core
In preparation for removing custom DSS calls from the DSI panel driver, this moves support for external tearing event GPIOs into the DSI host driver. This way tearing events are always handled in the core resulting in simplification of the panel drivers. The TE GPIO acquisition follows works in the same way as the exynos DSI implementation. Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com> Link: https://patchwork.freedesktop.org/patch/msgid/20201215104657.802264-25-tomi.valkeinen@ti.com
Diffstat (limited to 'drivers/gpu/drm/omapdrm/dss/dsi.c')
-rw-r--r--drivers/gpu/drm/omapdrm/dss/dsi.c145
1 files changed, 133 insertions, 12 deletions
diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c
index 501784137708..9f8e1fd89036 100644
--- a/drivers/gpu/drm/omapdrm/dss/dsi.c
+++ b/drivers/gpu/drm/omapdrm/dss/dsi.c
@@ -14,7 +14,9 @@
#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>
@@ -368,6 +370,12 @@ struct dsi_data {
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;
@@ -3828,23 +3836,35 @@ static void dsi_framedone_irq_callback(void *data)
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;
}
@@ -4092,6 +4112,14 @@ static int dsi_enable_te(struct omap_dss_device *dssdev, bool enable)
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;
}
@@ -4768,11 +4796,96 @@ static const struct omap_dss_device_ops dsi_ops = {
},
};
+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;
@@ -4787,13 +4900,20 @@ static int omap_dsi_host_attach(struct mipi_dsi_host *host,
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;
}
@@ -4810,6 +4930,7 @@ static int omap_dsi_host_detach(struct mipi_dsi_host *host,
if (dsi->vc[channel].dest != client)
return -EINVAL;
+ omap_dsi_unregister_te_irq(dsi);
dsi->vc[channel].dest = NULL;
return 0;
}