#define DSI_DT_RX_SHORT_READ_1 0x21
#define DSI_DT_RX_SHORT_READ_2 0x22
+typedef void (*omap_dsi_isr_t) (void *arg, u32 mask);
+
+#define DSI_MAX_NR_ISRS 2
+
+struct dsi_isr_data {
+ omap_dsi_isr_t isr;
+ void *arg;
+ u32 mask;
+};
+
enum fifo_size {
DSI_FIFO_SIZE_0 = 0,
DSI_FIFO_SIZE_32 = 1,
unsigned cio_irqs[32];
};
+struct dsi_isr_tables {
+ struct dsi_isr_data isr_table[DSI_MAX_NR_ISRS];
+ struct dsi_isr_data isr_table_vc[4][DSI_MAX_NR_ISRS];
+ struct dsi_isr_data isr_table_cio[DSI_MAX_NR_ISRS];
+};
+
static struct
{
struct platform_device *pdev;
unsigned pll_locked;
- struct completion bta_completion;
- void (*bta_callback)(void);
+ spinlock_t irq_lock;
+ struct dsi_isr_tables isr_tables;
+ /* space for a copy used by the interrupt handler */
+ struct dsi_isr_tables isr_tables_copy;
int update_channel;
struct dsi_update_region update_region;
return dsi.bus_lock.count == 0;
}
+static void dsi_completion_handler(void *data, u32 mask)
+{
+ complete((struct completion *)data);
+}
+
static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum,
int value)
{
static void print_irq_status(u32 status)
{
+ if (status == 0)
+ return;
+
#ifndef VERBOSE_IRQ
if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0)
return;
static void print_irq_status_vc(int channel, u32 status)
{
+ if (status == 0)
+ return;
+
#ifndef VERBOSE_IRQ
if ((status & ~DSI_VC_IRQ_PACKET_SENT) == 0)
return;
static void print_irq_status_cio(u32 status)
{
+ if (status == 0)
+ return;
+
printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status);
#define PIS(x) \
}
}
+static void dsi_call_isrs(struct dsi_isr_data *isr_array,
+ unsigned isr_array_size, u32 irqstatus)
+{
+ struct dsi_isr_data *isr_data;
+ int i;
+
+ for (i = 0; i < isr_array_size; i++) {
+ isr_data = &isr_array[i];
+ if (isr_data->isr && isr_data->mask & irqstatus)
+ isr_data->isr(isr_data->arg, irqstatus);
+ }
+}
+
+static void dsi_handle_isrs(struct dsi_isr_tables *isr_tables,
+ u32 irqstatus, u32 *vcstatus, u32 ciostatus)
+{
+ int i;
+
+ dsi_call_isrs(isr_tables->isr_table,
+ ARRAY_SIZE(isr_tables->isr_table),
+ irqstatus);
+
+ for (i = 0; i < 4; ++i) {
+ if (vcstatus[i] == 0)
+ continue;
+ dsi_call_isrs(isr_tables->isr_table_vc[i],
+ ARRAY_SIZE(isr_tables->isr_table_vc[i]),
+ vcstatus[i]);
+ }
+
+ if (ciostatus != 0)
+ dsi_call_isrs(isr_tables->isr_table_cio,
+ ARRAY_SIZE(isr_tables->isr_table_cio),
+ ciostatus);
+}
+
static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
{
u32 irqstatus, vcstatus[4], ciostatus;
int i;
+ spin_lock(&dsi.irq_lock);
+
irqstatus = dsi_read_reg(DSI_IRQSTATUS);
/* IRQ is not for us */
- if (!irqstatus)
+ if (!irqstatus) {
+ spin_unlock(&dsi.irq_lock);
return IRQ_NONE;
+ }
dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK);
/* flush posted write */
del_timer(&dsi.te_timer);
#endif
- for (i = 0; i < 4; ++i) {
- if (vcstatus[i] == 0)
- continue;
+ /* make a copy and unlock, so that isrs can unregister
+ * themselves */
+ memcpy(&dsi.isr_tables_copy, &dsi.isr_tables, sizeof(dsi.isr_tables));
- if (vcstatus[i] & DSI_VC_IRQ_BTA) {
- complete(&dsi.bta_completion);
+ spin_unlock(&dsi.irq_lock);
- if (dsi.bta_callback)
- dsi.bta_callback();
- }
- }
+ dsi_handle_isrs(&dsi.isr_tables_copy, irqstatus, vcstatus, ciostatus);
dsi_handle_irq_errors(irqstatus, vcstatus, ciostatus);
return IRQ_HANDLED;
}
-static void _dsi_initialize_irq(void)
+/* dsi.irq_lock has to be locked by the caller */
+static void _omap_dsi_configure_irqs(struct dsi_isr_data *isr_array,
+ unsigned isr_array_size, u32 default_mask,
+ const struct dsi_reg enable_reg,
+ const struct dsi_reg status_reg)
{
- u32 l;
+ struct dsi_isr_data *isr_data;
+ u32 mask;
+ u32 old_mask;
int i;
- /* disable all interrupts */
- dsi_write_reg(DSI_IRQENABLE, 0);
- for (i = 0; i < 4; ++i)
- dsi_write_reg(DSI_VC_IRQENABLE(i), 0);
- dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, 0);
+ mask = default_mask;
- /* clear interrupt status */
- l = dsi_read_reg(DSI_IRQSTATUS);
- dsi_write_reg(DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK);
+ for (i = 0; i < isr_array_size; i++) {
+ isr_data = &isr_array[i];
- for (i = 0; i < 4; ++i) {
- l = dsi_read_reg(DSI_VC_IRQSTATUS(i));
- dsi_write_reg(DSI_VC_IRQSTATUS(i), l);
+ if (isr_data->isr == NULL)
+ continue;
+
+ mask |= isr_data->mask;
}
- l = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS);
- dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, l);
+ old_mask = dsi_read_reg(enable_reg);
+ /* clear the irqstatus for newly enabled irqs */
+ dsi_write_reg(status_reg, (mask ^ old_mask) & mask);
+ dsi_write_reg(enable_reg, mask);
- /* enable error irqs */
- l = DSI_IRQ_ERROR_MASK;
+ /* flush posted writes */
+ dsi_read_reg(enable_reg);
+ dsi_read_reg(status_reg);
+}
+
+/* dsi.irq_lock has to be locked by the caller */
+static void _omap_dsi_set_irqs(void)
+{
+ u32 mask = DSI_IRQ_ERROR_MASK;
#ifdef DSI_CATCH_MISSING_TE
- l |= DSI_IRQ_TE_TRIGGER;
+ mask |= DSI_IRQ_TE_TRIGGER;
#endif
- dsi_write_reg(DSI_IRQENABLE, l);
+ _omap_dsi_configure_irqs(dsi.isr_tables.isr_table,
+ ARRAY_SIZE(dsi.isr_tables.isr_table), mask,
+ DSI_IRQENABLE, DSI_IRQSTATUS);
+}
- l = DSI_VC_IRQ_ERROR_MASK;
- for (i = 0; i < 4; ++i)
- dsi_write_reg(DSI_VC_IRQENABLE(i), l);
+/* dsi.irq_lock has to be locked by the caller */
+static void _omap_dsi_set_irqs_vc(int vc)
+{
+ _omap_dsi_configure_irqs(dsi.isr_tables.isr_table_vc[vc],
+ ARRAY_SIZE(dsi.isr_tables.isr_table_vc[vc]),
+ DSI_VC_IRQ_ERROR_MASK,
+ DSI_VC_IRQENABLE(vc), DSI_VC_IRQSTATUS(vc));
+}
- l = DSI_CIO_IRQ_ERROR_MASK;
- dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, l);
+/* dsi.irq_lock has to be locked by the caller */
+static void _omap_dsi_set_irqs_cio(void)
+{
+ _omap_dsi_configure_irqs(dsi.isr_tables.isr_table_cio,
+ ARRAY_SIZE(dsi.isr_tables.isr_table_cio),
+ DSI_CIO_IRQ_ERROR_MASK,
+ DSI_COMPLEXIO_IRQ_ENABLE, DSI_COMPLEXIO_IRQ_STATUS);
}
-static u32 dsi_get_errors(void)
+static void _dsi_initialize_irq(void)
{
unsigned long flags;
- u32 e;
- spin_lock_irqsave(&dsi.errors_lock, flags);
- e = dsi.errors;
- dsi.errors = 0;
- spin_unlock_irqrestore(&dsi.errors_lock, flags);
- return e;
+ int vc;
+
+ spin_lock_irqsave(&dsi.irq_lock, flags);
+
+ memset(&dsi.isr_tables, 0, sizeof(dsi.isr_tables));
+
+ _omap_dsi_set_irqs();
+ for (vc = 0; vc < 4; ++vc)
+ _omap_dsi_set_irqs_vc(vc);
+ _omap_dsi_set_irqs_cio();
+
+ spin_unlock_irqrestore(&dsi.irq_lock, flags);
}
-static void dsi_vc_enable_bta_irq(int channel)
+static int _dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
+ struct dsi_isr_data *isr_array, unsigned isr_array_size)
{
- u32 l;
+ struct dsi_isr_data *isr_data;
+ int free_idx;
+ int i;
+
+ BUG_ON(isr == NULL);
+
+ /* check for duplicate entry and find a free slot */
+ free_idx = -1;
+ for (i = 0; i < isr_array_size; i++) {
+ isr_data = &isr_array[i];
- dsi_write_reg(DSI_VC_IRQSTATUS(channel), DSI_VC_IRQ_BTA);
+ if (isr_data->isr == isr && isr_data->arg == arg &&
+ isr_data->mask == mask) {
+ return -EINVAL;
+ }
+
+ if (isr_data->isr == NULL && free_idx == -1)
+ free_idx = i;
+ }
- l = dsi_read_reg(DSI_VC_IRQENABLE(channel));
- l |= DSI_VC_IRQ_BTA;
- dsi_write_reg(DSI_VC_IRQENABLE(channel), l);
+ if (free_idx == -1)
+ return -EBUSY;
+
+ isr_data = &isr_array[free_idx];
+ isr_data->isr = isr;
+ isr_data->arg = arg;
+ isr_data->mask = mask;
+
+ return 0;
}
-static void dsi_vc_disable_bta_irq(int channel)
+static int _dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask,
+ struct dsi_isr_data *isr_array, unsigned isr_array_size)
{
- u32 l;
+ struct dsi_isr_data *isr_data;
+ int i;
+
+ for (i = 0; i < isr_array_size; i++) {
+ isr_data = &isr_array[i];
+ if (isr_data->isr != isr || isr_data->arg != arg ||
+ isr_data->mask != mask)
+ continue;
+
+ isr_data->isr = NULL;
+ isr_data->arg = NULL;
+ isr_data->mask = 0;
+
+ return 0;
+ }
- l = dsi_read_reg(DSI_VC_IRQENABLE(channel));
- l &= ~DSI_VC_IRQ_BTA;
- dsi_write_reg(DSI_VC_IRQENABLE(channel), l);
+ return -EINVAL;
+}
+
+static int dsi_register_isr(omap_dsi_isr_t isr, void *arg, u32 mask)
+{
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&dsi.irq_lock, flags);
+
+ r = _dsi_register_isr(isr, arg, mask, dsi.isr_tables.isr_table,
+ ARRAY_SIZE(dsi.isr_tables.isr_table));
+
+ if (r == 0)
+ _omap_dsi_set_irqs();
+
+ spin_unlock_irqrestore(&dsi.irq_lock, flags);
+
+ return r;
+}
+
+static int dsi_unregister_isr(omap_dsi_isr_t isr, void *arg, u32 mask)
+{
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&dsi.irq_lock, flags);
+
+ r = _dsi_unregister_isr(isr, arg, mask, dsi.isr_tables.isr_table,
+ ARRAY_SIZE(dsi.isr_tables.isr_table));
+
+ if (r == 0)
+ _omap_dsi_set_irqs();
+
+ spin_unlock_irqrestore(&dsi.irq_lock, flags);
+
+ return r;
+}
+
+static int dsi_register_isr_vc(int channel, omap_dsi_isr_t isr, void *arg,
+ u32 mask)
+{
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&dsi.irq_lock, flags);
+
+ r = _dsi_register_isr(isr, arg, mask,
+ dsi.isr_tables.isr_table_vc[channel],
+ ARRAY_SIZE(dsi.isr_tables.isr_table_vc[channel]));
+
+ if (r == 0)
+ _omap_dsi_set_irqs_vc(channel);
+
+ spin_unlock_irqrestore(&dsi.irq_lock, flags);
+
+ return r;
+}
+
+static int dsi_unregister_isr_vc(int channel, omap_dsi_isr_t isr, void *arg,
+ u32 mask)
+{
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&dsi.irq_lock, flags);
+
+ r = _dsi_unregister_isr(isr, arg, mask,
+ dsi.isr_tables.isr_table_vc[channel],
+ ARRAY_SIZE(dsi.isr_tables.isr_table_vc[channel]));
+
+ if (r == 0)
+ _omap_dsi_set_irqs_vc(channel);
+
+ spin_unlock_irqrestore(&dsi.irq_lock, flags);
+
+ return r;
+}
+
+static int dsi_register_isr_cio(omap_dsi_isr_t isr, void *arg, u32 mask)
+{
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&dsi.irq_lock, flags);
+
+ r = _dsi_register_isr(isr, arg, mask, dsi.isr_tables.isr_table_cio,
+ ARRAY_SIZE(dsi.isr_tables.isr_table_cio));
+
+ if (r == 0)
+ _omap_dsi_set_irqs_cio();
+
+ spin_unlock_irqrestore(&dsi.irq_lock, flags);
+
+ return r;
+}
+
+static int dsi_unregister_isr_cio(omap_dsi_isr_t isr, void *arg, u32 mask)
+{
+ unsigned long flags;
+ int r;
+
+ spin_lock_irqsave(&dsi.irq_lock, flags);
+
+ r = _dsi_unregister_isr(isr, arg, mask, dsi.isr_tables.isr_table_cio,
+ ARRAY_SIZE(dsi.isr_tables.isr_table_cio));
+
+ if (r == 0)
+ _omap_dsi_set_irqs_cio();
+
+ spin_unlock_irqrestore(&dsi.irq_lock, flags);
+
+ return r;
+}
+
+static u32 dsi_get_errors(void)
+{
+ unsigned long flags;
+ u32 e;
+ spin_lock_irqsave(&dsi.errors_lock, flags);
+ e = dsi.errors;
+ dsi.errors = 0;
+ spin_unlock_irqrestore(&dsi.errors_lock, flags);
+ return e;
}
/* DSI func clock. this could also be dsi_pll_hsdiv_dsi_clk */
{
int t = 0;
+ /* DSI-PLL power command 0x3 is not working */
+ if (dss_has_feature(FEAT_DSI_PLL_PWR_BUG) &&
+ state == DSI_PLL_POWER_ON_DIV)
+ state = DSI_PLL_POWER_ON_ALL;
+
REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */
/* PLL_PWR_STATUS */
DSSDBGF();
+ dsi.current_cinfo.use_sys_clk = cinfo->use_sys_clk;
+ dsi.current_cinfo.highfreq = cinfo->highfreq;
+
dsi.current_cinfo.fint = cinfo->fint;
dsi.current_cinfo.clkin4ddr = cinfo->clkin4ddr;
dsi.current_cinfo.dsi_pll_hsdiv_dispc_clk =
void dsi_dump_clocks(struct seq_file *s)
{
- int clksel;
struct dsi_clock_info *cinfo = &dsi.current_cinfo;
enum dss_clk_source dispc_clk_src, dsi_clk_src;
enable_clocks(1);
- clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11);
-
seq_printf(s, "- DSI PLL -\n");
seq_printf(s, "dsi pll source = %s\n",
- clksel == 0 ?
- "dss_sys_clk" : "pclkfree");
+ cinfo->use_sys_clk ? "dss_sys_clk" : "pclkfree");
seq_printf(s, "Fint\t\t%-16luregn %u\n", cinfo->fint, cinfo->regn);
int dsi_vc_send_bta_sync(int channel)
{
+ DECLARE_COMPLETION_ONSTACK(completion);
int r = 0;
u32 err;
- INIT_COMPLETION(dsi.bta_completion);
+ r = dsi_register_isr_vc(channel, dsi_completion_handler,
+ &completion, DSI_VC_IRQ_BTA);
+ if (r)
+ goto err0;
- dsi_vc_enable_bta_irq(channel);
+ r = dsi_register_isr(dsi_completion_handler, &completion,
+ DSI_IRQ_ERROR_MASK);
+ if (r)
+ goto err1;
r = dsi_vc_send_bta(channel);
if (r)
- goto err;
+ goto err2;
- if (wait_for_completion_timeout(&dsi.bta_completion,
+ if (wait_for_completion_timeout(&completion,
msecs_to_jiffies(500)) == 0) {
DSSERR("Failed to receive BTA\n");
r = -EIO;
- goto err;
+ goto err2;
}
err = dsi_get_errors();
if (err) {
DSSERR("Error while sending BTA: %x\n", err);
r = -EIO;
- goto err;
+ goto err2;
}
-err:
- dsi_vc_disable_bta_irq(channel);
-
+err2:
+ dsi_unregister_isr(dsi_completion_handler, &completion,
+ DSI_IRQ_ERROR_MASK);
+err1:
+ dsi_unregister_isr_vc(channel, dsi_completion_handler,
+ &completion, DSI_VC_IRQ_BTA);
+err0:
return r;
}
EXPORT_SYMBOL(dsi_vc_send_bta_sync);
}
#endif
+static void dsi_framedone_bta_callback(void *data, u32 mask);
+
static void dsi_handle_framedone(int error)
{
const int channel = dsi.update_channel;
- cancel_delayed_work(&dsi.framedone_timeout_work);
+ dsi_unregister_isr_vc(channel, dsi_framedone_bta_callback,
+ NULL, DSI_VC_IRQ_BTA);
- dsi_vc_disable_bta_irq(channel);
+ cancel_delayed_work(&dsi.framedone_timeout_work);
/* SIDLEMODE back to smart-idle */
dispc_enable_sidle();
- dsi.bta_callback = NULL;
-
if (dsi.te_enabled) {
/* enable LP_RX_TO again after the TE */
REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */
dsi_handle_framedone(-ETIMEDOUT);
}
-static void dsi_framedone_bta_callback(void)
+static void dsi_framedone_bta_callback(void *data, u32 mask)
{
dsi_handle_framedone(0);
* asynchronously.
* */
- dsi.bta_callback = dsi_framedone_bta_callback;
-
- barrier();
-
- dsi_vc_enable_bta_irq(channel);
+ r = dsi_register_isr_vc(channel, dsi_framedone_bta_callback,
+ NULL, DSI_VC_IRQ_BTA);
+ if (r) {
+ DSSERR("Failed to register BTA ISR\n");
+ dsi_handle_framedone(-EIO);
+ return;
+ }
r = dsi_vc_send_bta(channel);
if (r) {
DSSERR("BTA after framedone failed\n");
+ dsi_unregister_isr_vc(channel, dsi_framedone_bta_callback,
+ NULL, DSI_VC_IRQ_BTA);
dsi_handle_framedone(-EIO);
}
}
int r, i;
struct resource *dsi_mem;
+ spin_lock_init(&dsi.irq_lock);
spin_lock_init(&dsi.errors_lock);
dsi.errors = 0;
dsi.irq_stats.last_reset = jiffies;
#endif
- init_completion(&dsi.bta_completion);
-
mutex_init(&dsi.lock);
sema_init(&dsi.bus_lock, 1);