cregit-Linux how code gets into the kernel

Release 4.11 drivers/video/fbdev/omap2/omapfb/dss/dispc-compat.c

/*
 * Copyright (C) 2012 Texas Instruments
 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#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 }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen270100.00%1100.00%
Total270100.00%1100.00%

#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); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen70100.00%1100.00%
Total70100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen210100.00%1100.00%
Total210100.00%1100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen135100.00%1100.00%
Total135100.00%1100.00%

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 }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen105100.00%1100.00%
Total105100.00%1100.00%

/* 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 = &registered_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; }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen277100.00%2100.00%
Total277100.00%2100.00%


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(); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen335100.00%1100.00%
Total335100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen154100.00%2100.00%
Total154100.00%2100.00%


void dss_dispc_uninitialize_irq(void) { dispc_free_irq(&dispc_compat); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen13100.00%1100.00%
Total13100.00%1100.00%


static void dispc_mgr_disable_isr(void *data, u32 mask) { struct completion *compl = data; complete(compl); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen25100.00%1100.00%
Total25100.00%1100.00%


static void dispc_mgr_enable_lcd_out(enum omap_channel channel) { dispc_mgr_enable(channel, true); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen17100.00%1100.00%
Total17100.00%1100.00%


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"); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen119100.00%1100.00%
Total119100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen36100.00%1100.00%
Total36100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen116100.00%1100.00%
Total116100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen165100.00%1100.00%
Total165100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen37100.00%1100.00%
Total37100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen37100.00%1100.00%
Total37100.00%1100.00%


static inline void dispc_irq_wait_handler(void *data, u32 mask) { complete((struct completion *)data); }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen24100.00%1100.00%
Total24100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen8093.02%150.00%
Nicholas Mc Guire66.98%150.00%
Total86100.00%2100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
Tomi Valkeinen236199.70%250.00%
Nicholas Mc Guire60.25%125.00%
Peter Ujfalusi10.04%125.00%
Total2368100.00%4100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with cregit.