]> 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 a288b466f3fffde6e1f0950661fcafe26bb41e7c..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
  * 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 <linux/err.h>
 #include <linux/interrupt.h>
+#include <linux/clk.h>
 
 #include <asm/io.h>
-#include <asm/hardware/clock.h>
 
 #include <asm/arch/omapfb.h>
 
 #include "dispc.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
@@ -68,6 +64,12 @@ static struct {
        void            (*lcdc_callback)(void *data);
        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;
 
 static inline void rfbi_write_reg(int idx, u32 val)
@@ -80,84 +82,374 @@ static inline u32 rfbi_read_reg(int idx)
        return __raw_readl(rfbi.base + idx);
 }
 
-static int ns_to_l4_ticks(int time)
+static int rfbi_get_clocks(void)
 {
-       unsigned long tick_ps;
-       int ret;
+       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);
+       }
 
-       /* Calculate in picosecs to yield more exact results */
-       tick_ps = 1000000000 / (rfbi.l4_khz);
+       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);
+       }
 
-       ret = (time * 1000 + tick_ps - 1) / tick_ps;
+       return 0;
+}
 
-       return ret * 2;
+static void rfbi_put_clocks(void)
+{
+       clk_put(rfbi.dss1_fck);
+       clk_put(rfbi.dss_ick);
 }
 
-#ifdef OMAPFB_DBG
-static void print_timings(void)
+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;
+       u32 time;
 
-       DBGPRINT(1, "Tick time %lu ps\n", 1000000000 / rfbi.l4_khz);
+       l = rfbi_read_reg(RFBI_CONFIG0);
+       time = 1000000000 / rfbi.l4_khz;
+       if (l & (1 << 4))
+               time *= 2;
+
+       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) {}
 #endif
 
 static void rfbi_set_timings(const struct extif_timings *t)
 {
        u32 l;
-       int on, off;
-
-       on = ns_to_l4_ticks(t->cs_on_time) & 0x0f;
-       l = on;
-       off = ns_to_l4_ticks(t->cs_off_time) & 0x3f;
-       if (off <= on)
-               off = on + 2;
-       l |= off << 4;
-
-       on = ns_to_l4_ticks(t->we_on_time) & 0x0f;
-       l |= on << 10;
-       off = ns_to_l4_ticks(t->we_off_time) & 0x3f;
-       if (off <= on)
-               off = on + 2;
-       l |= off << 14;
-
-       l |= (ns_to_l4_ticks(t->re_on_time) & 0x0f) << 20;
-       l |= (ns_to_l4_ticks(t->re_off_time) & 0x3f) << 24;
-       rfbi_write_reg(RFBI_ONOFF_TIME0, l);
-
-       l = ns_to_l4_ticks(t->we_cycle_time) & 0x3f;
-       l |= (ns_to_l4_ticks(t->re_cycle_time) & 0x3f) << 6;
-       l |= (ns_to_l4_ticks(t->cs_pulse_width) & 0x3f) << 12;
-       l |= (ns_to_l4_ticks(t->access_time) & 0x3f) << 22;
-       rfbi_write_reg(RFBI_CYCLE_TIME0, l);
+
+       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]);
+
+       l = rfbi_read_reg(RFBI_CONFIG0);
+       l &= ~(1 << 4);
+       l |= (t->tim[2] ? 1 : 0) << 4;
+       rfbi_write_reg(RFBI_CONFIG0, l);
+
+       rfbi_print_timings();
+       rfbi_enable_clocks(0);
 }
 
-static void rfbi_write_command(u32 cmd)
+static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div)
 {
-       rfbi_write_reg(RFBI_CMD, cmd);
+       *clk_period = 1000000000 / rfbi.l4_khz;
+       *max_clk_div = 2;
 }
 
-static u32 rfbi_read_data(void)
+static int ps_to_rfbi_ticks(int time, int div)
 {
-       u32 val;
+       unsigned long tick_ps;
+       int ret;
+
+       /* Calculate in picosecs to yield more exact results */
+       tick_ps = 1000000000 / (rfbi.l4_khz) * div;
+
+       ret = (time + tick_ps - 1) / tick_ps;
 
-       rfbi_write_reg(RFBI_READ, 0);
-       val = rfbi_read_reg(RFBI_READ);
-       return val;
+       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 void rfbi_write_data(u32 val)
+
+static int rfbi_convert_timings(struct extif_timings *t)
 {
-       rfbi_write_reg(RFBI_PARAM, val);
+       u32 l;
+       int reon, reoff, weon, weoff, cson, csoff, cs_pulse;
+       int actim, recyc, wecyc;
+       int div = t->clk_div;
+
+       if (div <= 0 || div > 2)
+               return -1;
+
+       /* Make sure that after conversion it still holds that:
+        * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff,
+        * csoff > cson, csoff >= max(weoff, reoff), actim > reon
+        */
+       weon = ps_to_rfbi_ticks(t->we_on_time, div);
+       weoff = ps_to_rfbi_ticks(t->we_off_time, div);
+       if (weoff <= weon)
+               weoff = weon + 1;
+       if (weon > 0x0f)
+               return -1;
+       if (weoff > 0x3f)
+               return -1;
+
+       reon = ps_to_rfbi_ticks(t->re_on_time, div);
+       reoff = ps_to_rfbi_ticks(t->re_off_time, div);
+       if (reoff <= reon)
+               reoff = reon + 1;
+       if (reon > 0x0f)
+               return -1;
+       if (reoff > 0x3f)
+               return -1;
+
+       cson = ps_to_rfbi_ticks(t->cs_on_time, div);
+       csoff = ps_to_rfbi_ticks(t->cs_off_time, div);
+       if (csoff <= cson)
+               csoff = cson + 1;
+       if (csoff < max(weoff, reoff))
+               csoff = max(weoff, reoff);
+       if (cson > 0x0f)
+               return -1;
+       if (csoff > 0x3f)
+               return -1;
+
+       l =  cson;
+       l |= csoff << 4;
+       l |= weon  << 10;
+       l |= weoff << 14;
+       l |= reon  << 20;
+       l |= reoff << 24;
+
+       t->tim[0] = l;
+
+       actim = ps_to_rfbi_ticks(t->access_time, div);
+       if (actim <= reon)
+               actim = reon + 1;
+       if (actim > 0x3f)
+               return -1;
+
+       wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div);
+       if (wecyc < weoff)
+               wecyc = weoff;
+       if (wecyc > 0x3f)
+               return -1;
+
+       recyc = ps_to_rfbi_ticks(t->re_cycle_time, div);
+       if (recyc < reoff)
+               recyc = reoff;
+       if (recyc > 0x3f)
+               return -1;
+
+       cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div);
+       if (cs_pulse > 0x3f)
+               return -1;
+
+       l =  wecyc;
+       l |= recyc    << 6;
+       l |= cs_pulse << 12;
+       l |= actim    << 22;
+
+       t->tim[1] = l;
+
+       t->tim[2] = div - 1;
+
+       t->converted = 1;
+
+       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);
+               for (; len; len -= 2)
+                       rfbi_write_reg(RFBI_CMD, *w++);
+       } else {
+               const u8 *b = buf;
+               BUG_ON(rfbi.bits_per_cycle != 8);
+               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);
+               for (; len; len -= 2) {
+                       rfbi_write_reg(RFBI_READ, 0);
+                       *w++ = rfbi_read_reg(RFBI_READ);
+               }
+       } else {
+               u8 *b = buf;
+               BUG_ON(rfbi.bits_per_cycle != 8);
+               for (; len; len--) {
+                       rfbi_write_reg(RFBI_READ, 0);
+                       *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);
+               for (; len; len -= 2)
+                       rfbi_write_reg(RFBI_PARAM, *w++);
+       } else {
+               const u8 *b = buf;
+               BUG_ON(rfbi.bits_per_cycle != 8);
+               for (; len; len--)
+                       rfbi_write_reg(RFBI_PARAM, *b++);
+       }
+       rfbi_enable_clocks(0);
 }
 
 static void rfbi_transfer_area(int width, int height,
@@ -167,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;
@@ -175,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);
 }
@@ -187,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)
@@ -195,25 +491,41 @@ static void rfbi_dma_callback(void *data)
        rfbi.lcdc_callback(rfbi.lcdc_callback_data);
 }
 
-static int rfbi_init(void)
+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)
+       {
+       case 8:
+               break;
+       case 16:
+               l |= 3;
+               break;
+       default:
+               BUG();
+       }
+       rfbi_write_reg(RFBI_CONFIG0, l);
+       rfbi.bits_per_cycle = bpc;
+       rfbi_enable_clocks(0);
+}
+
+static int rfbi_init(struct omapfb_device *fbdev)
 {
        u32 l;
        int r;
-       struct clk *dss_ick;
 
-       memset(&rfbi, 0, sizeof(rfbi));
+       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);
+       if ((r = rfbi_get_clocks()) < 0)
+               return r;
+       rfbi_enable_clocks(1);
 
-       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);
-       }
-       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);
@@ -229,36 +541,49 @@ static int rfbi_init(void)
        l |= (0 << 9) | (1 << 20) | (1 << 21);
        rfbi_write_reg(RFBI_CONFIG0, l);
 
-       l = 0x10;
-       rfbi_write_reg(RFBI_DATA_CYCLE1_0, l);
-       rfbi_write_reg(RFBI_DATA_CYCLE2_0, l);
-       rfbi_write_reg(RFBI_DATA_CYCLE3_0, l);
+       rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010);
 
        l = rfbi_read_reg(RFBI_CONTROL);
-       /* Select CS0 */
+       /* Select CS0, clear bypass mode */
        l = (0x01 << 2);
        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,
        .write_command          = rfbi_write_command,
        .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,
 };