658 lines
15 KiB
C
658 lines
15 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-only
|
||
|
/*
|
||
|
* Copyright (C) 2012 Texas Instruments
|
||
|
* Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
|
||
|
*/
|
||
|
|
||
|
#define DSS_SUBSYS_NAME "APPLY"
|
||
|
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/module.h>
|
||
|
#include <linux/slab.h>
|
||
|
#include <linux/spinlock.h>
|
||
|
#include <linux/jiffies.h>
|
||
|
#include <linux/delay.h>
|
||
|
#include <linux/interrupt.h>
|
||
|
#include <linux/seq_file.h>
|
||
|
|
||
|
#include <video/omapfb_dss.h>
|
||
|
|
||
|
#include "dss.h"
|
||
|
#include "dss_features.h"
|
||
|
#include "dispc-compat.h"
|
||
|
|
||
|
#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \
|
||
|
DISPC_IRQ_OCP_ERR | \
|
||
|
DISPC_IRQ_VID1_FIFO_UNDERFLOW | \
|
||
|
DISPC_IRQ_VID2_FIFO_UNDERFLOW | \
|
||
|
DISPC_IRQ_SYNC_LOST | \
|
||
|
DISPC_IRQ_SYNC_LOST_DIGIT)
|
||
|
|
||
|
#define DISPC_MAX_NR_ISRS 8
|
||
|
|
||
|
struct omap_dispc_isr_data {
|
||
|
omap_dispc_isr_t isr;
|
||
|
void *arg;
|
||
|
u32 mask;
|
||
|
};
|
||
|
|
||
|
struct dispc_irq_stats {
|
||
|
unsigned long last_reset;
|
||
|
unsigned irq_count;
|
||
|
unsigned irqs[32];
|
||
|
};
|
||
|
|
||
|
static struct {
|
||
|
spinlock_t irq_lock;
|
||
|
u32 irq_error_mask;
|
||
|
struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
|
||
|
u32 error_irqs;
|
||
|
struct work_struct error_work;
|
||
|
|
||
|
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||
|
spinlock_t irq_stats_lock;
|
||
|
struct dispc_irq_stats irq_stats;
|
||
|
#endif
|
||
|
} dispc_compat;
|
||
|
|
||
|
|
||
|
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||
|
static void dispc_dump_irqs(struct seq_file *s)
|
||
|
{
|
||
|
unsigned long flags;
|
||
|
struct dispc_irq_stats stats;
|
||
|
|
||
|
spin_lock_irqsave(&dispc_compat.irq_stats_lock, flags);
|
||
|
|
||
|
stats = dispc_compat.irq_stats;
|
||
|
memset(&dispc_compat.irq_stats, 0, sizeof(dispc_compat.irq_stats));
|
||
|
dispc_compat.irq_stats.last_reset = jiffies;
|
||
|
|
||
|
spin_unlock_irqrestore(&dispc_compat.irq_stats_lock, flags);
|
||
|
|
||
|
seq_printf(s, "period %u ms\n",
|
||
|
jiffies_to_msecs(jiffies - stats.last_reset));
|
||
|
|
||
|
seq_printf(s, "irqs %d\n", stats.irq_count);
|
||
|
#define PIS(x) \
|
||
|
seq_printf(s, "%-20s %10d\n", #x, stats.irqs[ffs(DISPC_IRQ_##x)-1])
|
||
|
|
||
|
PIS(FRAMEDONE);
|
||
|
PIS(VSYNC);
|
||
|
PIS(EVSYNC_EVEN);
|
||
|
PIS(EVSYNC_ODD);
|
||
|
PIS(ACBIAS_COUNT_STAT);
|
||
|
PIS(PROG_LINE_NUM);
|
||
|
PIS(GFX_FIFO_UNDERFLOW);
|
||
|
PIS(GFX_END_WIN);
|
||
|
PIS(PAL_GAMMA_MASK);
|
||
|
PIS(OCP_ERR);
|
||
|
PIS(VID1_FIFO_UNDERFLOW);
|
||
|
PIS(VID1_END_WIN);
|
||
|
PIS(VID2_FIFO_UNDERFLOW);
|
||
|
PIS(VID2_END_WIN);
|
||
|
if (dss_feat_get_num_ovls() > 3) {
|
||
|
PIS(VID3_FIFO_UNDERFLOW);
|
||
|
PIS(VID3_END_WIN);
|
||
|
}
|
||
|
PIS(SYNC_LOST);
|
||
|
PIS(SYNC_LOST_DIGIT);
|
||
|
PIS(WAKEUP);
|
||
|
if (dss_has_feature(FEAT_MGR_LCD2)) {
|
||
|
PIS(FRAMEDONE2);
|
||
|
PIS(VSYNC2);
|
||
|
PIS(ACBIAS_COUNT_STAT2);
|
||
|
PIS(SYNC_LOST2);
|
||
|
}
|
||
|
if (dss_has_feature(FEAT_MGR_LCD3)) {
|
||
|
PIS(FRAMEDONE3);
|
||
|
PIS(VSYNC3);
|
||
|
PIS(ACBIAS_COUNT_STAT3);
|
||
|
PIS(SYNC_LOST3);
|
||
|
}
|
||
|
#undef PIS
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* dispc.irq_lock has to be locked by the caller */
|
||
|
static void _omap_dispc_set_irqs(void)
|
||
|
{
|
||
|
u32 mask;
|
||
|
int i;
|
||
|
struct omap_dispc_isr_data *isr_data;
|
||
|
|
||
|
mask = dispc_compat.irq_error_mask;
|
||
|
|
||
|
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||
|
isr_data = &dispc_compat.registered_isr[i];
|
||
|
|
||
|
if (isr_data->isr == NULL)
|
||
|
continue;
|
||
|
|
||
|
mask |= isr_data->mask;
|
||
|
}
|
||
|
|
||
|
dispc_write_irqenable(mask);
|
||
|
}
|
||
|
|
||
|
int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
|
||
|
{
|
||
|
int i;
|
||
|
int ret;
|
||
|
unsigned long flags;
|
||
|
struct omap_dispc_isr_data *isr_data;
|
||
|
|
||
|
if (isr == NULL)
|
||
|
return -EINVAL;
|
||
|
|
||
|
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||
|
|
||
|
/* check for duplicate entry */
|
||
|
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||
|
isr_data = &dispc_compat.registered_isr[i];
|
||
|
if (isr_data->isr == isr && isr_data->arg == arg &&
|
||
|
isr_data->mask == mask) {
|
||
|
ret = -EINVAL;
|
||
|
goto err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
isr_data = NULL;
|
||
|
ret = -EBUSY;
|
||
|
|
||
|
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||
|
isr_data = &dispc_compat.registered_isr[i];
|
||
|
|
||
|
if (isr_data->isr != NULL)
|
||
|
continue;
|
||
|
|
||
|
isr_data->isr = isr;
|
||
|
isr_data->arg = arg;
|
||
|
isr_data->mask = mask;
|
||
|
ret = 0;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (ret)
|
||
|
goto err;
|
||
|
|
||
|
_omap_dispc_set_irqs();
|
||
|
|
||
|
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||
|
|
||
|
return 0;
|
||
|
err:
|
||
|
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(omap_dispc_register_isr);
|
||
|
|
||
|
int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask)
|
||
|
{
|
||
|
int i;
|
||
|
unsigned long flags;
|
||
|
int ret = -EINVAL;
|
||
|
struct omap_dispc_isr_data *isr_data;
|
||
|
|
||
|
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||
|
|
||
|
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||
|
isr_data = &dispc_compat.registered_isr[i];
|
||
|
if (isr_data->isr != isr || isr_data->arg != arg ||
|
||
|
isr_data->mask != mask)
|
||
|
continue;
|
||
|
|
||
|
/* found the correct isr */
|
||
|
|
||
|
isr_data->isr = NULL;
|
||
|
isr_data->arg = NULL;
|
||
|
isr_data->mask = 0;
|
||
|
|
||
|
ret = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (ret == 0)
|
||
|
_omap_dispc_set_irqs();
|
||
|
|
||
|
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
EXPORT_SYMBOL(omap_dispc_unregister_isr);
|
||
|
|
||
|
static void print_irq_status(u32 status)
|
||
|
{
|
||
|
if ((status & dispc_compat.irq_error_mask) == 0)
|
||
|
return;
|
||
|
|
||
|
#define PIS(x) (status & DISPC_IRQ_##x) ? (#x " ") : ""
|
||
|
|
||
|
pr_debug("DISPC IRQ: 0x%x: %s%s%s%s%s%s%s%s%s\n",
|
||
|
status,
|
||
|
PIS(OCP_ERR),
|
||
|
PIS(GFX_FIFO_UNDERFLOW),
|
||
|
PIS(VID1_FIFO_UNDERFLOW),
|
||
|
PIS(VID2_FIFO_UNDERFLOW),
|
||
|
dss_feat_get_num_ovls() > 3 ? PIS(VID3_FIFO_UNDERFLOW) : "",
|
||
|
PIS(SYNC_LOST),
|
||
|
PIS(SYNC_LOST_DIGIT),
|
||
|
dss_has_feature(FEAT_MGR_LCD2) ? PIS(SYNC_LOST2) : "",
|
||
|
dss_has_feature(FEAT_MGR_LCD3) ? PIS(SYNC_LOST3) : "");
|
||
|
#undef PIS
|
||
|
}
|
||
|
|
||
|
/* Called from dss.c. Note that we don't touch clocks here,
|
||
|
* but we presume they are on because we got an IRQ. However,
|
||
|
* an irq handler may turn the clocks off, so we may not have
|
||
|
* clock later in the function. */
|
||
|
static irqreturn_t omap_dispc_irq_handler(int irq, void *arg)
|
||
|
{
|
||
|
int i;
|
||
|
u32 irqstatus, irqenable;
|
||
|
u32 handledirqs = 0;
|
||
|
u32 unhandled_errors;
|
||
|
struct omap_dispc_isr_data *isr_data;
|
||
|
struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS];
|
||
|
|
||
|
spin_lock(&dispc_compat.irq_lock);
|
||
|
|
||
|
irqstatus = dispc_read_irqstatus();
|
||
|
irqenable = dispc_read_irqenable();
|
||
|
|
||
|
/* IRQ is not for us */
|
||
|
if (!(irqstatus & irqenable)) {
|
||
|
spin_unlock(&dispc_compat.irq_lock);
|
||
|
return IRQ_NONE;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||
|
spin_lock(&dispc_compat.irq_stats_lock);
|
||
|
dispc_compat.irq_stats.irq_count++;
|
||
|
dss_collect_irq_stats(irqstatus, dispc_compat.irq_stats.irqs);
|
||
|
spin_unlock(&dispc_compat.irq_stats_lock);
|
||
|
#endif
|
||
|
|
||
|
print_irq_status(irqstatus);
|
||
|
|
||
|
/* Ack the interrupt. Do it here before clocks are possibly turned
|
||
|
* off */
|
||
|
dispc_clear_irqstatus(irqstatus);
|
||
|
/* flush posted write */
|
||
|
dispc_read_irqstatus();
|
||
|
|
||
|
/* make a copy and unlock, so that isrs can unregister
|
||
|
* themselves */
|
||
|
memcpy(registered_isr, dispc_compat.registered_isr,
|
||
|
sizeof(registered_isr));
|
||
|
|
||
|
spin_unlock(&dispc_compat.irq_lock);
|
||
|
|
||
|
for (i = 0; i < DISPC_MAX_NR_ISRS; i++) {
|
||
|
isr_data = ®istered_isr[i];
|
||
|
|
||
|
if (!isr_data->isr)
|
||
|
continue;
|
||
|
|
||
|
if (isr_data->mask & irqstatus) {
|
||
|
isr_data->isr(isr_data->arg, irqstatus);
|
||
|
handledirqs |= isr_data->mask;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
spin_lock(&dispc_compat.irq_lock);
|
||
|
|
||
|
unhandled_errors = irqstatus & ~handledirqs & dispc_compat.irq_error_mask;
|
||
|
|
||
|
if (unhandled_errors) {
|
||
|
dispc_compat.error_irqs |= unhandled_errors;
|
||
|
|
||
|
dispc_compat.irq_error_mask &= ~unhandled_errors;
|
||
|
_omap_dispc_set_irqs();
|
||
|
|
||
|
schedule_work(&dispc_compat.error_work);
|
||
|
}
|
||
|
|
||
|
spin_unlock(&dispc_compat.irq_lock);
|
||
|
|
||
|
return IRQ_HANDLED;
|
||
|
}
|
||
|
|
||
|
static void dispc_error_worker(struct work_struct *work)
|
||
|
{
|
||
|
int i;
|
||
|
u32 errors;
|
||
|
unsigned long flags;
|
||
|
static const unsigned fifo_underflow_bits[] = {
|
||
|
DISPC_IRQ_GFX_FIFO_UNDERFLOW,
|
||
|
DISPC_IRQ_VID1_FIFO_UNDERFLOW,
|
||
|
DISPC_IRQ_VID2_FIFO_UNDERFLOW,
|
||
|
DISPC_IRQ_VID3_FIFO_UNDERFLOW,
|
||
|
};
|
||
|
|
||
|
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||
|
errors = dispc_compat.error_irqs;
|
||
|
dispc_compat.error_irqs = 0;
|
||
|
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||
|
|
||
|
dispc_runtime_get();
|
||
|
|
||
|
for (i = 0; i < omap_dss_get_num_overlays(); ++i) {
|
||
|
struct omap_overlay *ovl;
|
||
|
unsigned bit;
|
||
|
|
||
|
ovl = omap_dss_get_overlay(i);
|
||
|
bit = fifo_underflow_bits[i];
|
||
|
|
||
|
if (bit & errors) {
|
||
|
DSSERR("FIFO UNDERFLOW on %s, disabling the overlay\n",
|
||
|
ovl->name);
|
||
|
ovl->disable(ovl);
|
||
|
msleep(50);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
|
||
|
struct omap_overlay_manager *mgr;
|
||
|
unsigned bit;
|
||
|
|
||
|
mgr = omap_dss_get_overlay_manager(i);
|
||
|
bit = dispc_mgr_get_sync_lost_irq(i);
|
||
|
|
||
|
if (bit & errors) {
|
||
|
int j;
|
||
|
|
||
|
DSSERR("SYNC_LOST on channel %s, restarting the output "
|
||
|
"with video overlays disabled\n",
|
||
|
mgr->name);
|
||
|
|
||
|
dss_mgr_disable(mgr);
|
||
|
|
||
|
for (j = 0; j < omap_dss_get_num_overlays(); ++j) {
|
||
|
struct omap_overlay *ovl;
|
||
|
ovl = omap_dss_get_overlay(j);
|
||
|
|
||
|
if (ovl->id != OMAP_DSS_GFX &&
|
||
|
ovl->manager == mgr)
|
||
|
ovl->disable(ovl);
|
||
|
}
|
||
|
|
||
|
dss_mgr_enable(mgr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (errors & DISPC_IRQ_OCP_ERR) {
|
||
|
DSSERR("OCP_ERR\n");
|
||
|
for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) {
|
||
|
struct omap_overlay_manager *mgr;
|
||
|
|
||
|
mgr = omap_dss_get_overlay_manager(i);
|
||
|
dss_mgr_disable(mgr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
spin_lock_irqsave(&dispc_compat.irq_lock, flags);
|
||
|
dispc_compat.irq_error_mask |= errors;
|
||
|
_omap_dispc_set_irqs();
|
||
|
spin_unlock_irqrestore(&dispc_compat.irq_lock, flags);
|
||
|
|
||
|
dispc_runtime_put();
|
||
|
}
|
||
|
|
||
|
int dss_dispc_initialize_irq(void)
|
||
|
{
|
||
|
int r;
|
||
|
|
||
|
#ifdef CONFIG_FB_OMAP2_DSS_COLLECT_IRQ_STATS
|
||
|
spin_lock_init(&dispc_compat.irq_stats_lock);
|
||
|
dispc_compat.irq_stats.last_reset = jiffies;
|
||
|
dss_debugfs_create_file("dispc_irq", dispc_dump_irqs);
|
||
|
#endif
|
||
|
|
||
|
spin_lock_init(&dispc_compat.irq_lock);
|
||
|
|
||
|
memset(dispc_compat.registered_isr, 0,
|
||
|
sizeof(dispc_compat.registered_isr));
|
||
|
|
||
|
dispc_compat.irq_error_mask = DISPC_IRQ_MASK_ERROR;
|
||
|
if (dss_has_feature(FEAT_MGR_LCD2))
|
||
|
dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST2;
|
||
|
if (dss_has_feature(FEAT_MGR_LCD3))
|
||
|
dispc_compat.irq_error_mask |= DISPC_IRQ_SYNC_LOST3;
|
||
|
if (dss_feat_get_num_ovls() > 3)
|
||
|
dispc_compat.irq_error_mask |= DISPC_IRQ_VID3_FIFO_UNDERFLOW;
|
||
|
|
||
|
/*
|
||
|
* there's SYNC_LOST_DIGIT waiting after enabling the DSS,
|
||
|
* so clear it
|
||
|
*/
|
||
|
dispc_clear_irqstatus(dispc_read_irqstatus());
|
||
|
|
||
|
INIT_WORK(&dispc_compat.error_work, dispc_error_worker);
|
||
|
|
||
|
_omap_dispc_set_irqs();
|
||
|
|
||
|
r = dispc_request_irq(omap_dispc_irq_handler, &dispc_compat);
|
||
|
if (r) {
|
||
|
DSSERR("dispc_request_irq failed\n");
|
||
|
return r;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void dss_dispc_uninitialize_irq(void)
|
||
|
{
|
||
|
dispc_free_irq(&dispc_compat);
|
||
|
}
|
||
|
|
||
|
static void dispc_mgr_disable_isr(void *data, u32 mask)
|
||
|
{
|
||
|
struct completion *compl = data;
|
||
|
complete(compl);
|
||
|
}
|
||
|
|
||
|
static void dispc_mgr_enable_lcd_out(enum omap_channel channel)
|
||
|
{
|
||
|
dispc_mgr_enable(channel, true);
|
||
|
}
|
||
|
|
||
|
static void dispc_mgr_disable_lcd_out(enum omap_channel channel)
|
||
|
{
|
||
|
DECLARE_COMPLETION_ONSTACK(framedone_compl);
|
||
|
int r;
|
||
|
u32 irq;
|
||
|
|
||
|
if (!dispc_mgr_is_enabled(channel))
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
* When we disable LCD output, we need to wait for FRAMEDONE to know
|
||
|
* that DISPC has finished with the LCD output.
|
||
|
*/
|
||
|
|
||
|
irq = dispc_mgr_get_framedone_irq(channel);
|
||
|
|
||
|
r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||
|
irq);
|
||
|
if (r)
|
||
|
DSSERR("failed to register FRAMEDONE isr\n");
|
||
|
|
||
|
dispc_mgr_enable(channel, false);
|
||
|
|
||
|
/* if we couldn't register for framedone, just sleep and exit */
|
||
|
if (r) {
|
||
|
msleep(100);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!wait_for_completion_timeout(&framedone_compl,
|
||
|
msecs_to_jiffies(100)))
|
||
|
DSSERR("timeout waiting for FRAME DONE\n");
|
||
|
|
||
|
r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||
|
irq);
|
||
|
if (r)
|
||
|
DSSERR("failed to unregister FRAMEDONE isr\n");
|
||
|
}
|
||
|
|
||
|
static void dispc_digit_out_enable_isr(void *data, u32 mask)
|
||
|
{
|
||
|
struct completion *compl = data;
|
||
|
|
||
|
/* ignore any sync lost interrupts */
|
||
|
if (mask & (DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD))
|
||
|
complete(compl);
|
||
|
}
|
||
|
|
||
|
static void dispc_mgr_enable_digit_out(void)
|
||
|
{
|
||
|
DECLARE_COMPLETION_ONSTACK(vsync_compl);
|
||
|
int r;
|
||
|
u32 irq_mask;
|
||
|
|
||
|
if (dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
* Digit output produces some sync lost interrupts during the first
|
||
|
* frame when enabling. Those need to be ignored, so we register for the
|
||
|
* sync lost irq to prevent the error handler from triggering.
|
||
|
*/
|
||
|
|
||
|
irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT) |
|
||
|
dispc_mgr_get_sync_lost_irq(OMAP_DSS_CHANNEL_DIGIT);
|
||
|
|
||
|
r = omap_dispc_register_isr(dispc_digit_out_enable_isr, &vsync_compl,
|
||
|
irq_mask);
|
||
|
if (r) {
|
||
|
DSSERR("failed to register %x isr\n", irq_mask);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, true);
|
||
|
|
||
|
/* wait for the first evsync */
|
||
|
if (!wait_for_completion_timeout(&vsync_compl, msecs_to_jiffies(100)))
|
||
|
DSSERR("timeout waiting for digit out to start\n");
|
||
|
|
||
|
r = omap_dispc_unregister_isr(dispc_digit_out_enable_isr, &vsync_compl,
|
||
|
irq_mask);
|
||
|
if (r)
|
||
|
DSSERR("failed to unregister %x isr\n", irq_mask);
|
||
|
}
|
||
|
|
||
|
static void dispc_mgr_disable_digit_out(void)
|
||
|
{
|
||
|
DECLARE_COMPLETION_ONSTACK(framedone_compl);
|
||
|
int r, i;
|
||
|
u32 irq_mask;
|
||
|
int num_irqs;
|
||
|
|
||
|
if (!dispc_mgr_is_enabled(OMAP_DSS_CHANNEL_DIGIT))
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
* When we disable the digit output, we need to wait for FRAMEDONE to
|
||
|
* know that DISPC has finished with the output.
|
||
|
*/
|
||
|
|
||
|
irq_mask = dispc_mgr_get_framedone_irq(OMAP_DSS_CHANNEL_DIGIT);
|
||
|
num_irqs = 1;
|
||
|
|
||
|
if (!irq_mask) {
|
||
|
/*
|
||
|
* omap 2/3 don't have framedone irq for TV, so we need to use
|
||
|
* vsyncs for this.
|
||
|
*/
|
||
|
|
||
|
irq_mask = dispc_mgr_get_vsync_irq(OMAP_DSS_CHANNEL_DIGIT);
|
||
|
/*
|
||
|
* We need to wait for both even and odd vsyncs. Note that this
|
||
|
* is not totally reliable, as we could get a vsync interrupt
|
||
|
* before we disable the output, which leads to timeout in the
|
||
|
* wait_for_completion.
|
||
|
*/
|
||
|
num_irqs = 2;
|
||
|
}
|
||
|
|
||
|
r = omap_dispc_register_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||
|
irq_mask);
|
||
|
if (r)
|
||
|
DSSERR("failed to register %x isr\n", irq_mask);
|
||
|
|
||
|
dispc_mgr_enable(OMAP_DSS_CHANNEL_DIGIT, false);
|
||
|
|
||
|
/* if we couldn't register the irq, just sleep and exit */
|
||
|
if (r) {
|
||
|
msleep(100);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < num_irqs; ++i) {
|
||
|
if (!wait_for_completion_timeout(&framedone_compl,
|
||
|
msecs_to_jiffies(100)))
|
||
|
DSSERR("timeout waiting for digit out to stop\n");
|
||
|
}
|
||
|
|
||
|
r = omap_dispc_unregister_isr(dispc_mgr_disable_isr, &framedone_compl,
|
||
|
irq_mask);
|
||
|
if (r)
|
||
|
DSSERR("failed to unregister %x isr\n", irq_mask);
|
||
|
}
|
||
|
|
||
|
void dispc_mgr_enable_sync(enum omap_channel channel)
|
||
|
{
|
||
|
if (dss_mgr_is_lcd(channel))
|
||
|
dispc_mgr_enable_lcd_out(channel);
|
||
|
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
|
||
|
dispc_mgr_enable_digit_out();
|
||
|
else
|
||
|
WARN_ON(1);
|
||
|
}
|
||
|
|
||
|
void dispc_mgr_disable_sync(enum omap_channel channel)
|
||
|
{
|
||
|
if (dss_mgr_is_lcd(channel))
|
||
|
dispc_mgr_disable_lcd_out(channel);
|
||
|
else if (channel == OMAP_DSS_CHANNEL_DIGIT)
|
||
|
dispc_mgr_disable_digit_out();
|
||
|
else
|
||
|
WARN_ON(1);
|
||
|
}
|
||
|
|
||
|
static inline void dispc_irq_wait_handler(void *data, u32 mask)
|
||
|
{
|
||
|
complete((struct completion *)data);
|
||
|
}
|
||
|
|
||
|
int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask,
|
||
|
unsigned long timeout)
|
||
|
{
|
||
|
|
||
|
int r;
|
||
|
long time_left;
|
||
|
DECLARE_COMPLETION_ONSTACK(completion);
|
||
|
|
||
|
r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion,
|
||
|
irqmask);
|
||
|
|
||
|
if (r)
|
||
|
return r;
|
||
|
|
||
|
time_left = wait_for_completion_interruptible_timeout(&completion,
|
||
|
timeout);
|
||
|
|
||
|
omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask);
|
||
|
|
||
|
if (time_left == 0)
|
||
|
return -ETIMEDOUT;
|
||
|
|
||
|
if (time_left == -ERESTARTSYS)
|
||
|
return -ERESTARTSYS;
|
||
|
|
||
|
return 0;
|
||
|
}
|