]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/video/omap/rfbi.c
ARM: OMAP: Frambuffer driver CodingStyle changes.
[linux-2.6-omap-h63xx.git] / drivers / video / omap / rfbi.c
index 82415f50a6961d2fb5ff0b438f998a4f60356a3e..316b08916f7f4fb8945291ca4a2f6a3490a7b028 100644 (file)
@@ -1,6 +1,4 @@
 /*
- * File: drivers/video/omap/omap2/rfbi.c
- *
  * OMAP2 Remote Frame Buffer Interface support
  *
  * Copyright (C) 2005 Nokia Corporation
@@ -21,7 +19,6 @@
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
-
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 
 #include "dispc.h"
 
-/* #define OMAPFB_DBG 1 */
-
-#include "debug.h"
-
-#define MODULE_NAME "omapfb-rfbi"
-
-#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args)
+/* To work around an RFBI transfer rate limitation */
+#define OMAP_RFBI_RATE_LIMIT   1
 
 #define RFBI_BASE              0x48050800
 #define RFBI_REVISION          0x0000
@@ -73,10 +65,13 @@ static struct {
        void            *lcdc_callback_data;
        unsigned long   l4_khz;
        int             bits_per_cycle;
+       struct omapfb_device *fbdev;
+       struct clk      *dss_ick;
+       struct clk      *dss1_fck;
+       unsigned        tearsync_pin_cnt;
+       unsigned        tearsync_mode;
 } rfbi;
 
-struct lcd_ctrl_extif rfbi_extif;
-
 static inline void rfbi_write_reg(int idx, u32 val)
 {
        __raw_writel(val, rfbi.base + idx);
@@ -87,7 +82,41 @@ static inline u32 rfbi_read_reg(int idx)
        return __raw_readl(rfbi.base + idx);
 }
 
-#ifdef OMAPFB_DBG
+static int rfbi_get_clocks(void)
+{
+       if (IS_ERR((rfbi.dss_ick = clk_get(rfbi.fbdev->dev, "dss_ick")))) {
+               dev_err(rfbi.fbdev->dev, "can't get dss_ick");
+               return PTR_ERR(rfbi.dss_ick);
+       }
+
+       if (IS_ERR((rfbi.dss1_fck = clk_get(rfbi.fbdev->dev, "dss1_fck")))) {
+               dev_err(rfbi.fbdev->dev, "can't get dss1_fck");
+               clk_put(rfbi.dss_ick);
+               return PTR_ERR(rfbi.dss1_fck);
+       }
+
+       return 0;
+}
+
+static void rfbi_put_clocks(void)
+{
+       clk_put(rfbi.dss1_fck);
+       clk_put(rfbi.dss_ick);
+}
+
+static void rfbi_enable_clocks(int enable)
+{
+       if (enable) {
+               clk_enable(rfbi.dss_ick);
+               clk_enable(rfbi.dss1_fck);
+       } else {
+               clk_disable(rfbi.dss1_fck);
+               clk_disable(rfbi.dss_ick);
+       }
+}
+
+
+#ifdef VERBOSE
 static void rfbi_print_timings(void)
 {
        u32 l;
@@ -98,16 +127,20 @@ static void rfbi_print_timings(void)
        if (l & (1 << 4))
                time *= 2;
 
-       DBGPRINT(1, "Tick time %u ps\n", time);
+       dev_dbg(rfbi.fbdev->dev, "Tick time %u ps\n", time);
        l = rfbi_read_reg(RFBI_ONOFF_TIME0);
-       DBGPRINT(1, "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, "
-              "REONTIME %d, REOFFTIME %d\n",
-              l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f,
-              (l >> 20) & 0x0f, (l >> 24) & 0x3f);
+       dev_dbg(rfbi.fbdev->dev,
+               "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, "
+               "REONTIME %d, REOFFTIME %d\n",
+               l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f,
+               (l >> 20) & 0x0f, (l >> 24) & 0x3f);
+
        l = rfbi_read_reg(RFBI_CYCLE_TIME0);
-       DBGPRINT(1, "WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, "
-              "ACCESSTIME %d\n",
-              (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, (l >> 22) & 0x3f);
+       dev_dbg(rfbi.fbdev->dev,
+               "WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, "
+               "ACCESSTIME %d\n",
+               (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f,
+               (l >> 22) & 0x3f);
 }
 #else
 static void rfbi_print_timings(void) {}
@@ -119,6 +152,7 @@ static void rfbi_set_timings(const struct extif_timings *t)
 
        BUG_ON(!t->converted);
 
+       rfbi_enable_clocks(1);
        rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]);
        rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]);
 
@@ -128,6 +162,7 @@ static void rfbi_set_timings(const struct extif_timings *t)
        rfbi_write_reg(RFBI_CONFIG0, l);
 
        rfbi_print_timings();
+       rfbi_enable_clocks(0);
 }
 
 static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
@@ -149,6 +184,61 @@ static int ps_to_rfbi_ticks(int time, int div)
        return ret;
 }
 
+#ifdef OMAP_RFBI_RATE_LIMIT
+static unsigned long rfbi_get_max_tx_rate(void)
+{
+       unsigned long   l4_rate, dss1_rate;
+       int             min_l4_ticks = 0;
+       int             i;
+
+       /* According to TI this can't be calculated so make the
+        * adjustments for a couple of known frequencies and warn for
+        * others.
+        */
+       static const struct {
+               unsigned long l4_clk;           /* HZ */
+               unsigned long dss1_clk;         /* HZ */
+               unsigned long min_l4_ticks;
+       } ftab[] = {
+               { 55,   132,    7, },           /* 7.86 MPix/s */
+               { 110,  110,    12, },          /* 9.16 MPix/s */
+               { 110,  132,    10, },          /* 11   Mpix/s */
+               { 120,  120,    10, },          /* 12   Mpix/s */
+               { 133,  133,    10, },          /* 13.3 Mpix/s */
+       };
+
+       l4_rate = rfbi.l4_khz / 1000;
+       dss1_rate = clk_get_rate(rfbi.dss1_fck) / 1000000;
+
+       for (i = 0; i < ARRAY_SIZE(ftab); i++) {
+               /* Use a window instead of an exact match, to account
+                * for different DPLL multiplier / divider pairs.
+                */
+               if (abs(ftab[i].l4_clk - l4_rate) < 3 &&
+                   abs(ftab[i].dss1_clk - dss1_rate) < 3) {
+                       min_l4_ticks = ftab[i].min_l4_ticks;
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(ftab)) {
+               /* Can't be sure, return anyway the maximum not
+                * rate-limited. This might cause a problem only for the
+                * tearing synchronisation.
+                */
+               dev_err(rfbi.fbdev->dev,
+                       "can't determine maximum RFBI transfer rate\n");
+               return rfbi.l4_khz * 1000;
+       }
+       return rfbi.l4_khz * 1000 / min_l4_ticks;
+}
+#else
+static int rfbi_get_max_tx_rate(void)
+{
+       return rfbi.l4_khz * 1000;
+}
+#endif
+
+
 static int rfbi_convert_timings(struct extif_timings *t)
 {
        u32 l;
@@ -237,8 +327,79 @@ static int rfbi_convert_timings(struct extif_timings *t)
        return 0;
 }
 
+static int rfbi_setup_tearsync(unsigned pin_cnt,
+                              unsigned hs_pulse_time, unsigned vs_pulse_time,
+                              int hs_pol_inv, int vs_pol_inv, int extif_div)
+{
+       int hs, vs;
+       int min;
+       u32 l;
+
+       if (pin_cnt != 1 && pin_cnt != 2)
+               return -EINVAL;
+
+       hs = ps_to_rfbi_ticks(hs_pulse_time, 1);
+       vs = ps_to_rfbi_ticks(vs_pulse_time, 1);
+       if (hs < 2)
+               return -EDOM;
+       if (pin_cnt == 2)
+               min = 2;
+       else
+               min = 4;
+       if (vs < min)
+               return -EDOM;
+       if (vs == hs)
+               return -EINVAL;
+       rfbi.tearsync_pin_cnt = pin_cnt;
+       dev_dbg(rfbi.fbdev->dev,
+               "setup_tearsync: pins %d hs %d vs %d hs_inv %d vs_inv %d\n",
+               pin_cnt, hs, vs, hs_pol_inv, vs_pol_inv);
+
+       rfbi_enable_clocks(1);
+       rfbi_write_reg(RFBI_HSYNC_WIDTH, hs);
+       rfbi_write_reg(RFBI_VSYNC_WIDTH, vs);
+
+       l = rfbi_read_reg(RFBI_CONFIG0);
+       if (hs_pol_inv)
+               l &= ~(1 << 21);
+       else
+               l |= 1 << 21;
+       if (vs_pol_inv)
+               l &= ~(1 << 20);
+       else
+               l |= 1 << 20;
+       rfbi_enable_clocks(0);
+
+       return 0;
+}
+
+static int rfbi_enable_tearsync(int enable, unsigned line)
+{
+       u32 l;
+
+       dev_dbg(rfbi.fbdev->dev, "tearsync %d line %d mode %d\n",
+               enable, line, rfbi.tearsync_mode);
+       if (line > (1 << 11) - 1)
+               return -EINVAL;
+
+       rfbi_enable_clocks(1);
+       l = rfbi_read_reg(RFBI_CONFIG0);
+       l &= ~(0x3 << 2);
+       if (enable) {
+               rfbi.tearsync_mode = rfbi.tearsync_pin_cnt;
+               l |= rfbi.tearsync_mode << 2;
+       } else
+               rfbi.tearsync_mode = 0;
+       rfbi_write_reg(RFBI_CONFIG0, l);
+       rfbi_write_reg(RFBI_LINE_NUMBER, line);
+       rfbi_enable_clocks(0);
+
+       return 0;
+}
+
 static void rfbi_write_command(const void *buf, unsigned int len)
 {
+       rfbi_enable_clocks(1);
        if (rfbi.bits_per_cycle == 16) {
                const u16 *w = buf;
                BUG_ON(len & 1);
@@ -250,10 +411,12 @@ static void rfbi_write_command(const void *buf, unsigned int len)
                for (; len; len--)
                        rfbi_write_reg(RFBI_CMD, *b++);
        }
+       rfbi_enable_clocks(0);
 }
 
 static void rfbi_read_data(void *buf, unsigned int len)
 {
+       rfbi_enable_clocks(1);
        if (rfbi.bits_per_cycle == 16) {
                u16 *w = buf;
                BUG_ON(len & ~1);
@@ -269,10 +432,12 @@ static void rfbi_read_data(void *buf, unsigned int len)
                        *b++ = rfbi_read_reg(RFBI_READ);
                }
        }
+       rfbi_enable_clocks(0);
 }
 
 static void rfbi_write_data(const void *buf, unsigned int len)
 {
+       rfbi_enable_clocks(1);
        if (rfbi.bits_per_cycle == 16) {
                const u16 *w = buf;
                BUG_ON(len & 1);
@@ -284,6 +449,7 @@ static void rfbi_write_data(const void *buf, unsigned int len)
                for (; len; len--)
                        rfbi_write_reg(RFBI_PARAM, *b++);
        }
+       rfbi_enable_clocks(0);
 }
 
 static void rfbi_transfer_area(int width, int height,
@@ -293,6 +459,7 @@ static void rfbi_transfer_area(int width, int height,
 
        BUG_ON(callback == NULL);
 
+       rfbi_enable_clocks(1);
        omap_dispc_set_lcd_size(width, height);
 
        rfbi.lcdc_callback = callback;
@@ -301,8 +468,10 @@ static void rfbi_transfer_area(int width, int height,
        rfbi_write_reg(RFBI_PIXEL_CNT, width * height);
 
        w = rfbi_read_reg(RFBI_CONTROL);
-       /* Enable, Internal trigger */
-       rfbi_write_reg(RFBI_CONTROL, w | (1 << 0) | (1 << 4));
+       w |= 1;                         /* enable */
+       if (!rfbi.tearsync_mode)
+               w |= 1 << 4;            /* internal trigger, reset by HW */
+       rfbi_write_reg(RFBI_CONTROL, w);
 
        omap_dispc_enable_lcd_out(1);
 }
@@ -313,6 +482,7 @@ static inline void _stop_transfer(void)
 
        w = rfbi_read_reg(RFBI_CONTROL);
        rfbi_write_reg(RFBI_CONTROL, w & ~(1 << 0));
+       rfbi_enable_clocks(0);
 }
 
 static void rfbi_dma_callback(void *data)
@@ -325,6 +495,7 @@ static void rfbi_set_bits_per_cycle(int bpc)
 {
        u32 l;
 
+       rfbi_enable_clocks(1);
        l = rfbi_read_reg(RFBI_CONFIG0);
        l &= ~(0x03 << 0);
        switch (bpc)
@@ -339,27 +510,22 @@ static void rfbi_set_bits_per_cycle(int bpc)
        }
        rfbi_write_reg(RFBI_CONFIG0, l);
        rfbi.bits_per_cycle = bpc;
+       rfbi_enable_clocks(0);
 }
 
-static int rfbi_init(void)
+static int rfbi_init(struct omapfb_device *fbdev)
 {
        u32 l;
        int r;
-       struct clk *dss_ick;
 
+       rfbi.fbdev = fbdev;
        rfbi.base = io_p2v(RFBI_BASE);
 
-       l = rfbi_read_reg(RFBI_REVISION);
-       pr_info(MODULE_NAME ": version %d.%d\n", (l >> 4) & 0x0f, l & 0x0f);
-
-       dss_ick = clk_get(NULL, "dss_ick");
-       if (IS_ERR(dss_ick)) {
-               pr_err("can't get dss_ick\n");
-               return PTR_ERR(dss_ick);
-       }
+       if ((r = rfbi_get_clocks()) < 0)
+               return r;
+       rfbi_enable_clocks(1);
 
-       rfbi.l4_khz = clk_get_rate(dss_ick) / 1000;
-       clk_put(dss_ick);
+       rfbi.l4_khz = clk_get_rate(rfbi.dss_ick) / 1000;
 
        /* Reset */
        rfbi_write_reg(RFBI_SYSCONFIG, 1 << 1);
@@ -383,22 +549,31 @@ static int rfbi_init(void)
        rfbi_write_reg(RFBI_CONTROL, l);
 
        if ((r = omap_dispc_request_irq(rfbi_dma_callback, NULL)) < 0) {
-               pr_err("can't get DISPC irq\n");
+               dev_err(fbdev->dev, "can't get DISPC irq\n");
+               rfbi_enable_clocks(0);
                return r;
        }
 
+       l = rfbi_read_reg(RFBI_REVISION);
+       pr_info("omapfb: RFBI version %d.%d initialized\n",
+               (l >> 4) & 0x0f, l & 0x0f);
+
+       rfbi_enable_clocks(0);
+
        return 0;
 }
 
 static void rfbi_cleanup(void)
 {
        omap_dispc_free_irq();
+       rfbi_put_clocks();
 }
 
-struct lcd_ctrl_extif rfbi_extif = {
+const struct lcd_ctrl_extif omap2_ext_if = {
        .init                   = rfbi_init,
        .cleanup                = rfbi_cleanup,
        .get_clk_info           = rfbi_get_clk_info,
+       .get_max_tx_rate        = rfbi_get_max_tx_rate,
        .set_bits_per_cycle     = rfbi_set_bits_per_cycle,
        .convert_timings        = rfbi_convert_timings,
        .set_timings            = rfbi_set_timings,
@@ -406,6 +581,9 @@ struct lcd_ctrl_extif rfbi_extif = {
        .read_data              = rfbi_read_data,
        .write_data             = rfbi_write_data,
        .transfer_area          = rfbi_transfer_area,
+       .setup_tearsync         = rfbi_setup_tearsync,
+       .enable_tearsync        = rfbi_enable_tearsync,
+
        .max_transmit_size      = (u32)~0,
 };