Skip to content

Commit

Permalink
drm/exynos: dsi: add TE interrupt handler to support LCD I80 interface
Browse files Browse the repository at this point in the history
This is a temporary solution and should be made by more
generic way.

To support LCD I80 interface, the DSI host should register
TE interrupt handler from the TE GPIO of attached panel.
So the panel generates a tearing effect synchronization signal
then the DSI host calls the CRTC device manager to trigger
to transfer video image.

Signed-off-by: YoungJun Cho <yj44.cho@samsung.com>
Acked-by: Inki Dae <inki.dae@samsung.com>
Acked-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Inki Dae <inki.dae@samsung.com>
  • Loading branch information
YoungJun Cho authored and Inki Dae committed Aug 3, 2014
1 parent 5595d4d commit e17ddec
Showing 1 changed file with 96 additions and 2 deletions.
98 changes: 96 additions & 2 deletions drivers/gpu/drm/exynos/exynos_drm_dsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@
#include <drm/drm_panel.h>

#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/irq.h>
#include <linux/of_gpio.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
#include <linux/component.h>

#include <video/mipi_display.h>
#include <video/videomode.h>

#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"

/* returns true iff both arguments logically differs */
Expand Down Expand Up @@ -247,6 +250,7 @@ struct exynos_dsi {
struct clk *bus_clk;
struct regulator_bulk_data supplies[2];
int irq;
int te_gpio;

u32 pll_clk_rate;
u32 burst_clk_rate;
Expand Down Expand Up @@ -954,17 +958,89 @@ static irqreturn_t exynos_dsi_irq(int irq, void *dev_id)
return IRQ_HANDLED;
}

static irqreturn_t exynos_dsi_te_irq_handler(int irq, void *dev_id)
{
struct exynos_dsi *dsi = (struct exynos_dsi *)dev_id;
struct drm_encoder *encoder = dsi->encoder;

if (dsi->state & DSIM_STATE_ENABLED)
exynos_drm_crtc_te_handler(encoder->crtc);

return IRQ_HANDLED;
}

static void exynos_dsi_enable_irq(struct exynos_dsi *dsi)
{
enable_irq(dsi->irq);

if (gpio_is_valid(dsi->te_gpio))
enable_irq(gpio_to_irq(dsi->te_gpio));
}

static void exynos_dsi_disable_irq(struct exynos_dsi *dsi)
{
if (gpio_is_valid(dsi->te_gpio))
disable_irq(gpio_to_irq(dsi->te_gpio));

disable_irq(dsi->irq);
}

static int exynos_dsi_init(struct exynos_dsi *dsi)
{
exynos_dsi_enable_clock(dsi);
exynos_dsi_reset(dsi);
enable_irq(dsi->irq);
exynos_dsi_enable_irq(dsi);
exynos_dsi_wait_for_reset(dsi);
exynos_dsi_init_link(dsi);

return 0;
}

static int exynos_dsi_register_te_irq(struct exynos_dsi *dsi)
{
int ret;

dsi->te_gpio = of_get_named_gpio(dsi->panel_node, "te-gpios", 0);
if (!gpio_is_valid(dsi->te_gpio)) {
dev_err(dsi->dev, "no te-gpios specified\n");
ret = dsi->te_gpio;
goto out;
}

ret = gpio_request_one(dsi->te_gpio, GPIOF_IN, "te_gpio");
if (ret) {
dev_err(dsi->dev, "gpio request failed with %d\n", ret);
goto out;
}

/*
* This TE GPIO IRQ should not be set to IRQ_NOAUTOEN, because panel
* calls drm_panel_init() first then calls mipi_dsi_attach() in probe().
* It means that te_gpio is invalid when exynos_dsi_enable_irq() is
* called by drm_panel_init() before panel is attached.
*/
ret = request_threaded_irq(gpio_to_irq(dsi->te_gpio),
exynos_dsi_te_irq_handler, NULL,
IRQF_TRIGGER_RISING, "TE", dsi);
if (ret) {
dev_err(dsi->dev, "request interrupt failed with %d\n", ret);
gpio_free(dsi->te_gpio);
goto out;
}

out:
return ret;
}

static void exynos_dsi_unregister_te_irq(struct exynos_dsi *dsi)
{
if (gpio_is_valid(dsi->te_gpio)) {
free_irq(gpio_to_irq(dsi->te_gpio), dsi);
gpio_free(dsi->te_gpio);
dsi->te_gpio = -ENOENT;
}
}

static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *device)
{
Expand All @@ -978,6 +1054,19 @@ static int exynos_dsi_host_attach(struct mipi_dsi_host *host,
if (dsi->connector.dev)
drm_helper_hpd_irq_event(dsi->connector.dev);

/*
* This is a temporary solution and should be made by more generic way.
*
* If attached panel device is for command mode one, dsi should register
* TE interrupt handler.
*/
if (!(dsi->mode_flags & MIPI_DSI_MODE_VIDEO)) {
int ret = exynos_dsi_register_te_irq(dsi);

if (ret)
return ret;
}

return 0;
}

Expand All @@ -986,6 +1075,8 @@ static int exynos_dsi_host_detach(struct mipi_dsi_host *host,
{
struct exynos_dsi *dsi = host_to_dsi(host);

exynos_dsi_unregister_te_irq(dsi);

dsi->panel_node = NULL;

if (dsi->connector.dev)
Expand Down Expand Up @@ -1099,7 +1190,7 @@ static void exynos_dsi_poweroff(struct exynos_dsi *dsi)

exynos_dsi_disable_clock(dsi);

disable_irq(dsi->irq);
exynos_dsi_disable_irq(dsi);
}

dsi->state &= ~DSIM_STATE_CMD_LPM;
Expand Down Expand Up @@ -1445,6 +1536,9 @@ static int exynos_dsi_probe(struct platform_device *pdev)
goto err_del_component;
}

/* To be checked as invalid one */
dsi->te_gpio = -ENOENT;

init_completion(&dsi->completed);
spin_lock_init(&dsi->transfer_lock);
INIT_LIST_HEAD(&dsi->transfer_list);
Expand Down

0 comments on commit e17ddec

Please sign in to comment.