]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/video/omap2/dss/dsi.c
OMAP: DSS2: DSI: Fix DSI PLL power bug
[mv-sheeva.git] / drivers / video / omap2 / dss / dsi.c
index b6c08a6f2da158cb7209c0c9ef1eab8599cf9709..cbd9ca48d6ecef175e1c5204956ba3fbb7915974 100644 (file)
@@ -185,6 +185,16 @@ struct dsi_reg { u16 idx; };
 #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,
@@ -211,6 +221,12 @@ struct dsi_irq_stats {
        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;
@@ -233,8 +249,10 @@ static struct
 
        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;
@@ -317,6 +335,11 @@ static bool dsi_bus_is_locked(void)
        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)
 {
@@ -386,6 +409,9 @@ static void dsi_perf_show(const char *name)
 
 static void print_irq_status(u32 status)
 {
+       if (status == 0)
+               return;
+
 #ifndef VERBOSE_IRQ
        if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0)
                return;
@@ -421,6 +447,9 @@ static void print_irq_status(u32 status)
 
 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;
@@ -447,6 +476,9 @@ static void print_irq_status_vc(int channel, u32 status)
 
 static void print_irq_status_cio(u32 status)
 {
+       if (status == 0)
+               return;
+
        printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status);
 
 #define PIS(x) \
@@ -532,16 +564,56 @@ static void dsi_handle_irq_errors(u32 irqstatus, u32 *vcstatus, u32 ciostatus)
        }
 }
 
+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 */
@@ -575,17 +647,13 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
                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);
 
@@ -594,73 +662,262 @@ static irqreturn_t omap_dsi_irq_handler(int irq, void *arg)
        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 */
@@ -802,6 +1059,11 @@ static int dsi_pll_power(enum dsi_pll_power_state state)
 {
        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 */
@@ -1019,6 +1281,9 @@ int dsi_pll_set_clock_div(struct dsi_clock_info *cinfo)
 
        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 =
@@ -1231,7 +1496,6 @@ void dsi_pll_uninit(void)
 
 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;
 
@@ -1240,13 +1504,10 @@ void dsi_dump_clocks(struct seq_file *s)
 
        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);
 
@@ -1985,33 +2246,44 @@ static int dsi_vc_send_bta(int channel)
 
 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);
@@ -2825,19 +3097,20 @@ static void dsi_te_timeout(unsigned long arg)
 }
 #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 */
@@ -2871,7 +3144,7 @@ static void dsi_framedone_timeout_work_callback(struct work_struct *work)
        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);
 
@@ -2911,15 +3184,19 @@ static void dsi_framedone_irq_callback(void *data, u32 mask)
         * 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);
        }
 }
@@ -3383,6 +3660,7 @@ static int dsi_init(struct platform_device *pdev)
        int r, i;
        struct resource *dsi_mem;
 
+       spin_lock_init(&dsi.irq_lock);
        spin_lock_init(&dsi.errors_lock);
        dsi.errors = 0;
 
@@ -3391,8 +3669,6 @@ static int dsi_init(struct platform_device *pdev)
        dsi.irq_stats.last_reset = jiffies;
 #endif
 
-       init_completion(&dsi.bta_completion);
-
        mutex_init(&dsi.lock);
        sema_init(&dsi.bus_lock, 1);